8154058: [TIFF] ignoreMetadata parameter of TIFFImageReader's setInput() method affects TIFFImageReadParam in non-obvious way
authorbpb
Tue, 13 Dec 2016 12:02:37 -0800
changeset 42749 91fb907a8732
parent 42748 fca460b63839
child 42750 562fee8337a3
child 42933 96d0c48f3ebc
8154058: [TIFF] ignoreMetadata parameter of TIFFImageReader's setInput() method affects TIFFImageReadParam in non-obvious way Summary: Add readUnknownTags to TIFFImageReadParam and add ReadParamTest Reviewed-by: prr
jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java
jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageMetadata.java
jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReader.java
jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java
jdk/src/java.desktop/share/classes/javax/imageio/metadata/doc-files/tiff_metadata.html
jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFImageReadParam.java
jdk/test/javax/imageio/plugins/tiff/MultiPageImageTIFFFieldTest.java
jdk/test/javax/imageio/plugins/tiff/ReadUnknownTagsTest.java
jdk/test/javax/imageio/plugins/tiff/TIFFImageReadParamTest.java
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java	Mon Dec 12 20:54:41 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java	Tue Dec 13 12:02:37 2016 -0800
@@ -541,10 +541,10 @@
     }
 
     // Stream position initially at beginning, left at end
