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