8154058: [TIFF] ignoreMetadata parameter of TIFFImageReader's setInput() method affects TIFFImageReadParam in non-obvious way

Add readUnknownTags to TIFFImageReadParam and add ReadParamTest

Reviewed-by: prr
This commit is contained in:
Brian Burkhalter 2016-12-13 12:02:37 -08:00
parent a4865ea66a
commit bcfb267efe
9 changed files with 344 additions and 27 deletions

View File

@ -541,10 +541,10 @@ public class TIFFIFD extends TIFFDirectory {
}
// 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 @@ public class TIFFIFD extends TIFFDirectory {
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 @@ public class TIFFIFD extends TIFFDirectory {
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 @@ public class TIFFIFD extends TIFFDirectory {
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 {

View File

@ -82,9 +82,10 @@ public class TIFFImageMetadata extends IIOMetadata {
}
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) {

View File

@ -305,16 +305,19 @@ public class TIFFImageReader extends ImageReader {
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) {

View File

@ -3015,7 +3015,7 @@ public class TIFFImageWriter extends ImageWriter {
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;

View File

@ -216,22 +216,27 @@ second on the inferred color space.</p>
<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

View File

@ -46,6 +46,10 @@ import javax.imageio.ImageReadParam;
* {@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 @@ public final class TIFFImageReadParam extends ImageReadParam {
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 final class TIFFImageReadParam extends ImageReadParam {
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;
}
}

View File

@ -223,7 +223,7 @@ public class MultiPageImageTIFFFieldTest {
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");

View File

@ -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();
}
}
}
}
}

View File

@ -159,7 +159,7 @@ public class TIFFImageReadParamTest {
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);