Changeset 15217 in josm for trunk/src/com/drew/metadata/exif/ExifTiffHandler.java
- Timestamp:
- 2019-07-07T01:56:46+02:00 (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/com/drew/metadata/exif/ExifTiffHandler.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 30 30 import com.drew.imaging.tiff.TiffProcessingException; 31 31 import com.drew.imaging.tiff.TiffReader; 32 import com.drew.lang.BufferBoundsException; 32 33 import com.drew.lang.Charsets; 33 34 import com.drew.lang.RandomAccessReader; … … 38 39 import com.drew.metadata.Metadata; 39 40 import com.drew.metadata.StringValue; 40 import com.drew.metadata.exif.makernotes.*; 41 import com.drew.metadata.exif.makernotes.AppleMakernoteDirectory; 42 import com.drew.metadata.exif.makernotes.CanonMakernoteDirectory; 43 import com.drew.metadata.exif.makernotes.CasioType1MakernoteDirectory; 44 import com.drew.metadata.exif.makernotes.CasioType2MakernoteDirectory; 45 import com.drew.metadata.exif.makernotes.FujifilmMakernoteDirectory; 46 import com.drew.metadata.exif.makernotes.KodakMakernoteDirectory; 47 import com.drew.metadata.exif.makernotes.KyoceraMakernoteDirectory; 48 import com.drew.metadata.exif.makernotes.LeicaMakernoteDirectory; 49 import com.drew.metadata.exif.makernotes.LeicaType5MakernoteDirectory; 50 import com.drew.metadata.exif.makernotes.NikonType1MakernoteDirectory; 51 import com.drew.metadata.exif.makernotes.NikonType2MakernoteDirectory; 52 import com.drew.metadata.exif.makernotes.OlympusCameraSettingsMakernoteDirectory; 53 import com.drew.metadata.exif.makernotes.OlympusEquipmentMakernoteDirectory; 54 import com.drew.metadata.exif.makernotes.OlympusFocusInfoMakernoteDirectory; 55 import com.drew.metadata.exif.makernotes.OlympusImageProcessingMakernoteDirectory; 56 import com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory; 57 import com.drew.metadata.exif.makernotes.OlympusRawDevelopment2MakernoteDirectory; 58 import com.drew.metadata.exif.makernotes.OlympusRawDevelopmentMakernoteDirectory; 59 import com.drew.metadata.exif.makernotes.OlympusRawInfoMakernoteDirectory; 60 import com.drew.metadata.exif.makernotes.PanasonicMakernoteDirectory; 61 import com.drew.metadata.exif.makernotes.PentaxMakernoteDirectory; 62 import com.drew.metadata.exif.makernotes.ReconyxHyperFireMakernoteDirectory; 63 import com.drew.metadata.exif.makernotes.ReconyxUltraFireMakernoteDirectory; 64 import com.drew.metadata.exif.makernotes.RicohMakernoteDirectory; 65 import com.drew.metadata.exif.makernotes.SamsungType2MakernoteDirectory; 66 import com.drew.metadata.exif.makernotes.SanyoMakernoteDirectory; 67 import com.drew.metadata.exif.makernotes.SigmaMakernoteDirectory; 68 import com.drew.metadata.exif.makernotes.SonyType1MakernoteDirectory; 69 import com.drew.metadata.exif.makernotes.SonyType6MakernoteDirectory; 41 70 import com.drew.metadata.iptc.IptcReader; 42 71 import com.drew.metadata.tiff.DirectoryTiffHandler; … … 54 83 public ExifTiffHandler(@NotNull Metadata metadata, @Nullable Directory parentDirectory) 55 84 { 56 super(metadata); 57 58 if (parentDirectory != null) 59 _currentDirectory.setParent(parentDirectory); 85 super(metadata, parentDirectory); 60 86 } 61 87 … … 67 93 final int panasonicRawTiffMarker = 0x0055; // for RW2 files 68 94 69 switch (marker) 70 { 95 switch (marker) { 71 96 case standardTiffMarker: 72 case olympusRawTiffMarker: // T odo:implement an IFD0, if there is one73 case olympusRawTiffMarker2: // T odo:implement an IFD0, if there is one97 case olympusRawTiffMarker: // TODO implement an IFD0, if there is one 98 case olympusRawTiffMarker2: // TODO implement an IFD0, if there is one 74 99 pushDirectory(ExifIFD0Directory.class); 75 100 break; … … 172 197 173 198 // an unknown (0) formatCode needs to be potentially handled later as a highly custom directory tag 174 if (formatCode == 0)199 if (formatCode == 0) 175 200 return 0L; 176 201 … … 185 210 final int byteCount) throws IOException 186 211 { 212 assert(_currentDirectory != null); 213 187 214 // Some 0x0000 tags have a 0 byteCount. Determine whether it's bad. 188 if (tagId == 0) 189 { 190 if (_currentDirectory.containsTag(tagId)) 191 { 215 if (tagId == 0) { 216 if (_currentDirectory.containsTag(tagId)) { 192 217 // Let it go through for now. Some directories handle it, some don't 193 218 return false; … … 215 240 } 216 241 217 if ( HandlePrintIM(_currentDirectory, tagId))242 if (handlePrintIM(_currentDirectory, tagId)) 218 243 { 219 244 PrintIMDirectory printIMDirectory = new PrintIMDirectory(); 220 245 printIMDirectory.setParent(_currentDirectory); 221 246 _metadata.addDirectory(printIMDirectory); 222 ProcessPrintIM(printIMDirectory, tagOffset, reader, byteCount);247 processPrintIM(printIMDirectory, tagOffset, reader, byteCount); 223 248 return true; 224 249 } … … 226 251 // Note: these also appear in tryEnterSubIfd because some are IFD pointers while others begin immediately 227 252 // for the same directories 228 if(_currentDirectory instanceof OlympusMakernoteDirectory) 229 { 230 switch (tagId) 231 { 253 if (_currentDirectory instanceof OlympusMakernoteDirectory) { 254 switch (tagId) { 232 255 case OlympusMakernoteDirectory.TAG_EQUIPMENT: 233 256 pushDirectory(OlympusEquipmentMakernoteDirectory.class); … … 265 288 } 266 289 267 if (_currentDirectory instanceof PanasonicRawIFD0Directory) 268 { 290 if (_currentDirectory instanceof PanasonicRawIFD0Directory) { 269 291 // these contain binary data with specific offsets, and can't be processed as regular ifd's. 270 292 // The binary data is broken into 'fake' tags and there is a pattern. 271 switch (tagId) 272 { 293 switch (tagId) { 273 294 case PanasonicRawIFD0Directory.TagWbInfo: 274 295 PanasonicRawWbInfoDirectory dirWbInfo = new PanasonicRawWbInfoDirectory(); 275 296 dirWbInfo.setParent(_currentDirectory); 276 297 _metadata.addDirectory(dirWbInfo); 277 ProcessBinary(dirWbInfo, tagOffset, reader, byteCount, false, 2);298 processBinary(dirWbInfo, tagOffset, reader, byteCount, false, 2); 278 299 return true; 279 300 case PanasonicRawIFD0Directory.TagWbInfo2: … … 281 302 dirWbInfo2.setParent(_currentDirectory); 282 303 _metadata.addDirectory(dirWbInfo2); 283 ProcessBinary(dirWbInfo2, tagOffset, reader, byteCount, false, 3);304 processBinary(dirWbInfo2, tagOffset, reader, byteCount, false, 3); 284 305 return true; 285 306 case PanasonicRawIFD0Directory.TagDistortionInfo: … … 287 308 dirDistort.setParent(_currentDirectory); 288 309 _metadata.addDirectory(dirDistort); 289 ProcessBinary(dirDistort, tagOffset, reader, byteCount, true, 1);310 processBinary(dirDistort, tagOffset, reader, byteCount, true, 1); 290 311 return true; 291 312 } … … 293 314 294 315 // Panasonic RAW sometimes contains an embedded version of the data as a JPG file. 295 if (tagId == PanasonicRawIFD0Directory.TagJpgFromRaw && _currentDirectory instanceof PanasonicRawIFD0Directory) 296 { 316 if (tagId == PanasonicRawIFD0Directory.TagJpgFromRaw && _currentDirectory instanceof PanasonicRawIFD0Directory) { 297 317 byte[] jpegrawbytes = reader.getBytes(tagOffset, byteCount); 298 318 … … 316 336 } 317 337 318 private static void ProcessBinary(@NotNull final Directory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount, final Boolean issigned, final int arrayLength) throws IOException338 private static void processBinary(@NotNull final Directory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount, final Boolean isSigned, final int arrayLength) throws IOException 319 339 { 320 340 // expects signed/unsigned int16 (for now) 321 //int byteSize = is signed ? sizeof(short) : sizeof(ushort);341 //int byteSize = isSigned ? sizeof(short) : sizeof(ushort); 322 342 int byteSize = 2; 323 343 324 344 // 'directory' is assumed to contain tags that correspond to the byte position unless it's a set of bytes 325 for (int i = 0; i < byteCount; i++) 326 { 327 if (directory.hasTagName(i)) 328 { 345 for (int i = 0; i < byteCount; i++) { 346 if (directory.hasTagName(i)) { 329 347 // only process this tag if the 'next' integral tag exists. Otherwise, it's a set of bytes 330 if (i < byteCount - 1 && directory.hasTagName(i + 1)) 331 { 332 if(issigned) 348 if (i < byteCount - 1 && directory.hasTagName(i + 1)) { 349 if (isSigned) 333 350 directory.setObject(i, reader.getInt16(tagValueOffset + (i* byteSize))); 334 351 else 335 352 directory.setObject(i, reader.getUInt16(tagValueOffset + (i* byteSize))); 336 } 337 else 338 { 353 } else { 339 354 // the next arrayLength bytes are a multi-byte value 340 if (issigned) 341 { 355 if (isSigned) { 342 356 short[] val = new short[arrayLength]; 343 357 for (int j = 0; j<val.length; j++) 344 358 val[j] = reader.getInt16(tagValueOffset + ((i + j) * byteSize)); 345 359 directory.setObjectArray(i, val); 346 } 347 else 348 { 360 } else { 349 361 int[] val = new int[arrayLength]; 350 362 for (int j = 0; j<val.length; j++) … … 359 371 } 360 372 373 /** Read a given number of bytes from the stream 374 * 375 * This method is employed to "suppress" attempts to read beyond end of the 376 * file as may happen at the beginning of processMakernote when we read 377 * increasingly longer camera makes. 378 * 379 * Instead of failing altogether in this context we return an empty string 380 * which will fail all sensible attempts to compare to makes while avoiding 381 * a full-on failure. 382 */ 383 @NotNull 384 private static String getReaderString(final @NotNull RandomAccessReader reader, final int makernoteOffset, final int bytesRequested) throws IOException 385 { 386 try { 387 return reader.getString(makernoteOffset, bytesRequested, Charsets.UTF_8); 388 } catch(BufferBoundsException e) { 389 return ""; 390 } 391 } 392 361 393 private boolean processMakernote(final int makernoteOffset, 362 394 final @NotNull Set<Integer> processedIfdOffsets, … … 364 396 final @NotNull RandomAccessReader reader) throws IOException 365 397 { 398 assert(_currentDirectory != null); 399 366 400 // Determine the camera model and makernote format. 367 401 Directory ifd0Directory = _metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); … … 369 403 String cameraMake = ifd0Directory == null ? null : ifd0Directory.getString(ExifIFD0Directory.TAG_MAKE); 370 404 371 final String firstTwoChars = reader.getString(makernoteOffset, 2, Charsets.UTF_8);372 final String firstThreeChars = reader.getString(makernoteOffset, 3, Charsets.UTF_8);373 final String firstFourChars = reader.getString(makernoteOffset, 4, Charsets.UTF_8);374 final String firstFiveChars = reader.getString(makernoteOffset, 5, Charsets.UTF_8);375 final String firstSixChars = reader.getString(makernoteOffset, 6, Charsets.UTF_8);376 final String firstSevenChars = reader.getString(makernoteOffset, 7, Charsets.UTF_8);377 final String firstEightChars = reader.getString(makernoteOffset, 8, Charsets.UTF_8);378 final String firstNineChars = reader.getString(makernoteOffset, 9, Charsets.UTF_8);379 final String firstTenChars = reader.getString(makernoteOffset, 10, Charsets.UTF_8);380 final String firstTwelveChars = reader.getString(makernoteOffset, 12, Charsets.UTF_8);405 final String firstTwoChars = getReaderString(reader, makernoteOffset, 2); 406 final String firstThreeChars = getReaderString(reader, makernoteOffset, 3); 407 final String firstFourChars = getReaderString(reader, makernoteOffset, 4); 408 final String firstFiveChars = getReaderString(reader, makernoteOffset, 5); 409 final String firstSixChars = getReaderString(reader, makernoteOffset, 6); 410 final String firstSevenChars = getReaderString(reader, makernoteOffset, 7); 411 final String firstEightChars = getReaderString(reader, makernoteOffset, 8); 412 final String firstNineChars = getReaderString(reader, makernoteOffset, 9); 413 final String firstTenChars = getReaderString(reader, makernoteOffset, 10); 414 final String firstTwelveChars = getReaderString(reader, makernoteOffset, 12); 381 415 382 416 boolean byteOrderBefore = reader.isMotorolaByteOrder(); … … 501 535 return false; 502 536 } 503 } else if ("Panasonic\u0000\u0000\u0000".equals( reader.getString(makernoteOffset, 12, Charsets.UTF_8))) {537 } else if ("Panasonic\u0000\u0000\u0000".equals(firstTwelveChars)) { 504 538 // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD 505 539 // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment … … 575 609 } 576 610 577 private static Boolean HandlePrintIM(@NotNull final Directory directory, final int tagId)611 private static boolean handlePrintIM(@NotNull final Directory directory, final int tagId) 578 612 { 579 613 if (tagId == ExifDirectoryBase.TAG_PRINT_IMAGE_MATCHING_INFO) 580 614 return true; 581 615 582 if (tagId == 0x0E00) 583 { 616 if (tagId == 0x0E00) { 584 617 // Tempting to say every tagid of 0x0E00 is a PIM tag, but can't be 100% sure 585 618 if (directory instanceof CasioType2MakernoteDirectory || … … 606 639 /// lib\Image\ExifTool\PrintIM.pm 607 640 /// </remarks> 608 private static void ProcessPrintIM(@NotNull final PrintIMDirectory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount) throws IOException641 private static void processPrintIM(@NotNull final PrintIMDirectory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount) throws IOException 609 642 { 610 643 Boolean resetByteOrder = null; 611 644 612 if (byteCount == 0) 613 { 645 if (byteCount == 0) { 614 646 directory.addError("Empty PrintIM data"); 615 647 return; 616 648 } 617 649 618 if (byteCount <= 15) 619 { 650 if (byteCount <= 15) { 620 651 directory.addError("Bad PrintIM data"); 621 652 return; … … 624 655 String header = reader.getString(tagValueOffset, 12, Charsets.UTF_8); 625 656 626 if (!header.startsWith("PrintIM")) //, StringComparison.Ordinal)) 627 { 657 if (!header.startsWith("PrintIM")) { 628 658 directory.addError("Invalid PrintIM header"); 629 659 return; … … 632 662 // check size of PrintIM block 633 663 int num = reader.getUInt16(tagValueOffset + 14); 634 if (byteCount < 16 + num * 6) 635 {664 665 if (byteCount < 16 + num * 6) { 636 666 // size is too big, maybe byte ordering is wrong 637 667 resetByteOrder = reader.isMotorolaByteOrder(); 638 668 reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder()); 639 669 num = reader.getUInt16(tagValueOffset + 14); 640 if (byteCount < 16 + num * 6) 641 { 670 if (byteCount < 16 + num * 6) { 642 671 directory.addError("Bad PrintIM size"); 643 672 return; … … 647 676 directory.setObject(PrintIMDirectory.TagPrintImVersion, header.substring(8, 12)); 648 677 649 for (int n = 0; n < num; n++) 650 { 678 for (int n = 0; n < num; n++) { 651 679 int pos = tagValueOffset + 16 + n * 6; 652 680 int tag = reader.getUInt16(pos); … … 712 740 build = null; 713 741 } 714 if (build != null) 715 {742 743 if (build != null) { 716 744 directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d.%s", major, minor, revision, build)); 717 } 718 else 719 { 745 } else { 720 746 directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d", major, minor, revision)); 721 747 directory.addError("Error processing Reconyx HyperFire makernote data: build '" + buildYearAndDate + "' is not in the expected format and will be omitted from Firmware Version."); … … 746 772 (month >= 1 && month < 13) && 747 773 (day >= 1 && day < 32) && 748 (year >= 1 && year <= 9999)) 749 { 774 (year >= 1 && year <= 9999)) { 750 775 directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL, 751 776 String.format("%4d:%2d:%2d %2d:%2d:%2d", year, month, day, hour, minutes, seconds)); 752 } 753 else 754 { 777 } else { 755 778 directory.addError("Error processing Reconyx HyperFire makernote data: Date/Time Original " + year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + " is not a valid date/time."); 756 779 } … … 778 801 /*uint makernoteID = ByteConvert.FromBigEndianToNative(reader.GetUInt32(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagMakernoteID)); 779 802 directory.Set(ReconyxUltraFireMakernoteDirectory.TagMakernoteID, makernoteID); 780 if (makernoteID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_ID) 781 { 803 if (makernoteID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_ID) { 782 804 directory.addError("Error processing Reconyx UltraFire makernote data: unknown Makernote ID 0x" + makernoteID.ToString("x8")); 783 805 return; … … 786 808 uint makernotePublicID = ByteConvert.FromBigEndianToNative(reader.GetUInt32(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagMakernotePublicID)); 787 809 directory.Set(ReconyxUltraFireMakernoteDirectory.TagMakernotePublicID, makernotePublicID); 788 if (makernotePublicID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_PUBLIC_ID) 789 { 810 if (makernotePublicID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_PUBLIC_ID) { 790 811 directory.addError("Error processing Reconyx UltraFire makernote data: unknown Makernote Public ID 0x" + makernotePublicID.ToString("x8")); 791 812 return; … … 818 839 (month >= 1 && month < 13) && 819 840 (day >= 1 && day < 32) && 820 (year >= 1 && year <= 9999)) 821 { 841 (year >= 1 && year <= 9999)) { 822 842 directory.Set(ReconyxUltraFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL, new DateTime(year, month, day, hour, minutes, seconds, DateTimeKind.Unspecified)); 823 } 824 else 825 { 843 } else { 826 844 directory.addError("Error processing Reconyx UltraFire makernote data: Date/Time Original " + year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + " is not a valid date/time."); 827 845 }*/
Note:
See TracChangeset
for help on using the changeset viewer.