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.List; 026 027import org.apache.commons.imaging.ImagingConstants; 028import org.apache.commons.imaging.ImagingException; 029import org.apache.commons.imaging.bytesource.ByteSource; 030import org.apache.commons.imaging.formats.jpeg.JpegConstants; 031import org.apache.commons.imaging.formats.jpeg.JpegImagingParameters; 032import org.apache.commons.imaging.formats.jpeg.xmp.JpegRewriter; 033 034/** 035 * Interface for Exif write/update/remove functionality for Jpeg/JFIF images. 036 */ 037public class JpegIptcRewriter extends JpegRewriter { 038 039 /** 040 * Reads a JPEG image, removes all IPTC data from the App13 segment but leaves the other data in that segment (if present) unchanged and writes the result 041 * to a stream. 042 * <p> 043 * 044 * @param src Byte array containing JPEG image data. 045 * @param os OutputStream to write the image to. 046 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 047 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 048 * @throws ImagingException if it fails to write the target image 049 */ 050 public void removeIptc(final byte[] src, final OutputStream os) throws ImagingException, IOException, ImagingException { 051 removeIptc(src, os, false); 052 } 053 054 /** 055 * Reads a JPEG image, removes all IPTC data from the App13 segment but leaves the other data in that segment (if present) unchanged (unless removeSegment 056 * is true) and writes the result to a stream. 057 * <p> 058 * 059 * @param src Byte array containing JPEG image data. 060 * @param os OutputStream to write the image to. 061 * @param removeSegment Remove the App13 segment. 062 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 063 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 064 * @throws ImagingException if it fails to write the target image 065 */ 066 public void removeIptc(final byte[] src, final OutputStream os, final boolean removeSegment) throws ImagingException, IOException, ImagingException { 067 final ByteSource byteSource = ByteSource.array(src); 068 removeIptc(byteSource, os, removeSegment); 069 } 070 071 /** 072 * Reads a JPEG image, removes all IPTC data from the App13 segment but leaves the other data in that segment (if present) unchanged and writes the result 073 * to a stream. 074 * <p> 075 * 076 * @param byteSource ByteSource containing JPEG image data. 077 * @param os OutputStream to write the image to. 078 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 079 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 080 * @throws ImagingException if it fails to write the target image 081 */ 082 public void removeIptc(final ByteSource byteSource, final OutputStream os) throws ImagingException, IOException, ImagingException { 083 removeIptc(byteSource, os, false); 084 } 085 086 /** 087 * Reads a JPEG image, removes all IPTC data from the App13 segment but leaves the other data in that segment (if present) unchanged (unless removeSegment 088 * is true) and writes the result to a stream. 089 * <p> 090 * 091 * @param byteSource ByteSource containing JPEG image data. 092 * @param os OutputStream to write the image to. 093 * @param removeSegment Remove the App13 segment. 094 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 095 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 096 * @throws ImagingException if it fails to write the target image 097 */ 098 public void removeIptc(final ByteSource byteSource, final OutputStream os, final boolean removeSegment) 099 throws ImagingException, IOException, ImagingException { 100 final JFIFPieces jfifPieces = analyzeJfif(byteSource); 101 final List<JFIFPiece> oldPieces = jfifPieces.pieces; 102 final List<JFIFPiece> photoshopApp13Segments = findPhotoshopApp13Segments(oldPieces); 103 104 if (photoshopApp13Segments.size() > 1) { 105 throw new ImagingException("Image contains more than one Photoshop App13 segment."); 106 } 107 final List<JFIFPiece> newPieces = removePhotoshopApp13Segments(oldPieces); 108 if (!removeSegment && photoshopApp13Segments.size() == 1) { 109 final JFIFPieceSegment oldSegment = (JFIFPieceSegment) photoshopApp13Segments.get(0); 110 final JpegImagingParameters params = new JpegImagingParameters(); 111 final PhotoshopApp13Data oldData = new IptcParser().parsePhotoshopSegment(oldSegment.getSegmentData(), params); 112 final List<IptcBlock> newBlocks = oldData.getNonIptcBlocks(); 113 final List<IptcRecord> newRecords = new ArrayList<>(); 114 final PhotoshopApp13Data newData = new PhotoshopApp13Data(newRecords, newBlocks); 115 final byte[] segmentBytes = new IptcParser().writePhotoshopApp13Segment(newData); 116 final JFIFPieceSegment newSegment = new JFIFPieceSegment(oldSegment.marker, segmentBytes); 117 newPieces.add(oldPieces.indexOf(oldSegment), newSegment); 118 } 119 writeSegments(os, newPieces); 120 } 121 122 /** 123 * Reads a JPEG image, removes all IPTC data from the App13 segment but leaves the other data in that segment (if present) unchanged and writes the result 124 * to a stream. 125 * <p> 126 * 127 * @param src Image file. 128 * @param os OutputStream to write the image to. 129 * 130 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 131 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 132 * @throws ImagingException if it fails to write the target image 133 * @see java.io.File 134 * @see java.io.OutputStream 135 */ 136 public void removeIptc(final File src, final OutputStream os) throws ImagingException, IOException, ImagingException { 137 removeIptc(src, os, false); 138 } 139 140 /** 141 * Reads a JPEG image, removes all IPTC data from the App13 segment but leaves the other data in that segment (if present) unchanged (unless removeSegment 142 * is true) and writes the result to a stream. 143 * <p> 144 * 145 * @param src Image file. 146 * @param os OutputStream to write the image to. 147 * @param removeSegment Remove the App13 segment. 148 * 149 * @see java.io.File 150 * @see java.io.OutputStream 151 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 152 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 153 * @throws ImagingException if it fails to write the target image 154 */ 155 public void removeIptc(final File src, final OutputStream os, final boolean removeSegment) throws ImagingException, IOException, ImagingException { 156 final ByteSource byteSource = ByteSource.file(src); 157 removeIptc(byteSource, os, removeSegment); 158 } 159 160 /** 161 * Reads a JPEG image, removes all IPTC data from the App13 segment but leaves the other data in that segment (if present) unchanged and writes the result 162 * to a stream. 163 * <p> 164 * 165 * @param src InputStream containing JPEG image data. 166 * @param os OutputStream to write the image to. 167 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 168 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 169 * @throws ImagingException if it fails to write the target image 170 */ 171 public void removeIptc(final InputStream src, final OutputStream os) throws ImagingException, IOException, ImagingException { 172 removeIptc(src, os, false); 173 } 174 175 /** 176 * Reads a JPEG image, removes all IPTC data from the App13 segment but leaves the other data in that segment (if present) unchanged (unless removeSegment 177 * is true) and writes the result to a stream. 178 * <p> 179 * 180 * @param src InputStream containing JPEG image data. 181 * @param os OutputStream to write the image to. 182 * @param removeSegment Remove the App13 segment. 183 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 184 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 185 * @throws ImagingException if it fails to write the target image 186 */ 187 public void removeIptc(final InputStream src, final OutputStream os, final boolean removeSegment) throws ImagingException, IOException, ImagingException { 188 final ByteSource byteSource = ByteSource.inputStream(src, null); 189 removeIptc(byteSource, os, removeSegment); 190 } 191 192 /** 193 * Reads a JPEG image, replaces the IPTC data in the App13 segment but leaves the other data in that segment (if present) unchanged and writes the result to 194 * a stream. 195 * 196 * @param src Byte array containing JPEG image data. 197 * @param os OutputStream to write the image to. 198 * @param newData structure containing IPTC data. 199 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 200 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 201 * @throws ImagingException if it fails to write the target image 202 */ 203 public void writeIptc(final byte[] src, final OutputStream os, final PhotoshopApp13Data newData) throws ImagingException, IOException, ImagingException { 204 final ByteSource byteSource = ByteSource.array(src); 205 writeIptc(byteSource, os, newData); 206 } 207 208 /** 209 * Reads a JPEG image, replaces the IPTC data in the App13 segment but leaves the other data in that segment (if present) unchanged and writes the result to 210 * a stream. 211 * 212 * @param byteSource ByteSource containing JPEG image data. 213 * @param os OutputStream to write the image to. 214 * @param newData structure containing IPTC data. 215 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 216 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 217 * @throws ImagingException if it fails to write the target image 218 */ 219 public void writeIptc(final ByteSource byteSource, final OutputStream os, PhotoshopApp13Data newData) 220 throws ImagingException, IOException, ImagingException { 221 final JFIFPieces jfifPieces = analyzeJfif(byteSource); 222 final List<JFIFPiece> oldPieces = jfifPieces.pieces; 223 final List<JFIFPiece> photoshopApp13Segments = findPhotoshopApp13Segments(oldPieces); 224 225 if (photoshopApp13Segments.size() > 1) { 226 throw new ImagingException("Image contains more than one Photoshop App13 segment."); 227 } 228 List<JFIFPiece> newPieces = removePhotoshopApp13Segments(oldPieces); 229 230 { 231 // discard old iptc blocks. 232 final List<IptcBlock> newBlocks = newData.getNonIptcBlocks(); 233 final byte[] newBlockBytes = new IptcParser().writeIptcBlock(newData.getRecords()); 234 235 final int blockType = IptcConstants.IMAGE_RESOURCE_BLOCK_IPTC_DATA; 236 final byte[] blockNameBytes = ImagingConstants.EMPTY_BYTE_ARRAY; 237 final IptcBlock newBlock = new IptcBlock(blockType, blockNameBytes, newBlockBytes); 238 newBlocks.add(newBlock); 239 240 newData = new PhotoshopApp13Data(newData.getRecords(), newBlocks); 241 242 final byte[] segmentBytes = new IptcParser().writePhotoshopApp13Segment(newData); 243 final JFIFPieceSegment newSegment = new JFIFPieceSegment(JpegConstants.JPEG_APP13_MARKER, segmentBytes); 244 245 newPieces = insertAfterLastAppSegments(newPieces, Arrays.asList(newSegment)); 246 } 247 248 writeSegments(os, newPieces); 249 } 250 251 /** 252 * Reads a JPEG image, replaces the IPTC data in the App13 segment but leaves the other data in that segment (if present) unchanged and writes the result to 253 * a stream. 254 * 255 * @param src Image file. 256 * @param os OutputStream to write the image to. 257 * @param newData structure containing IPTC data. 258 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 259 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 260 * @throws ImagingException if it fails to write the target image 261 */ 262 public void writeIptc(final File src, final OutputStream os, final PhotoshopApp13Data newData) throws ImagingException, IOException, ImagingException { 263 final ByteSource byteSource = ByteSource.file(src); 264 writeIptc(byteSource, os, newData); 265 } 266 267 /** 268 * Reads a JPEG image, replaces the IPTC data in the App13 segment but leaves the other data in that segment (if present) unchanged and writes the result to 269 * a stream. 270 * 271 * @param src InputStream containing JPEG image data. 272 * @param os OutputStream to write the image to. 273 * @param newData structure containing IPTC data. 274 * @throws ImagingException if there are more than one Photoshop App13 segment, or if the Photoshop segment cannot be parsed 275 * @throws IOException if it fails to read from the origin byte source, or to write to the target byte source 276 * @throws ImagingException if it fails to write the target image 277 */ 278 public void writeIptc(final InputStream src, final OutputStream os, final PhotoshopApp13Data newData) 279 throws ImagingException, IOException, ImagingException { 280 final ByteSource byteSource = ByteSource.inputStream(src, null); 281 writeIptc(byteSource, os, newData); 282 } 283 284}