1 /* 2 * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.imageio.plugins.tiff; 26 27 import java.io.IOException; 28 import java.lang.reflect.InvocationTargetException; 29 import java.lang.reflect.Method; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.HashMap; 33 import java.util.Iterator; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.StringTokenizer; 37 import javax.imageio.metadata.IIOMetadata; 38 import javax.imageio.metadata.IIOInvalidTreeException; 39 import javax.imageio.metadata.IIOMetadataFormatImpl; 40 import javax.imageio.metadata.IIOMetadataNode; 41 import javax.imageio.stream.ImageInputStream; 42 import org.w3c.dom.NamedNodeMap; 43 import org.w3c.dom.Node; 44 import org.w3c.dom.NodeList; 45 import javax.imageio.plugins.tiff.BaselineTIFFTagSet; 46 import javax.imageio.plugins.tiff.ExifParentTIFFTagSet; 47 import javax.imageio.plugins.tiff.TIFFField; 48 import javax.imageio.plugins.tiff.TIFFTag; 49 import javax.imageio.plugins.tiff.TIFFTagSet; 50 51 public class TIFFImageMetadata extends IIOMetadata { 52 53 // package scope 54 55 public static final String NATIVE_METADATA_FORMAT_NAME = 56 "javax_imageio_tiff_image_1.0"; 57 58 public static final String NATIVE_METADATA_FORMAT_CLASS_NAME = 59 "javax.imageio.plugins.tiff.TIFFImageMetadataFormat"; 60 61 private List<TIFFTagSet> tagSets; 62 63 TIFFIFD rootIFD; 64 65 public TIFFImageMetadata(List<TIFFTagSet> tagSets) { 66 super(true, 67 NATIVE_METADATA_FORMAT_NAME, 68 NATIVE_METADATA_FORMAT_CLASS_NAME, 69 null, null); 70 71 this.tagSets = tagSets; 72 this.rootIFD = new TIFFIFD(tagSets); 73 } 74 75 public TIFFImageMetadata(TIFFIFD ifd) { 76 super(true, 77 NATIVE_METADATA_FORMAT_NAME, 78 NATIVE_METADATA_FORMAT_CLASS_NAME, 79 null, null); 80 this.tagSets = ifd.getTagSetList(); 81 this.rootIFD = ifd; 82 } 83 84 public void initializeFromStream(ImageInputStream stream, 85 boolean ignoreUnknownFields) 86 throws IOException { 87 rootIFD.initialize(stream, true, ignoreUnknownFields); 88 } 89 90 public void addShortOrLongField(int tagNumber, int value) { 91 TIFFField field = new TIFFField(rootIFD.getTag(tagNumber), value); 92 rootIFD.addTIFFField(field); 93 } 94 95 public boolean isReadOnly() { 96 return false; 97 } 98 99 private Node getIFDAsTree(TIFFIFD ifd, 100 String parentTagName, int parentTagNumber) { 101 IIOMetadataNode IFDRoot = new IIOMetadataNode("TIFFIFD"); 102 if (parentTagNumber != 0) { 103 IFDRoot.setAttribute("parentTagNumber", 104 Integer.toString(parentTagNumber)); 105 } 106 if (parentTagName != null) { 107 IFDRoot.setAttribute("parentTagName", parentTagName); 108 } 109 110 List<TIFFTagSet> tagSets = ifd.getTagSetList(); 111 if (tagSets.size() > 0) { 112 Iterator<TIFFTagSet> iter = tagSets.iterator(); 113 StringBuilder tagSetNames = new StringBuilder(); 114 while (iter.hasNext()) { 115 TIFFTagSet tagSet = iter.next(); 116 tagSetNames.append(tagSet.getClass().getName()); 117 if (iter.hasNext()) { 118 tagSetNames.append(","); 119 } 120 } 121 122 IFDRoot.setAttribute("tagSets", tagSetNames.toString()); 123 } 124 125 Iterator<TIFFField> iter = ifd.iterator(); 126 while (iter.hasNext()) { 127 TIFFField f = iter.next(); 128 int tagNumber = f.getTagNumber(); 129 TIFFTag tag = TIFFIFD.getTag(tagNumber, tagSets); 130 131 Node node = null; 132 if (tag == null) { 133 node = f.getAsNativeNode(); 134 } else if (tag.isIFDPointer() && f.hasDirectory()) { 135 TIFFIFD subIFD = TIFFIFD.getDirectoryAsIFD(f.getDirectory()); 136 137 // Recurse 138 node = getIFDAsTree(subIFD, tag.getName(), tag.getNumber()); 139 } else { 140 node = f.getAsNativeNode(); 141 } 142 143 if (node != null) { 144 IFDRoot.appendChild(node); 145 } 146 } 147 148 return IFDRoot; 149 } 150 151 public Node getAsTree(String formatName) { 152 if (formatName.equals(nativeMetadataFormatName)) { 153 return getNativeTree(); 154 } else if (formatName.equals 155 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 156 return getStandardTree(); 157 } else { 158 throw new IllegalArgumentException("Not a recognized format!"); 159 } 160 } 161 162 private Node getNativeTree() { 163 IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName); 164 165 Node IFDNode = getIFDAsTree(rootIFD, null, 0); 166 root.appendChild(IFDNode); 167 168 return root; 169 } 170 171 private static final String[] colorSpaceNames = { 172 "GRAY", // WhiteIsZero 173 "GRAY", // BlackIsZero 174 "RGB", // RGB 175 "RGB", // PaletteColor 176 "GRAY", // TransparencyMask 177 "CMYK", // CMYK 178 "YCbCr", // YCbCr 179 "Lab", // CIELab 180 "Lab", // ICCLab 181 }; 182 183 public IIOMetadataNode getStandardChromaNode() { 184 IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma"); 185 IIOMetadataNode node = null; // scratch node 186 187 TIFFField f; 188 189 // Set the PhotometricInterpretation and the palette color flag. 190 int photometricInterpretation = -1; 191 boolean isPaletteColor = false; 192 f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION); 193 if (f != null) { 194 photometricInterpretation = f.getAsInt(0); 195 196 isPaletteColor = 197 photometricInterpretation == 198 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR; 199 } 200 201 // Determine the number of channels. 202 int numChannels = -1; 203 if(isPaletteColor) { 204 numChannels = 3; 205 } else { 206 f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); 207 if (f != null) { 208 numChannels = f.getAsInt(0); 209 } else { // f == null 210 f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); 211 if(f != null) { 212 numChannels = f.getCount(); 213 } 214 } 215 } 216 217 if(photometricInterpretation != -1) { 218 if (photometricInterpretation >= 0 && 219 photometricInterpretation < colorSpaceNames.length) { 220 node = new IIOMetadataNode("ColorSpaceType"); 221 String csName; 222 if(photometricInterpretation == 223 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK && 224 numChannels == 3) { 225 csName = "CMY"; 226 } else { 227 csName = colorSpaceNames[photometricInterpretation]; 228 } 229 node.setAttribute("name", csName); 230 chroma_node.appendChild(node); 231 } 232 233 node = new IIOMetadataNode("BlackIsZero"); 234 node.setAttribute("value", 235 (photometricInterpretation == 236 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO) 237 ? "FALSE" : "TRUE"); 238 chroma_node.appendChild(node); 239 } 240 241 if(numChannels != -1) { 242 node = new IIOMetadataNode("NumChannels"); 243 node.setAttribute("value", Integer.toString(numChannels)); 244 chroma_node.appendChild(node); 245 } 246 247 f = getTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP); 248 if (f != null) { 249 // NOTE: The presence of hasAlpha is vestigial: there is 250 // no way in TIFF to represent an alpha component in a palette 251 // color image. See bug 5086341. 252 boolean hasAlpha = false; 253 254 node = new IIOMetadataNode("Palette"); 255 int len = f.getCount()/(hasAlpha ? 4 : 3); 256 for (int i = 0; i < len; i++) { 257 IIOMetadataNode entry = 258 new IIOMetadataNode("PaletteEntry"); 259 entry.setAttribute("index", Integer.toString(i)); 260 261 int r = (f.getAsInt(i)*255)/65535; 262 int g = (f.getAsInt(len + i)*255)/65535; 263 int b = (f.getAsInt(2*len + i)*255)/65535; 264 265 entry.setAttribute("red", Integer.toString(r)); 266 entry.setAttribute("green", Integer.toString(g)); 267 entry.setAttribute("blue", Integer.toString(b)); 268 if (hasAlpha) { 269 int alpha = 0; 270 entry.setAttribute("alpha", Integer.toString(alpha)); 271 } 272 node.appendChild(entry); 273 } 274 chroma_node.appendChild(node); 275 } 276 277 return chroma_node; 278 } 279 280 public IIOMetadataNode getStandardCompressionNode() { 281 IIOMetadataNode compression_node = new IIOMetadataNode("Compression"); 282 IIOMetadataNode node = null; // scratch node 283 284 TIFFField f; 285 286 f = getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION); 287 if (f != null) { 288 String compressionTypeName = null; 289 int compression = f.getAsInt(0); 290 boolean isLossless = true; // obligate initialization. 291 if(compression == BaselineTIFFTagSet.COMPRESSION_NONE) { 292 compressionTypeName = "None"; 293 isLossless = true; 294 } else { 295 int[] compressionNumbers = TIFFImageWriter.compressionNumbers; 296 for(int i = 0; i < compressionNumbers.length; i++) { 297 if(compression == compressionNumbers[i]) { 298 compressionTypeName = 299 TIFFImageWriter.compressionTypes[i]; 300 isLossless = 301 TIFFImageWriter.isCompressionLossless[i]; 302 break; 303 } 304 } 305 } 306 307 if (compressionTypeName != null) { 308 node = new IIOMetadataNode("CompressionTypeName"); 309 node.setAttribute("value", compressionTypeName); 310 compression_node.appendChild(node); 311 312 node = new IIOMetadataNode("Lossless"); 313 node.setAttribute("value", isLossless ? "TRUE" : "FALSE"); 314 compression_node.appendChild(node); 315 } 316 } 317 318 node = new IIOMetadataNode("NumProgressiveScans"); 319 node.setAttribute("value", "1"); 320 compression_node.appendChild(node); 321 322 return compression_node; 323 } 324 325 private String repeat(String s, int times) { 326 if (times == 1) { 327 return s; 328 } 329 StringBuffer sb = new StringBuffer((s.length() + 1)*times - 1); 330 sb.append(s); 331 for (int i = 1; i < times; i++) { 332 sb.append(" "); 333 sb.append(s); 334 } 335 return sb.toString(); 336 } 337 338 public IIOMetadataNode getStandardDataNode() { 339 IIOMetadataNode data_node = new IIOMetadataNode("Data"); 340 IIOMetadataNode node = null; // scratch node 341 342 TIFFField f; 343 344 boolean isPaletteColor = false; 345 f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION); 346 if (f != null) { 347 isPaletteColor = 348 f.getAsInt(0) == 349 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR; 350 } 351 352 f = getTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION); 353 String planarConfiguration = "PixelInterleaved"; 354 if (f != null && 355 f.getAsInt(0) == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) { 356 planarConfiguration = "PlaneInterleaved"; 357 } 358 359 node = new IIOMetadataNode("PlanarConfiguration"); 360 node.setAttribute("value", planarConfiguration); 361 data_node.appendChild(node); 362 363 f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION); 364 if (f != null) { 365 int photometricInterpretation = f.getAsInt(0); 366 String sampleFormat = "UnsignedIntegral"; 367 368 if (photometricInterpretation == 369 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR) { 370 sampleFormat = "Index"; 371 } else { 372 f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT); 373 if (f != null) { 374 int format = f.getAsInt(0); 375 if (format == 376 BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER) { 377 sampleFormat = "SignedIntegral"; 378 } else if (format == 379 BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER) { 380 sampleFormat = "UnsignedIntegral"; 381 } else if (format == 382 BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) { 383 sampleFormat = "Real"; 384 } else { 385 sampleFormat = null; // don't know 386 } 387 } 388 } 389 if (sampleFormat != null) { 390 node = new IIOMetadataNode("SampleFormat"); 391 node.setAttribute("value", sampleFormat); 392 data_node.appendChild(node); 393 } 394 } 395 396 f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); 397 int[] bitsPerSample = null; 398 if(f != null) { 399 bitsPerSample = f.getAsInts(); 400 } else { 401 f = getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION); 402 int compression = f != null ? 403 f.getAsInt(0) : BaselineTIFFTagSet.COMPRESSION_NONE; 404 if(getTIFFField(ExifParentTIFFTagSet.TAG_EXIF_IFD_POINTER) != 405 null || 406 compression == BaselineTIFFTagSet.COMPRESSION_JPEG || 407 compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG || 408 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) != 409 null) { 410 f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION); 411 if(f != null && 412 (f.getAsInt(0) == 413 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO || 414 f.getAsInt(0) == 415 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO)) { 416 bitsPerSample = new int[] {8}; 417 } else { 418 bitsPerSample = new int[] {8, 8, 8}; 419 } 420 } else { 421 bitsPerSample = new int[] {1}; 422 } 423 } 424 StringBuffer sb = new StringBuffer(); 425 for (int i = 0; i < bitsPerSample.length; i++) { 426 if (i > 0) { 427 sb.append(" "); 428 } 429 sb.append(Integer.toString(bitsPerSample[i])); 430 } 431 node = new IIOMetadataNode("BitsPerSample"); 432 if(isPaletteColor) { 433 node.setAttribute("value", repeat(sb.toString(), 3)); 434 } else { 435 node.setAttribute("value", sb.toString()); 436 } 437 data_node.appendChild(node); 438 439 // SampleMSB 440 f = getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER); 441 int fillOrder = f != null ? 442 f.getAsInt(0) : BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT; 443 sb = new StringBuffer(); 444 for (int i = 0; i < bitsPerSample.length; i++) { 445 if (i > 0) { 446 sb.append(" "); 447 } 448 int maxBitIndex = bitsPerSample[i] == 1 ? 449 7 : bitsPerSample[i] - 1; 450 int msb = 451 fillOrder == BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT ? 452 maxBitIndex : 0; 453 sb.append(Integer.toString(msb)); 454 } 455 node = new IIOMetadataNode("SampleMSB"); 456 if(isPaletteColor) { 457 node.setAttribute("value", repeat(sb.toString(), 3)); 458 } else { 459 node.setAttribute("value", sb.toString()); 460 } 461 data_node.appendChild(node); 462 463 return data_node; 464 } 465 466 private static final String[] orientationNames = { 467 null, 468 "Normal", 469 "FlipH", 470 "Rotate180", 471 "FlipV", 472 "FlipHRotate90", 473 "Rotate270", 474 "FlipVRotate90", 475 "Rotate90", 476 }; 477 478 public IIOMetadataNode getStandardDimensionNode() { 479 IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension"); 480 IIOMetadataNode node = null; // scratch node 481 482 TIFFField f; 483 484 long[] xres = null; 485 long[] yres = null; 486 487 f = getTIFFField(BaselineTIFFTagSet.TAG_X_RESOLUTION); 488 if (f != null) { 489 xres = f.getAsRational(0).clone(); 490 } 491 492 f = getTIFFField(BaselineTIFFTagSet.TAG_Y_RESOLUTION); 493 if (f != null) { 494 yres = f.getAsRational(0).clone(); 495 } 496 497 if (xres != null && yres != null) { 498 node = new IIOMetadataNode("PixelAspectRatio"); 499 500 // Compute (1/xres)/(1/yres) 501 // (xres_denom/xres_num)/(yres_denom/yres_num) = 502 // (xres_denom/xres_num)*(yres_num/yres_denom) = 503 // (xres_denom*yres_num)/(xres_num*yres_denom) 504 float ratio = (float)((double)xres[1]*yres[0])/(xres[0]*yres[1]); 505 node.setAttribute("value", Float.toString(ratio)); 506 dimension_node.appendChild(node); 507 } 508 509 if (xres != null || yres != null) { 510 // Get unit field. 511 f = getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT); 512 513 // Set resolution unit. 514 int resolutionUnit = f != null ? 515 f.getAsInt(0) : BaselineTIFFTagSet.RESOLUTION_UNIT_INCH; 516 517 // Have size if either centimeters or inches. 518 boolean gotPixelSize = 519 resolutionUnit != BaselineTIFFTagSet.RESOLUTION_UNIT_NONE; 520 521 // Convert pixels/inch to pixels/centimeter. 522 if (resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) { 523 // Divide xres by 2.54 524 if (xres != null) { 525 xres[0] *= 100; 526 xres[1] *= 254; 527 } 528 529 // Divide yres by 2.54 530 if (yres != null) { 531 yres[0] *= 100; 532 yres[1] *= 254; 533 } 534 } 535 536 if (gotPixelSize) { 537 if (xres != null) { 538 float horizontalPixelSize = (float)(10.0*xres[1]/xres[0]); 539 node = new IIOMetadataNode("HorizontalPixelSize"); 540 node.setAttribute("value", 541 Float.toString(horizontalPixelSize)); 542 dimension_node.appendChild(node); 543 } 544 545 if (yres != null) { 546 float verticalPixelSize = (float)(10.0*yres[1]/yres[0]); 547 node = new IIOMetadataNode("VerticalPixelSize"); 548 node.setAttribute("value", 549 Float.toString(verticalPixelSize)); 550 dimension_node.appendChild(node); 551 } 552 } 553 } 554 555 f = getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT); 556 int resolutionUnit = f != null ? 557 f.getAsInt(0) : BaselineTIFFTagSet.RESOLUTION_UNIT_INCH; 558 if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH || 559 resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER) { 560 f = getTIFFField(BaselineTIFFTagSet.TAG_X_POSITION); 561 if(f != null) { 562 long[] xpos = f.getAsRational(0); 563 float xPosition = (float)xpos[0]/(float)xpos[1]; 564 // Convert to millimeters. 565 if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) { 566 xPosition *= 254F; 567 } else { 568 xPosition *= 10F; 569 } 570 node = new IIOMetadataNode("HorizontalPosition"); 571 node.setAttribute("value", 572 Float.toString(xPosition)); 573 dimension_node.appendChild(node); 574 } 575 576 f = getTIFFField(BaselineTIFFTagSet.TAG_Y_POSITION); 577 if(f != null) { 578 long[] ypos = f.getAsRational(0); 579 float yPosition = (float)ypos[0]/(float)ypos[1]; 580 // Convert to millimeters. 581 if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) { 582 yPosition *= 254F; 583 } else { 584 yPosition *= 10F; 585 } 586 node = new IIOMetadataNode("VerticalPosition"); 587 node.setAttribute("value", 588 Float.toString(yPosition)); 589 dimension_node.appendChild(node); 590 } 591 } 592 593 f = getTIFFField(BaselineTIFFTagSet.TAG_ORIENTATION); 594 if (f != null) { 595 int o = f.getAsInt(0); 596 if (o >= 0 && o < orientationNames.length) { 597 node = new IIOMetadataNode("ImageOrientation"); 598 node.setAttribute("value", orientationNames[o]); 599 dimension_node.appendChild(node); 600 } 601 } 602 603 return dimension_node; 604 } 605 606 public IIOMetadataNode getStandardDocumentNode() { 607 IIOMetadataNode document_node = new IIOMetadataNode("Document"); 608 IIOMetadataNode node = null; // scratch node 609 610 TIFFField f; 611 612 node = new IIOMetadataNode("FormatVersion"); 613 node.setAttribute("value", "6.0"); 614 document_node.appendChild(node); 615 616 f = getTIFFField(BaselineTIFFTagSet.TAG_NEW_SUBFILE_TYPE); 617 if(f != null) { 618 int newSubFileType = f.getAsInt(0); 619 String value = null; 620 if((newSubFileType & 621 BaselineTIFFTagSet.NEW_SUBFILE_TYPE_TRANSPARENCY) != 0) { 622 value = "TransparencyMask"; 623 } else if((newSubFileType & 624 BaselineTIFFTagSet.NEW_SUBFILE_TYPE_REDUCED_RESOLUTION) != 0) { 625 value = "ReducedResolution"; 626 } else if((newSubFileType & 627 BaselineTIFFTagSet.NEW_SUBFILE_TYPE_SINGLE_PAGE) != 0) { 628 value = "SinglePage"; 629 } 630 if(value != null) { 631 node = new IIOMetadataNode("SubimageInterpretation"); 632 node.setAttribute("value", value); 633 document_node.appendChild(node); 634 } 635 } 636 637 f = getTIFFField(BaselineTIFFTagSet.TAG_DATE_TIME); 638 if (f != null) { 639 String s = f.getAsString(0); 640 641 // DateTime should be formatted as "YYYY:MM:DD hh:mm:ss". 642 if(s.length() == 19) { 643 node = new IIOMetadataNode("ImageCreationTime"); 644 645 // Files with incorrect DateTime format have been 646 // observed so anticipate an exception from substring() 647 // and only add the node if the format is presumably 648 // correct. 649 boolean appendNode; 650 try { 651 node.setAttribute("year", s.substring(0, 4)); 652 node.setAttribute("month", s.substring(5, 7)); 653 node.setAttribute("day", s.substring(8, 10)); 654 node.setAttribute("hour", s.substring(11, 13)); 655 node.setAttribute("minute", s.substring(14, 16)); 656 node.setAttribute("second", s.substring(17, 19)); 657 appendNode = true; 658 } catch(IndexOutOfBoundsException e) { 659 appendNode = false; 660 } 661 662 if(appendNode) { 663 document_node.appendChild(node); 664 } 665 } 666 } 667 668 return document_node; 669 } 670 671 public IIOMetadataNode getStandardTextNode() { 672 IIOMetadataNode text_node = null; 673 IIOMetadataNode node = null; // scratch node 674 675 TIFFField f; 676 677 int[] textFieldTagNumbers = new int[] { 678 BaselineTIFFTagSet.TAG_DOCUMENT_NAME, 679 BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION, 680 BaselineTIFFTagSet.TAG_MAKE, 681 BaselineTIFFTagSet.TAG_MODEL, 682 BaselineTIFFTagSet.TAG_PAGE_NAME, 683 BaselineTIFFTagSet.TAG_SOFTWARE, 684 BaselineTIFFTagSet.TAG_ARTIST, 685 BaselineTIFFTagSet.TAG_HOST_COMPUTER, 686 BaselineTIFFTagSet.TAG_INK_NAMES, 687 BaselineTIFFTagSet.TAG_COPYRIGHT 688 }; 689 690 for(int i = 0; i < textFieldTagNumbers.length; i++) { 691 f = getTIFFField(textFieldTagNumbers[i]); 692 if(f != null) { 693 String value = f.getAsString(0); 694 if(text_node == null) { 695 text_node = new IIOMetadataNode("Text"); 696 } 697 node = new IIOMetadataNode("TextEntry"); 698 node.setAttribute("keyword", f.getTag().getName()); 699 node.setAttribute("value", value); 700 text_node.appendChild(node); 701 } 702 } 703 704 return text_node; 705 } 706 707 public IIOMetadataNode getStandardTransparencyNode() { 708 IIOMetadataNode transparency_node = 709 new IIOMetadataNode("Transparency"); 710 IIOMetadataNode node = null; // scratch node 711 712 TIFFField f; 713 714 node = new IIOMetadataNode("Alpha"); 715 String value = "none"; 716 717 f = getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES); 718 if(f != null) { 719 int[] extraSamples = f.getAsInts(); 720 for(int i = 0; i < extraSamples.length; i++) { 721 if(extraSamples[i] == 722 BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) { 723 value = "premultiplied"; 724 break; 725 } else if(extraSamples[i] == 726 BaselineTIFFTagSet.EXTRA_SAMPLES_UNASSOCIATED_ALPHA) { 727 value = "nonpremultiplied"; 728 break; 729 } 730 } 731 } 732 733 node.setAttribute("value", value); 734 transparency_node.appendChild(node); 735 736 return transparency_node; 737 } 738 739 // Shorthand for throwing an IIOInvalidTreeException 740 private static void fatal(Node node, String reason) 741 throws IIOInvalidTreeException { 742 throw new IIOInvalidTreeException(reason, node); 743 } 744 745 private int[] listToIntArray(String list) { 746 StringTokenizer st = new StringTokenizer(list, " "); 747 ArrayList<Integer> intList = new ArrayList<Integer>(); 748 while (st.hasMoreTokens()) { 749 String nextInteger = st.nextToken(); 750 Integer nextInt = Integer.valueOf(nextInteger); 751 intList.add(nextInt); 752 } 753 754 int[] intArray = new int[intList.size()]; 755 for(int i = 0; i < intArray.length; i++) { 756 intArray[i] = intList.get(i); 757 } 758 759 return intArray; 760 } 761 762 private char[] listToCharArray(String list) { 763 StringTokenizer st = new StringTokenizer(list, " "); 764 ArrayList<Integer> intList = new ArrayList<Integer>(); 765 while (st.hasMoreTokens()) { 766 String nextInteger = st.nextToken(); 767 Integer nextInt = Integer.valueOf(nextInteger); 768 intList.add(nextInt); 769 } 770 771 char[] charArray = new char[intList.size()]; 772 for(int i = 0; i < charArray.length; i++) { 773 charArray[i] = (char)(intList.get(i).intValue()); 774 } 775 776 return charArray; 777 } 778 779 private void mergeStandardTree(Node root) 780 throws IIOInvalidTreeException { 781 TIFFField f; 782 TIFFTag tag; 783 784 Node node = root; 785 if (!node.getNodeName() 786 .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) { 787 fatal(node, "Root must be " + 788 IIOMetadataFormatImpl.standardMetadataFormatName); 789 } 790 791 // Obtain the sample format and set the palette flag if appropriate. 792 String sampleFormat = null; 793 Node dataNode = getChildNode(root, "Data"); 794 boolean isPaletteColor = false; 795 if(dataNode != null) { 796 Node sampleFormatNode = getChildNode(dataNode, "SampleFormat"); 797 if(sampleFormatNode != null) { 798 sampleFormat = getAttribute(sampleFormatNode, "value"); 799 isPaletteColor = sampleFormat.equals("Index"); 800 } 801 } 802 803 // If palette flag not set check for palette. 804 if(!isPaletteColor) { 805 Node chromaNode = getChildNode(root, "Chroma"); 806 if(chromaNode != null && 807 getChildNode(chromaNode, "Palette") != null) { 808 isPaletteColor = true; 809 } 810 } 811 812 node = node.getFirstChild(); 813 while (node != null) { 814 String name = node.getNodeName(); 815 816 if (name.equals("Chroma")) { 817 String colorSpaceType = null; 818 String blackIsZero = null; 819 boolean gotPalette = false; 820 Node child = node.getFirstChild(); 821 while (child != null) { 822 String childName = child.getNodeName(); 823 if (childName.equals("ColorSpaceType")) { 824 colorSpaceType = getAttribute(child, "name"); 825 } else if (childName.equals("NumChannels")) { 826 tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); 827 int samplesPerPixel = isPaletteColor ? 828 1 : Integer.parseInt(getAttribute(child, "value")); 829 f = new TIFFField(tag, samplesPerPixel); 830 rootIFD.addTIFFField(f); 831 } else if (childName.equals("BlackIsZero")) { 832 blackIsZero = getAttribute(child, "value"); 833 } else if (childName.equals("Palette")) { 834 Node entry = child.getFirstChild(); 835 HashMap<Integer,char[]> palette = new HashMap<>(); 836 int maxIndex = -1; 837 while(entry != null) { 838 String entryName = entry.getNodeName(); 839 if(entryName.equals("PaletteEntry")) { 840 String idx = getAttribute(entry, "index"); 841 int id = Integer.parseInt(idx); 842 if(id > maxIndex) { 843 maxIndex = id; 844 } 845 char red = 846 (char)Integer.parseInt(getAttribute(entry, 847 "red")); 848 char green = 849 (char)Integer.parseInt(getAttribute(entry, 850 "green")); 851 char blue = 852 (char)Integer.parseInt(getAttribute(entry, 853 "blue")); 854 palette.put(Integer.valueOf(id), 855 new char[] {red, green, blue}); 856 857 gotPalette = true; 858 } 859 entry = entry.getNextSibling(); 860 } 861 862 if(gotPalette) { 863 int mapSize = maxIndex + 1; 864 int paletteLength = 3*mapSize; 865 char[] paletteEntries = new char[paletteLength]; 866 Iterator<Map.Entry<Integer,char[]>> paletteIter 867 = palette.entrySet().iterator(); 868 while(paletteIter.hasNext()) { 869 Map.Entry<Integer,char[]> paletteEntry 870 = paletteIter.next(); 871 int index = paletteEntry.getKey(); 872 char[] rgb = paletteEntry.getValue(); 873 paletteEntries[index] = 874 (char)((rgb[0]*65535)/255); 875 paletteEntries[mapSize + index] = 876 (char)((rgb[1]*65535)/255); 877 paletteEntries[2*mapSize + index] = 878 (char)((rgb[2]*65535)/255); 879 } 880 881 tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_COLOR_MAP); 882 f = new TIFFField(tag, TIFFTag.TIFF_SHORT, 883 paletteLength, paletteEntries); 884 rootIFD.addTIFFField(f); 885 } 886 } 887 888 child = child.getNextSibling(); 889 } 890 891 int photometricInterpretation = -1; 892 if((colorSpaceType == null || colorSpaceType.equals("GRAY")) && 893 blackIsZero != null && 894 blackIsZero.equalsIgnoreCase("FALSE")) { 895 photometricInterpretation = 896 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO; 897 } else if(colorSpaceType != null) { 898 if(colorSpaceType.equals("GRAY")) { 899 boolean isTransparency = false; 900 if(root instanceof IIOMetadataNode) { 901 IIOMetadataNode iioRoot = (IIOMetadataNode)root; 902 NodeList siNodeList = 903 iioRoot.getElementsByTagName("SubimageInterpretation"); 904 if(siNodeList.getLength() == 1) { 905 Node siNode = siNodeList.item(0); 906 String value = getAttribute(siNode, "value"); 907 if(value.equals("TransparencyMask")) { 908 isTransparency = true; 909 } 910 } 911 } 912 if(isTransparency) { 913 photometricInterpretation = 914 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK; 915 } else { 916 photometricInterpretation = 917 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO; 918 } 919 } else if(colorSpaceType.equals("RGB")) { 920 photometricInterpretation = 921 gotPalette ? 922 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR : 923 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB; 924 } else if(colorSpaceType.equals("YCbCr")) { 925 photometricInterpretation = 926 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR; 927 } else if(colorSpaceType.equals("CMYK")) { 928 photometricInterpretation = 929 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK; 930 } else if(colorSpaceType.equals("Lab")) { 931 photometricInterpretation = 932 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB; 933 } 934 } 935 936 if(photometricInterpretation != -1) { 937 tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION); 938 f = new TIFFField(tag, photometricInterpretation); 939 rootIFD.addTIFFField(f); 940 } 941 } else if (name.equals("Compression")) { 942 Node child = node.getFirstChild(); 943 while (child != null) { 944 String childName = child.getNodeName(); 945 if (childName.equals("CompressionTypeName")) { 946 int compression = -1; 947 String compressionTypeName = 948 getAttribute(child, "value"); 949 if(compressionTypeName.equalsIgnoreCase("None")) { 950 compression = 951 BaselineTIFFTagSet.COMPRESSION_NONE; 952 } else { 953 String[] compressionNames = 954 TIFFImageWriter.compressionTypes; 955 for(int i = 0; i < compressionNames.length; i++) { 956 if(compressionNames[i].equalsIgnoreCase(compressionTypeName)) { 957 compression = 958 TIFFImageWriter.compressionNumbers[i]; 959 break; 960 } 961 } 962 } 963 964 if(compression != -1) { 965 tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_COMPRESSION); 966 f = new TIFFField(tag, compression); 967 rootIFD.addTIFFField(f); 968 969 // Lossless is irrelevant. 970 } 971 } 972 973 child = child.getNextSibling(); 974 } 975 } else if (name.equals("Data")) { 976 Node child = node.getFirstChild(); 977 while (child != null) { 978 String childName = child.getNodeName(); 979 980 if (childName.equals("PlanarConfiguration")) { 981 String pc = getAttribute(child, "value"); 982 int planarConfiguration = -1; 983 if(pc.equals("PixelInterleaved")) { 984 planarConfiguration = 985 BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY; 986 } else if(pc.equals("PlaneInterleaved")) { 987 planarConfiguration = 988 BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR; 989 } 990 if(planarConfiguration != -1) { 991 tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION); 992 f = new TIFFField(tag, planarConfiguration); 993 rootIFD.addTIFFField(f); 994 } 995 } else if (childName.equals("BitsPerSample")) { 996 String bps = getAttribute(child, "value"); 997 char[] bitsPerSample = listToCharArray(bps); 998 tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); 999 if(isPaletteColor) { 1000 f = new TIFFField(tag, TIFFTag.TIFF_SHORT, 1, 1001 new char[] {bitsPerSample[0]}); 1002 } else { 1003 f = new TIFFField(tag, TIFFTag.TIFF_SHORT, 1004 bitsPerSample.length, 1005 bitsPerSample); 1006 } 1007 rootIFD.addTIFFField(f); 1008 } else if (childName.equals("SampleMSB")) { 1009 // Add FillOrder only if lsb-to-msb (right to left) 1010 // for all bands, i.e., SampleMSB is zero for all 1011 // channels. 1012 String sMSB = getAttribute(child, "value"); 1013 int[] sampleMSB = listToIntArray(sMSB); 1014 boolean isRightToLeft = true; 1015 for(int i = 0; i < sampleMSB.length; i++) { 1016 if(sampleMSB[i] != 0) { 1017 isRightToLeft = false; 1018 break; 1019 } 1020 } 1021 int fillOrder = isRightToLeft ? 1022 BaselineTIFFTagSet.FILL_ORDER_RIGHT_TO_LEFT : 1023 BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT; 1024 tag = 1025 rootIFD.getTag(BaselineTIFFTagSet.TAG_FILL_ORDER); 1026 f = new TIFFField(tag, fillOrder); 1027 rootIFD.addTIFFField(f); 1028 } 1029 1030 child = child.getNextSibling(); 1031 } 1032 } else if (name.equals("Dimension")) { 1033 float pixelAspectRatio = -1.0f; 1034 boolean gotPixelAspectRatio = false; 1035 1036 float horizontalPixelSize = -1.0f; 1037 boolean gotHorizontalPixelSize = false; 1038 1039 float verticalPixelSize = -1.0f; 1040 boolean gotVerticalPixelSize = false; 1041 1042 boolean sizeIsAbsolute = false; 1043 1044 float horizontalPosition = -1.0f; 1045 boolean gotHorizontalPosition = false; 1046 1047 float verticalPosition = -1.0f; 1048 boolean gotVerticalPosition = false; 1049 1050 Node child = node.getFirstChild(); 1051 while (child != null) { 1052 String childName = child.getNodeName(); 1053 if (childName.equals("PixelAspectRatio")) { 1054 String par = getAttribute(child, "value"); 1055 pixelAspectRatio = Float.parseFloat(par); 1056 gotPixelAspectRatio = true; 1057 } else if (childName.equals("ImageOrientation")) { 1058 String orientation = getAttribute(child, "value"); 1059 for (int i = 0; i < orientationNames.length; i++) { 1060 if (orientation.equals(orientationNames[i])) { 1061 char[] oData = new char[1]; 1062 oData[0] = (char)i; 1063 1064 f = new TIFFField( 1065 rootIFD.getTag(BaselineTIFFTagSet.TAG_ORIENTATION), 1066 TIFFTag.TIFF_SHORT, 1067 1, 1068 oData); 1069 1070 rootIFD.addTIFFField(f); 1071 break; 1072 } 1073 } 1074 1075 } else if (childName.equals("HorizontalPixelSize")) { 1076 String hps = getAttribute(child, "value"); 1077 horizontalPixelSize = Float.parseFloat(hps); 1078 gotHorizontalPixelSize = true; 1079 } else if (childName.equals("VerticalPixelSize")) { 1080 String vps = getAttribute(child, "value"); 1081 verticalPixelSize = Float.parseFloat(vps); 1082 gotVerticalPixelSize = true; 1083 } else if (childName.equals("HorizontalPosition")) { 1084 String hp = getAttribute(child, "value"); 1085 horizontalPosition = Float.parseFloat(hp); 1086 gotHorizontalPosition = true; 1087 } else if (childName.equals("VerticalPosition")) { 1088 String vp = getAttribute(child, "value"); 1089 verticalPosition = Float.parseFloat(vp); 1090 gotVerticalPosition = true; 1091 } 1092 1093 child = child.getNextSibling(); 1094 } 1095 1096 sizeIsAbsolute = gotHorizontalPixelSize || 1097 gotVerticalPixelSize; 1098 1099 // Fill in pixel size data from aspect ratio 1100 if (gotPixelAspectRatio) { 1101 if (gotHorizontalPixelSize && !gotVerticalPixelSize) { 1102 verticalPixelSize = 1103 horizontalPixelSize/pixelAspectRatio; 1104 gotVerticalPixelSize = true; 1105 } else if (gotVerticalPixelSize && 1106 !gotHorizontalPixelSize) { 1107 horizontalPixelSize = 1108 verticalPixelSize*pixelAspectRatio; 1109 gotHorizontalPixelSize = true; 1110 } else if (!gotHorizontalPixelSize && 1111 !gotVerticalPixelSize) { 1112 horizontalPixelSize = pixelAspectRatio; 1113 verticalPixelSize = 1.0f; 1114 gotHorizontalPixelSize = true; 1115 gotVerticalPixelSize = true; 1116 } 1117 } 1118 1119 // Compute pixels/centimeter 1120 if (gotHorizontalPixelSize) { 1121 float xResolution = 1122 (sizeIsAbsolute ? 10.0f : 1.0f)/horizontalPixelSize; 1123 long[][] hData = new long[1][2]; 1124 hData[0] = new long[2]; 1125 hData[0][0] = (long)(xResolution*10000.0f); 1126 hData[0][1] = (long)10000; 1127 1128 f = new TIFFField( 1129 rootIFD.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION), 1130 TIFFTag.TIFF_RATIONAL, 1131 1, 1132 hData); 1133 rootIFD.addTIFFField(f); 1134 } 1135 1136 if (gotVerticalPixelSize) { 1137 float yResolution = 1138 (sizeIsAbsolute ? 10.0f : 1.0f)/verticalPixelSize; 1139 long[][] vData = new long[1][2]; 1140 vData[0] = new long[2]; 1141 vData[0][0] = (long)(yResolution*10000.0f); 1142 vData[0][1] = (long)10000; 1143 1144 f = new TIFFField( 1145 rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION), 1146 TIFFTag.TIFF_RATIONAL, 1147 1, 1148 vData); 1149 rootIFD.addTIFFField(f); 1150 } 1151 1152 // Emit ResolutionUnit tag 1153 char[] res = new char[1]; 1154 res[0] = (char)(sizeIsAbsolute ? 1155 BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER : 1156 BaselineTIFFTagSet.RESOLUTION_UNIT_NONE); 1157 1158 f = new TIFFField( 1159 rootIFD.getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT), 1160 TIFFTag.TIFF_SHORT, 1161 1, 1162 res); 1163 rootIFD.addTIFFField(f); 1164 1165 // Position 1166 if(sizeIsAbsolute) { 1167 if(gotHorizontalPosition) { 1168 // Convert from millimeters to centimeters via 1169 // numerator multiplier = denominator/10. 1170 long[][] hData = new long[1][2]; 1171 hData[0][0] = (long)(horizontalPosition*10000.0f); 1172 hData[0][1] = (long)100000; 1173 1174 f = new TIFFField( 1175 rootIFD.getTag(BaselineTIFFTagSet.TAG_X_POSITION), 1176 TIFFTag.TIFF_RATIONAL, 1177 1, 1178 hData); 1179 rootIFD.addTIFFField(f); 1180 } 1181 1182 if(gotVerticalPosition) { 1183 // Convert from millimeters to centimeters via 1184 // numerator multiplier = denominator/10. 1185 long[][] vData = new long[1][2]; 1186 vData[0][0] = (long)(verticalPosition*10000.0f); 1187 vData[0][1] = (long)100000; 1188 1189 f = new TIFFField( 1190 rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_POSITION), 1191 TIFFTag.TIFF_RATIONAL, 1192 1, 1193 vData); 1194 rootIFD.addTIFFField(f); 1195 } 1196 } 1197 } else if (name.equals("Document")) { 1198 Node child = node.getFirstChild(); 1199 while (child != null) { 1200 String childName = child.getNodeName(); 1201 1202 if (childName.equals("SubimageInterpretation")) { 1203 String si = getAttribute(child, "value"); 1204 int newSubFileType = -1; 1205 if(si.equals("TransparencyMask")) { 1206 newSubFileType = 1207 BaselineTIFFTagSet.NEW_SUBFILE_TYPE_TRANSPARENCY; 1208 } else if(si.equals("ReducedResolution")) { 1209 newSubFileType = 1210 BaselineTIFFTagSet.NEW_SUBFILE_TYPE_REDUCED_RESOLUTION; 1211 } else if(si.equals("SinglePage")) { 1212 newSubFileType = 1213 BaselineTIFFTagSet.NEW_SUBFILE_TYPE_SINGLE_PAGE; 1214 } 1215 if(newSubFileType != -1) { 1216 tag = 1217 rootIFD.getTag(BaselineTIFFTagSet.TAG_NEW_SUBFILE_TYPE); 1218 f = new TIFFField(tag, newSubFileType); 1219 rootIFD.addTIFFField(f); 1220 } 1221 } 1222 1223 if (childName.equals("ImageCreationTime")) { 1224 String year = getAttribute(child, "year"); 1225 String month = getAttribute(child, "month"); 1226 String day = getAttribute(child, "day"); 1227 String hour = getAttribute(child, "hour"); 1228 String minute = getAttribute(child, "minute"); 1229 String second = getAttribute(child, "second"); 1230 1231 StringBuffer sb = new StringBuffer(); 1232 sb.append(year); 1233 sb.append(":"); 1234 if(month.length() == 1) { 1235 sb.append("0"); 1236 } 1237 sb.append(month); 1238 sb.append(":"); 1239 if(day.length() == 1) { 1240 sb.append("0"); 1241 } 1242 sb.append(day); 1243 sb.append(" "); 1244 if(hour.length() == 1) { 1245 sb.append("0"); 1246 } 1247 sb.append(hour); 1248 sb.append(":"); 1249 if(minute.length() == 1) { 1250 sb.append("0"); 1251 } 1252 sb.append(minute); 1253 sb.append(":"); 1254 if(second.length() == 1) { 1255 sb.append("0"); 1256 } 1257 sb.append(second); 1258 1259 String[] dt = new String[1]; 1260 dt[0] = sb.toString(); 1261 1262 f = new TIFFField( 1263 rootIFD.getTag(BaselineTIFFTagSet.TAG_DATE_TIME), 1264 TIFFTag.TIFF_ASCII, 1265 1, 1266 dt); 1267 rootIFD.addTIFFField(f); 1268 } 1269 1270 child = child.getNextSibling(); 1271 } 1272 } else if (name.equals("Text")) { 1273 Node child = node.getFirstChild(); 1274 String theAuthor = null; 1275 String theDescription = null; 1276 String theTitle = null; 1277 while (child != null) { 1278 String childName = child.getNodeName(); 1279 if(childName.equals("TextEntry")) { 1280 int tagNumber = -1; 1281 NamedNodeMap childAttrs = child.getAttributes(); 1282 Node keywordNode = childAttrs.getNamedItem("keyword"); 1283 if(keywordNode != null) { 1284 String keyword = keywordNode.getNodeValue(); 1285 String value = getAttribute(child, "value"); 1286 if(!keyword.equals("") && !value.equals("")) { 1287 if(keyword.equalsIgnoreCase("DocumentName")) { 1288 tagNumber = 1289 BaselineTIFFTagSet.TAG_DOCUMENT_NAME; 1290 } else if(keyword.equalsIgnoreCase("ImageDescription")) { 1291 tagNumber = 1292 BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION; 1293 } else if(keyword.equalsIgnoreCase("Make")) { 1294 tagNumber = 1295 BaselineTIFFTagSet.TAG_MAKE; 1296 } else if(keyword.equalsIgnoreCase("Model")) { 1297 tagNumber = 1298 BaselineTIFFTagSet.TAG_MODEL; 1299 } else if(keyword.equalsIgnoreCase("PageName")) { 1300 tagNumber = 1301 BaselineTIFFTagSet.TAG_PAGE_NAME; 1302 } else if(keyword.equalsIgnoreCase("Software")) { 1303 tagNumber = 1304 BaselineTIFFTagSet.TAG_SOFTWARE; 1305 } else if(keyword.equalsIgnoreCase("Artist")) { 1306 tagNumber = 1307 BaselineTIFFTagSet.TAG_ARTIST; 1308 } else if(keyword.equalsIgnoreCase("HostComputer")) { 1309 tagNumber = 1310 BaselineTIFFTagSet.TAG_HOST_COMPUTER; 1311 } else if(keyword.equalsIgnoreCase("InkNames")) { 1312 tagNumber = 1313 BaselineTIFFTagSet.TAG_INK_NAMES; 1314 } else if(keyword.equalsIgnoreCase("Copyright")) { 1315 tagNumber = 1316 BaselineTIFFTagSet.TAG_COPYRIGHT; 1317 } else if(keyword.equalsIgnoreCase("author")) { 1318 theAuthor = value; 1319 } else if(keyword.equalsIgnoreCase("description")) { 1320 theDescription = value; 1321 } else if(keyword.equalsIgnoreCase("title")) { 1322 theTitle = value; 1323 } 1324 if(tagNumber != -1) { 1325 f = new TIFFField(rootIFD.getTag(tagNumber), 1326 TIFFTag.TIFF_ASCII, 1327 1, 1328 new String[] {value}); 1329 rootIFD.addTIFFField(f); 1330 } 1331 } 1332 } 1333 } 1334 child = child.getNextSibling(); 1335 } // child != null 1336 if(theAuthor != null && 1337 getTIFFField(BaselineTIFFTagSet.TAG_ARTIST) == null) { 1338 f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_ARTIST), 1339 TIFFTag.TIFF_ASCII, 1340 1, 1341 new String[] {theAuthor}); 1342 rootIFD.addTIFFField(f); 1343 } 1344 if(theDescription != null && 1345 getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION) == null) { 1346 f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION), 1347 TIFFTag.TIFF_ASCII, 1348 1, 1349 new String[] {theDescription}); 1350 rootIFD.addTIFFField(f); 1351 } 1352 if(theTitle != null && 1353 getTIFFField(BaselineTIFFTagSet.TAG_DOCUMENT_NAME) == null) { 1354 f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_DOCUMENT_NAME), 1355 TIFFTag.TIFF_ASCII, 1356 1, 1357 new String[] {theTitle}); 1358 rootIFD.addTIFFField(f); 1359 } 1360 } else if (name.equals("Transparency")) { 1361 Node child = node.getFirstChild(); 1362 while (child != null) { 1363 String childName = child.getNodeName(); 1364 1365 if (childName.equals("Alpha")) { 1366 String alpha = getAttribute(child, "value"); 1367 1368 f = null; 1369 if (alpha.equals("premultiplied")) { 1370 f = new TIFFField( 1371 rootIFD.getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES), 1372 BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA); 1373 } else if (alpha.equals("nonpremultiplied")) { 1374 f = new TIFFField( 1375 rootIFD.getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES), 1376 BaselineTIFFTagSet.EXTRA_SAMPLES_UNASSOCIATED_ALPHA); 1377 } 1378 if (f != null) { 1379 rootIFD.addTIFFField(f); 1380 } 1381 } 1382 1383 child = child.getNextSibling(); 1384 } 1385 } 1386 1387 node = node.getNextSibling(); 1388 } 1389 1390 // Set SampleFormat. 1391 if(sampleFormat != null) { 1392 // Derive the value. 1393 int sf = -1; 1394 if(sampleFormat.equals("SignedIntegral")) { 1395 sf = BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER; 1396 } else if(sampleFormat.equals("UnsignedIntegral")) { 1397 sf = BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER; 1398 } else if(sampleFormat.equals("Real")) { 1399 sf = BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT; 1400 } else if(sampleFormat.equals("Index")) { 1401 sf = BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER; 1402 } 1403 1404 if(sf != -1) { 1405 // Derive the count. 1406 int count = 1; 1407 1408 // Try SamplesPerPixel first. 1409 f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); 1410 if(f != null) { 1411 count = f.getAsInt(0); 1412 } else { 1413 // Try BitsPerSample. 1414 f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); 1415 if(f != null) { 1416 count = f.getCount(); 1417 } 1418 } 1419 1420 char[] sampleFormatArray = new char[count]; 1421 Arrays.fill(sampleFormatArray, (char)sf); 1422 1423 // Add SampleFormat. 1424 tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT); 1425 f = new TIFFField(tag, TIFFTag.TIFF_SHORT, 1426 sampleFormatArray.length, sampleFormatArray); 1427 rootIFD.addTIFFField(f); 1428 } 1429 } 1430 } 1431 1432 private static String getAttribute(Node node, String attrName) { 1433 NamedNodeMap attrs = node.getAttributes(); 1434 Node attr = attrs.getNamedItem(attrName); 1435 return attr != null ? attr.getNodeValue() : null; 1436 } 1437 1438 private Node getChildNode(Node node, String childName) { 1439 Node childNode = null; 1440 if(node.hasChildNodes()) { 1441 NodeList childNodes = node.getChildNodes(); 1442 int length = childNodes.getLength(); 1443 for(int i = 0; i < length; i++) { 1444 Node item = childNodes.item(i); 1445 if(item.getNodeName().equals(childName)) { 1446 childNode = item; 1447 break; 1448 } 1449 } 1450 } 1451 return childNode; 1452 } 1453 1454 public static TIFFIFD parseIFD(Node node) throws IIOInvalidTreeException { 1455 if (!node.getNodeName().equals("TIFFIFD")) { 1456 fatal(node, "Expected \"TIFFIFD\" node"); 1457 } 1458 1459 String tagSetNames = getAttribute(node, "tagSets"); 1460 List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(5); 1461 1462 if (tagSetNames != null) { 1463 StringTokenizer st = new StringTokenizer(tagSetNames, ","); 1464 while (st.hasMoreTokens()) { 1465 String className = st.nextToken(); 1466 1467 Object o = null; 1468 Class<?> setClass = null; 1469 try { 1470 ClassLoader cl = TIFFImageMetadata.class.getClassLoader(); 1471 setClass = Class.forName(className, false, cl); 1472 if (!TIFFTagSet.class.isAssignableFrom(setClass)) { 1473 fatal(node, "TagSets in IFD must be subset of" 1474 + " TIFFTagSet class"); 1475 } 1476 Method getInstanceMethod = 1477 setClass.getMethod("getInstance", (Class[])null); 1478 o = getInstanceMethod.invoke(null, (Object[])null); 1479 } catch (NoSuchMethodException e) { 1480 throw new RuntimeException(e); 1481 } catch (IllegalAccessException e) { 1482 throw new RuntimeException(e); 1483 } catch (InvocationTargetException e) { 1484 throw new RuntimeException(e); 1485 } catch (ClassNotFoundException e) { 1486 throw new RuntimeException(e); 1487 } 1488 1489 if (!(o instanceof TIFFTagSet)) { 1490 fatal(node, "Specified tag set class \"" + 1491 className + 1492 "\" is not an instance of TIFFTagSet"); 1493 } else { 1494 tagSets.add((TIFFTagSet)o); 1495 } 1496 } 1497 } 1498 1499 TIFFIFD ifd = new TIFFIFD(tagSets); 1500 1501 node = node.getFirstChild(); 1502 while (node != null) { 1503 String name = node.getNodeName(); 1504 1505 TIFFField f = null; 1506 if (name.equals("TIFFIFD")) { 1507 TIFFIFD subIFD = parseIFD(node); 1508 String parentTagName = getAttribute(node, "parentTagName"); 1509 String parentTagNumber = getAttribute(node, "parentTagNumber"); 1510 TIFFTag tag = null; 1511 if(parentTagName != null) { 1512 tag = TIFFIFD.getTag(parentTagName, tagSets); 1513 } else if(parentTagNumber != null) { 1514 int tagNumber = Integer.parseUnsignedInt(parentTagNumber); 1515 tag = TIFFIFD.getTag(tagNumber, tagSets); 1516 } 1517 1518 int type; 1519 if (tag == null) { 1520 type = TIFFTag.TIFF_LONG; 1521 tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, 0, 1 << type); 1522 } else { 1523 if (tag.isDataTypeOK(TIFFTag.TIFF_IFD_POINTER)) { 1524 type = TIFFTag.TIFF_IFD_POINTER; 1525 } else if (tag.isDataTypeOK(TIFFTag.TIFF_LONG)) { 1526 type = TIFFTag.TIFF_LONG; 1527 } else { 1528 for (type = TIFFTag.MAX_DATATYPE; 1529 type >= TIFFTag.MIN_DATATYPE; 1530 type--) { 1531 if (tag.isDataTypeOK(type)) { 1532 break; 1533 } 1534 } 1535 } 1536 } 1537 1538 f = new TIFFField(tag, type, 1L, subIFD); 1539 } else if (name.equals("TIFFField")) { 1540 int number = Integer.parseInt(getAttribute(node, "number")); 1541 1542 TIFFTagSet tagSet = null; 1543 Iterator<TIFFTagSet> iter = tagSets.iterator(); 1544 while (iter.hasNext()) { 1545 TIFFTagSet t = iter.next(); 1546 if (t.getTag(number) != null) { 1547 tagSet = t; 1548 break; 1549 } 1550 } 1551 1552 f = TIFFField.createFromMetadataNode(tagSet, node); 1553 } else { 1554 fatal(node, 1555 "Expected either \"TIFFIFD\" or \"TIFFField\" node, got " 1556 + name); 1557 } 1558 1559 ifd.addTIFFField(f); 1560 node = node.getNextSibling(); 1561 } 1562 1563 return ifd; 1564 } 1565 1566 private void mergeNativeTree(Node root) throws IIOInvalidTreeException { 1567 Node node = root; 1568 if (!node.getNodeName().equals(nativeMetadataFormatName)) { 1569 fatal(node, "Root must be " + nativeMetadataFormatName); 1570 } 1571 1572 node = node.getFirstChild(); 1573 if (node == null || !node.getNodeName().equals("TIFFIFD")) { 1574 fatal(root, "Root must have \"TIFFIFD\" child"); 1575 } 1576 TIFFIFD ifd = parseIFD(node); 1577 1578 List<TIFFTagSet> rootIFDTagSets = rootIFD.getTagSetList(); 1579 Iterator<TIFFTagSet> tagSetIter = ifd.getTagSetList().iterator(); 1580 while(tagSetIter.hasNext()) { 1581 Object o = tagSetIter.next(); 1582 if(o instanceof TIFFTagSet && !rootIFDTagSets.contains(o)) { 1583 rootIFD.addTagSet((TIFFTagSet)o); 1584 } 1585 } 1586 1587 Iterator<TIFFField> ifdIter = ifd.iterator(); 1588 while(ifdIter.hasNext()) { 1589 TIFFField field = ifdIter.next(); 1590 rootIFD.addTIFFField(field); 1591 } 1592 } 1593 1594 public void mergeTree(String formatName, Node root) 1595 throws IIOInvalidTreeException{ 1596 if (formatName.equals(nativeMetadataFormatName)) { 1597 if (root == null) { 1598 throw new NullPointerException("root == null!"); 1599 } 1600 mergeNativeTree(root); 1601 } else if (formatName.equals 1602 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 1603 if (root == null) { 1604 throw new NullPointerException("root == null!"); 1605 } 1606 mergeStandardTree(root); 1607 } else { 1608 throw new IllegalArgumentException("Not a recognized format!"); 1609 } 1610 } 1611 1612 public void reset() { 1613 rootIFD = new TIFFIFD(tagSets); 1614 } 1615 1616 public TIFFIFD getRootIFD() { 1617 return rootIFD; 1618 } 1619 1620 public TIFFField getTIFFField(int tagNumber) { 1621 return rootIFD.getTIFFField(tagNumber); 1622 } 1623 1624 public void removeTIFFField(int tagNumber) { 1625 rootIFD.removeTIFFField(tagNumber); 1626 } 1627 1628 /** 1629 * Returns a {@code TIFFImageMetadata} wherein all fields in the 1630 * root IFD from the {@code BaselineTIFFTagSet} are copied by value 1631 * and all other fields copied by reference. 1632 */ 1633 public TIFFImageMetadata getShallowClone() { 1634 return new TIFFImageMetadata(rootIFD.getShallowClone()); 1635 } 1636 }