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