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
--- 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);