/* * Copyright (c) 2005, 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. 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.getDirectoryAsIFD(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; Class<?> setClass = null; try { ClassLoader cl = TIFFImageMetadata.class.getClassLoader(); setClass = Class.forName(className, false, cl); if (!TIFFTagSet.class.isAssignableFrom(setClass)) { fatal(node, "TagSets in IFD must be subset of" + " TIFFTagSet class"); } 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} wherein all fields in the * root IFD from the {@code BaselineTIFFTagSet} are copied by value * and all other fields copied by reference. */ public TIFFImageMetadata getShallowClone() { return new TIFFImageMetadata(rootIFD.getShallowClone()); } }