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