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 } 183 if (o instanceof String) { 184 return "'" + o.toString().trim() + "'"; 185 } 186 if (o instanceof Date) { 187 final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH); 188 return df.format((Date) o); 189 } 190 if (o instanceof Object[]) { 191 final Object[] objects = (Object[]) o; 192 final StringBuilder result = new StringBuilder(); 193 194 for (int i = 0; i < objects.length; i++) { 195 final Object object = objects[i]; 196 197 if (i > 50) { 198 result.append("... (").append(objects.length).append(")"); 199 break; 200 } 201 if (i > 0) { 202 result.append(", "); 203 } 204 result.append(object.toString()); 205 } 206 return result.toString(); 207 // } else if (o instanceof Number[]) 208 // { 209 // Number numbers[] = (Number[]) o; 210 // StringBuilder result = new StringBuilder(); 211 // 212 // for (int i = 0; i < numbers.length; i++) 213 // { 214 // Number number = numbers[i]; 215 // 216 // if (i > 0) 217 // result.append(", "); 218 // result.append("" + number); 219 // } 220 // return result.toString(); 221 // } 222 } 223 if (o instanceof short[]) { 224 final short[] values = (short[]) o; 225 final StringBuilder result = new StringBuilder(); 226 227 for (int i = 0; i < values.length; i++) { 228 final short sVal = values[i]; 229 230 if (i > 50) { 231 result.append("... (").append(values.length).append(")"); 232 break; 233 } 234 if (i > 0) { 235 result.append(", "); 236 } 237 result.append(sVal); 238 } 239 return result.toString(); 240 } 241 if (o instanceof int[]) { 242 final int[] values = (int[]) o; 243 final StringBuilder result = new StringBuilder(); 244 245 for (int i = 0; i < values.length; i++) { 246 final int iVal = values[i]; 247 248 if (i > 50) { 249 result.append("... (").append(values.length).append(")"); 250 break; 251 } 252 if (i > 0) { 253 result.append(", "); 254 } 255 result.append(iVal); 256 } 257 return result.toString(); 258 } 259 if (o instanceof long[]) { 260 final long[] values = (long[]) o; 261 final StringBuilder result = new StringBuilder(); 262 263 for (int i = 0; i < values.length; i++) { 264 final long lVal = values[i]; 265 266 if (i > 50) { 267 result.append("... (").append(values.length).append(")"); 268 break; 269 } 270 if (i > 0) { 271 result.append(", "); 272 } 273 result.append(lVal); 274 } 275 return result.toString(); 276 } 277 if (o instanceof double[]) { 278 final double[] values = (double[]) o; 279 final StringBuilder result = new StringBuilder(); 280 281 for (int i = 0; i < values.length; i++) { 282 final double dVal = values[i]; 283 284 if (i > 50) { 285 result.append("... (").append(values.length).append(")"); 286 break; 287 } 288 if (i > 0) { 289 result.append(", "); 290 } 291 result.append(dVal); 292 } 293 return result.toString(); 294 } 295 if (o instanceof byte[]) { 296 final byte[] values = (byte[]) o; 297 final StringBuilder result = new StringBuilder(); 298 299 for (int i = 0; i < values.length; i++) { 300 final byte bVal = values[i]; 301 302 if (i > 50) { 303 result.append("... (").append(values.length).append(")"); 304 break; 305 } 306 if (i > 0) { 307 result.append(", "); 308 } 309 result.append(bVal); 310 } 311 return result.toString(); 312 } 313 if (o instanceof char[]) { 314 final char[] values = (char[]) o; 315 final StringBuilder result = new StringBuilder(); 316 317 for (int i = 0; i < values.length; i++) { 318 final char cVal = values[i]; 319 320 if (i > 50) { 321 result.append("... (").append(values.length).append(")"); 322 break; 323 } 324 if (i > 0) { 325 result.append(", "); 326 } 327 result.append(cVal); 328 } 329 return result.toString(); 330 } 331 if (o instanceof float[]) { 332 final float[] values = (float[]) o; 333 final StringBuilder result = new StringBuilder(); 334 335 for (int i = 0; i < values.length; i++) { 336 final float fVal = values[i]; 337 338 if (i > 50) { 339 result.append("... (").append(values.length).append(")"); 340 break; 341 } 342 if (i > 0) { 343 result.append(", "); 344 } 345 result.append(fVal); 346 } 347 return result.toString(); 348 } 349 // else if (o instanceof short[]) 350 // { 351 // short numbers[] = (short[]) o; 352 // StringBuilder result = new StringBuilder(); 353 // 354 // for (int i = 0; i < numbers.length; i++) 355 // { 356 // short number = numbers[i]; 357 // 358 // if (i > 0) 359 // result.append(", "); 360 // result.append("" + number); 361 // } 362 // return result.toString(); 363 // } 364 365 return "Unknown: " + o.getClass().getName(); 366 } 367 368 public void dump() { 369 try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) { 370 dump(pw); 371 pw.flush(); 372 sw.flush(); 373 LOGGER.fine(sw.toString()); 374 } catch (final IOException e) { 375 LOGGER.log(Level.SEVERE, e.getMessage(), e); 376 } 377 } 378 379 public void dump(final PrintWriter pw) { 380 dump(pw, null); 381 } 382 383 public void dump(final PrintWriter pw, final String prefix) { 384 if (prefix != null) { 385 pw.print(prefix + ": "); 386 } 387 388 pw.println(toString()); 389 pw.flush(); 390 } 391 392 public String getDescriptionWithoutValue() { 393 return getTag() + " (0x" + Integer.toHexString(getTag()) + ": " + getTagInfo().name 394 + "): "; 395 } 396 397 @Override 398 public String toString() { 399 return getTag() + 400 " (0x" + 401 Integer.toHexString(getTag()) + 402 ": " + 403 getTagInfo().name + 404 "): " + 405 getValueDescription() + 406 " (" + 407 getCount() + 408 " " + 409 getFieldType().getName() + 410 ")"; 411 } 412 413 public String getTagName() { 414 if (getTagInfo() == TiffTagConstants.TIFF_TAG_UNKNOWN) { 415 return getTagInfo().name + " (0x" + Integer.toHexString(getTag()) + ")"; 416 } 417 return getTagInfo().name; 418 } 419 420 public String getFieldTypeName() { 421 return getFieldType().getName(); 422 } 423 424 public Object getValue() throws ImageReadException { 425 // System.out.print("getValue"); 426 return getTagInfo().getValue(this); 427 } 428 429 public String getStringValue() throws ImageReadException { 430 final Object o = getValue(); 431 if (o == null) { 432 return null; 433 } 434 if (!(o instanceof String)) { 435 throw new ImageReadException("Expected String value(" 436 + getTagInfo().getDescription() + "): " + o); 437 } 438 return (String) o; 439 } 440 441 public int[] getIntArrayValue() throws ImageReadException { 442 final Object o = getValue(); 443 // if (o == null) 444 // return null; 445 446 if (o instanceof Number) { 447 return new int[] { ((Number) o).intValue() }; 448 } 449 if (o instanceof Number[]) { 450 final Number[] numbers = (Number[]) o; 451 final int[] result = new int[numbers.length]; 452 for (int i = 0; i < numbers.length; i++) { 453 result[i] = numbers[i].intValue(); 454 } 455 return result; 456 } 457 if (o instanceof short[]) { 458 final short[] numbers = (short[]) o; 459 final int[] result = new int[numbers.length]; 460 for (int i = 0; i < numbers.length; i++) { 461 result[i] = 0xffff & numbers[i]; 462 } 463 return result; 464 } 465 if (o instanceof int[]) { 466 final int[] numbers = (int[]) o; 467 final int[] result = new int[numbers.length]; 468 System.arraycopy(numbers, 0, result, 0, numbers.length); 469 return result; 470 } 471 472 throw new ImageReadException("Unknown value: " + o + " for: " 473 + getTagInfo().getDescription()); 474 // return null; 475 } 476 477 public double[] getDoubleArrayValue() throws ImageReadException { 478 final Object o = getValue(); 479 // if (o == null) 480 // return null; 481 482 if (o instanceof Number) { 483 return new double[] { ((Number) o).doubleValue() }; 484 } 485 if (o instanceof Number[]) { 486 final Number[] numbers = (Number[]) o; 487 final double[] result = new double[numbers.length]; 488 for (int i = 0; i < numbers.length; i++) { 489 result[i] = numbers[i].doubleValue(); 490 } 491 return result; 492 } 493 if (o instanceof short[]) { 494 final short[] numbers = (short[]) o; 495 final double[] result = new double[numbers.length]; 496 for (int i = 0; i < numbers.length; i++) { 497 result[i] = numbers[i]; 498 } 499 return result; 500 } 501 if (o instanceof int[]) { 502 final int[] numbers = (int[]) o; 503 final double[] result = new double[numbers.length]; 504 for (int i = 0; i < numbers.length; i++) { 505 result[i] = numbers[i]; 506 } 507 return result; 508 } 509 if (o instanceof float[]) { 510 final float[] numbers = (float[]) o; 511 final double[] result = new double[numbers.length]; 512 for (int i = 0; i < numbers.length; i++) { 513 result[i] = numbers[i]; 514 } 515 return result; 516 } 517 if (o instanceof double[]) { 518 final double[] numbers = (double[]) o; 519 final double[] result = new double[numbers.length]; 520 System.arraycopy(numbers, 0, result, 0, numbers.length); 521 return result; 522 } 523 524 throw new ImageReadException("Unknown value: " + o + " for: " 525 + getTagInfo().getDescription()); 526 // return null; 527 } 528 529 public int getIntValueOrArraySum() throws ImageReadException { 530 final Object o = getValue(); 531 // if (o == null) 532 // return -1; 533 534 if (o instanceof Number) { 535 return ((Number) o).intValue(); 536 } 537 if (o instanceof Number[]) { 538 final Number[] numbers = (Number[]) o; 539 int sum = 0; 540 for (final Number number : numbers) { 541 sum += number.intValue(); 542 } 543 return sum; 544 } 545 if (o instanceof short[]) { 546 final short[] numbers = (short[]) o; 547 int sum = 0; 548 for (final short number : numbers) { 549 sum += number; 550 } 551 return sum; 552 } 553 if (o instanceof int[]) { 554 final int[] numbers = (int[]) o; 555 int sum = 0; 556 for (final int number : numbers) { 557 sum += number; 558 } 559 return sum; 560 } 561 562 throw new ImageReadException("Unknown value: " + o + " for: " 563 + getTagInfo().getDescription()); 564 // return -1; 565 } 566 567 public int getIntValue() throws ImageReadException { 568 final Object o = getValue(); 569 if (o == null) { 570 throw new ImageReadException("Missing value: " 571 + getTagInfo().getDescription()); 572 } 573 574 return ((Number) o).intValue(); 575 } 576 577 public double getDoubleValue() throws ImageReadException { 578 final Object o = getValue(); 579 if (o == null) { 580 throw new ImageReadException("Missing value: " 581 + getTagInfo().getDescription()); 582 } 583 584 return ((Number) o).doubleValue(); 585 } 586}