1 /* 2 * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.imageio.plugins.tiff; 26 27 import java.io.EOFException; 28 import java.io.IOException; 29 import java.nio.charset.StandardCharsets; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.Set; 35 import javax.imageio.IIOException; 36 import javax.imageio.stream.ImageInputStream; 37 import javax.imageio.stream.ImageOutputStream; 38 import javax.imageio.plugins.tiff.BaselineTIFFTagSet; 39 import javax.imageio.plugins.tiff.TIFFDirectory; 40 import javax.imageio.plugins.tiff.TIFFField; 41 import javax.imageio.plugins.tiff.TIFFTag; 42 import javax.imageio.plugins.tiff.TIFFTagSet; 43 44 public class TIFFIFD extends TIFFDirectory { 45 private static final long MAX_SAMPLES_PER_PIXEL = 0xffff; 46 private static final long MAX_ASCII_SIZE = 0xffff; 47 48 private long stripOrTileByteCountsPosition = -1; 49 private long stripOrTileOffsetsPosition = -1; 50 private long lastPosition = -1; 51 52 // 53 // A set of tag numbers corresponding to tags essential to decoding 54 // the image and metadata required to interpret its samples. 55 // 56 private static volatile Set<Integer> essentialTags = null; 57 initializeEssentialTags()58 private static void initializeEssentialTags() { 59 Set<Integer> tags = essentialTags; 60 if (tags == null) { 61 essentialTags = tags = Set.of( 62 BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE, 63 BaselineTIFFTagSet.TAG_COLOR_MAP, 64 BaselineTIFFTagSet.TAG_COMPRESSION, 65 BaselineTIFFTagSet.TAG_EXTRA_SAMPLES, 66 BaselineTIFFTagSet.TAG_FILL_ORDER, 67 BaselineTIFFTagSet.TAG_ICC_PROFILE, 68 BaselineTIFFTagSet.TAG_IMAGE_LENGTH, 69 BaselineTIFFTagSet.TAG_IMAGE_WIDTH, 70 BaselineTIFFTagSet.TAG_JPEG_AC_TABLES, 71 BaselineTIFFTagSet.TAG_JPEG_DC_TABLES, 72 BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT, 73 BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 74 BaselineTIFFTagSet.TAG_JPEG_PROC, 75 BaselineTIFFTagSet.TAG_JPEG_Q_TABLES, 76 BaselineTIFFTagSet.TAG_JPEG_RESTART_INTERVAL, 77 BaselineTIFFTagSet.TAG_JPEG_TABLES, 78 BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION, 79 BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION, 80 BaselineTIFFTagSet.TAG_PREDICTOR, 81 BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE, 82 BaselineTIFFTagSet.TAG_ROWS_PER_STRIP, 83 BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL, 84 BaselineTIFFTagSet.TAG_SAMPLE_FORMAT, 85 BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS, 86 BaselineTIFFTagSet.TAG_STRIP_OFFSETS, 87 BaselineTIFFTagSet.TAG_T4_OPTIONS, 88 BaselineTIFFTagSet.TAG_T6_OPTIONS, 89 BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS, 90 BaselineTIFFTagSet.TAG_TILE_LENGTH, 91 BaselineTIFFTagSet.TAG_TILE_OFFSETS, 92 BaselineTIFFTagSet.TAG_TILE_WIDTH, 93 BaselineTIFFTagSet.TAG_Y_CB_CR_COEFFICIENTS, 94 BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING 95 ); 96 } 97 } 98 99 /** 100 * Converts a {@code TIFFDirectory} to a {@code TIFFIFD}. 101 */ getDirectoryAsIFD(TIFFDirectory dir)102 public static TIFFIFD getDirectoryAsIFD(TIFFDirectory dir) { 103 if(dir instanceof TIFFIFD) { 104 return (TIFFIFD)dir; 105 } 106 107 TIFFIFD ifd = new TIFFIFD(Arrays.asList(dir.getTagSets()), 108 dir.getParentTag()); 109 TIFFField[] fields = dir.getTIFFFields(); 110 int numFields = fields.length; 111 for(int i = 0; i < numFields; i++) { 112 TIFFField f = fields[i]; 113 TIFFTag tag = f.getTag(); 114 if(tag.isIFDPointer()) { 115 TIFFDirectory subDir = null; 116 if (f.hasDirectory()) { 117 subDir = f.getDirectory(); 118 } else if (f.getData() instanceof TIFFDirectory) { 119 subDir = (TIFFDirectory)f.getData(); 120 } 121 if (subDir != null) { 122 TIFFDirectory subIFD = getDirectoryAsIFD(subDir); 123 f = new TIFFField(tag, f.getType(), (long)f.getCount(), 124 subIFD); 125 } else { 126 f = null; 127 } 128 } 129 if (f != null) { 130 ifd.addTIFFField(f); 131 } 132 } 133 134 return ifd; 135 } 136 getTag(int tagNumber, List<TIFFTagSet> tagSets)137 public static TIFFTag getTag(int tagNumber, List<TIFFTagSet> tagSets) { 138 Iterator<TIFFTagSet> iter = tagSets.iterator(); 139 while (iter.hasNext()) { 140 TIFFTagSet tagSet = iter.next(); 141 TIFFTag tag = tagSet.getTag(tagNumber); 142 if (tag != null) { 143 return tag; 144 } 145 } 146 147 return null; 148 } 149 getTag(String tagName, List<TIFFTagSet> tagSets)150 public static TIFFTag getTag(String tagName, List<TIFFTagSet> tagSets) { 151 Iterator<TIFFTagSet> iter = tagSets.iterator(); 152 while (iter.hasNext()) { 153 TIFFTagSet tagSet = iter.next(); 154 TIFFTag tag = tagSet.getTag(tagName); 155 if (tag != null) { 156 return tag; 157 } 158 } 159 160 return null; 161 } 162 writeTIFFFieldToStream(TIFFField field, ImageOutputStream stream)163 private static void writeTIFFFieldToStream(TIFFField field, 164 ImageOutputStream stream) 165 throws IOException { 166 int count = field.getCount(); 167 Object data = field.getData(); 168 169 switch (field.getType()) { 170 case TIFFTag.TIFF_ASCII: 171 for (int i = 0; i < count; i++) { 172 String s = ((String[])data)[i]; 173 int length = s.length(); 174 for (int j = 0; j < length; j++) { 175 stream.writeByte(s.charAt(j) & 0xff); 176 } 177 stream.writeByte(0); 178 } 179 break; 180 case TIFFTag.TIFF_UNDEFINED: 181 case TIFFTag.TIFF_BYTE: 182 case TIFFTag.TIFF_SBYTE: 183 stream.write((byte[])data); 184 break; 185 case TIFFTag.TIFF_SHORT: 186 stream.writeChars((char[])data, 0, ((char[])data).length); 187 break; 188 case TIFFTag.TIFF_SSHORT: 189 stream.writeShorts((short[])data, 0, ((short[])data).length); 190 break; 191 case TIFFTag.TIFF_SLONG: 192 stream.writeInts((int[])data, 0, ((int[])data).length); 193 break; 194 case TIFFTag.TIFF_LONG: 195 for (int i = 0; i < count; i++) { 196 stream.writeInt((int)(((long[])data)[i])); 197 } 198 break; 199 case TIFFTag.TIFF_IFD_POINTER: 200 stream.writeInt(0); // will need to be backpatched 201 break; 202 case TIFFTag.TIFF_FLOAT: 203 stream.writeFloats((float[])data, 0, ((float[])data).length); 204 break; 205 case TIFFTag.TIFF_DOUBLE: 206 stream.writeDoubles((double[])data, 0, ((double[])data).length); 207 break; 208 case TIFFTag.TIFF_SRATIONAL: 209 for (int i = 0; i < count; i++) { 210 stream.writeInt(((int[][])data)[i][0]); 211 stream.writeInt(((int[][])data)[i][1]); 212 } 213 break; 214 case TIFFTag.TIFF_RATIONAL: 215 for (int i = 0; i < count; i++) { 216 long num = ((long[][])data)[i][0]; 217 long den = ((long[][])data)[i][1]; 218 stream.writeInt((int)num); 219 stream.writeInt((int)den); 220 } 221 break; 222 default: 223 // error 224 } 225 } 226 TIFFIFD(List<TIFFTagSet> tagSets, TIFFTag parentTag)227 public TIFFIFD(List<TIFFTagSet> tagSets, TIFFTag parentTag) { 228 super(tagSets.toArray(new TIFFTagSet[tagSets.size()]), 229 parentTag); 230 } 231 TIFFIFD(List<TIFFTagSet> tagSets)232 public TIFFIFD(List<TIFFTagSet> tagSets) { 233 this(tagSets, null); 234 } 235 getTagSetList()236 public List<TIFFTagSet> getTagSetList() { 237 return Arrays.asList(getTagSets()); 238 } 239 240 /** 241 * Returns an {@code Iterator} over the TIFF fields. The 242 * traversal is in the order of increasing tag number. 243 */ 244 // Note: the sort is guaranteed for low fields by the use of an 245 // array wherein the index corresponds to the tag number and for 246 // the high fields by the use of a TreeMap with tag number keys. iterator()247 public Iterator<TIFFField> iterator() { 248 return Arrays.asList(getTIFFFields()).iterator(); 249 } 250 251 /** 252 * Read the value of a field. The {@code data} parameter should be 253 * an array of length 1 of Object. 254 * 255 * @param stream the input stream 256 * @param type the type as read from the stream 257 * @param count the count read from the stream 258 * @param data a container for the data 259 * @return the updated count 260 * @throws IOException 261 */ readFieldValue(ImageInputStream stream, int type, int count, Object[] data)262 private static int readFieldValue(ImageInputStream stream, 263 int type, int count, Object[] data) throws IOException { 264 Object obj; 265 final int UNIT_SIZE = 1024000; 266 267 switch (type) { 268 case TIFFTag.TIFF_BYTE: 269 case TIFFTag.TIFF_SBYTE: 270 case TIFFTag.TIFF_UNDEFINED: 271 case TIFFTag.TIFF_ASCII: 272 if (type == TIFFTag.TIFF_ASCII) { 273 byte[] bvalues = new byte[count]; 274 stream.readFully(bvalues, 0, count); 275 // Can be multiple strings 276 ArrayList<String> v = new ArrayList<>(); 277 boolean inString = false; 278 int prevIndex = 0; 279 for (int index = 0; index <= count; index++) { 280 if (index < count && bvalues[index] != 0) { 281 if (!inString) { 282 // start of string 283 prevIndex = index; 284 inString = true; 285 } 286 } else { // null or special case at end of string 287 if (inString) { 288 // end of string 289 String s = new String(bvalues, prevIndex, 290 index - prevIndex, 291 StandardCharsets.US_ASCII); 292 v.add(s); 293 inString = false; 294 } 295 } 296 } 297 298 count = v.size(); 299 String[] strings; 300 if (count != 0) { 301 strings = new String[count]; 302 for (int c = 0; c < count; c++) { 303 strings[c] = v.get(c); 304 } 305 } else { 306 // This case has been observed when the value of 307 // 'count' recorded in the field is non-zero but 308 // the value portion contains all nulls. 309 count = 1; 310 strings = new String[]{""}; 311 } 312 313 obj = strings; 314 } else { 315 if (count < UNIT_SIZE) { 316 byte[] bvalues = new byte[count]; 317 stream.readFully(bvalues, 0, count); 318 obj = bvalues; 319 } else { 320 int bytesToRead = count; 321 int bytesRead = 0; 322 List<byte[]> bufs = new ArrayList<>(); 323 while (bytesToRead != 0) { 324 int sz = Math.min(bytesToRead, UNIT_SIZE); 325 byte[] unit = new byte[sz]; 326 stream.readFully(unit, bytesRead, sz); 327 bufs.add(unit); 328 bytesRead += sz; 329 bytesToRead -= sz; 330 } 331 byte[] tagData = new byte[bytesRead]; 332 int copiedBytes = 0; 333 for (byte[] ba : bufs) { 334 System.arraycopy(ba, 0, tagData, copiedBytes, ba.length); 335 copiedBytes += ba.length; 336 } 337 obj = tagData; 338 } 339 } 340 break; 341 342 case TIFFTag.TIFF_SHORT: 343 final int SHORT_TILE_SIZE = 344 UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_SHORT); 345 if (count < SHORT_TILE_SIZE) { 346 char[] cvalues = new char[count]; 347 for (int j = 0; j < count; j++) { 348 cvalues[j] = (char) (stream.readUnsignedShort()); 349 } 350 obj = cvalues; 351 } else { 352 int charsToRead = count; 353 int charsRead = 0; 354 List<char[]> bufs = new ArrayList<>(); 355 while (charsToRead != 0) { 356 int sz = Math.min(charsToRead, SHORT_TILE_SIZE); 357 char[] unit = new char[sz]; 358 for (int i = 0; i < sz ; i++) { 359 unit[i] = (char) (stream.readUnsignedShort()); 360 } 361 bufs.add(unit); 362 charsRead += sz; 363 charsToRead -= sz; 364 } 365 char[] tagData = new char[charsRead]; 366 int copiedChars = 0; 367 for (char[] ca : bufs) { 368 System.arraycopy(ca, 0, tagData, copiedChars, ca.length); 369 copiedChars += ca.length; 370 } 371 obj = tagData; 372 } 373 break; 374 375 case TIFFTag.TIFF_LONG: 376 case TIFFTag.TIFF_IFD_POINTER: 377 final int LONG_TILE_SIZE = 378 UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_LONG); 379 if (count < LONG_TILE_SIZE) { 380 long[] lvalues = new long[count]; 381 for (int j = 0; j < count; j++) { 382 lvalues[j] = stream.readUnsignedInt(); 383 } 384 obj = lvalues; 385 } else { 386 int longsToRead = count; 387 int longsRead = 0; 388 List<long[]> bufs = new ArrayList<>(); 389 while (longsToRead != 0) { 390 int sz = Math.min(longsToRead, LONG_TILE_SIZE); 391 long[] unit = new long[sz]; 392 for (int i = 0; i < sz ; i++) { 393 unit[i] = stream.readUnsignedInt(); 394 } 395 bufs.add(unit); 396 longsRead += sz; 397 longsToRead -= sz; 398 } 399 long[] tagData = new long[longsRead]; 400 int copiedLongs = 0; 401 for (long[] la : bufs) { 402 System.arraycopy(la, 0, tagData, copiedLongs, la.length); 403 copiedLongs += la.length; 404 } 405 obj = tagData; 406 } 407 break; 408 409 case TIFFTag.TIFF_RATIONAL: 410 final int RATIONAL_TILE_SIZE = 411 UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_RATIONAL); 412 if (count < RATIONAL_TILE_SIZE) { 413 long[][] llvalues = new long[count][2]; 414 for (int j = 0; j < count; j++) { 415 llvalues[j][0] = stream.readUnsignedInt(); 416 llvalues[j][1] = stream.readUnsignedInt(); 417 } 418 obj = llvalues; 419 } else { 420 int rationalsToRead = count; 421 int rationalsRead = 0; 422 List<long[]> bufs = new ArrayList<>(); 423 while (rationalsToRead != 0) { 424 int sz = Math.min(rationalsToRead, RATIONAL_TILE_SIZE); 425 long[] unit = new long[sz * 2]; 426 for (int i = 0; i < (sz * 2) ; i++) { 427 unit[i] = stream.readUnsignedInt(); 428 } 429 bufs.add(unit); 430 rationalsRead += sz; 431 rationalsToRead -= sz; 432 } 433 long[][] tagData = new long[rationalsRead][2]; 434 int copiedRationals = 0; 435 for (long[] la : bufs) { 436 for (int i = 0; i < la.length; i = i + 2) { 437 tagData[copiedRationals + i][0] = la[i]; 438 tagData[copiedRationals + i][1] = la[i + 1]; 439 } 440 copiedRationals += (la.length / 2); 441 } 442 obj = tagData; 443 } 444 break; 445 446 case TIFFTag.TIFF_SSHORT: 447 final int SSHORT_TILE_SIZE = 448 UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_SSHORT); 449 if (count < SSHORT_TILE_SIZE) { 450 short[] svalues = new short[count]; 451 for (int j = 0; j < count; j++) { 452 svalues[j] = stream.readShort(); 453 } 454 obj = svalues; 455 } else { 456 int shortsToRead = count; 457 int shortsRead = 0; 458 List<short[]> bufs = new ArrayList<>(); 459 while (shortsToRead != 0) { 460 int sz = Math.min(shortsToRead, SSHORT_TILE_SIZE); 461 short[] unit = new short[sz]; 462 stream.readFully(unit, shortsRead, sz); 463 bufs.add(unit); 464 shortsRead += sz; 465 shortsToRead -= sz; 466 } 467 short[] tagData = new short[shortsRead]; 468 int copiedShorts = 0; 469 for (short[] sa : bufs) { 470 System.arraycopy(sa, 0, tagData, copiedShorts, sa.length); 471 copiedShorts += sa.length; 472 } 473 obj = tagData; 474 } 475 break; 476 477 case TIFFTag.TIFF_SLONG: 478 final int INT_TILE_SIZE = 479 UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_SLONG); 480 if (count < INT_TILE_SIZE) { 481 int[] ivalues = new int[count]; 482 for (int j = 0; j < count; j++) { 483 ivalues[j] = stream.readInt(); 484 } 485 obj = ivalues; 486 } else { 487 int intsToRead = count; 488 int intsRead = 0; 489 List<int[]> bufs = new ArrayList<>(); 490 while (intsToRead != 0) { 491 int sz = Math.min(intsToRead, INT_TILE_SIZE); 492 int[] unit = new int[sz]; 493 stream.readFully(unit, intsToRead, sz); 494 bufs.add(unit); 495 intsRead += sz; 496 intsToRead -= sz; 497 } 498 int[] tagData = new int[intsRead]; 499 int copiedInts = 0; 500 for (int[] ia : bufs) { 501 System.arraycopy(ia, 0, tagData, copiedInts, ia.length); 502 copiedInts += ia.length; 503 } 504 obj = tagData; 505 } 506 break; 507 508 case TIFFTag.TIFF_SRATIONAL: 509 final int SRATIONAL_TILE_SIZE = 510 UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_SRATIONAL); 511 if (count < SRATIONAL_TILE_SIZE) { 512 int[][] iivalues = new int[count][2]; 513 for (int j = 0; j < count; j++) { 514 iivalues[j][0] = stream.readInt(); 515 iivalues[j][1] = stream.readInt(); 516 } 517 obj = iivalues; 518 } else { 519 int srationalsToRead = count; 520 int srationalsRead = 0; 521 List<int[]> bufs = new ArrayList<>(); 522 while (srationalsToRead != 0) { 523 int sz = Math.min(srationalsToRead, SRATIONAL_TILE_SIZE); 524 int[] unit = new int[sz * 2]; 525 stream.readFully(unit, (srationalsToRead * 2), (sz * 2)); 526 bufs.add(unit); 527 srationalsRead += sz; 528 srationalsToRead -= sz; 529 } 530 int[][] tagData = new int[srationalsRead][2]; 531 int copiedSrationals = 0; 532 for (int[] ia : bufs) { 533 for (int i = 0; i < ia.length; i = i + 2) { 534 tagData[copiedSrationals + i][0] = ia[i]; 535 tagData[copiedSrationals + i][1] = ia[i + 1]; 536 } 537 copiedSrationals += (ia.length / 2); 538 } 539 obj = tagData; 540 } 541 break; 542 543 case TIFFTag.TIFF_FLOAT: 544 final int FLOAT_TILE_SIZE = 545 UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_FLOAT); 546 if (count < FLOAT_TILE_SIZE) { 547 float[] fvalues = new float[count]; 548 for (int j = 0; j < count; j++) { 549 fvalues[j] = stream.readFloat(); 550 } 551 obj = fvalues; 552 } else { 553 int floatsToRead = count; 554 int floatsRead = 0; 555 List<float[]> bufs = new ArrayList<>(); 556 while (floatsToRead != 0) { 557 int sz = Math.min(floatsToRead, FLOAT_TILE_SIZE); 558 float[] unit = new float[sz]; 559 stream.readFully(unit, floatsToRead, sz); 560 bufs.add(unit); 561 floatsRead += sz; 562 floatsToRead -= sz; 563 } 564 float[] tagData = new float[floatsRead]; 565 int copiedFloats = 0; 566 for (float[] fa : bufs) { 567 System.arraycopy(fa, 0, tagData, copiedFloats, fa.length); 568 copiedFloats += fa.length; 569 } 570 obj = tagData; 571 } 572 break; 573 574 case TIFFTag.TIFF_DOUBLE: 575 final int DOUBLE_TILE_SIZE = 576 UNIT_SIZE / TIFFTag.getSizeOfType(TIFFTag.TIFF_DOUBLE); 577 if (count < DOUBLE_TILE_SIZE) { 578 double[] dvalues = new double[count]; 579 for (int j = 0; j < count; j++) { 580 dvalues[j] = stream.readDouble(); 581 } 582 obj = dvalues; 583 } else { 584 int doublesToRead = count; 585 int doublesRead = 0; 586 List<double[]> bufs = new ArrayList<>(); 587 while (doublesToRead != 0) { 588 int sz = Math.min(doublesToRead, DOUBLE_TILE_SIZE); 589 double[] unit = new double[sz]; 590 stream.readFully(unit, doublesToRead, sz); 591 bufs.add(unit); 592 doublesRead += sz; 593 doublesToRead -= sz; 594 } 595 double[] tagData = new double[doublesRead]; 596 int copiedDoubles = 0; 597 for (double[] da : bufs) { 598 System.arraycopy(da, 0, tagData, copiedDoubles, da.length); 599 copiedDoubles += da.length; 600 } 601 obj = tagData; 602 } 603 break; 604 default: 605 obj = null; 606 break; 607 } 608 609 data[0] = obj; 610 611 return count; 612 } 613 614 // 615 // Class to represent an IFD entry where the actual content is at an offset 616 // in the stream somewhere outside the IFD itself. This occurs when the 617 // value cannot be contained within four bytes. Seeking is required to read 618 // such field values. 619 // 620 private static class TIFFIFDEntry { 621 public final TIFFTag tag; 622 public final int type; 623 public final int count; 624 public final long offset; 625 TIFFIFDEntry(TIFFTag tag, int type, int count, long offset)626 TIFFIFDEntry(TIFFTag tag, int type, int count, long offset) { 627 this.tag = tag; 628 this.type = type; 629 this.count = count; 630 this.offset = offset; 631 } 632 } 633 634 // 635 // Retrieve the value of a baseline field as a long. 636 // getFieldAsLong(int tagNumber)637 private long getFieldAsLong(int tagNumber) { 638 TIFFField f = getTIFFField(tagNumber); 639 return f == null ? -1 : f.getAsLong(0); 640 } 641 642 // 643 // Retrieve the value of a baseline field as an int. 644 // getFieldAsInt(int tagNumber)645 private int getFieldAsInt(int tagNumber) { 646 TIFFField f = getTIFFField(tagNumber); 647 return f == null ? -1 : f.getAsInt(0); 648 } 649 650 // 651 // Calculate the number of bytes in each strip or tile. This method 652 // is to be used if and only if no fields exist which provide this 653 // information. The parameter must be empty and if the method succeeds 654 // will contain a single element. 655 // calculateByteCounts(int expectedSize, List<TIFFField> byteCounts)656 private boolean calculateByteCounts(int expectedSize, 657 List<TIFFField> byteCounts) { 658 if (!byteCounts.isEmpty()) { 659 throw new IllegalArgumentException("byteCounts is not empty"); 660 } 661 662 // must be interleaved 663 if (getFieldAsInt(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION) == 664 BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) { 665 return false; 666 } 667 668 // must be uncompressed 669 if (getFieldAsInt(BaselineTIFFTagSet.TAG_COMPRESSION) != 670 BaselineTIFFTagSet.COMPRESSION_NONE) { 671 return false; 672 } 673 674 // must have image dimensions 675 long w = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_WIDTH); 676 if (w < 0) { 677 return false; 678 } 679 long h = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_LENGTH); 680 if (h < 0) { 681 return false; 682 } 683 684 long tw = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_WIDTH); 685 if (tw < 0) { 686 tw = w; 687 } 688 long th = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_LENGTH); 689 if (th < 0) { 690 th = getFieldAsLong(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP); 691 if (th < 0) { 692 th = h; 693 } 694 } 695 696 int[] bitsPerSample = null; 697 TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); 698 if (f != null) { 699 bitsPerSample = f.getAsInts(); 700 } else { 701 int samplesPerPixel = 702 getFieldAsInt(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); 703 if (samplesPerPixel < 0) { 704 samplesPerPixel = 1; 705 } 706 bitsPerSample = new int[samplesPerPixel]; 707 Arrays.fill(bitsPerSample, 8); 708 } 709 710 int bitsPerPixel = 0; 711 for (int bps : bitsPerSample) { 712 bitsPerPixel += bps; 713 } 714 715 int bytesPerRow = (int)(tw*bitsPerPixel + 7)/8; 716 int bytesPerPacket = (int)th*bytesPerRow; 717 718 long nx = (w + tw - 1)/tw; 719 long ny = (h + th - 1)/th; 720 721 if (nx*ny != expectedSize) { 722 return false; 723 } 724 725 boolean isTiled = 726 getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS) != null; 727 728 int tagNumber; 729 if (isTiled) { 730 tagNumber = BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS; 731 } else { 732 tagNumber = BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS; 733 } 734 735 TIFFTag t = BaselineTIFFTagSet.getInstance().getTag(tagNumber); 736 f = getTIFFField(tagNumber); 737 if (f != null) { 738 removeTIFFField(tagNumber); 739 } 740 741 int numPackets = (int)(nx*ny); 742 long[] packetByteCounts = new long[numPackets]; 743 Arrays.fill(packetByteCounts, bytesPerPacket); 744 745 // if the strip or tile width does not exceed the image width and the 746 // image height is not a multiple of the strip or tile height, then 747 // truncate the estimate of the byte count of the last strip to avoid 748 // reading past the end of the data 749 if (tw <= w && h % th != 0) { 750 int numRowsInLastStrip = (int)(h - (ny - 1)*th); 751 packetByteCounts[numPackets - 1] = numRowsInLastStrip*bytesPerRow; 752 } 753 754 f = new TIFFField(t, TIFFTag.TIFF_LONG, numPackets, packetByteCounts); 755 addTIFFField(f); 756 byteCounts.add(f); 757 758 return true; 759 } 760 761 // 762 // Verify that data pointed to outside of the IFD itself are within the 763 // stream. To be called after all fields have been read and populated. 764 // checkFieldOffsets(long streamLength)765 private void checkFieldOffsets(long streamLength) throws IIOException { 766 if (streamLength < 0) { 767 return; 768 } 769 770 // StripOffsets 771 List<TIFFField> offsets = new ArrayList<>(); 772 TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 773 int count = 0; 774 if (f != null) { 775 count = f.getCount(); 776 offsets.add(f); 777 } 778 779 // TileOffsets 780 f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 781 if (f != null) { 782 int sz = offsets.size(); 783 int newCount = f.getCount(); 784 if (sz > 0 && newCount != count) { 785 throw new IIOException 786 ("StripOffsets count != TileOffsets count"); 787 } 788 789 if (sz == 0) { 790 count = newCount; 791 } 792 offsets.add(f); 793 } 794 795 List<TIFFField> byteCounts = new ArrayList<>(); 796 if (offsets.size() > 0) { 797 // StripByteCounts 798 f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS); 799 if (f != null) { 800 if (f.getCount() != count) { 801 throw new IIOException 802 ("StripByteCounts count != number of offsets"); 803 } 804 byteCounts.add(f); 805 } 806 807 // TileByteCounts 808 f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS); 809 if (f != null) { 810 if (f.getCount() != count) { 811 throw new IIOException 812 ("TileByteCounts count != number of offsets"); 813 } 814 byteCounts.add(f); 815 } 816 817 if (byteCounts.size() > 0) { 818 for (TIFFField offset : offsets) { 819 for (TIFFField byteCount : byteCounts) { 820 for (int i = 0; i < count; i++) { 821 long dataOffset = offset.getAsLong(i); 822 long dataByteCount = byteCount.getAsLong(i); 823 if (dataOffset + dataByteCount > streamLength) { 824 throw new IIOException 825 ("Data segment out of stream"); 826 } 827 } 828 } 829 } 830 } 831 } 832 833 // JPEGInterchangeFormat and JPEGInterchangeFormatLength 834 TIFFField jpegOffset = 835 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT); 836 if (jpegOffset != null) { 837 TIFFField jpegLength = 838 getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 839 if (jpegLength != null) { 840 if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0) 841 > streamLength) { 842 throw new IIOException 843 ("JPEGInterchangeFormat data out of stream"); 844 } 845 } 846 } 847 848 // Ensure there is at least a data pointer for JPEG interchange format or 849 // both data offsets and byte counts for other compression types. 850 if (jpegOffset == null 851 && (offsets.size() == 0 || byteCounts.size() == 0)) { 852 boolean throwException = true; 853 if (offsets.size() != 0 && byteCounts.size() == 0) { 854 // Attempt to calculate missing byte counts 855 int expectedSize = offsets.get(0).getCount(); 856 throwException = 857 !calculateByteCounts(expectedSize, byteCounts); 858 } 859 if (throwException) { 860 throw new IIOException 861 ("Insufficient data offsets or byte counts"); 862 } 863 } 864 865 // JPEGQTables - one 64-byte table for each offset. 866 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES); 867 if (f != null) { 868 long[] tableOffsets = f.getAsLongs(); 869 for (long off : tableOffsets) { 870 if (off + 64 > streamLength) { 871 throw new IIOException("JPEGQTables data out of stream"); 872 } 873 } 874 } 875 876 // JPEGDCTables 877 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES); 878 if (f != null) { 879 long[] tableOffsets = f.getAsLongs(); 880 for (long off : tableOffsets) { 881 if (off + 16 > streamLength) { 882 throw new IIOException("JPEGDCTables data out of stream"); 883 } 884 } 885 } 886 887 // JPEGACTables 888 f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_AC_TABLES); 889 if (f != null) { 890 long[] tableOffsets = f.getAsLongs(); 891 for (long off : tableOffsets) { 892 if (off + 16 > streamLength) { 893 throw new IIOException("JPEGACTables data out of stream"); 894 } 895 } 896 } 897 } 898 899 // Stream position initially at beginning, left at end 900 // if readUnknownTags is false, do not load fields for which 901 // a tag cannot be found in an allowed TagSet. initialize(ImageInputStream stream, boolean isPrimaryIFD, boolean ignoreMetadata, boolean readUnknownTags)902 public void initialize(ImageInputStream stream, boolean isPrimaryIFD, 903 boolean ignoreMetadata, boolean readUnknownTags) throws IOException { 904 905 removeTIFFFields(); 906 907 long streamLength = stream.length(); 908 boolean haveStreamLength = streamLength != -1; 909 910 List<TIFFTagSet> tagSetList = getTagSetList(); 911 912 // Configure essential tag variables if this is the primary IFD and 913 // either all metadata are being ignored, or metadata are not being 914 // ignored but both unknown tags are being ignored and the tag set 915 // list does not contain the baseline tags. 916 boolean ensureEssentialTags = false; 917 TIFFTagSet baselineTagSet = null; 918 if (isPrimaryIFD && 919 (ignoreMetadata || 920 (!readUnknownTags && 921 !tagSetList.contains(BaselineTIFFTagSet.getInstance())))) { 922 ensureEssentialTags = true; 923 initializeEssentialTags(); 924 baselineTagSet = BaselineTIFFTagSet.getInstance(); 925 } 926 927 List<Object> entries = new ArrayList<>(); 928 Object[] entryData = new Object[1]; // allocate once for later reuse. 929 930 // Read the IFD entries, loading the field values which are no more than 931 // four bytes long, and storing the 4-byte offsets for the others. 932 int numEntries = stream.readUnsignedShort(); 933 for (int i = 0; i < numEntries; i++) { 934 // Read tag number, value type, and value count. 935 int tagNumber = stream.readUnsignedShort(); 936 int type = stream.readUnsignedShort(); 937 int sizeOfType; 938 try { 939 sizeOfType = TIFFTag.getSizeOfType(type); 940 } catch (IllegalArgumentException ignored) { 941 // Continue with the next IFD entry. 942 stream.skipBytes(4); 943 continue; 944 } 945 long longCount = stream.readUnsignedInt(); 946 947 // Get the associated TIFFTag. 948 TIFFTag tag = getTag(tagNumber, tagSetList); 949 950 if (tag == null && ensureEssentialTags 951 && essentialTags.contains(tagNumber)) { 952 tag = baselineTagSet.getTag(tagNumber); 953 } 954 955 // Ignore non-essential fields, unknown fields unless forcibly 956 // being read, fields with unknown type, and fields 957 // with count out of int range. 958 if((ignoreMetadata && 959 (!ensureEssentialTags || !essentialTags.contains(tagNumber))) 960 || (tag == null && !readUnknownTags) 961 || (tag != null && !tag.isDataTypeOK(type)) 962 || longCount > Integer.MAX_VALUE) { 963 // Skip the value/offset so as to leave the stream 964 // position at the start of the next IFD entry. 965 stream.skipBytes(4); 966 967 // Continue with the next IFD entry. 968 continue; 969 } 970 971 int count = (int)longCount; 972 973 if (tag == null) { 974 tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber, 975 1 << type, count); 976 } else { 977 int expectedCount = tag.getCount(); 978 if (expectedCount > 0) { 979 // If the tag count is positive then the tag defines a 980 // specific, fixed count that the field must match. 981 if (count != expectedCount) { 982 throw new IIOException("Unexpected count " 983 + count + " for " + tag.getName() + " field"); 984 } 985 } else if (type == TIFFTag.TIFF_ASCII) { 986 // Clamp the size of ASCII fields of unspecified length 987 // to a maximum value. 988 int asciiSize = TIFFTag.getSizeOfType(TIFFTag.TIFF_ASCII); 989 if (count*asciiSize > MAX_ASCII_SIZE) { 990 count = (int)(MAX_ASCII_SIZE/asciiSize); 991 } 992 } 993 } 994 995 long longSize = longCount*sizeOfType; 996 if (longSize > Integer.MAX_VALUE) { 997 // Continue with the next IFD entry. 998 stream.skipBytes(4); 999 continue; 1000 } 1001 int size = (int)longSize; 1002 1003 if (size > 4 || tag.isIFDPointer()) { 1004 // The IFD entry value is a pointer to the actual field value. 1005 long offset = stream.readUnsignedInt(); 1006 1007 // Check whether the the field value is within the stream. 1008 if (haveStreamLength && offset + size > streamLength) { 1009 continue; 1010 } 1011 1012 // Add a TIFFIFDEntry as a placeholder. This avoids a mark, 1013 // seek to the data, and a reset. 1014 entries.add(new TIFFIFDEntry(tag, type, count, offset)); 1015 } else { 1016 // The IFD entry value is the actual field value of no more than 1017 // four bytes. 1018 Object obj = null; 1019 try { 1020 // Read the field value and update the count. 1021 count = readFieldValue(stream, type, count, entryData); 1022 obj = entryData[0]; 1023 } catch (EOFException eofe) { 1024 // The TIFF 6.0 fields have tag numbers less than or equal 1025 // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). 1026 // If there is an error reading a baseline tag, then re-throw 1027 // the exception and fail; otherwise continue with the next 1028 // field. 1029 if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) { 1030 throw eofe; 1031 } 1032 } 1033 1034 // If the field value is smaller than four bytes then skip 1035 // the remaining, unused bytes. 1036 if (size < 4) { 1037 stream.skipBytes(4 - size); 1038 } 1039 1040 // Add the populated TIFFField to the list of entries. 1041 entries.add(new TIFFField(tag, type, count, obj)); 1042 } 1043 } 1044 1045 // After reading the IFD entries the stream is positioned at an unsigned 1046 // four byte integer containing either the offset of the next IFD or 1047 // zero if this is the last IFD. 1048 long nextIFDOffset = stream.getStreamPosition(); 1049 1050 Object[] fieldData = new Object[1]; 1051 for (Object entry : entries) { 1052 if (entry instanceof TIFFField) { 1053 // Add the populated field directly. 1054 addTIFFField((TIFFField)entry); 1055 } else { 1056 TIFFIFDEntry e = (TIFFIFDEntry)entry; 1057 TIFFTag tag = e.tag; 1058 int tagNumber = tag.getNumber(); 1059 int type = e.type; 1060 int count = e.count; 1061 1062 stream.seek(e.offset); 1063 1064 if (tag.isIFDPointer()) { 1065 List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1); 1066 tagSets.add(tag.getTagSet()); 1067 TIFFIFD subIFD = new TIFFIFD(tagSets); 1068 1069 subIFD.initialize(stream, false, ignoreMetadata, 1070 readUnknownTags); 1071 TIFFField f = new TIFFField(tag, type, e.offset, subIFD); 1072 addTIFFField(f); 1073 } else { 1074 if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS 1075 || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS 1076 || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) { 1077 this.stripOrTileByteCountsPosition 1078 = stream.getStreamPosition(); 1079 } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS 1080 || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS 1081 || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) { 1082 this.stripOrTileOffsetsPosition 1083 = stream.getStreamPosition(); 1084 } 1085 1086 Object obj = null; 1087 try { 1088 count = readFieldValue(stream, type, count, fieldData); 1089 obj = fieldData[0]; 1090 } catch (EOFException eofe) { 1091 // The TIFF 6.0 fields have tag numbers less than or equal 1092 // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). 1093 // If there is an error reading a baseline tag, then re-throw 1094 // the exception and fail; otherwise continue with the next 1095 // field. 1096 if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) != null) { 1097 throw eofe; 1098 } 1099 } 1100 1101 if (obj == null) { 1102 continue; 1103 } 1104 1105 TIFFField f = new TIFFField(tag, type, count, obj); 1106 addTIFFField(f); 1107 } 1108 } 1109 } 1110 1111 if(isPrimaryIFD && haveStreamLength) { 1112 checkFieldOffsets(streamLength); 1113 } 1114 1115 stream.seek(nextIFDOffset); 1116 this.lastPosition = stream.getStreamPosition(); 1117 } 1118 writeToStream(ImageOutputStream stream)1119 public void writeToStream(ImageOutputStream stream) 1120 throws IOException { 1121 1122 int numFields = getNumTIFFFields(); 1123 stream.writeShort(numFields); 1124 1125 long nextSpace = stream.getStreamPosition() + 12*numFields + 4; 1126 1127 Iterator<TIFFField> iter = iterator(); 1128 while (iter.hasNext()) { 1129 TIFFField f = iter.next(); 1130 1131 TIFFTag tag = f.getTag(); 1132 1133 int type = f.getType(); 1134 int count = f.getCount(); 1135 1136 // Deal with unknown tags 1137 if (type == 0) { 1138 type = TIFFTag.TIFF_UNDEFINED; 1139 } 1140 int size = count*TIFFTag.getSizeOfType(type); 1141 1142 if (type == TIFFTag.TIFF_ASCII) { 1143 int chars = 0; 1144 for (int i = 0; i < count; i++) { 1145 chars += f.getAsString(i).length() + 1; 1146 } 1147 count = chars; 1148 size = count; 1149 } 1150 1151 int tagNumber = f.getTagNumber(); 1152 stream.writeShort(tagNumber); 1153 stream.writeShort(type); 1154 stream.writeInt(count); 1155 1156 // Write a dummy value to fill space 1157 stream.writeInt(0); 1158 stream.mark(); // Mark beginning of next field 1159 stream.skipBytes(-4); 1160 1161 long pos; 1162 1163 if (size > 4 || tag.isIFDPointer()) { 1164 // Ensure IFD or value is written on a word boundary 1165 nextSpace = (nextSpace + 3) & ~0x3; 1166 1167 stream.writeInt((int)nextSpace); 1168 stream.seek(nextSpace); 1169 pos = nextSpace; 1170 1171 if (tag.isIFDPointer() && f.hasDirectory()) { 1172 TIFFIFD subIFD = getDirectoryAsIFD(f.getDirectory()); 1173 subIFD.writeToStream(stream); 1174 nextSpace = subIFD.lastPosition; 1175 } else { 1176 writeTIFFFieldToStream(f, stream); 1177 nextSpace = stream.getStreamPosition(); 1178 } 1179 } else { 1180 pos = stream.getStreamPosition(); 1181 writeTIFFFieldToStream(f, stream); 1182 } 1183 1184 // If we are writing the data for the 1185 // StripByteCounts, TileByteCounts, StripOffsets, 1186 // TileOffsets, JPEGInterchangeFormat, or 1187 // JPEGInterchangeFormatLength fields, record the current stream 1188 // position for backpatching 1189 if (tagNumber == 1190 BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS || 1191 tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS || 1192 tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) { 1193 this.stripOrTileByteCountsPosition = pos; 1194 } else if (tagNumber == 1195 BaselineTIFFTagSet.TAG_STRIP_OFFSETS || 1196 tagNumber == 1197 BaselineTIFFTagSet.TAG_TILE_OFFSETS || 1198 tagNumber == 1199 BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) { 1200 this.stripOrTileOffsetsPosition = pos; 1201 } 1202 1203 stream.reset(); // Go to marked position of next field 1204 } 1205 1206 this.lastPosition = nextSpace; 1207 } 1208 getStripOrTileByteCountsPosition()1209 public long getStripOrTileByteCountsPosition() { 1210 return stripOrTileByteCountsPosition; 1211 } 1212 getStripOrTileOffsetsPosition()1213 public long getStripOrTileOffsetsPosition() { 1214 return stripOrTileOffsetsPosition; 1215 } 1216 getLastPosition()1217 public long getLastPosition() { 1218 return lastPosition; 1219 } 1220 setPositions(long stripOrTileOffsetsPosition, long stripOrTileByteCountsPosition, long lastPosition)1221 void setPositions(long stripOrTileOffsetsPosition, 1222 long stripOrTileByteCountsPosition, 1223 long lastPosition) { 1224 this.stripOrTileOffsetsPosition = stripOrTileOffsetsPosition; 1225 this.stripOrTileByteCountsPosition = stripOrTileByteCountsPosition; 1226 this.lastPosition = lastPosition; 1227 } 1228 1229 /** 1230 * Returns a {@code TIFFIFD} wherein all fields from the 1231 * {@code BaselineTIFFTagSet} are copied by value and all other 1232 * fields copied by reference. 1233 */ getShallowClone()1234 public TIFFIFD getShallowClone() { 1235 // Get the baseline TagSet. 1236 TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance(); 1237 1238 // If the baseline TagSet is not included just return. 1239 List<TIFFTagSet> tagSetList = getTagSetList(); 1240 if(!tagSetList.contains(baselineTagSet)) { 1241 return this; 1242 } 1243 1244 // Create a new object. 1245 TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag()); 1246 1247 // Get the tag numbers in the baseline set. 1248 Set<Integer> baselineTagNumbers = baselineTagSet.getTagNumbers(); 1249 1250 // Iterate over the fields in this IFD. 1251 Iterator<TIFFField> fields = iterator(); 1252 while(fields.hasNext()) { 1253 // Get the next field. 1254 TIFFField field = fields.next(); 1255 1256 // Get its tag number. 1257 Integer tagNumber = Integer.valueOf(field.getTagNumber()); 1258 1259 // Branch based on membership in baseline set. 1260 TIFFField fieldClone; 1261 if(baselineTagNumbers.contains(tagNumber)) { 1262 // Copy by value. 1263 Object fieldData = field.getData(); 1264 1265 int fieldType = field.getType(); 1266 1267 try { 1268 switch (fieldType) { 1269 case TIFFTag.TIFF_BYTE: 1270 case TIFFTag.TIFF_SBYTE: 1271 case TIFFTag.TIFF_UNDEFINED: 1272 fieldData = ((byte[])fieldData).clone(); 1273 break; 1274 case TIFFTag.TIFF_ASCII: 1275 fieldData = ((String[])fieldData).clone(); 1276 break; 1277 case TIFFTag.TIFF_SHORT: 1278 fieldData = ((char[])fieldData).clone(); 1279 break; 1280 case TIFFTag.TIFF_LONG: 1281 case TIFFTag.TIFF_IFD_POINTER: 1282 fieldData = ((long[])fieldData).clone(); 1283 break; 1284 case TIFFTag.TIFF_RATIONAL: 1285 fieldData = ((long[][])fieldData).clone(); 1286 break; 1287 case TIFFTag.TIFF_SSHORT: 1288 fieldData = ((short[])fieldData).clone(); 1289 break; 1290 case TIFFTag.TIFF_SLONG: 1291 fieldData = ((int[])fieldData).clone(); 1292 break; 1293 case TIFFTag.TIFF_SRATIONAL: 1294 fieldData = ((int[][])fieldData).clone(); 1295 break; 1296 case TIFFTag.TIFF_FLOAT: 1297 fieldData = ((float[])fieldData).clone(); 1298 break; 1299 case TIFFTag.TIFF_DOUBLE: 1300 fieldData = ((double[])fieldData).clone(); 1301 break; 1302 default: 1303 // Shouldn't happen but do nothing ... 1304 } 1305 } catch(Exception e) { 1306 // Ignore it and copy by reference ... 1307 } 1308 1309 fieldClone = new TIFFField(field.getTag(), fieldType, 1310 field.getCount(), fieldData); 1311 } else { 1312 // Copy by reference. 1313 fieldClone = field; 1314 } 1315 1316 // Add the field to the clone. 1317 shallowClone.addTIFFField(fieldClone); 1318 } 1319 1320 // Set positions. 1321 shallowClone.setPositions(stripOrTileOffsetsPosition, 1322 stripOrTileByteCountsPosition, 1323 lastPosition); 1324 1325 return shallowClone; 1326 } 1327 } 1328