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 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 055 * the Photoshop segment cannot be parsed 056 * @throws IOException if it fails to read from the origin byte source, or to write to the 057 * target byte source 058 * @throws ImageWriteException if it fails to write the target image 059 * @see java.io.File 060 * @see java.io.OutputStream 061 */ 062 public void removeIPTC(final File src, final OutputStream os) 063 throws ImageReadException, IOException, ImageWriteException { 064 removeIPTC(src, os, false); 065 } 066 067 /** 068 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 069 * leaves the other data in that segment (if present) unchanged (unless 070 * removeSegment is true) and writes the result to a stream. 071 * <p> 072 * 073 * @param src 074 * Image file. 075 * @param os 076 * OutputStream to write the image to. 077 * @param removeSegment 078 * Remove the App13 segment. 079 * 080 * @see java.io.File 081 * @see java.io.OutputStream 082 * @throws ImageReadException if there are more than one Photoshop App13 segment, or if 083 * the Photoshop segment cannot be parsed 084 * @throws IOException if it fails to read from the origin byte source, or to write to the 085 * target byte source 086 * @throws ImageWriteException if it fails to write the target image 087 */ 088 public void removeIPTC(final File src, final OutputStream os, final boolean removeSegment) 089 throws ImageReadException, IOException, ImageWriteException { 090 final ByteSource byteSource = new ByteSourceFile(src); 091 removeIPTC(byteSource, os, removeSegment); 092 } 093 094 /** 095 * Reads a Jpeg image, removes all IPTC data from the App13 segment but 096 * leaves the other data in that segment (if present) unchanged and writes 097 * the result to a stream. 098 * <p> 099 * 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 Map<String, Object> params = new HashMap<>(); 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 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 = new byte[0]; 358 final 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}