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</code> 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</code> 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("... (" + objects.length + ")"); 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("... (" + values.length + ")"); 228 break; 229 } 230 if (i > 0) { 231 result.append(", "); 232 } 233 result.append(Short.toString(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("... (" + values.length + ")"); 245 break; 246 } 247 if (i > 0) { 248 result.append(", "); 249 } 250 result.append(Integer.toString(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("... (" + values.length + ")"); 262 break; 263 } 264 if (i > 0) { 265 result.append(", "); 266 } 267 result.append(Long.toString(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("... (" + values.length + ")"); 279 break; 280 } 281 if (i > 0) { 282 result.append(", "); 283 } 284 result.append(Double.toString(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("... (" + values.length + ")"); 296 break; 297 } 298 if (i > 0) { 299 result.append(", "); 300 } 301 result.append(Byte.toString(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("... (" + values.length + ")"); 313 break; 314 } 315 if (i > 0) { 316 result.append(", "); 317 } 318 result.append(Character.toString(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("... (" + values.length + ")"); 330 break; 331 } 332 if (i > 0) { 333 result.append(", "); 334 } 335 result.append(Float.toString(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 final StringBuilder result = new StringBuilder(); 390 391 result.append(getTag() + " (0x" + Integer.toHexString(getTag()) + ": " 392 + getTagInfo().name + "): "); 393 result.append(getValueDescription() + " (" + getCount() + " " 394 + getFieldType().getName() + ")"); 395 396 return result.toString(); 397 } 398 399 public String getTagName() { 400 if (getTagInfo() == TiffTagConstants.TIFF_TAG_UNKNOWN) { 401 return getTagInfo().name + " (0x" + Integer.toHexString(getTag()) + ")"; 402 } 403 return getTagInfo().name; 404 } 405 406 public String getFieldTypeName() { 407 return getFieldType().getName(); 408 } 409 410 public Object getValue() throws ImageReadException { 411 // System.out.print("getValue"); 412 return getTagInfo().getValue(this); 413 } 414 415 public String getStringValue() throws ImageReadException { 416 final Object o = getValue(); 417 if (o == null) { 418 return null; 419 } 420 if (!(o instanceof String)) { 421 throw new ImageReadException("Expected String value(" 422 + getTagInfo().getDescription() + "): " + o); 423 } 424 return (String) o; 425 } 426 427 public int[] getIntArrayValue() throws ImageReadException { 428 final Object o = getValue(); 429 // if (o == null) 430 // return null; 431 432 if (o instanceof Number) { 433 return new int[] { ((Number) o).intValue() }; 434 } else if (o instanceof Number[]) { 435 final Number[] numbers = (Number[]) o; 436 final int[] result = new int[numbers.length]; 437 for (int i = 0; i < numbers.length; i++) { 438 result[i] = numbers[i].intValue(); 439 } 440 return result; 441 } else if (o instanceof short[]) { 442 final short[] numbers = (short[]) o; 443 final int[] result = new int[numbers.length]; 444 for (int i = 0; i < numbers.length; i++) { 445 result[i] = 0xffff & numbers[i]; 446 } 447 return result; 448 } else if (o instanceof int[]) { 449 final int[] numbers = (int[]) o; 450 final int[] result = new int[numbers.length]; 451 System.arraycopy(numbers, 0, result, 0, numbers.length); 452 return result; 453 } 454 455 throw new ImageReadException("Unknown value: " + o + " for: " 456 + getTagInfo().getDescription()); 457 // return null; 458 } 459 460 public double[] getDoubleArrayValue() throws ImageReadException { 461 final Object o = getValue(); 462 // if (o == null) 463 // return null; 464 465 if (o instanceof Number) { 466 return new double[] { ((Number) o).doubleValue() }; 467 } else if (o instanceof Number[]) { 468 final Number[] numbers = (Number[]) o; 469 final double[] result = new double[numbers.length]; 470 for (int i = 0; i < numbers.length; i++) { 471 result[i] = numbers[i].doubleValue(); 472 } 473 return result; 474 } else if (o instanceof short[]) { 475 final short[] numbers = (short[]) o; 476 final double[] result = new double[numbers.length]; 477 for (int i = 0; i < numbers.length; i++) { 478 result[i] = numbers[i]; 479 } 480 return result; 481 } else if (o instanceof int[]) { 482 final int[] numbers = (int[]) o; 483 final double[] result = new double[numbers.length]; 484 for (int i = 0; i < numbers.length; i++) { 485 result[i] = numbers[i]; 486 } 487 return result; 488 } else if (o instanceof float[]) { 489 final float[] numbers = (float[]) o; 490 final double[] result = new double[numbers.length]; 491 for (int i = 0; i < numbers.length; i++) { 492 result[i] = numbers[i]; 493 } 494 return result; 495 } else if (o instanceof double[]) { 496 final double[] numbers = (double[]) o; 497 final double[] result = new double[numbers.length]; 498 System.arraycopy(numbers, 0, result, 0, numbers.length); 499 return result; 500 } 501 502 throw new ImageReadException("Unknown value: " + o + " for: " 503 + getTagInfo().getDescription()); 504 // return null; 505 } 506 507 public int getIntValueOrArraySum() throws ImageReadException { 508 final Object o = getValue(); 509 // if (o == null) 510 // return -1; 511 512 if (o instanceof Number) { 513 return ((Number) o).intValue(); 514 } else if (o instanceof Number[]) { 515 final Number[] numbers = (Number[]) o; 516 int sum = 0; 517 for (final Number number : numbers) { 518 sum += number.intValue(); 519 } 520 return sum; 521 } else if (o instanceof short[]) { 522 final short[] numbers = (short[]) o; 523 int sum = 0; 524 for (final short number : numbers) { 525 sum += number; 526 } 527 return sum; 528 } else if (o instanceof int[]) { 529 final int[] numbers = (int[]) o; 530 int sum = 0; 531 for (final int number : numbers) { 532 sum += number; 533 } 534 return sum; 535 } 536 537 throw new ImageReadException("Unknown value: " + o + " for: " 538 + getTagInfo().getDescription()); 539 // return -1; 540 } 541 542 public int getIntValue() throws ImageReadException { 543 final Object o = getValue(); 544 if (o == null) { 545 throw new ImageReadException("Missing value: " 546 + getTagInfo().getDescription()); 547 } 548 549 return ((Number) o).intValue(); 550 } 551 552 public double getDoubleValue() throws ImageReadException { 553 final Object o = getValue(); 554 if (o == null) { 555 throw new ImageReadException("Missing value: " 556 + getTagInfo().getDescription()); 557 } 558 559 return ((Number) o).doubleValue(); 560 } 561}