--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageMetadata.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,1630 @@
+/*
+ * Copyright (c) 2005, 2015, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package com.sun.imageio.plugins.tiff;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.stream.ImageInputStream;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.ExifParentTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFTag;
+import javax.imageio.plugins.tiff.TIFFTagSet;
+
+public class TIFFImageMetadata extends IIOMetadata {
+
+ // package scope
+
+ public static final String NATIVE_METADATA_FORMAT_NAME =
+ "javax_imageio_tiff_image_1.0";
+
+ public static final String NATIVE_METADATA_FORMAT_CLASS_NAME =
+ "javax.imageio.plugins.tiff.TIFFImageMetadataFormat";
+
+ private List<TIFFTagSet> tagSets;
+
+ TIFFIFD rootIFD;
+
+ public TIFFImageMetadata(List<TIFFTagSet> tagSets) {
+ super(true,
+ NATIVE_METADATA_FORMAT_NAME,
+ NATIVE_METADATA_FORMAT_CLASS_NAME,
+ null, null);
+
+ this.tagSets = tagSets;
+ this.rootIFD = new TIFFIFD(tagSets);
+ }
+
+ public TIFFImageMetadata(TIFFIFD ifd) {
+ super(true,
+ NATIVE_METADATA_FORMAT_NAME,
+ NATIVE_METADATA_FORMAT_CLASS_NAME,
+ null, null);
+ this.tagSets = ifd.getTagSetList();
+ this.rootIFD = ifd;
+ }
+
+ public void initializeFromStream(ImageInputStream stream,
+ boolean ignoreUnknownFields)
+ throws IOException {
+ rootIFD.initialize(stream, true, ignoreUnknownFields);
+ }
+
+ public void addShortOrLongField(int tagNumber, int value) {
+ TIFFField field = new TIFFField(rootIFD.getTag(tagNumber), value);
+ rootIFD.addTIFFField(field);
+ }
+
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ private Node getIFDAsTree(TIFFIFD ifd,
+ String parentTagName, int parentTagNumber) {
+ IIOMetadataNode IFDRoot = new IIOMetadataNode("TIFFIFD");
+ if (parentTagNumber != 0) {
+ IFDRoot.setAttribute("parentTagNumber",
+ Integer.toString(parentTagNumber));
+ }
+ if (parentTagName != null) {
+ IFDRoot.setAttribute("parentTagName", parentTagName);
+ }
+
+ List<TIFFTagSet> tagSets = ifd.getTagSetList();
+ if (tagSets.size() > 0) {
+ Iterator<TIFFTagSet> iter = tagSets.iterator();
+ StringBuilder tagSetNames = new StringBuilder();
+ while (iter.hasNext()) {
+ TIFFTagSet tagSet = iter.next();
+ tagSetNames.append(tagSet.getClass().getName());
+ if (iter.hasNext()) {
+ tagSetNames.append(",");
+ }
+ }
+
+ IFDRoot.setAttribute("tagSets", tagSetNames.toString());
+ }
+
+ Iterator<TIFFField> iter = ifd.iterator();
+ while (iter.hasNext()) {
+ TIFFField f = iter.next();
+ int tagNumber = f.getTagNumber();
+ TIFFTag tag = TIFFIFD.getTag(tagNumber, tagSets);
+
+ Node node = null;
+ if (tag == null) {
+ node = f.getAsNativeNode();
+ } else if (tag.isIFDPointer() && f.hasDirectory()) {
+ TIFFIFD subIFD = (TIFFIFD)f.getDirectory();
+
+ // Recurse
+ node = getIFDAsTree(subIFD, tag.getName(), tag.getNumber());
+ } else {
+ node = f.getAsNativeNode();
+ }
+
+ if (node != null) {
+ IFDRoot.appendChild(node);
+ }
+ }
+
+ return IFDRoot;
+ }
+
+ public Node getAsTree(String formatName) {
+ if (formatName.equals(nativeMetadataFormatName)) {
+ return getNativeTree();
+ } else if (formatName.equals
+ (IIOMetadataFormatImpl.standardMetadataFormatName)) {
+ return getStandardTree();
+ } else {
+ throw new IllegalArgumentException("Not a recognized format!");
+ }
+ }
+
+ private Node getNativeTree() {
+ IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName);
+
+ Node IFDNode = getIFDAsTree(rootIFD, null, 0);
+ root.appendChild(IFDNode);
+
+ return root;
+ }
+
+ private static final String[] colorSpaceNames = {
+ "GRAY", // WhiteIsZero
+ "GRAY", // BlackIsZero
+ "RGB", // RGB
+ "RGB", // PaletteColor
+ "GRAY", // TransparencyMask
+ "CMYK", // CMYK
+ "YCbCr", // YCbCr
+ "Lab", // CIELab
+ "Lab", // ICCLab
+ };
+
+ public IIOMetadataNode getStandardChromaNode() {
+ IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ // Set the PhotometricInterpretation and the palette color flag.
+ int photometricInterpretation = -1;
+ boolean isPaletteColor = false;
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if (f != null) {
+ photometricInterpretation = f.getAsInt(0);
+
+ isPaletteColor =
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
+ }
+
+ // Determine the number of channels.
+ int numChannels = -1;
+ if(isPaletteColor) {
+ numChannels = 3;
+ } else {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+ if (f != null) {
+ numChannels = f.getAsInt(0);
+ } else { // f == null
+ f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ if(f != null) {
+ numChannels = f.getCount();
+ }
+ }
+ }
+
+ if(photometricInterpretation != -1) {
+ if (photometricInterpretation >= 0 &&
+ photometricInterpretation < colorSpaceNames.length) {
+ node = new IIOMetadataNode("ColorSpaceType");
+ String csName;
+ if(photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK &&
+ numChannels == 3) {
+ csName = "CMY";
+ } else {
+ csName = colorSpaceNames[photometricInterpretation];
+ }
+ node.setAttribute("name", csName);
+ chroma_node.appendChild(node);
+ }
+
+ node = new IIOMetadataNode("BlackIsZero");
+ node.setAttribute("value",
+ (photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO)
+ ? "FALSE" : "TRUE");
+ chroma_node.appendChild(node);
+ }
+
+ if(numChannels != -1) {
+ node = new IIOMetadataNode("NumChannels");
+ node.setAttribute("value", Integer.toString(numChannels));
+ chroma_node.appendChild(node);
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP);
+ if (f != null) {
+ // NOTE: The presence of hasAlpha is vestigial: there is
+ // no way in TIFF to represent an alpha component in a palette
+ // color image. See bug 5086341.
+ boolean hasAlpha = false;
+
+ node = new IIOMetadataNode("Palette");
+ int len = f.getCount()/(hasAlpha ? 4 : 3);
+ for (int i = 0; i < len; i++) {
+ IIOMetadataNode entry =
+ new IIOMetadataNode("PaletteEntry");
+ entry.setAttribute("index", Integer.toString(i));
+
+ int r = (f.getAsInt(i)*255)/65535;
+ int g = (f.getAsInt(len + i)*255)/65535;
+ int b = (f.getAsInt(2*len + i)*255)/65535;
+
+ entry.setAttribute("red", Integer.toString(r));
+ entry.setAttribute("green", Integer.toString(g));
+ entry.setAttribute("blue", Integer.toString(b));
+ if (hasAlpha) {
+ int alpha = 0;
+ entry.setAttribute("alpha", Integer.toString(alpha));
+ }
+ node.appendChild(entry);
+ }
+ chroma_node.appendChild(node);
+ }
+
+ return chroma_node;
+ }
+
+ public IIOMetadataNode getStandardCompressionNode() {
+ IIOMetadataNode compression_node = new IIOMetadataNode("Compression");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ if (f != null) {
+ String compressionTypeName = null;
+ int compression = f.getAsInt(0);
+ boolean isLossless = true; // obligate initialization.
+ if(compression == BaselineTIFFTagSet.COMPRESSION_NONE) {
+ compressionTypeName = "None";
+ isLossless = true;
+ } else {
+ int[] compressionNumbers = TIFFImageWriter.compressionNumbers;
+ for(int i = 0; i < compressionNumbers.length; i++) {
+ if(compression == compressionNumbers[i]) {
+ compressionTypeName =
+ TIFFImageWriter.compressionTypes[i];
+ isLossless =
+ TIFFImageWriter.isCompressionLossless[i];
+ break;
+ }
+ }
+ }
+
+ if (compressionTypeName != null) {
+ node = new IIOMetadataNode("CompressionTypeName");
+ node.setAttribute("value", compressionTypeName);
+ compression_node.appendChild(node);
+
+ node = new IIOMetadataNode("Lossless");
+ node.setAttribute("value", isLossless ? "TRUE" : "FALSE");
+ compression_node.appendChild(node);
+ }
+ }
+
+ node = new IIOMetadataNode("NumProgressiveScans");
+ node.setAttribute("value", "1");
+ compression_node.appendChild(node);
+
+ return compression_node;
+ }
+
+ private String repeat(String s, int times) {
+ if (times == 1) {
+ return s;
+ }
+ StringBuffer sb = new StringBuffer((s.length() + 1)*times - 1);
+ sb.append(s);
+ for (int i = 1; i < times; i++) {
+ sb.append(" ");
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+
+ public IIOMetadataNode getStandardDataNode() {
+ IIOMetadataNode data_node = new IIOMetadataNode("Data");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ boolean isPaletteColor = false;
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if (f != null) {
+ isPaletteColor =
+ f.getAsInt(0) ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
+ String planarConfiguration = "PixelInterleaved";
+ if (f != null &&
+ f.getAsInt(0) == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
+ planarConfiguration = "PlaneInterleaved";
+ }
+
+ node = new IIOMetadataNode("PlanarConfiguration");
+ node.setAttribute("value", planarConfiguration);
+ data_node.appendChild(node);
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if (f != null) {
+ int photometricInterpretation = f.getAsInt(0);
+ String sampleFormat = "UnsignedIntegral";
+
+ if (photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR) {
+ sampleFormat = "Index";
+ } else {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
+ if (f != null) {
+ int format = f.getAsInt(0);
+ if (format ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER) {
+ sampleFormat = "SignedIntegral";
+ } else if (format ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER) {
+ sampleFormat = "UnsignedIntegral";
+ } else if (format ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ sampleFormat = "Real";
+ } else {
+ sampleFormat = null; // don't know
+ }
+ }
+ }
+ if (sampleFormat != null) {
+ node = new IIOMetadataNode("SampleFormat");
+ node.setAttribute("value", sampleFormat);
+ data_node.appendChild(node);
+ }
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ int[] bitsPerSample = null;
+ if(f != null) {
+ bitsPerSample = f.getAsInts();
+ } else {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ int compression = f != null ?
+ f.getAsInt(0) : BaselineTIFFTagSet.COMPRESSION_NONE;
+ if(getTIFFField(ExifParentTIFFTagSet.TAG_EXIF_IFD_POINTER) !=
+ null ||
+ compression == BaselineTIFFTagSet.COMPRESSION_JPEG ||
+ compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG ||
+ getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) !=
+ null) {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if(f != null &&
+ (f.getAsInt(0) ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO ||
+ f.getAsInt(0) ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO)) {
+ bitsPerSample = new int[] {8};
+ } else {
+ bitsPerSample = new int[] {8, 8, 8};
+ }
+ } else {
+ bitsPerSample = new int[] {1};
+ }
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ if (i > 0) {
+ sb.append(" ");
+ }
+ sb.append(Integer.toString(bitsPerSample[i]));
+ }
+ node = new IIOMetadataNode("BitsPerSample");
+ if(isPaletteColor) {
+ node.setAttribute("value", repeat(sb.toString(), 3));
+ } else {
+ node.setAttribute("value", sb.toString());
+ }
+ data_node.appendChild(node);
+
+ // SampleMSB
+ f = getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);
+ int fillOrder = f != null ?
+ f.getAsInt(0) : BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT;
+ sb = new StringBuffer();
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ if (i > 0) {
+ sb.append(" ");
+ }
+ int maxBitIndex = bitsPerSample[i] == 1 ?
+ 7 : bitsPerSample[i] - 1;
+ int msb =
+ fillOrder == BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT ?
+ maxBitIndex : 0;
+ sb.append(Integer.toString(msb));
+ }
+ node = new IIOMetadataNode("SampleMSB");
+ if(isPaletteColor) {
+ node.setAttribute("value", repeat(sb.toString(), 3));
+ } else {
+ node.setAttribute("value", sb.toString());
+ }
+ data_node.appendChild(node);
+
+ return data_node;
+ }
+
+ private static final String[] orientationNames = {
+ null,
+ "Normal",
+ "FlipH",
+ "Rotate180",
+ "FlipV",
+ "FlipHRotate90",
+ "Rotate270",
+ "FlipVRotate90",
+ "Rotate90",
+ };
+
+ public IIOMetadataNode getStandardDimensionNode() {
+ IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ long[] xres = null;
+ long[] yres = null;
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_X_RESOLUTION);
+ if (f != null) {
+ xres = f.getAsRational(0).clone();
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
+ if (f != null) {
+ yres = f.getAsRational(0).clone();
+ }
+
+ if (xres != null && yres != null) {
+ node = new IIOMetadataNode("PixelAspectRatio");
+
+ // Compute (1/xres)/(1/yres)
+ // (xres_denom/xres_num)/(yres_denom/yres_num) =
+ // (xres_denom/xres_num)*(yres_num/yres_denom) =
+ // (xres_denom*yres_num)/(xres_num*yres_denom)
+ float ratio = (float)((double)xres[1]*yres[0])/(xres[0]*yres[1]);
+ node.setAttribute("value", Float.toString(ratio));
+ dimension_node.appendChild(node);
+ }
+
+ if (xres != null || yres != null) {
+ // Get unit field.
+ f = getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT);
+
+ // Set resolution unit.
+ int resolutionUnit = f != null ?
+ f.getAsInt(0) : BaselineTIFFTagSet.RESOLUTION_UNIT_INCH;
+
+ // Have size if either centimeters or inches.
+ boolean gotPixelSize =
+ resolutionUnit != BaselineTIFFTagSet.RESOLUTION_UNIT_NONE;
+
+ // Convert pixels/inch to pixels/centimeter.
+ if (resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) {
+ // Divide xres by 2.54
+ if (xres != null) {
+ xres[0] *= 100;
+ xres[1] *= 254;
+ }
+
+ // Divide yres by 2.54
+ if (yres != null) {
+ yres[0] *= 100;
+ yres[1] *= 254;
+ }
+ }
+
+ if (gotPixelSize) {
+ if (xres != null) {
+ float horizontalPixelSize = (float)(10.0*xres[1]/xres[0]);
+ node = new IIOMetadataNode("HorizontalPixelSize");
+ node.setAttribute("value",
+ Float.toString(horizontalPixelSize));
+ dimension_node.appendChild(node);
+ }
+
+ if (yres != null) {
+ float verticalPixelSize = (float)(10.0*yres[1]/yres[0]);
+ node = new IIOMetadataNode("VerticalPixelSize");
+ node.setAttribute("value",
+ Float.toString(verticalPixelSize));
+ dimension_node.appendChild(node);
+ }
+ }
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT);
+ int resolutionUnit = f != null ?
+ f.getAsInt(0) : BaselineTIFFTagSet.RESOLUTION_UNIT_INCH;
+ if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH ||
+ resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER) {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_X_POSITION);
+ if(f != null) {
+ long[] xpos = f.getAsRational(0);
+ float xPosition = (float)xpos[0]/(float)xpos[1];
+ // Convert to millimeters.
+ if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) {
+ xPosition *= 254F;
+ } else {
+ xPosition *= 10F;
+ }
+ node = new IIOMetadataNode("HorizontalPosition");
+ node.setAttribute("value",
+ Float.toString(xPosition));
+ dimension_node.appendChild(node);
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_Y_POSITION);
+ if(f != null) {
+ long[] ypos = f.getAsRational(0);
+ float yPosition = (float)ypos[0]/(float)ypos[1];
+ // Convert to millimeters.
+ if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) {
+ yPosition *= 254F;
+ } else {
+ yPosition *= 10F;
+ }
+ node = new IIOMetadataNode("VerticalPosition");
+ node.setAttribute("value",
+ Float.toString(yPosition));
+ dimension_node.appendChild(node);
+ }
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_ORIENTATION);
+ if (f != null) {
+ int o = f.getAsInt(0);
+ if (o >= 0 && o < orientationNames.length) {
+ node = new IIOMetadataNode("ImageOrientation");
+ node.setAttribute("value", orientationNames[o]);
+ dimension_node.appendChild(node);
+ }
+ }
+
+ return dimension_node;
+ }
+
+ public IIOMetadataNode getStandardDocumentNode() {
+ IIOMetadataNode document_node = new IIOMetadataNode("Document");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ node = new IIOMetadataNode("FormatVersion");
+ node.setAttribute("value", "6.0");
+ document_node.appendChild(node);
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_NEW_SUBFILE_TYPE);
+ if(f != null) {
+ int newSubFileType = f.getAsInt(0);
+ String value = null;
+ if((newSubFileType &
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_TRANSPARENCY) != 0) {
+ value = "TransparencyMask";
+ } else if((newSubFileType &
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_REDUCED_RESOLUTION) != 0) {
+ value = "ReducedResolution";
+ } else if((newSubFileType &
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_SINGLE_PAGE) != 0) {
+ value = "SinglePage";
+ }
+ if(value != null) {
+ node = new IIOMetadataNode("SubimageInterpretation");
+ node.setAttribute("value", value);
+ document_node.appendChild(node);
+ }
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_DATE_TIME);
+ if (f != null) {
+ String s = f.getAsString(0);
+
+ // DateTime should be formatted as "YYYY:MM:DD hh:mm:ss".
+ if(s.length() == 19) {
+ node = new IIOMetadataNode("ImageCreationTime");
+
+ // Files with incorrect DateTime format have been
+ // observed so anticipate an exception from substring()
+ // and only add the node if the format is presumably
+ // correct.
+ boolean appendNode;
+ try {
+ node.setAttribute("year", s.substring(0, 4));
+ node.setAttribute("month", s.substring(5, 7));
+ node.setAttribute("day", s.substring(8, 10));
+ node.setAttribute("hour", s.substring(11, 13));
+ node.setAttribute("minute", s.substring(14, 16));
+ node.setAttribute("second", s.substring(17, 19));
+ appendNode = true;
+ } catch(IndexOutOfBoundsException e) {
+ appendNode = false;
+ }
+
+ if(appendNode) {
+ document_node.appendChild(node);
+ }
+ }
+ }
+
+ return document_node;
+ }
+
+ public IIOMetadataNode getStandardTextNode() {
+ IIOMetadataNode text_node = null;
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ int[] textFieldTagNumbers = new int[] {
+ BaselineTIFFTagSet.TAG_DOCUMENT_NAME,
+ BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION,
+ BaselineTIFFTagSet.TAG_MAKE,
+ BaselineTIFFTagSet.TAG_MODEL,
+ BaselineTIFFTagSet.TAG_PAGE_NAME,
+ BaselineTIFFTagSet.TAG_SOFTWARE,
+ BaselineTIFFTagSet.TAG_ARTIST,
+ BaselineTIFFTagSet.TAG_HOST_COMPUTER,
+ BaselineTIFFTagSet.TAG_INK_NAMES,
+ BaselineTIFFTagSet.TAG_COPYRIGHT
+ };
+
+ for(int i = 0; i < textFieldTagNumbers.length; i++) {
+ f = getTIFFField(textFieldTagNumbers[i]);
+ if(f != null) {
+ String value = f.getAsString(0);
+ if(text_node == null) {
+ text_node = new IIOMetadataNode("Text");
+ }
+ node = new IIOMetadataNode("TextEntry");
+ node.setAttribute("keyword", f.getTag().getName());
+ node.setAttribute("value", value);
+ text_node.appendChild(node);
+ }
+ }
+
+ return text_node;
+ }
+
+ public IIOMetadataNode getStandardTransparencyNode() {
+ IIOMetadataNode transparency_node =
+ new IIOMetadataNode("Transparency");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ node = new IIOMetadataNode("Alpha");
+ String value = "none";
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
+ if(f != null) {
+ int[] extraSamples = f.getAsInts();
+ for(int i = 0; i < extraSamples.length; i++) {
+ if(extraSamples[i] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ value = "premultiplied";
+ break;
+ } else if(extraSamples[i] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_UNASSOCIATED_ALPHA) {
+ value = "nonpremultiplied";
+ break;
+ }
+ }
+ }
+
+ node.setAttribute("value", value);
+ transparency_node.appendChild(node);
+
+ return transparency_node;
+ }
+
+ // Shorthand for throwing an IIOInvalidTreeException
+ private static void fatal(Node node, String reason)
+ throws IIOInvalidTreeException {
+ throw new IIOInvalidTreeException(reason, node);
+ }
+
+ private int[] listToIntArray(String list) {
+ StringTokenizer st = new StringTokenizer(list, " ");
+ ArrayList<Integer> intList = new ArrayList<Integer>();
+ while (st.hasMoreTokens()) {
+ String nextInteger = st.nextToken();
+ Integer nextInt = Integer.valueOf(nextInteger);
+ intList.add(nextInt);
+ }
+
+ int[] intArray = new int[intList.size()];
+ for(int i = 0; i < intArray.length; i++) {
+ intArray[i] = intList.get(i);
+ }
+
+ return intArray;
+ }
+
+ private char[] listToCharArray(String list) {
+ StringTokenizer st = new StringTokenizer(list, " ");
+ ArrayList<Integer> intList = new ArrayList<Integer>();
+ while (st.hasMoreTokens()) {
+ String nextInteger = st.nextToken();
+ Integer nextInt = Integer.valueOf(nextInteger);
+ intList.add(nextInt);
+ }
+
+ char[] charArray = new char[intList.size()];
+ for(int i = 0; i < charArray.length; i++) {
+ charArray[i] = (char)(intList.get(i).intValue());
+ }
+
+ return charArray;
+ }
+
+ private void mergeStandardTree(Node root)
+ throws IIOInvalidTreeException {
+ TIFFField f;
+ TIFFTag tag;
+
+ Node node = root;
+ if (!node.getNodeName()
+ .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
+ fatal(node, "Root must be " +
+ IIOMetadataFormatImpl.standardMetadataFormatName);
+ }
+
+ // Obtain the sample format and set the palette flag if appropriate.
+ String sampleFormat = null;
+ Node dataNode = getChildNode(root, "Data");
+ boolean isPaletteColor = false;
+ if(dataNode != null) {
+ Node sampleFormatNode = getChildNode(dataNode, "SampleFormat");
+ if(sampleFormatNode != null) {
+ sampleFormat = getAttribute(sampleFormatNode, "value");
+ isPaletteColor = sampleFormat.equals("Index");
+ }
+ }
+
+ // If palette flag not set check for palette.
+ if(!isPaletteColor) {
+ Node chromaNode = getChildNode(root, "Chroma");
+ if(chromaNode != null &&
+ getChildNode(chromaNode, "Palette") != null) {
+ isPaletteColor = true;
+ }
+ }
+
+ node = node.getFirstChild();
+ while (node != null) {
+ String name = node.getNodeName();
+
+ if (name.equals("Chroma")) {
+ String colorSpaceType = null;
+ String blackIsZero = null;
+ boolean gotPalette = false;
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+ if (childName.equals("ColorSpaceType")) {
+ colorSpaceType = getAttribute(child, "name");
+ } else if (childName.equals("NumChannels")) {
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+ int samplesPerPixel = isPaletteColor ?
+ 1 : Integer.parseInt(getAttribute(child, "value"));
+ f = new TIFFField(tag, samplesPerPixel);
+ rootIFD.addTIFFField(f);
+ } else if (childName.equals("BlackIsZero")) {
+ blackIsZero = getAttribute(child, "value");
+ } else if (childName.equals("Palette")) {
+ Node entry = child.getFirstChild();
+ HashMap<Integer,char[]> palette = new HashMap<>();
+ int maxIndex = -1;
+ while(entry != null) {
+ String entryName = entry.getNodeName();
+ if(entryName.equals("PaletteEntry")) {
+ String idx = getAttribute(entry, "index");
+ int id = Integer.parseInt(idx);
+ if(id > maxIndex) {
+ maxIndex = id;
+ }
+ char red =
+ (char)Integer.parseInt(getAttribute(entry,
+ "red"));
+ char green =
+ (char)Integer.parseInt(getAttribute(entry,
+ "green"));
+ char blue =
+ (char)Integer.parseInt(getAttribute(entry,
+ "blue"));
+ palette.put(Integer.valueOf(id),
+ new char[] {red, green, blue});
+
+ gotPalette = true;
+ }
+ entry = entry.getNextSibling();
+ }
+
+ if(gotPalette) {
+ int mapSize = maxIndex + 1;
+ int paletteLength = 3*mapSize;
+ char[] paletteEntries = new char[paletteLength];
+ Iterator<Map.Entry<Integer,char[]>> paletteIter
+ = palette.entrySet().iterator();
+ while(paletteIter.hasNext()) {
+ Map.Entry<Integer,char[]> paletteEntry
+ = paletteIter.next();
+ int index = paletteEntry.getKey();
+ char[] rgb = paletteEntry.getValue();
+ paletteEntries[index] =
+ (char)((rgb[0]*65535)/255);
+ paletteEntries[mapSize + index] =
+ (char)((rgb[1]*65535)/255);
+ paletteEntries[2*mapSize + index] =
+ (char)((rgb[2]*65535)/255);
+ }
+
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_COLOR_MAP);
+ f = new TIFFField(tag, TIFFTag.TIFF_SHORT,
+ paletteLength, paletteEntries);
+ rootIFD.addTIFFField(f);
+ }
+ }
+
+ child = child.getNextSibling();
+ }
+
+ int photometricInterpretation = -1;
+ if((colorSpaceType == null || colorSpaceType.equals("GRAY")) &&
+ blackIsZero != null &&
+ blackIsZero.equalsIgnoreCase("FALSE")) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
+ } else if(colorSpaceType != null) {
+ if(colorSpaceType.equals("GRAY")) {
+ boolean isTransparency = false;
+ if(root instanceof IIOMetadataNode) {
+ IIOMetadataNode iioRoot = (IIOMetadataNode)root;
+ NodeList siNodeList =
+ iioRoot.getElementsByTagName("SubimageInterpretation");
+ if(siNodeList.getLength() == 1) {
+ Node siNode = siNodeList.item(0);
+ String value = getAttribute(siNode, "value");
+ if(value.equals("TransparencyMask")) {
+ isTransparency = true;
+ }
+ }
+ }
+ if(isTransparency) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK;
+ } else {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
+ }
+ } else if(colorSpaceType.equals("RGB")) {
+ photometricInterpretation =
+ gotPalette ?
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR :
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
+ } else if(colorSpaceType.equals("YCbCr")) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR;
+ } else if(colorSpaceType.equals("CMYK")) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK;
+ } else if(colorSpaceType.equals("Lab")) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB;
+ }
+ }
+
+ if(photometricInterpretation != -1) {
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ f = new TIFFField(tag, photometricInterpretation);
+ rootIFD.addTIFFField(f);
+ }
+ } else if (name.equals("Compression")) {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+ if (childName.equals("CompressionTypeName")) {
+ int compression = -1;
+ String compressionTypeName =
+ getAttribute(child, "value");
+ if(compressionTypeName.equalsIgnoreCase("None")) {
+ compression =
+ BaselineTIFFTagSet.COMPRESSION_NONE;
+ } else {
+ String[] compressionNames =
+ TIFFImageWriter.compressionTypes;
+ for(int i = 0; i < compressionNames.length; i++) {
+ if(compressionNames[i].equalsIgnoreCase(compressionTypeName)) {
+ compression =
+ TIFFImageWriter.compressionNumbers[i];
+ break;
+ }
+ }
+ }
+
+ if(compression != -1) {
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_COMPRESSION);
+ f = new TIFFField(tag, compression);
+ rootIFD.addTIFFField(f);
+
+ // Lossless is irrelevant.
+ }
+ }
+
+ child = child.getNextSibling();
+ }
+ } else if (name.equals("Data")) {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+
+ if (childName.equals("PlanarConfiguration")) {
+ String pc = getAttribute(child, "value");
+ int planarConfiguration = -1;
+ if(pc.equals("PixelInterleaved")) {
+ planarConfiguration =
+ BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
+ } else if(pc.equals("PlaneInterleaved")) {
+ planarConfiguration =
+ BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR;
+ }
+ if(planarConfiguration != -1) {
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
+ f = new TIFFField(tag, planarConfiguration);
+ rootIFD.addTIFFField(f);
+ }
+ } else if (childName.equals("BitsPerSample")) {
+ String bps = getAttribute(child, "value");
+ char[] bitsPerSample = listToCharArray(bps);
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ if(isPaletteColor) {
+ f = new TIFFField(tag, TIFFTag.TIFF_SHORT, 1,
+ new char[] {bitsPerSample[0]});
+ } else {
+ f = new TIFFField(tag, TIFFTag.TIFF_SHORT,
+ bitsPerSample.length,
+ bitsPerSample);
+ }
+ rootIFD.addTIFFField(f);
+ } else if (childName.equals("SampleMSB")) {
+ // Add FillOrder only if lsb-to-msb (right to left)
+ // for all bands, i.e., SampleMSB is zero for all
+ // channels.
+ String sMSB = getAttribute(child, "value");
+ int[] sampleMSB = listToIntArray(sMSB);
+ boolean isRightToLeft = true;
+ for(int i = 0; i < sampleMSB.length; i++) {
+ if(sampleMSB[i] != 0) {
+ isRightToLeft = false;
+ break;
+ }
+ }
+ int fillOrder = isRightToLeft ?
+ BaselineTIFFTagSet.FILL_ORDER_RIGHT_TO_LEFT :
+ BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT;
+ tag =
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_FILL_ORDER);
+ f = new TIFFField(tag, fillOrder);
+ rootIFD.addTIFFField(f);
+ }
+
+ child = child.getNextSibling();
+ }
+ } else if (name.equals("Dimension")) {
+ float pixelAspectRatio = -1.0f;
+ boolean gotPixelAspectRatio = false;
+
+ float horizontalPixelSize = -1.0f;
+ boolean gotHorizontalPixelSize = false;
+
+ float verticalPixelSize = -1.0f;
+ boolean gotVerticalPixelSize = false;
+
+ boolean sizeIsAbsolute = false;
+
+ float horizontalPosition = -1.0f;
+ boolean gotHorizontalPosition = false;
+
+ float verticalPosition = -1.0f;
+ boolean gotVerticalPosition = false;
+
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+ if (childName.equals("PixelAspectRatio")) {
+ String par = getAttribute(child, "value");
+ pixelAspectRatio = Float.parseFloat(par);
+ gotPixelAspectRatio = true;
+ } else if (childName.equals("ImageOrientation")) {
+ String orientation = getAttribute(child, "value");
+ for (int i = 0; i < orientationNames.length; i++) {
+ if (orientation.equals(orientationNames[i])) {
+ char[] oData = new char[1];
+ oData[0] = (char)i;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_ORIENTATION),
+ TIFFTag.TIFF_SHORT,
+ 1,
+ oData);
+
+ rootIFD.addTIFFField(f);
+ break;
+ }
+ }
+
+ } else if (childName.equals("HorizontalPixelSize")) {
+ String hps = getAttribute(child, "value");
+ horizontalPixelSize = Float.parseFloat(hps);
+ gotHorizontalPixelSize = true;
+ } else if (childName.equals("VerticalPixelSize")) {
+ String vps = getAttribute(child, "value");
+ verticalPixelSize = Float.parseFloat(vps);
+ gotVerticalPixelSize = true;
+ } else if (childName.equals("HorizontalPosition")) {
+ String hp = getAttribute(child, "value");
+ horizontalPosition = Float.parseFloat(hp);
+ gotHorizontalPosition = true;
+ } else if (childName.equals("VerticalPosition")) {
+ String vp = getAttribute(child, "value");
+ verticalPosition = Float.parseFloat(vp);
+ gotVerticalPosition = true;
+ }
+
+ child = child.getNextSibling();
+ }
+
+ sizeIsAbsolute = gotHorizontalPixelSize ||
+ gotVerticalPixelSize;
+
+ // Fill in pixel size data from aspect ratio
+ if (gotPixelAspectRatio) {
+ if (gotHorizontalPixelSize && !gotVerticalPixelSize) {
+ verticalPixelSize =
+ horizontalPixelSize/pixelAspectRatio;
+ gotVerticalPixelSize = true;
+ } else if (gotVerticalPixelSize &&
+ !gotHorizontalPixelSize) {
+ horizontalPixelSize =
+ verticalPixelSize*pixelAspectRatio;
+ gotHorizontalPixelSize = true;
+ } else if (!gotHorizontalPixelSize &&
+ !gotVerticalPixelSize) {
+ horizontalPixelSize = pixelAspectRatio;
+ verticalPixelSize = 1.0f;
+ gotHorizontalPixelSize = true;
+ gotVerticalPixelSize = true;
+ }
+ }
+
+ // Compute pixels/centimeter
+ if (gotHorizontalPixelSize) {
+ float xResolution =
+ (sizeIsAbsolute ? 10.0f : 1.0f)/horizontalPixelSize;
+ long[][] hData = new long[1][2];
+ hData[0] = new long[2];
+ hData[0][0] = (long)(xResolution*10000.0f);
+ hData[0][1] = (long)10000;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ hData);
+ rootIFD.addTIFFField(f);
+ }
+
+ if (gotVerticalPixelSize) {
+ float yResolution =
+ (sizeIsAbsolute ? 10.0f : 1.0f)/verticalPixelSize;
+ long[][] vData = new long[1][2];
+ vData[0] = new long[2];
+ vData[0][0] = (long)(yResolution*10000.0f);
+ vData[0][1] = (long)10000;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ vData);
+ rootIFD.addTIFFField(f);
+ }
+
+ // Emit ResolutionUnit tag
+ char[] res = new char[1];
+ res[0] = (char)(sizeIsAbsolute ?
+ BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER :
+ BaselineTIFFTagSet.RESOLUTION_UNIT_NONE);
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
+ TIFFTag.TIFF_SHORT,
+ 1,
+ res);
+ rootIFD.addTIFFField(f);
+
+ // Position
+ if(sizeIsAbsolute) {
+ if(gotHorizontalPosition) {
+ // Convert from millimeters to centimeters via
+ // numerator multiplier = denominator/10.
+ long[][] hData = new long[1][2];
+ hData[0][0] = (long)(horizontalPosition*10000.0f);
+ hData[0][1] = (long)100000;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_X_POSITION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ hData);
+ rootIFD.addTIFFField(f);
+ }
+
+ if(gotVerticalPosition) {
+ // Convert from millimeters to centimeters via
+ // numerator multiplier = denominator/10.
+ long[][] vData = new long[1][2];
+ vData[0][0] = (long)(verticalPosition*10000.0f);
+ vData[0][1] = (long)100000;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_POSITION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ vData);
+ rootIFD.addTIFFField(f);
+ }
+ }
+ } else if (name.equals("Document")) {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+
+ if (childName.equals("SubimageInterpretation")) {
+ String si = getAttribute(child, "value");
+ int newSubFileType = -1;
+ if(si.equals("TransparencyMask")) {
+ newSubFileType =
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_TRANSPARENCY;
+ } else if(si.equals("ReducedResolution")) {
+ newSubFileType =
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_REDUCED_RESOLUTION;
+ } else if(si.equals("SinglePage")) {
+ newSubFileType =
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_SINGLE_PAGE;
+ }
+ if(newSubFileType != -1) {
+ tag =
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_NEW_SUBFILE_TYPE);
+ f = new TIFFField(tag, newSubFileType);
+ rootIFD.addTIFFField(f);
+ }
+ }
+
+ if (childName.equals("ImageCreationTime")) {
+ String year = getAttribute(child, "year");
+ String month = getAttribute(child, "month");
+ String day = getAttribute(child, "day");
+ String hour = getAttribute(child, "hour");
+ String minute = getAttribute(child, "minute");
+ String second = getAttribute(child, "second");
+
+ StringBuffer sb = new StringBuffer();
+ sb.append(year);
+ sb.append(":");
+ if(month.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(month);
+ sb.append(":");
+ if(day.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(day);
+ sb.append(" ");
+ if(hour.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(hour);
+ sb.append(":");
+ if(minute.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(minute);
+ sb.append(":");
+ if(second.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(second);
+
+ String[] dt = new String[1];
+ dt[0] = sb.toString();
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_DATE_TIME),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ dt);
+ rootIFD.addTIFFField(f);
+ }
+
+ child = child.getNextSibling();
+ }
+ } else if (name.equals("Text")) {
+ Node child = node.getFirstChild();
+ String theAuthor = null;
+ String theDescription = null;
+ String theTitle = null;
+ while (child != null) {
+ String childName = child.getNodeName();
+ if(childName.equals("TextEntry")) {
+ int tagNumber = -1;
+ NamedNodeMap childAttrs = child.getAttributes();
+ Node keywordNode = childAttrs.getNamedItem("keyword");
+ if(keywordNode != null) {
+ String keyword = keywordNode.getNodeValue();
+ String value = getAttribute(child, "value");
+ if(!keyword.equals("") && !value.equals("")) {
+ if(keyword.equalsIgnoreCase("DocumentName")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_DOCUMENT_NAME;
+ } else if(keyword.equalsIgnoreCase("ImageDescription")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION;
+ } else if(keyword.equalsIgnoreCase("Make")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_MAKE;
+ } else if(keyword.equalsIgnoreCase("Model")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_MODEL;
+ } else if(keyword.equalsIgnoreCase("PageName")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_PAGE_NAME;
+ } else if(keyword.equalsIgnoreCase("Software")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_SOFTWARE;
+ } else if(keyword.equalsIgnoreCase("Artist")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_ARTIST;
+ } else if(keyword.equalsIgnoreCase("HostComputer")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_HOST_COMPUTER;
+ } else if(keyword.equalsIgnoreCase("InkNames")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_INK_NAMES;
+ } else if(keyword.equalsIgnoreCase("Copyright")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_COPYRIGHT;
+ } else if(keyword.equalsIgnoreCase("author")) {
+ theAuthor = value;
+ } else if(keyword.equalsIgnoreCase("description")) {
+ theDescription = value;
+ } else if(keyword.equalsIgnoreCase("title")) {
+ theTitle = value;
+ }
+ if(tagNumber != -1) {
+ f = new TIFFField(rootIFD.getTag(tagNumber),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ new String[] {value});
+ rootIFD.addTIFFField(f);
+ }
+ }
+ }
+ }
+ child = child.getNextSibling();
+ } // child != null
+ if(theAuthor != null &&
+ getTIFFField(BaselineTIFFTagSet.TAG_ARTIST) == null) {
+ f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_ARTIST),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ new String[] {theAuthor});
+ rootIFD.addTIFFField(f);
+ }
+ if(theDescription != null &&
+ getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION) == null) {
+ f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ new String[] {theDescription});
+ rootIFD.addTIFFField(f);
+ }
+ if(theTitle != null &&
+ getTIFFField(BaselineTIFFTagSet.TAG_DOCUMENT_NAME) == null) {
+ f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_DOCUMENT_NAME),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ new String[] {theTitle});
+ rootIFD.addTIFFField(f);
+ }
+ } else if (name.equals("Transparency")) {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+
+ if (childName.equals("Alpha")) {
+ String alpha = getAttribute(child, "value");
+
+ f = null;
+ if (alpha.equals("premultiplied")) {
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES),
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA);
+ } else if (alpha.equals("nonpremultiplied")) {
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES),
+ BaselineTIFFTagSet.EXTRA_SAMPLES_UNASSOCIATED_ALPHA);
+ }
+ if (f != null) {
+ rootIFD.addTIFFField(f);
+ }
+ }
+
+ child = child.getNextSibling();
+ }
+ }
+
+ node = node.getNextSibling();
+ }
+
+ // Set SampleFormat.
+ if(sampleFormat != null) {
+ // Derive the value.
+ int sf = -1;
+ if(sampleFormat.equals("SignedIntegral")) {
+ sf = BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER;
+ } else if(sampleFormat.equals("UnsignedIntegral")) {
+ sf = BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER;
+ } else if(sampleFormat.equals("Real")) {
+ sf = BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT;
+ } else if(sampleFormat.equals("Index")) {
+ sf = BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER;
+ }
+
+ if(sf != -1) {
+ // Derive the count.
+ int count = 1;
+
+ // Try SamplesPerPixel first.
+ f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+ if(f != null) {
+ count = f.getAsInt(0);
+ } else {
+ // Try BitsPerSample.
+ f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ if(f != null) {
+ count = f.getCount();
+ }
+ }
+
+ char[] sampleFormatArray = new char[count];
+ Arrays.fill(sampleFormatArray, (char)sf);
+
+ // Add SampleFormat.
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
+ f = new TIFFField(tag, TIFFTag.TIFF_SHORT,
+ sampleFormatArray.length, sampleFormatArray);
+ rootIFD.addTIFFField(f);
+ }
+ }
+ }
+
+ private static String getAttribute(Node node, String attrName) {
+ NamedNodeMap attrs = node.getAttributes();
+ Node attr = attrs.getNamedItem(attrName);
+ return attr != null ? attr.getNodeValue() : null;
+ }
+
+ private Node getChildNode(Node node, String childName) {
+ Node childNode = null;
+ if(node.hasChildNodes()) {
+ NodeList childNodes = node.getChildNodes();
+ int length = childNodes.getLength();
+ for(int i = 0; i < length; i++) {
+ Node item = childNodes.item(i);
+ if(item.getNodeName().equals(childName)) {
+ childNode = item;
+ break;
+ }
+ }
+ }
+ return childNode;
+ }
+
+ public static TIFFIFD parseIFD(Node node) throws IIOInvalidTreeException {
+ if (!node.getNodeName().equals("TIFFIFD")) {
+ fatal(node, "Expected \"TIFFIFD\" node");
+ }
+
+ String tagSetNames = getAttribute(node, "tagSets");
+ List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(5);
+
+ if (tagSetNames != null) {
+ StringTokenizer st = new StringTokenizer(tagSetNames, ",");
+ while (st.hasMoreTokens()) {
+ String className = st.nextToken();
+
+ Object o = null;
+ try {
+ Class<?> setClass = Class.forName(className);
+ Method getInstanceMethod =
+ setClass.getMethod("getInstance", (Class[])null);
+ o = getInstanceMethod.invoke(null, (Object[])null);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (!(o instanceof TIFFTagSet)) {
+ fatal(node, "Specified tag set class \"" +
+ className +
+ "\" is not an instance of TIFFTagSet");
+ } else {
+ tagSets.add((TIFFTagSet)o);
+ }
+ }
+ }
+
+ TIFFIFD ifd = new TIFFIFD(tagSets);
+
+ node = node.getFirstChild();
+ while (node != null) {
+ String name = node.getNodeName();
+
+ TIFFField f = null;
+ if (name.equals("TIFFIFD")) {
+ TIFFIFD subIFD = parseIFD(node);
+ String parentTagName = getAttribute(node, "parentTagName");
+ String parentTagNumber = getAttribute(node, "parentTagNumber");
+ TIFFTag tag = null;
+ if(parentTagName != null) {
+ tag = TIFFIFD.getTag(parentTagName, tagSets);
+ } else if(parentTagNumber != null) {
+ int tagNumber = Integer.parseUnsignedInt(parentTagNumber);
+ tag = TIFFIFD.getTag(tagNumber, tagSets);
+ }
+
+ int type;
+ if (tag == null) {
+ type = TIFFTag.TIFF_LONG;
+ tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, 0, 1 << type);
+ } else {
+ if (tag.isDataTypeOK(TIFFTag.TIFF_IFD_POINTER)) {
+ type = TIFFTag.TIFF_IFD_POINTER;
+ } else if (tag.isDataTypeOK(TIFFTag.TIFF_LONG)) {
+ type = TIFFTag.TIFF_LONG;
+ } else {
+ for (type = TIFFTag.MAX_DATATYPE;
+ type >= TIFFTag.MIN_DATATYPE;
+ type--) {
+ if (tag.isDataTypeOK(type)) {
+ break;
+ }
+ }
+ }
+ }
+
+ f = new TIFFField(tag, type, 1L, subIFD);
+ } else if (name.equals("TIFFField")) {
+ int number = Integer.parseInt(getAttribute(node, "number"));
+
+ TIFFTagSet tagSet = null;
+ Iterator<TIFFTagSet> iter = tagSets.iterator();
+ while (iter.hasNext()) {
+ TIFFTagSet t = iter.next();
+ if (t.getTag(number) != null) {
+ tagSet = t;
+ break;
+ }
+ }
+
+ f = TIFFField.createFromMetadataNode(tagSet, node);
+ } else {
+ fatal(node,
+ "Expected either \"TIFFIFD\" or \"TIFFField\" node, got "
+ + name);
+ }
+
+ ifd.addTIFFField(f);
+ node = node.getNextSibling();
+ }
+
+ return ifd;
+ }
+
+ private void mergeNativeTree(Node root) throws IIOInvalidTreeException {
+ Node node = root;
+ if (!node.getNodeName().equals(nativeMetadataFormatName)) {
+ fatal(node, "Root must be " + nativeMetadataFormatName);
+ }
+
+ node = node.getFirstChild();
+ if (node == null || !node.getNodeName().equals("TIFFIFD")) {
+ fatal(root, "Root must have \"TIFFIFD\" child");
+ }
+ TIFFIFD ifd = parseIFD(node);
+
+ List<TIFFTagSet> rootIFDTagSets = rootIFD.getTagSetList();
+ Iterator<TIFFTagSet> tagSetIter = ifd.getTagSetList().iterator();
+ while(tagSetIter.hasNext()) {
+ Object o = tagSetIter.next();
+ if(o instanceof TIFFTagSet && !rootIFDTagSets.contains(o)) {
+ rootIFD.addTagSet((TIFFTagSet)o);
+ }
+ }
+
+ Iterator<TIFFField> ifdIter = ifd.iterator();
+ while(ifdIter.hasNext()) {
+ TIFFField field = ifdIter.next();
+ rootIFD.addTIFFField(field);
+ }
+ }
+
+ public void mergeTree(String formatName, Node root)
+ throws IIOInvalidTreeException{
+ if (formatName.equals(nativeMetadataFormatName)) {
+ if (root == null) {
+ throw new NullPointerException("root == null!");
+ }
+ mergeNativeTree(root);
+ } else if (formatName.equals
+ (IIOMetadataFormatImpl.standardMetadataFormatName)) {
+ if (root == null) {
+ throw new NullPointerException("root == null!");
+ }
+ mergeStandardTree(root);
+ } else {
+ throw new IllegalArgumentException("Not a recognized format!");
+ }
+ }
+
+ public void reset() {
+ rootIFD = new TIFFIFD(tagSets);
+ }
+
+ public TIFFIFD getRootIFD() {
+ return rootIFD;
+ }
+
+ public TIFFField getTIFFField(int tagNumber) {
+ return rootIFD.getTIFFField(tagNumber);
+ }
+
+ public void removeTIFFField(int tagNumber) {
+ rootIFD.removeTIFFField(tagNumber);
+ }
+
+ /**
+ * Returns a <code>TIFFImageMetadata</code> wherein all fields in the
+ * root IFD from the <code>BaselineTIFFTagSet</code> are copied by value
+ * and all other fields copied by reference.
+ */
+ public TIFFImageMetadata getShallowClone() {
+ return new TIFFImageMetadata(rootIFD.getShallowClone());
+ }
+}