001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.formats.jpeg.iptc; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.commons.imaging.ImageReadException; 030import org.apache.commons.imaging.ImageWriteException; 031import org.apache.commons.imaging.common.bytesource.ByteSource; 032import org.apache.commons.imaging.common.bytesource.ByteSourceArray; 033import org.apache.commons.imaging.common.bytesource.ByteSourceFile; 034import org.apache.commons.imaging.common.bytesource.ByteSourceInputStream; 035import org.apache.commons.imaging.formats.jpeg.JpegConstants; 036import org.apache.commons.imaging.formats.jpeg.xmp.JpegRewriter; 037 038/** 039 * Interface for Exif write/update/remove functionality for Jpeg/JFIF images. 040 */ 041public class JpegIptcRewriter extends JpegRewriter { 042 043 /** 044 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 045 * leaves the other data in that segment (if present) unchanged and writes 046 * the result to a stream. 047 * <p> 048 * 049 * @param src 050 * Image file. 051 * @param os 052 * OutputStream to write the image to. 053 * 054 * @see java.io.File 055 * @see java.io.OutputStream 056 */ 057 public void removeIPTC(final File src, final OutputStream os) 058 throws ImageReadException, IOException, ImageWriteException { 059 removeIPTC(src, os, false); 060 } 061 062 /** 063 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 064 * leaves the other data in that segment (if present) unchanged (unless 065 * removeSegment is true) and writes the result to a stream. 066 * <p> 067 * 068 * @param src 069 * Image file. 070 * @param os 071 * OutputStream to write the image to. 072 * @param removeSegment 073 * Remove the App13 segment. 074 * 075 * @see java.io.File 076 * @see java.io.OutputStream 077 */ 078 public void removeIPTC(final File src, final OutputStream os, final boolean removeSegment) 079 throws ImageReadException, IOException, ImageWriteException { 080 final ByteSource byteSource = new ByteSourceFile(src); 081 removeIPTC(byteSource, os, removeSegment); 082 } 083 084 /** 085 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 086 * leaves the other data in that segment (if present) unchanged and writes 087 * the result to a stream. 088 * <p> 089 * 090 * @param src 091 * Byte array containing Jpeg image data. 092 * @param os 093 * OutputStream to write the image to. 094 */ 095 public void removeIPTC(final byte[] src, final OutputStream os) 096 throws ImageReadException, IOException, ImageWriteException { 097 removeIPTC(src, os, false); 098 } 099 100 /** 101 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 102 * leaves the other data in that segment (if present) unchanged (unless 103 * removeSegment is true) and writes the result to a stream. 104 * <p> 105 * 106 * @param src 107 * Byte array containing Jpeg image data. 108 * @param os 109 * OutputStream to write the image to. 110 * @param removeSegment 111 * Remove the App13 segment. 112 */ 113 public void removeIPTC(final byte[] src, final OutputStream os, final boolean removeSegment) 114 throws ImageReadException, IOException, ImageWriteException { 115 final ByteSource byteSource = new ByteSourceArray(src); 116 removeIPTC(byteSource, os, removeSegment); 117 } 118 119 /** 120 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 121 * leaves the other data in that segment (if present) unchanged and writes 122 * the result to a stream. 123 * <p> 124 * 125 * @param src 126 * InputStream containing Jpeg image data. 127 * @param os 128 * OutputStream to write the image to. 129 */ 130 public void removeIPTC(final InputStream src, final OutputStream os) 131 throws ImageReadException, IOException, ImageWriteException { 132 removeIPTC(src, os, false); 133 } 134 135 /** 136 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 137 * leaves the other data in that segment (if present) unchanged (unless 138 * removeSegment is true) and writes the result to a stream. 139 * <p> 140 * 141 * @param src 142 * InputStream containing Jpeg image data. 143 * @param os 144 * OutputStream to write the image to. 145 * @param removeSegment 146 * Remove the App13 segment. 147 */ 148 public void removeIPTC(final InputStream src, final OutputStream os, final boolean removeSegment) 149 throws ImageReadException, IOException, ImageWriteException { 150 final ByteSource byteSource = new ByteSourceInputStream(src, null); 151 removeIPTC(byteSource, os, removeSegment); 152 } 153 154 /** 155 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 156 * leaves the other data in that segment (if present) unchanged and writes 157 * the result to a stream. 158 * <p> 159 * 160 * @param byteSource 161 * ByteSource containing Jpeg image data. 162 * @param os 163 * OutputStream to write the image to. 164 */ 165 public void removeIPTC(final ByteSource byteSource, final OutputStream os) 166 throws ImageReadException, IOException, ImageWriteException { 167 removeIPTC(byteSource, os, false); 168 } 169 170 /** 171 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 172 * leaves the other data in that segment (if present) unchanged (unless 173 * removeSegment is true) and writes the result to a stream. 174 * <p> 175 * 176 * @param byteSource 177 * ByteSource containing Jpeg image data. 178 * @param os 179 * OutputStream to write the image to. 180 * @param removeSegment 181 * Remove the App13 segment. 182 */ 183 public void removeIPTC(final ByteSource byteSource, final OutputStream os, final boolean removeSegment) 184 throws ImageReadException, IOException, ImageWriteException { 185 final JFIFPieces jfifPieces = analyzeJFIF(byteSource); 186 final List<JFIFPiece> oldPieces = jfifPieces.pieces; 187 final List<JFIFPiece> photoshopApp13Segments = findPhotoshopApp13Segments(oldPieces); 188 189 if (photoshopApp13Segments.size() > 1) { 190 throw new ImageReadException( 191 "Image contains more than one Photoshop App13 segment."); 192 } 193 final List<JFIFPiece> newPieces = removePhotoshopApp13Segments(oldPieces); 194 if (!removeSegment && photoshopApp13Segments.size() == 1) { 195 final JFIFPieceSegment oldSegment = (JFIFPieceSegment) photoshopApp13Segments.get(0); 196 final Map<String, Object> params = new HashMap<>(); 197 final PhotoshopApp13Data oldData = new IptcParser().parsePhotoshopSegment(oldSegment.getSegmentData(), params); 198 final List<IptcBlock> newBlocks = oldData.getNonIptcBlocks(); 199 final List<IptcRecord> newRecords = new ArrayList<>(); 200 final PhotoshopApp13Data newData = new PhotoshopApp13Data(newRecords, 201 newBlocks); 202 final byte[] segmentBytes = new IptcParser().writePhotoshopApp13Segment(newData); 203 final JFIFPieceSegment newSegment = new JFIFPieceSegment( 204 oldSegment.marker, segmentBytes); 205 newPieces.add(oldPieces.indexOf(oldSegment), newSegment); 206 } 207 writeSegments(os, newPieces); 208 } 209 210 /** 211 * Reads a Jpeg image, replaces the IPTC data in the App13 segment but 212 * leaves the other data in that segment (if present) unchanged and writes 213 * the result to a stream. 214 * 215 * @param src 216 * Byte array containing Jpeg image data. 217 * @param os 218 * OutputStream to write the image to. 219 * @param newData 220 * structure containing IPTC data. 221 */ 222 public void writeIPTC(final byte[] src, final OutputStream os, 223 final PhotoshopApp13Data newData) throws ImageReadException, IOException, 224 ImageWriteException { 225 final ByteSource byteSource = new ByteSourceArray(src); 226 writeIPTC(byteSource, os, newData); 227 } 228 229 /** 230 * Reads a Jpeg image, replaces the IPTC data in the App13 segment but 231 * leaves the other data in that segment (if present) unchanged and writes 232 * the result to a stream. 233 * 234 * @param src 235 * InputStream containing Jpeg image data. 236 * @param os 237 * OutputStream to write the image to. 238 * @param newData 239 * structure containing IPTC data. 240 */ 241 public void writeIPTC(final InputStream src, final OutputStream os, 242 final PhotoshopApp13Data newData) throws ImageReadException, IOException, 243 ImageWriteException { 244 final ByteSource byteSource = new ByteSourceInputStream(src, null); 245 writeIPTC(byteSource, os, newData); 246 } 247 248 /** 249 * Reads a Jpeg image, replaces the IPTC data in the App13 segment but 250 * leaves the other data in that segment (if present) unchanged and writes 251 * the result to a stream. 252 * 253 * @param src 254 * Image file. 255 * @param os 256 * OutputStream to write the image to. 257 * @param newData 258 * structure containing IPTC data. 259 */ 260 public void writeIPTC(final File src, final OutputStream os, final PhotoshopApp13Data newData) 261 throws ImageReadException, IOException, ImageWriteException { 262 final ByteSource byteSource = new ByteSourceFile(src); 263 writeIPTC(byteSource, os, newData); 264 } 265 266 /** 267 * Reads a Jpeg image, replaces the IPTC data in the App13 segment but 268 * leaves the other data in that segment (if present) unchanged and writes 269 * the result to a stream. 270 * 271 * @param byteSource 272 * ByteSource containing Jpeg image data. 273 * @param os 274 * OutputStream to write the image to. 275 * @param newData 276 * structure containing IPTC data. 277 */ 278 public void writeIPTC(final ByteSource byteSource, final OutputStream os, 279 PhotoshopApp13Data newData) throws ImageReadException, IOException, 280 ImageWriteException { 281 final JFIFPieces jfifPieces = analyzeJFIF(byteSource); 282 final List<JFIFPiece> oldPieces = jfifPieces.pieces; 283 final List<JFIFPiece> photoshopApp13Segments = findPhotoshopApp13Segments(oldPieces); 284 285 if (photoshopApp13Segments.size() > 1) { 286 throw new ImageReadException( 287 "Image contains more than one Photoshop App13 segment."); 288 } 289 List<JFIFPiece> newPieces = removePhotoshopApp13Segments(oldPieces); 290 291 { 292 // discard old iptc blocks. 293 final List<IptcBlock> newBlocks = newData.getNonIptcBlocks(); 294 final byte[] newBlockBytes = new IptcParser().writeIPTCBlock(newData.getRecords()); 295 296 final int blockType = IptcConstants.IMAGE_RESOURCE_BLOCK_IPTC_DATA; 297 final byte[] blockNameBytes = new byte[0]; 298 final IptcBlock newBlock = new IptcBlock(blockType, blockNameBytes, newBlockBytes); 299 newBlocks.add(newBlock); 300 301 newData = new PhotoshopApp13Data(newData.getRecords(), newBlocks); 302 303 final byte[] segmentBytes = new IptcParser().writePhotoshopApp13Segment(newData); 304 final JFIFPieceSegment newSegment = new JFIFPieceSegment(JpegConstants.JPEG_APP13_MARKER, segmentBytes); 305 306 newPieces = insertAfterLastAppSegments(newPieces, Arrays.asList(newSegment)); 307 } 308 309 writeSegments(os, newPieces); 310 } 311 312}