1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.imaging.formats.jpeg.iptc; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.io.OutputStream; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.List; 26 27 import org.apache.commons.imaging.ImageReadException; 28 import org.apache.commons.imaging.ImageWriteException; 29 import org.apache.commons.imaging.ImagingConstants; 30 import org.apache.commons.imaging.common.bytesource.ByteSource; 31 import org.apache.commons.imaging.common.bytesource.ByteSourceArray; 32 import org.apache.commons.imaging.common.bytesource.ByteSourceFile; 33 import org.apache.commons.imaging.common.bytesource.ByteSourceInputStream; 34 import org.apache.commons.imaging.formats.jpeg.JpegConstants; 35 import org.apache.commons.imaging.formats.jpeg.JpegImagingParameters; 36 import org.apache.commons.imaging.formats.jpeg.xmp.JpegRewriter; 37 38 /** 39 * Interface for Exif write/update/remove functionality for Jpeg/JFIF images. 40 */ 41 public class JpegIptcRewriter extends JpegRewriter { 42 43 /** 44 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 45 * leaves the other data in that segment (if present) unchanged and writes 46 * the result to a stream. 47 * <p> 48 * 49 * @param src 50 * Image file. 51 * @param os 52 * OutputStream to write the image to. 53 * 54 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 55 * the Photoshop segment cannot be parsed 56 * @throws IOException if it fails to read from the origin byte source, or to write to the 57 * target byte source 58 * @throws ImageWriteException if it fails to write the target image 59 * @see java.io.File 60 * @see java.io.OutputStream 61 */ 62 public void removeIPTC(final File src, final OutputStream os) 63 throws ImageReadException, IOException, ImageWriteException { 64 removeIPTC(src, os, false); 65 } 66 67 /** 68 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 69 * leaves the other data in that segment (if present) unchanged (unless 70 * removeSegment is true) and writes the result to a stream. 71 * <p> 72 * 73 * @param src 74 * Image file. 75 * @param os 76 * OutputStream to write the image to. 77 * @param removeSegment 78 * Remove the App13 segment. 79 * 80 * @see java.io.File 81 * @see java.io.OutputStream 82 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 83 * the Photoshop segment cannot be parsed 84 * @throws IOException if it fails to read from the origin byte source, or to write to the 85 * target byte source 86 * @throws ImageWriteException if it fails to write the target image 87 */ 88 public void removeIPTC(final File src, final OutputStream os, final boolean removeSegment) 89 throws ImageReadException, IOException, ImageWriteException { 90 final ByteSource byteSource = new ByteSourceFile(src); 91 removeIPTC(byteSource, os, removeSegment); 92 } 93 94 /** 95 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 96 * leaves the other data in that segment (if present) unchanged and writes 97 * the result to a stream. 98 * <p> 99 * 100 * @param src 101 * Byte array containing Jpeg image data. 102 * @param os 103 * OutputStream to write the image to. 104 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 105 * the Photoshop segment cannot be parsed 106 * @throws IOException if it fails to read from the origin byte source, or to write to the 107 * target byte source 108 * @throws ImageWriteException if it fails to write the target image 109 */ 110 public void removeIPTC(final byte[] src, final OutputStream os) 111 throws ImageReadException, IOException, ImageWriteException { 112 removeIPTC(src, os, false); 113 } 114 115 /** 116 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 117 * leaves the other data in that segment (if present) unchanged (unless 118 * removeSegment is true) and writes the result to a stream. 119 * <p> 120 * 121 * @param src 122 * Byte array containing Jpeg image data. 123 * @param os 124 * OutputStream to write the image to. 125 * @param removeSegment 126 * Remove the App13 segment. 127 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 128 * the Photoshop segment cannot be parsed 129 * @throws IOException if it fails to read from the origin byte source, or to write to the 130 * target byte source 131 * @throws ImageWriteException if it fails to write the target image 132 */ 133 public void removeIPTC(final byte[] src, final OutputStream os, final boolean removeSegment) 134 throws ImageReadException, IOException, ImageWriteException { 135 final ByteSource byteSource = new ByteSourceArray(src); 136 removeIPTC(byteSource, os, removeSegment); 137 } 138 139 /** 140 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 141 * leaves the other data in that segment (if present) unchanged and writes 142 * the result to a stream. 143 * <p> 144 * 145 * @param src 146 * InputStream containing Jpeg image data. 147 * @param os 148 * OutputStream to write the image to. 149 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 150 * the Photoshop segment cannot be parsed 151 * @throws IOException if it fails to read from the origin byte source, or to write to the 152 * target byte source 153 * @throws ImageWriteException if it fails to write the target image 154 */ 155 public void removeIPTC(final InputStream src, final OutputStream os) 156 throws ImageReadException, IOException, ImageWriteException { 157 removeIPTC(src, os, false); 158 } 159 160 /** 161 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 162 * leaves the other data in that segment (if present) unchanged (unless 163 * removeSegment is true) and writes the result to a stream. 164 * <p> 165 * 166 * @param src 167 * InputStream containing Jpeg image data. 168 * @param os 169 * OutputStream to write the image to. 170 * @param removeSegment 171 * Remove the App13 segment. 172 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 173 * the Photoshop segment cannot be parsed 174 * @throws IOException if it fails to read from the origin byte source, or to write to the 175 * target byte source 176 * @throws ImageWriteException if it fails to write the target image 177 */ 178 public void removeIPTC(final InputStream src, final OutputStream os, final boolean removeSegment) 179 throws ImageReadException, IOException, ImageWriteException { 180 final ByteSource byteSource = new ByteSourceInputStream(src, null); 181 removeIPTC(byteSource, os, removeSegment); 182 } 183 184 /** 185 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 186 * leaves the other data in that segment (if present) unchanged and writes 187 * the result to a stream. 188 * <p> 189 * 190 * @param byteSource 191 * ByteSource containing Jpeg image data. 192 * @param os 193 * OutputStream to write the image to. 194 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 195 * the Photoshop segment cannot be parsed 196 * @throws IOException if it fails to read from the origin byte source, or to write to the 197 * target byte source 198 * @throws ImageWriteException if it fails to write the target image 199 */ 200 public void removeIPTC(final ByteSource byteSource, final OutputStream os) 201 throws ImageReadException, IOException, ImageWriteException { 202 removeIPTC(byteSource, os, false); 203 } 204 205 /** 206 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 207 * leaves the other data in that segment (if present) unchanged (unless 208 * removeSegment is true) and writes the result to a stream. 209 * <p> 210 * 211 * @param byteSource 212 * ByteSource containing Jpeg image data. 213 * @param os 214 * OutputStream to write the image to. 215 * @param removeSegment 216 * Remove the App13 segment. 217 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 218 * the Photoshop segment cannot be parsed 219 * @throws IOException if it fails to read from the origin byte source, or to write to the 220 * target byte source 221 * @throws ImageWriteException if it fails to write the target image 222 */ 223 public void removeIPTC(final ByteSource byteSource, final OutputStream os, final boolean removeSegment) 224 throws ImageReadException, IOException, ImageWriteException { 225 final JFIFPieces jfifPieces = analyzeJFIF(byteSource); 226 final List<JFIFPiece> oldPieces = jfifPieces.pieces; 227 final List<JFIFPiece> photoshopApp13Segments = findPhotoshopApp13Segments(oldPieces); 228 229 if (photoshopApp13Segments.size() > 1) { 230 throw new ImageReadException( 231 "Image contains more than one Photoshop App13 segment."); 232 } 233 final List<JFIFPiece> newPieces = removePhotoshopApp13Segments(oldPieces); 234 if (!removeSegment && photoshopApp13Segments.size() == 1) { 235 final JFIFPieceSegment oldSegment = (JFIFPieceSegment) photoshopApp13Segments.get(0); 236 final JpegImagingParameters/jpeg/JpegImagingParameters.html#JpegImagingParameters">JpegImagingParameters params = new JpegImagingParameters(); 237 final PhotoshopApp13Data oldData = new IptcParser().parsePhotoshopSegment(oldSegment.getSegmentData(), params); 238 final List<IptcBlock> newBlocks = oldData.getNonIptcBlocks(); 239 final List<IptcRecord> newRecords = new ArrayList<>(); 240 final PhotoshopApp13Datats/jpeg/iptc/PhotoshopApp13Data.html#PhotoshopApp13Data">PhotoshopApp13Data newData = new PhotoshopApp13Data(newRecords, 241 newBlocks); 242 final byte[] segmentBytes = new IptcParser().writePhotoshopApp13Segment(newData); 243 final JFIFPieceSegment newSegment = new JFIFPieceSegment( 244 oldSegment.marker, segmentBytes); 245 newPieces.add(oldPieces.indexOf(oldSegment), newSegment); 246 } 247 writeSegments(os, newPieces); 248 } 249 250 /** 251 * Reads a Jpeg image, replaces the IPTC data in the App13 segment but 252 * leaves the other data in that segment (if present) unchanged and writes 253 * the result to a stream. 254 * 255 * @param src 256 * Byte array containing Jpeg image data. 257 * @param os 258 * OutputStream to write the image to. 259 * @param newData 260 * structure containing IPTC data. 261 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 262 * the Photoshop segment cannot be parsed 263 * @throws IOException if it fails to read from the origin byte source, or to write to the 264 * target byte source 265 * @throws ImageWriteException if it fails to write the target image 266 */ 267 public void writeIPTC(final byte[] src, final OutputStream os, 268 final PhotoshopApp13Data newData) throws ImageReadException, IOException, 269 ImageWriteException { 270 final ByteSource byteSource = new ByteSourceArray(src); 271 writeIPTC(byteSource, os, newData); 272 } 273 274 /** 275 * Reads a Jpeg image, replaces the IPTC data in the App13 segment but 276 * leaves the other data in that segment (if present) unchanged and writes 277 * the result to a stream. 278 * 279 * @param src 280 * InputStream containing Jpeg image data. 281 * @param os 282 * OutputStream to write the image to. 283 * @param newData 284 * structure containing IPTC data. 285 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 286 * the Photoshop segment cannot be parsed 287 * @throws IOException if it fails to read from the origin byte source, or to write to the 288 * target byte source 289 * @throws ImageWriteException if it fails to write the target image 290 */ 291 public void writeIPTC(final InputStream src, final OutputStream os, 292 final PhotoshopApp13Data newData) throws ImageReadException, IOException, 293 ImageWriteException { 294 final ByteSource byteSource = new ByteSourceInputStream(src, null); 295 writeIPTC(byteSource, os, newData); 296 } 297 298 /** 299 * Reads a Jpeg image, replaces the IPTC data in the App13 segment but 300 * leaves the other data in that segment (if present) unchanged and writes 301 * the result to a stream. 302 * 303 * @param src 304 * Image file. 305 * @param os 306 * OutputStream to write the image to. 307 * @param newData 308 * structure containing IPTC data. 309 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 310 * the Photoshop segment cannot be parsed 311 * @throws IOException if it fails to read from the origin byte source, or to write to the 312 * target byte source 313 * @throws ImageWriteException if it fails to write the target image 314 */ 315 public void writeIPTC(final File src, final OutputStream os, final PhotoshopApp13Data newData) 316 throws ImageReadException, IOException, ImageWriteException { 317 final ByteSource byteSource = new ByteSourceFile(src); 318 writeIPTC(byteSource, os, newData); 319 } 320 321 /** 322 * Reads a Jpeg image, replaces the IPTC data in the App13 segment but 323 * leaves the other data in that segment (if present) unchanged and writes 324 * the result to a stream. 325 * 326 * @param byteSource 327 * ByteSource containing Jpeg image data. 328 * @param os 329 * OutputStream to write the image to. 330 * @param newData 331 * structure containing IPTC data. 332 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 333 * the Photoshop segment cannot be parsed 334 * @throws IOException if it fails to read from the origin byte source, or to write to the 335 * target byte source 336 * @throws ImageWriteException if it fails to write the target image 337 */ 338 public void writeIPTC(final ByteSource byteSource, final OutputStream os, 339 PhotoshopApp13Data newData) throws ImageReadException, IOException, 340 ImageWriteException { 341 final JFIFPieces jfifPieces = analyzeJFIF(byteSource); 342 final List<JFIFPiece> oldPieces = jfifPieces.pieces; 343 final List<JFIFPiece> photoshopApp13Segments = findPhotoshopApp13Segments(oldPieces); 344 345 if (photoshopApp13Segments.size() > 1) { 346 throw new ImageReadException( 347 "Image contains more than one Photoshop App13 segment."); 348 } 349 List<JFIFPiece> newPieces = removePhotoshopApp13Segments(oldPieces); 350 351 { 352 // discard old iptc blocks. 353 final List<IptcBlock> newBlocks = newData.getNonIptcBlocks(); 354 final byte[] newBlockBytes = new IptcParser().writeIPTCBlock(newData.getRecords()); 355 356 final int blockType = IptcConstants.IMAGE_RESOURCE_BLOCK_IPTC_DATA; 357 final byte[] blockNameBytes = ImagingConstants.EMPTY_BYTE_ARRAY; 358 final IptcBlockng/formats/jpeg/iptc/IptcBlock.html#IptcBlock">IptcBlock newBlock = new IptcBlock(blockType, blockNameBytes, newBlockBytes); 359 newBlocks.add(newBlock); 360 361 newData = new PhotoshopApp13Data(newData.getRecords(), newBlocks); 362 363 final byte[] segmentBytes = new IptcParser().writePhotoshopApp13Segment(newData); 364 final JFIFPieceSegment newSegment = new JFIFPieceSegment(JpegConstants.JPEG_APP13_MARKER, segmentBytes); 365 366 newPieces = insertAfterLastAppSegments(newPieces, Arrays.asList(newSegment)); 367 } 368 369 writeSegments(os, newPieces); 370 } 371 372 }