001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.formats.tiff; 018 019import java.io.IOException; 020import java.io.PrintWriter; 021import java.io.StringWriter; 022import java.nio.ByteOrder; 023import java.text.DateFormat; 024import java.text.SimpleDateFormat; 025import java.util.Date; 026import java.util.Locale; 027import java.util.logging.Level; 028import java.util.logging.Logger; 029 030import org.apache.commons.imaging.ImageReadException; 031import org.apache.commons.imaging.common.BinaryFunctions; 032import org.apache.commons.imaging.formats.tiff.constants.TiffConstants; 033import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; 034import org.apache.commons.imaging.formats.tiff.fieldtypes.FieldType; 035import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; 036 037/** 038 * A TIFF field in a TIFF directory. Immutable. 039 */ 040public class TiffField { 041 042 private static final Logger LOGGER = Logger.getLogger(TiffField.class.getName()); 043 044 private final TagInfo tagInfo; 045 private final int tag; 046 private final int directoryType; 047 private final FieldType fieldType; 048 private final long count; 049 private final long offset; 050 private final byte[] value; 051 private final ByteOrder byteOrder; 052 private final int sortHint; 053 054 public TiffField(final int tag, final int directoryType, final FieldType fieldType, 055 final long count, final long offset, final byte[] value, 056 final ByteOrder byteOrder, final int sortHint) { 057 058 this.tag = tag; 059 this.directoryType = directoryType; 060 this.fieldType = fieldType; 061 this.count = count; 062 this.offset = offset; 063 this.value = value; 064 this.byteOrder = byteOrder; 065 this.sortHint = sortHint; 066 067 tagInfo = TiffTags.getTag(directoryType, tag); 068 } 069 070 public int getDirectoryType() { 071 return directoryType; 072 } 073 074 public TagInfo getTagInfo() { 075 return tagInfo; 076 } 077 078 /** 079 * Returns the field's tag, derived from bytes 0-1. 080 * @return the tag, as an {@code int} in which only the lowest 2 bytes are set 081 */ 082 public int getTag() { 083 return tag; 084 } 085 086 /** 087 * Returns the field's type, derived from bytes 2-3. 088 * @return the field's type, as a {@code FieldType} object. 089 */ 090 public FieldType getFieldType() { 091 return fieldType; 092 } 093 094 /** 095 * Returns the field's count, derived from bytes 4-7. 096 * @return the count 097 */ 098 public long getCount() { 099 return count; 100 } 101 102 /** 103 * Returns the TIFF field's offset/value field, derived from bytes 8-11. 104 * @return the field's offset in a {@code long} of 4 packed bytes, 105 * or its inlined value <= 4 bytes long encoded in the field's byte order. 106 */ 107 public int getOffset() { 108 return (int) offset; 109 } 110 111 /** 112 * Returns the field's byte order. 113 * @return the byte order 114 */ 115 public ByteOrder getByteOrder() { 116 return byteOrder; 117 } 118 119 public int getSortHint() { 120 return sortHint; 121 } 122 123 /** 124 * Indicates whether the field's value is inlined into the offset field. 125 * @return true if the value is inlined 126 */ 127 public boolean isLocalValue() { 128 return (count * fieldType.getSize()) <= TiffConstants.TIFF_ENTRY_MAX_VALUE_LENGTH; 129 } 130 131 /** 132 * The length of the field's value. 133 * @return the length, in bytes. 134 */ 135 public int getBytesLength() { 136 return (int) count * fieldType.getSize(); 137 } 138 139 /** 140 * Returns a copy of the raw value of the field. 141 * @return the value of the field, in the byte order of the field. 142 */ 143 public byte[] getByteArrayValue() { 144 return BinaryFunctions.head(value, getBytesLength()); 145 } 146 147 public final class OversizeValueElement extends TiffElement { 148 public OversizeValueElement(final int offset, final int length) { 149 super(offset, length); 150 } 151 152 @Override 153 public String getElementDescription() { 154 return "OversizeValueElement, tag: " + getTagInfo().name 155 + ", fieldType: " + getFieldType().getName(); 156 } 157 } 158 159 public TiffElement getOversizeValueElement() { 160 if (isLocalValue()) { 161 return null; 162 } 163 164 return new OversizeValueElement(getOffset(), value.length); 165 } 166 167 public String getValueDescription() { 168 try { 169 return getValueDescription(getValue()); 170 } catch (final ImageReadException e) { 171 return "Invalid value: " + e.getMessage(); 172 } 173 } 174 175 private String getValueDescription(final Object o) { 176 if (o == null) { 177 return null; 178 } 179 180 if (o instanceof Number) { 181 return o.toString(); 182 } else if (o instanceof String) { 183 return "'" + o.toString().trim() + "'"; 184 } else if (o instanceof Date) { 185 final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH); 186 return df.format((Date) o); 187 } else if (o instanceof Object[]) { 188 final Object[] objects = (Object[]) o; 189 final StringBuilder result = new StringBuilder(); 190 191 for (int i = 0; i < objects.length; i++) { 192 final Object object = objects[i]; 193 194 if (i > 50) { 195 result.append("... (").append(objects.length).append(")"); 196 break; 197 } 198 if (i > 0) { 199 result.append(", "); 200 } 201 result.append(object.toString()); 202 } 203 return result.toString(); 204 // } else if (o instanceof Number[]) 205 // { 206 // Number numbers[] = (Number[]) o; 207 // StringBuilder result = new StringBuilder(); 208 // 209 // for (int i = 0; i < numbers.length; i++) 210 // { 211 // Number number = numbers[i]; 212 // 213 // if (i > 0) 214 // result.append(", "); 215 // result.append("" + number); 216 // } 217 // return result.toString(); 218 // } 219 } else if (o instanceof short[]) { 220 final short[] values = (short[]) o; 221 final StringBuilder result = new StringBuilder(); 222 223 for (int i = 0; i < values.length; i++) { 224 final short sval = values[i]; 225 226 if (i > 50) { 227 result.append("... (").append(values.length).append(")"); 228 break; 229 } 230 if (i > 0) { 231 result.append(", "); 232 } 233 result.append(sval); 234 } 235 return result.toString(); 236 } else if (o instanceof int[]) { 237 final int[] values = (int[]) o; 238 final StringBuilder result = new StringBuilder(); 239 240 for (int i = 0; i < values.length; i++) { 241 final int iVal = values[i]; 242 243 if (i > 50) { 244 result.append("... (").append(values.length).append(")"); 245 break; 246 } 247 if (i > 0) { 248 result.append(", "); 249 } 250 result.append(iVal); 251 } 252 return result.toString(); 253 } else if (o instanceof long[]) { 254 final long[] values = (long[]) o; 255 final StringBuilder result = new StringBuilder(); 256 257 for (int i = 0; i < values.length; i++) { 258 final long lVal = values[i]; 259 260 if (i > 50) { 261 result.append("... (").append(values.length).append(")"); 262 break; 263 } 264 if (i > 0) { 265 result.append(", "); 266 } 267 result.append(lVal); 268 } 269 return result.toString(); 270 } else if (o instanceof double[]) { 271 final double[] values = (double[]) o; 272 final StringBuilder result = new StringBuilder(); 273 274 for (int i = 0; i < values.length; i++) { 275 final double dVal = values[i]; 276 277 if (i > 50) { 278 result.append("... (").append(values.length).append(")"); 279 break; 280 } 281 if (i > 0) { 282 result.append(", "); 283 } 284 result.append(dVal); 285 } 286 return result.toString(); 287 } else if (o instanceof byte[]) { 288 final byte[] values = (byte[]) o; 289 final StringBuilder result = new StringBuilder(); 290 291 for (int i = 0; i < values.length; i++) { 292 final byte bVal = values[i]; 293 294 if (i > 50) { 295 result.append("... (").append(values.length).append(")"); 296 break; 297 } 298 if (i > 0) { 299 result.append(", "); 300 } 301 result.append(bVal); 302 } 303 return result.toString(); 304 } else if (o instanceof char[]) { 305 final char[] values = (char[]) o; 306 final StringBuilder result = new StringBuilder(); 307 308 for (int i = 0; i < values.length; i++) { 309 final char cVal = values[i]; 310 311 if (i > 50) { 312 result.append("... (").append(values.length).append(")"); 313 break; 314 } 315 if (i > 0) { 316 result.append(", "); 317 } 318 result.append(cVal); 319 } 320 return result.toString(); 321 } else if (o instanceof float[]) { 322 final float[] values = (float[]) o; 323 final StringBuilder result = new StringBuilder(); 324 325 for (int i = 0; i < values.length; i++) { 326 final float fVal = values[i]; 327 328 if (i > 50) { 329 result.append("... (").append(values.length).append(")"); 330 break; 331 } 332 if (i > 0) { 333 result.append(", "); 334 } 335 result.append(fVal); 336 } 337 return result.toString(); 338 } 339 // else if (o instanceof short[]) 340 // { 341 // short numbers[] = (short[]) o; 342 // StringBuilder result = new StringBuilder(); 343 // 344 // for (int i = 0; i < numbers.length; i++) 345 // { 346 // short number = numbers[i]; 347 // 348 // if (i > 0) 349 // result.append(", "); 350 // result.append("" + number); 351 // } 352 // return result.toString(); 353 // } 354 355 return "Unknown: " + o.getClass().getName(); 356 } 357 358 public void dump() { 359 try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) { 360 dump(pw); 361 pw.flush(); 362 sw.flush(); 363 LOGGER.fine(sw.toString()); 364 } catch (final IOException e) { 365 LOGGER.log(Level.SEVERE, e.getMessage(), e); 366 } 367 } 368 369 public void dump(final PrintWriter pw) { 370 dump(pw, null); 371 } 372 373 public void dump(final PrintWriter pw, final String prefix) { 374 if (prefix != null) { 375 pw.print(prefix + ": "); 376 } 377 378 pw.println(toString()); 379 pw.flush(); 380 } 381 382 public String getDescriptionWithoutValue() { 383 return getTag() + " (0x" + Integer.toHexString(getTag()) + ": " + getTagInfo().name 384 + "): "; 385 } 386 387 @Override 388 public String toString() { 389 return getTag() + 390 " (0x" + 391 Integer.toHexString(getTag()) + 392 ": " + 393 getTagInfo().name + 394 "): " + 395 getValueDescription() + 396 " (" + 397 getCount() + 398 " " + 399 getFieldType().getName() + 400 ")"; 401 } 402 403 public String getTagName() { 404 if (getTagInfo() == TiffTagConstants.TIFF_TAG_UNKNOWN) { 405 return getTagInfo().name + " (0x" + Integer.toHexString(getTag()) + ")"; 406 } 407 return getTagInfo().name; 408 } 409 410 public String getFieldTypeName() { 411 return getFieldType().getName(); 412 } 413 414 public Object getValue() throws ImageReadException { 415 // System.out.print("getValue"); 416 return getTagInfo().getValue(this); 417 } 418 419 public String getStringValue() throws ImageReadException { 420 final Object o = getValue(); 421 if (o == null) { 422 return null; 423 } 424 if (!(o instanceof String)) { 425 throw new ImageReadException("Expected String value(" 426 + getTagInfo().getDescription() + "): " + o); 427 } 428 return (String) o; 429 } 430 431 public int[] getIntArrayValue() throws ImageReadException { 432 final Object o = getValue(); 433 // if (o == null) 434 // return null; 435 436 if (o instanceof Number) { 437 return new int[] { ((Number) o).intValue() }; 438 } else if (o instanceof Number[]) { 439 final Number[] numbers = (Number[]) o; 440 final int[] result = new int[numbers.length]; 441 for (int i = 0; i < numbers.length; i++) { 442 result[i] = numbers[i].intValue(); 443 } 444 return result; 445 } else if (o instanceof short[]) { 446 final short[] numbers = (short[]) o; 447 final int[] result = new int[numbers.length]; 448 for (int i = 0; i < numbers.length; i++) { 449 result[i] = 0xffff & numbers[i]; 450 } 451 return result; 452 } else if (o instanceof int[]) { 453 final int[] numbers = (int[]) o; 454 final int[] result = new int[numbers.length]; 455 System.arraycopy(numbers, 0, result, 0, numbers.length); 456 return result; 457 } 458 459 throw new ImageReadException("Unknown value: " + o + " for: " 460 + getTagInfo().getDescription()); 461 // return null; 462 } 463 464 public double[] getDoubleArrayValue() throws ImageReadException { 465 final Object o = getValue(); 466 // if (o == null) 467 // return null; 468 469 if (o instanceof Number) { 470 return new double[] { ((Number) o).doubleValue() }; 471 } else if (o instanceof Number[]) { 472 final Number[] numbers = (Number[]) o; 473 final double[] result = new double[numbers.length]; 474 for (int i = 0; i < numbers.length; i++) { 475 result[i] = numbers[i].doubleValue(); 476 } 477 return result; 478 } else if (o instanceof short[]) { 479 final short[] numbers = (short[]) o; 480 final double[] result = new double[numbers.length]; 481 for (int i = 0; i < numbers.length; i++) { 482 result[i] = numbers[i]; 483 } 484 return result; 485 } else if (o instanceof int[]) { 486 final int[] numbers = (int[]) o; 487 final double[] result = new double[numbers.length]; 488 for (int i = 0; i < numbers.length; i++) { 489 result[i] = numbers[i]; 490 } 491 return result; 492 } else if (o instanceof float[]) { 493 final float[] numbers = (float[]) o; 494 final double[] result = new double[numbers.length]; 495 for (int i = 0; i < numbers.length; i++) { 496 result[i] = numbers[i]; 497 } 498 return result; 499 } else if (o instanceof double[]) { 500 final double[] numbers = (double[]) o; 501 final double[] result = new double[numbers.length]; 502 System.arraycopy(numbers, 0, result, 0, numbers.length); 503 return result; 504 } 505 506 throw new ImageReadException("Unknown value: " + o + " for: " 507 + getTagInfo().getDescription()); 508 // return null; 509 } 510 511 public int getIntValueOrArraySum() throws ImageReadException { 512 final Object o = getValue(); 513 // if (o == null) 514 // return -1; 515 516 if (o instanceof Number) { 517 return ((Number) o).intValue(); 518 } else if (o instanceof Number[]) { 519 final Number[] numbers = (Number[]) o; 520 int sum = 0; 521 for (final Number number : numbers) { 522 sum += number.intValue(); 523 } 524 return sum; 525 } else if (o instanceof short[]) { 526 final short[] numbers = (short[]) o; 527 int sum = 0; 528 for (final short number : numbers) { 529 sum += number; 530 } 531 return sum; 532 } else if (o instanceof int[]) { 533 final int[] numbers = (int[]) o; 534 int sum = 0; 535 for (final int number : numbers) { 536 sum += number; 537 } 538 return sum; 539 } 540 541 throw new ImageReadException("Unknown value: " + o + " for: " 542 + getTagInfo().getDescription()); 543 // return -1; 544 } 545 546 public int getIntValue() throws ImageReadException { 547 final Object o = getValue(); 548 if (o == null) { 549 throw new ImageReadException("Missing value: " 550 + getTagInfo().getDescription()); 551 } 552 553 return ((Number) o).intValue(); 554 } 555 556 public double getDoubleValue() throws ImageReadException { 557 final Object o = getValue(); 558 if (o == null) { 559 throw new ImageReadException("Missing value: " 560 + getTagInfo().getDescription()); 561 } 562 563 return ((Number) o).doubleValue(); 564 } 565}