-    // if ignoreUnknownFields is true, do not load fields for which
+    // if readUnknownTags is false, do not load fields for which
     // a tag cannot be found in an allowed TagSet.
     public void initialize(ImageInputStream stream, boolean isPrimaryIFD,
-        boolean ignoreUnknownFields) throws IOException {
+        boolean ignoreMetadata, boolean readUnknownTags) throws IOException {
 
         removeTIFFFields();
 
@@ -553,10 +553,16 @@
 
         List<TIFFTagSet> tagSetList = getTagSetList();
 
+        // Configure essential tag variables if this is the primary IFD and
+        // either all metadata are being ignored, or metadata are not being
+        // ignored but both unknown tags are being ignored and the tag set
+        // list does not contain the baseline tags.
         boolean ensureEssentialTags = false;
         TIFFTagSet baselineTagSet = null;
-        if (isPrimaryIFD && ignoreUnknownFields
-            && !tagSetList.contains(BaselineTIFFTagSet.getInstance())) {
+        if (isPrimaryIFD &&
+            (ignoreMetadata ||
+             (!readUnknownTags &&
+              !tagSetList.contains(BaselineTIFFTagSet.getInstance())))) {
             ensureEssentialTags = true;
             initializeEssentialTags();
             baselineTagSet = BaselineTIFFTagSet.getInstance();
@@ -590,9 +596,12 @@
                 tag = baselineTagSet.getTag(tagNumber);
             }
 
-            // Ignore unknown fields, fields with unknown type, and fields
+            // Ignore non-essential fields, unknown fields unless forcibly
+            // being read, fields with unknown type, and fields
             // with count out of int range.
-            if((tag == null && ignoreUnknownFields)
+            if((ignoreMetadata &&
+                (!ensureEssentialTags || !essentialTags.contains(tagNumber)))
+                || (tag == null && !readUnknownTags)
                 || (tag != null && !tag.isDataTypeOK(type))
                 || longCount > Integer.MAX_VALUE) {
                 // Skip the value/offset so as to leave the stream
@@ -701,7 +710,8 @@
                     tagSets.add(tag.getTagSet());
                     TIFFIFD subIFD = new TIFFIFD(tagSets);
 
-                    subIFD.initialize(stream, false, ignoreUnknownFields);
+                    subIFD.initialize(stream, false, ignoreMetadata,
+                                      readUnknownTags);
                     TIFFField f = new TIFFField(tag, type, e.offset, subIFD);
                     addTIFFField(f);
                 } else {
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageMetadata.java	Mon Dec 12 20:54:41 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageMetadata.java	Tue Dec 13 12:02:37 2016 -0800
@@ -82,9 +82,10 @@
     }
 
     public void initializeFromStream(ImageInputStream stream,
-                                     boolean ignoreUnknownFields)
+                                     boolean ignoreMetadata,
+                                     boolean readUnknownTags)
         throws IOException {
-        rootIFD.initialize(stream, true, ignoreUnknownFields);
+        rootIFD.initialize(stream, true, ignoreMetadata, readUnknownTags);
     }
 
     public void addShortOrLongField(int tagNumber, long value) {
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReader.java	Mon Dec 12 20:54:41 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReader.java	Tue Dec 13 12:02:37 2016 -0800
@@ -305,16 +305,19 @@
         try {
             // Create an object to store the image metadata
             List<TIFFTagSet> tagSets;
+            boolean readUnknownTags = false;
             if (imageReadParam instanceof TIFFImageReadParam) {
-                tagSets
-                        = ((TIFFImageReadParam) imageReadParam).getAllowedTagSets();
+                TIFFImageReadParam tp = (TIFFImageReadParam)imageReadParam;
+                tagSets = tp.getAllowedTagSets();
+                readUnknownTags = tp.getReadUnknownTags();
             } else {
                 tagSets = new ArrayList<TIFFTagSet>(1);
                 tagSets.add(BaselineTIFFTagSet.getInstance());
             }
 
             this.imageMetadata = new TIFFImageMetadata(tagSets);
-            imageMetadata.initializeFromStream(stream, ignoreMetadata);
+            imageMetadata.initializeFromStream(stream, ignoreMetadata,
+                                               readUnknownTags);
         } catch (IIOException iioe) {
             throw iioe;
         } catch (IOException ioe) {
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java	Mon Dec 12 20:54:41 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java	Tue Dec 13 12:02:37 2016 -0800
@@ -3015,7 +3015,7 @@
         List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
         tagSets.add(BaselineTIFFTagSet.getInstance());
         TIFFIFD rootIFD = new TIFFIFD(tagSets);
-        rootIFD.initialize(stream, true, true);
+        rootIFD.initialize(stream, true, false, false);
         stream.reset();
 
         return rootIFD;
--- a/jdk/src/java.desktop/share/classes/javax/imageio/metadata/doc-files/tiff_metadata.html	Mon Dec 12 20:54:41 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/metadata/doc-files/tiff_metadata.html	Tue Dec 13 12:02:37 2016 -0800
@@ -216,22 +216,27 @@
 
 <h4><a name="MetadataIssuesRead"/>Metadata Issues</h4>
 
-By default all fields in the TIFF image file directory (IFD) are loaded into
-the native image metadata object. In cases where the IFD includes fields which
-contain large amounts of data this could be very inefficient. Which fields
-are loaded may be controlled by setting which TIFF tags the reader is allowed
-to recognize and whether it is ignoring metadata. The reader is informed to
-disregard metadata as usual via the <code>ignoreMetadata</code> parameter of
+By default all recognized fields in the TIFF image file directory (IFD) are
+loaded into the native image metadata object. Which fields are loaded may be
+controlled by setting which TIFF tags the reader is allowed to recognize,
+whether to read fields with unrecognized tags, and whether to ignore all
+metadata. The reader is informed to disregard all metadata as usual via the
+<code>ignoreMetadata</code> parameter of
 <code>ImageReader.setInput(Object,boolean,boolean)</code>. It is
 informed of which <a href="../../plugins/tiff/TIFFTag.html">TIFFTag</a>s to
 recognize or not to recognize via
-<code>TIFFImageReadParam.addAllowedTagSet(TIFFTagSet)</code>
-and
+<code>TIFFImageReadParam.addAllowedTagSet(TIFFTagSet)</code> and
 <code>TIFFImageReadParam.removeAllowedTagSet(TIFFTagSet)</code>.
-If <code>ignoreMetadata</code> is <code>true</code>, then the reader will
-load into the native image metadata object only those fields which have a
-<code>TIFFTag</code> contained in the one of the allowed
-<code>TIFFTagSet</code>s.
+If <code>ignoreMetadata</code> is <code>true</code>, then only metadata
+essential to reading the image will be loaded into the native image metadata
+object. If <code>ignoreMetadata</code> is <code>false</code>, then the reader
+will by default load into the native image metadata object only those fields
+which are either essential to reading the image or have a <code>TIFFTag</code>
+contained in the one of the allowed <code>TIFFTagSet</code>s. Reading of
+fields with tags not in the allowed <code>TIFFTagSet</code>s may be forced
+by passing in a <code>TIFFImageReadParam</code> on which
+<code>TIFFImageReadParam.setReadUnknownTags(boolean)</code> has been
+invoked with parameter <code>true</code>.
 
 <p>Use of a <a href="../../plugins/tiff/TIFFDirectory.html">TIFFDirectory</a>
 object may simplify gaining access to metadata values. An instance of
--- a/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFImageReadParam.java	Mon Dec 12 20:54:41 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFImageReadParam.java	Tue Dec 13 12:02:37 2016 -0800
@@ -46,6 +46,10 @@
  * {@code ExifParentTIFFTagSet}, and {@code GeoTIFFTagSet}
  * are included.
  *
+ * <p> Forcing reading of fields corresponding to {@code TIFFTag}s
+ * not in any of the allowed {@code TIFFTagSet}s may be effected via
+ * {@link #setReadUnknownTags setReadUnknownTags}.
+ *
  * @since 9
  */
 public final class TIFFImageReadParam extends ImageReadParam {
@@ -53,6 +57,8 @@
     private final List<TIFFTagSet> allowedTagSets =
         new ArrayList<TIFFTagSet>(4);
 
+    private boolean readUnknownTags = false;
+
     /**
      * Constructs a {@code TIFFImageReadParam}.  Tags defined by
      * the {@code TIFFTagSet}s {@code BaselineTIFFTagSet},
@@ -117,4 +123,27 @@
     public List<TIFFTagSet> getAllowedTagSets() {
         return allowedTagSets;
     }
+
+    /**
+     * Set whether to read fields corresponding to {@code TIFFTag}s not in
+     * the allowed {@code TIFFTagSet}s. The default setting is {@code false}.
+     * If the TIFF {@code ImageReader} is ignoring metadata, then a setting
+     * of {@code true} is overridden as all metadata are ignored except those
+     * essential to reading the image itself.
+     *
+     * @param readUnknownTags Whether to read fields of unrecognized tags
+     */
+    public void setReadUnknownTags(boolean readUnknownTags) {
+        this.readUnknownTags = readUnknownTags;
+    }
+
+    /**
+     * Retrieve the setting of whether to read fields corresponding to unknown
+     * {@code TIFFTag}s.
+     *
+     * @return Whether to read fields of unrecognized tags
+     */
+    public boolean getReadUnknownTags() {
+        return readUnknownTags;
+    }
 }
--- a/jdk/test/javax/imageio/plugins/tiff/MultiPageImageTIFFFieldTest.java	Mon Dec 12 20:54:41 2016 -0800
+++ b/jdk/test/javax/imageio/plugins/tiff/MultiPageImageTIFFFieldTest.java	Tue Dec 13 12:02:37 2016 -0800
@@ -223,7 +223,7 @@
         ImageReader reader = getTIFFReader();
 
         ImageInputStream s = ImageIO.createImageInputStream(new File(FILENAME));
-        reader.setInput(s, false, true);
+        reader.setInput(s, false, false);
 
         int ni = reader.getNumImages(true);
         check(ni == 2, "invalid number of images");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/imageio/plugins/tiff/ReadUnknownTagsTest.java	Tue Dec 13 12:02:37 2016 -0800
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug     8154058
+ * @author  a.stepanov
+ * @summary Some checks for ignoring metadata
+ * @run     main ReadUnknownTagsTest
+ */
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import javax.imageio.*;
+import javax.imageio.metadata.*;
+
+import javax.imageio.stream.*;
+import javax.imageio.plugins.tiff.*;
+
+
+public class ReadUnknownTagsTest {
+
+    private final static int SZ = 50;
+    private final static Color C = Color.RED;
+
+    private final static int DESCRIPTION_TAG =
+        BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION;
+    private final static String DESCRIPTION = "A Test Image";
+
+    private final static int FAX_TAG = FaxTIFFTagSet.TAG_CLEAN_FAX_DATA;
+    private final static short FAX_DATA =
+        FaxTIFFTagSet.CLEAN_FAX_DATA_ERRORS_UNCORRECTED;
+
+    private final boolean ignoreMetadata;
+    private final boolean readUnknownTags;
+
+    public ReadUnknownTagsTest(boolean ignoreMetadata,
+        boolean readUnknownTags) {
+        this.ignoreMetadata = ignoreMetadata;
+        this.readUnknownTags = readUnknownTags;
+    }
+
+    private ImageWriter getTIFFWriter() {
+
+        java.util.Iterator<ImageWriter> writers =
+            ImageIO.getImageWritersByFormatName("TIFF");
+        if (!writers.hasNext()) {
+            throw new RuntimeException("No writers available for TIFF format");
+        }
+        return writers.next();
+    }
+
+    private ImageReader getTIFFReader() {
+
+        java.util.Iterator<ImageReader> readers =
+            ImageIO.getImageReadersByFormatName("TIFF");
+        if (!readers.hasNext()) {
+            throw new RuntimeException("No readers available for TIFF format");
+        }
+        return readers.next();
+    }
+
+
+    private void writeImage() throws Exception {
+
+        String fn = "test-" + ignoreMetadata + ".tiff";
+        OutputStream s = new BufferedOutputStream(new FileOutputStream(fn));
+        try (ImageOutputStream ios = ImageIO.createImageOutputStream(s)) {
+
+            ImageWriter writer = getTIFFWriter();
+            writer.setOutput(ios);
+
+            BufferedImage img = new BufferedImage(SZ, SZ,
+                BufferedImage.TYPE_INT_RGB);
+            Graphics g = img.getGraphics();
+            g.setColor(C);
+            g.fillRect(0, 0, SZ, SZ);
+            g.dispose();
+
+            ImageWriteParam param = writer.getDefaultWriteParam();
+
+            IIOMetadata md = writer.getDefaultImageMetadata(
+                    new ImageTypeSpecifier(img), param);
+
+            TIFFDirectory dir = TIFFDirectory.createFromMetadata(md);
+
+            TIFFTag descTag =
+                BaselineTIFFTagSet.getInstance().getTag(DESCRIPTION_TAG);
+            dir.addTIFFField(new TIFFField(descTag, TIFFTag.TIFF_ASCII, 1,
+                new String[] {DESCRIPTION}));
+
+            TIFFTag faxTag = FaxTIFFTagSet.getInstance().getTag(FAX_TAG);
+            dir.addTIFFField(new TIFFField(faxTag, FAX_DATA));
+
+            writer.write(new IIOImage(img, null, dir.getAsMetadata()));
+
+            ios.flush();
+            writer.dispose();
+        }
+        s.close();
+    }
+
+    private void readAndCheckImage() throws Exception {
+
+        ImageReader reader = getTIFFReader();
+
+        String fn = "test-" + ignoreMetadata + ".tiff";
+        ImageInputStream s = ImageIO.createImageInputStream(new File(fn));
+
+        reader.setInput(s, false, ignoreMetadata);
+
+        int ni = reader.getNumImages(true);
+        check(ni == 1, "invalid number of images");
+
+
+        TIFFImageReadParam param = new TIFFImageReadParam();
+        // fax data are allowed by default
+        param.removeAllowedTagSet(FaxTIFFTagSet.getInstance());
+
+        // readUnknownTags setting
+        if (param.getReadUnknownTags()) {
+            throw new RuntimeException("Default readUnknownTags is not false");
+        }
+        param.setReadUnknownTags(readUnknownTags);
+        if (param.getReadUnknownTags() != readUnknownTags) {
+            throw new RuntimeException("Incorrect readUnknownTags setting "
+                + "\"" + readUnknownTags + "\"");
+        }
+
+        // read images and metadata
+        IIOImage i = reader.readAll(0, param);
+        BufferedImage bi = (BufferedImage) i.getRenderedImage();
+
+        check(bi.getWidth()  == SZ, "invalid width");
+        check(bi.getHeight() == SZ, "invalid height");
+        Color c = new Color(bi.getRGB(SZ / 2, SZ / 2));
+        check(c.equals(C), "invalid color");
+
+        IIOMetadata metadata = i.getMetadata();
+
+        //
+        // Verify presence of image metadata
+        //
+        if (metadata == null) {
+            throw new RuntimeException("No image metadata retrieved");
+        }
+
+        TIFFDirectory dir = TIFFDirectory.createFromMetadata(metadata);
+
+        //
+        // Verify presence of essential ImageWidth field regardless of
+        // settings of ignoreMetadata and readUnknownTags
+        //
+        int failures = 0;
+        if (!dir.containsTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH)) {
+            System.err.println("Metadata is missing essential ImageWidth tag");
+            failures++;
+        } else {
+            TIFFField widthField =
+                dir.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
+            System.out.printf("ImageWidth: %d%n", widthField.getAsLong(0));
+        }
+
+        //
+        // Verify presence of non-essential baseline ImageDescription field
+        // if and only if ignoreMetadata == false
+        //
+        boolean hasDescription = dir.containsTIFFField(DESCRIPTION_TAG);
+        System.out.println("ImageDescription (" + !ignoreMetadata + "): "
+            + hasDescription);
+        if (ignoreMetadata && hasDescription) {
+            System.err.println
+                ("Description metadata present despite ignoreMetadata");
+            failures++;
+        } else if (!ignoreMetadata && !hasDescription) {
+            System.err.println
+                ("Description metadata absent despite !ignoreMetadata");
+            failures++;
+        }
+
+        //
+        // Verify presence of CleanFaxData field if and only if
+        // ignoreMetadata == false and readUnknownTags == true
+        //
+        boolean shouldHaveFaxField = !ignoreMetadata && readUnknownTags;
+        boolean hasFaxField = dir.containsTIFFField(FAX_TAG);
+        System.out.println("CleanFaxData (" + shouldHaveFaxField + "): "
+            + hasFaxField);
+
+        if (ignoreMetadata) {
+            if (hasFaxField) {
+                System.err.println
+                    ("Fax metadata present despite ignoreMetadata");
+                failures++;
+            }
+        } else { // !ignoreMetadata
+            if (!readUnknownTags && hasFaxField) {
+                System.err.println
+                    ("Fax metadata present despite !readUnknownTags");
+                failures++;
+            } else if (readUnknownTags && !hasFaxField) {
+                System.err.println
+                    ("Fax metadata absent despite readUnknownTags");
+                failures++;
+            }
+        }
+
+        if (failures > 0) {
+            throw new RuntimeException("Test failed for ignoreMetadata "
+                + ignoreMetadata + " and readUnknownTags " + readUnknownTags);
+        }
+    }
+
+    public void run() {
+        try {
+            writeImage();
+            readAndCheckImage();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void check(boolean ok, String msg) {
+        if (!ok) { throw new RuntimeException(msg); }
+    }
+
+    public static void main(String[] args) {
+        int failures = 0;
+
+        System.out.println();
+        for (boolean ignoreMetadata : new boolean[] {false, true}) {
+            for (boolean readUnknownTags : new boolean[] {false, true}) {
+                try {
+                    System.out.printf
+                        ("ignoreMetadata: %s, readUnknownTags: %s%n",
+                        ignoreMetadata, readUnknownTags);
+                    (new ReadUnknownTagsTest(ignoreMetadata,
+                        readUnknownTags)).run();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    failures++;
+                } finally {
+                    System.out.println();
+                }
+            }
+        }
+    }
+}
--- a/jdk/test/javax/imageio/plugins/tiff/TIFFImageReadParamTest.java	Mon Dec 12 20:54:41 2016 -0800
+++ b/jdk/test/javax/imageio/plugins/tiff/TIFFImageReadParamTest.java	Tue Dec 13 12:02:37 2016 -0800
@@ -159,7 +159,7 @@
         ImageReader reader = getTIFFReader();
 
         ImageInputStream s = ImageIO.createImageInputStream(new File(FILENAME));
-        reader.setInput(s, false, true);
+        reader.setInput(s, false, false);
 
         int ni = reader.getNumImages(true);
         check(ni == 1, "invalid number of images: " + ni);