View Javadoc
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.exif;
18  
19  import static org.junit.Assert.assertTrue;
20  
21  import java.awt.image.BufferedImage;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.TreeMap;
29  
30  import org.apache.commons.imaging.ImageFormats;
31  import org.apache.commons.imaging.ImageReadException;
32  import org.apache.commons.imaging.ImageWriteException;
33  import org.apache.commons.imaging.Imaging;
34  import org.apache.commons.imaging.ImagingConstants;
35  import org.apache.commons.imaging.common.ImageMetadata;
36  import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
37  import org.apache.commons.imaging.formats.jpeg.iptc.JpegIptcRewriter;
38  import org.apache.commons.imaging.formats.jpeg.xmp.JpegXmpRewriter;
39  import org.apache.commons.imaging.formats.tiff.TiffDirectory;
40  import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
41  import org.apache.commons.imaging.formats.tiff.constants.MicrosoftTagConstants;
42  import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
43  import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
44  import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
45  import org.junit.Test;
46  
47  public class MicrosoftTagTest extends ExifBaseTest {
48      private static final String AUTHOR = "author";
49      private static final String COMMENT = "comment";
50      private static final String SUBJECT = "subject";
51      private static final String TITLE = "title";
52  
53      @Test
54      public void testWrite() throws Exception {
55          final BufferedImage image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
56          final TiffOutputSet exifSet = new TiffOutputSet();
57          final TiffOutputDirectory root = exifSet.getOrCreateRootDirectory();
58          root.add(MicrosoftTagConstants.EXIF_TAG_XPAUTHOR, AUTHOR);
59          root.add(MicrosoftTagConstants.EXIF_TAG_XPCOMMENT, COMMENT);
60          root.add(MicrosoftTagConstants.EXIF_TAG_XPSUBJECT, SUBJECT);
61          root.add(MicrosoftTagConstants.EXIF_TAG_XPTITLE, TITLE);
62          final Map<String, Object> params = new TreeMap<>();
63          params.put(ImagingConstants.PARAM_KEY_EXIF, exifSet);
64          final byte[] bytes = Imaging.writeImageToBytes(image, ImageFormats.TIFF, params);
65          checkFields(bytes);
66      }
67      
68      private TiffImageMetadata toTiffMetadata(final ImageMetadata metadata) throws Exception {
69          if (metadata instanceof JpegImageMetadata) {
70              return ((JpegImageMetadata)metadata).getExif();
71          } else if (metadata instanceof TiffImageMetadata) {
72              return ((TiffImageMetadata)metadata);
73          } else {
74              throw new Exception("bad metadata format");
75          }
76      }
77  
78      private byte[] cleanImage(final File imageWithExif) throws ImageReadException, ImageWriteException, IOException {
79          // Windows doesn't show XP tags if same-meaning tags exist in IPTC or XMP. Remove them: 
80          final ByteArrayOutputStream noXmp = new ByteArrayOutputStream();
81          new JpegXmpRewriter().removeXmpXml(imageWithExif, noXmp);
82          final ByteArrayOutputStream noXmpNoIptc = new ByteArrayOutputStream();
83          new JpegIptcRewriter().removeIPTC(noXmp.toByteArray(), noXmpNoIptc);
84          return noXmpNoIptc.toByteArray();
85      }
86  
87      @Test
88      public void testRewrite() throws Exception {
89          final byte[] imageWithExif = cleanImage(getImageWithExifData());
90  
91          final TiffImageMetadata metadata = toTiffMetadata(Imaging.getMetadata(imageWithExif));
92          final ExifRewriter rewriter = new ExifRewriter();
93          final TiffOutputSet outputSet = metadata.getOutputSet();
94          final TiffOutputDirectory root = outputSet.getOrCreateRootDirectory();
95          
96          // In Windows these will also hide XP fields:
97          root.removeField(TiffTagConstants.TIFF_TAG_IMAGE_DESCRIPTION);
98          root.removeField(TiffTagConstants.TIFF_TAG_ARTIST);
99          
100         // Duplicates can be a problem:
101         root.removeField(MicrosoftTagConstants.EXIF_TAG_XPAUTHOR);
102         root.add(MicrosoftTagConstants.EXIF_TAG_XPAUTHOR, AUTHOR);
103         
104         root.removeField(MicrosoftTagConstants.EXIF_TAG_XPCOMMENT);
105         root.add(MicrosoftTagConstants.EXIF_TAG_XPCOMMENT, COMMENT);
106         
107         root.removeField(MicrosoftTagConstants.EXIF_TAG_XPSUBJECT);
108         root.add(MicrosoftTagConstants.EXIF_TAG_XPSUBJECT, SUBJECT);
109         
110         root.removeField(MicrosoftTagConstants.EXIF_TAG_XPTITLE);
111         root.add(MicrosoftTagConstants.EXIF_TAG_XPTITLE, TITLE);
112         
113         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
114         rewriter.updateExifMetadataLossy(imageWithExif, baos, outputSet);
115         checkFields(baos.toByteArray());
116     }
117     
118     private void checkFields(final byte[] file) throws Exception {
119         final TiffImageMetadata metadata = toTiffMetadata(Imaging.getMetadata(file));
120         
121         // field values may be duplicated between directories, we have to check all
122         final List<Object> authorValues = new ArrayList<>();
123         final List<Object> commentValues = new ArrayList<>();
124         final List<Object> subjectValues = new ArrayList<>();
125         final List<Object> titleValues = new ArrayList<>();
126         for (final TiffDirectory d : metadata.contents.directories) {
127             titleValues.add(d.getFieldValue(MicrosoftTagConstants.EXIF_TAG_XPTITLE, false));
128             authorValues.add(d.getFieldValue(MicrosoftTagConstants.EXIF_TAG_XPAUTHOR, false));
129             commentValues.add(d.getFieldValue(MicrosoftTagConstants.EXIF_TAG_XPCOMMENT, false));
130             subjectValues.add(d.getFieldValue(MicrosoftTagConstants.EXIF_TAG_XPSUBJECT, false));
131         }
132         
133         assertTrue(authorValues.contains(AUTHOR));
134         assertTrue(commentValues.contains(COMMENT));
135         assertTrue(subjectValues.contains(SUBJECT));
136         assertTrue(titleValues.contains(TITLE));
137     }
138 }