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