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