exiftool-vendored.exe 12.55.0 → 12.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/bin/exiftool_files/Changes +97 -4
  2. package/bin/exiftool_files/README +19 -19
  3. package/bin/exiftool_files/arg_files/xmp2exif.args +4 -1
  4. package/bin/exiftool_files/config_files/example.config +1 -0
  5. package/bin/exiftool_files/config_files/rotate_regions.config +1 -1
  6. package/bin/exiftool_files/exiftool.pl +198 -123
  7. package/bin/exiftool_files/fmt_files/kml.fmt +3 -0
  8. package/bin/exiftool_files/fmt_files/kml_track.fmt +3 -0
  9. package/bin/exiftool_files/lib/Image/ExifTool/AIFF.pm +2 -2
  10. package/bin/exiftool_files/lib/Image/ExifTool/APE.pm +2 -2
  11. package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +25 -19
  12. package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +33 -7
  13. package/bin/exiftool_files/lib/Image/ExifTool/CanonRaw.pm +5 -1
  14. package/bin/exiftool_files/lib/Image/ExifTool/DJI.pm +28 -2
  15. package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +107 -20
  16. package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +92 -9
  17. package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +9 -4
  18. package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +7 -2
  19. package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +30 -7
  20. package/bin/exiftool_files/lib/Image/ExifTool/InfiRay.pm +227 -0
  21. package/bin/exiftool_files/lib/Image/ExifTool/JPEG.pm +53 -7
  22. package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +5 -5
  23. package/bin/exiftool_files/lib/Image/ExifTool/LIF.pm +10 -2
  24. package/bin/exiftool_files/lib/Image/ExifTool/LNK.pm +5 -4
  25. package/bin/exiftool_files/lib/Image/ExifTool/MIE.pm +3 -3
  26. package/bin/exiftool_files/lib/Image/ExifTool/MPEG.pm +2 -2
  27. package/bin/exiftool_files/lib/Image/ExifTool/MakerNotes.pm +3 -2
  28. package/bin/exiftool_files/lib/Image/ExifTool/Minolta.pm +6 -7
  29. package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +2 -1
  30. package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +1199 -1325
  31. package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +2 -2
  32. package/bin/exiftool_files/lib/Image/ExifTool/NikonSettings.pm +1 -1
  33. package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +88 -6
  34. package/bin/exiftool_files/lib/Image/ExifTool/OpenEXR.pm +32 -15
  35. package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +89 -3
  36. package/bin/exiftool_files/lib/Image/ExifTool/PanasonicRaw.pm +27 -1
  37. package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +8 -5
  38. package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +14 -1
  39. package/bin/exiftool_files/lib/Image/ExifTool/Photoshop.pm +38 -7
  40. package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +44 -13
  41. package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +63 -20
  42. package/bin/exiftool_files/lib/Image/ExifTool/README +19 -2
  43. package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +34 -13
  44. package/bin/exiftool_files/lib/Image/ExifTool/Rawzor.pm +2 -2
  45. package/bin/exiftool_files/lib/Image/ExifTool/Real.pm +2 -2
  46. package/bin/exiftool_files/lib/Image/ExifTool/Ricoh.pm +2 -1
  47. package/bin/exiftool_files/lib/Image/ExifTool/Sigma.pm +5 -4
  48. package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +9 -3
  49. package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +28 -1
  50. package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +4691 -4624
  51. package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +511 -117
  52. package/bin/exiftool_files/lib/Image/ExifTool/VCard.pm +19 -5
  53. package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +5 -5
  54. package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +42 -0
  55. package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +1 -1
  56. package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +150 -36
  57. package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +19 -4
  58. package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +2 -1
  59. package/bin/exiftool_files/lib/Image/ExifTool.pm +248 -54
  60. package/bin/exiftool_files/lib/Image/ExifTool.pod +94 -58
  61. package/package.json +2 -2
@@ -29,7 +29,7 @@ use vars qw($VERSION $RELEASE @ISA @EXPORT_OK %EXPORT_TAGS $AUTOLOAD @fileTypes
29
29
  %jpegMarker %specialTags %fileTypeLookup $testLen $exeDir
30
30
  %static_vars);
31
31
 
32
- $VERSION = '12.55';
32
+ $VERSION = '12.60';
33
33
  $RELEASE = '';
34
34
  @ISA = qw(Exporter);
35
35
  %EXPORT_TAGS = (
@@ -75,6 +75,7 @@ sub GetAllGroups($;$);
75
75
  sub GetNewGroups($);
76
76
  sub GetDeleteGroups();
77
77
  sub AddUserDefinedTags($%);
78
+ sub SetAlternateFile($$$);
78
79
  # non-public routines below
79
80
  sub InsertTagValues($$$;$$$);
80
81
  sub IsWritable($);
@@ -113,6 +114,7 @@ sub WriteTIFF($$$);
113
114
  sub PackUTF8(@);
114
115
  sub UnpackUTF8($);
115
116
  sub SetPreferredByteOrder($;$);
117
+ sub ImageDataMD5($$$;$$);
116
118
  sub CopyBlock($$$);
117
119
  sub CopyFileAttrs($$$);
118
120
  sub TimeNow(;$$);
@@ -150,8 +152,8 @@ sub ReadValue($$$;$$$);
150
152
  Real::Media Real::Audio Real::Metafile Red RIFF AIFF ASF WTV DICOM FITS MIE
151
153
  JSON HTML XMP::SVG Palm Palm::MOBI Palm::EXTH Torrent EXE EXE::PEVersion
152
154
  EXE::PEString EXE::MachO EXE::PEF EXE::ELF EXE::AR EXE::CHM LNK Font VCard
153
- Text VCard::VCalendar RSRC Rawzor ZIP ZIP::GZIP ZIP::RAR RTF OOXML iWork ISO
154
- FLIR::AFF FLIR::FPF MacOS MacOS::MDItem FlashPix::DocTable
155
+ Text VCard::VCalendar VCard::VNote RSRC Rawzor ZIP ZIP::GZIP ZIP::RAR RTF
156
+ OOXML iWork ISO FLIR::AFF FLIR::FPF MacOS MacOS::MDItem FlashPix::DocTable
155
157
  );
156
158
 
157
159
  # alphabetical list of current Lang modules
@@ -526,6 +528,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
526
528
  VCARD=> ['VCard','Virtual Card'],
527
529
  VCF => 'VCARD',
528
530
  VOB => ['MPEG', 'Video Object'],
531
+ VNT => [['FPX','VCard'], 'Scene7 Vignette or V-Note text file'],
529
532
  VRD => ['VRD', 'Canon VRD Recipe Data'],
530
533
  VSD => ['FPX', 'Microsoft Visio Drawing'],
531
534
  WAV => ['RIFF', 'WAVeform (Windows digital audio)'],
@@ -577,6 +580,7 @@ my %fileDescription = (
577
580
  'Win32 DLL' => 'Windows 32-bit Dynamic Link Library',
578
581
  'Win64 EXE' => 'Windows 64-bit Executable',
579
582
  'Win64 DLL' => 'Windows 64-bit Dynamic Link Library',
583
+ VNote => 'V-Note document',
580
584
  );
581
585
 
582
586
  # MIME types for applicable file types above
@@ -977,7 +981,7 @@ $testLen = 1024; # number of bytes to read when testing for magic number
977
981
  TAR => '.{257}ustar( )?\0', # (this doesn't catch old-style tar files)
978
982
  TXT => '(\xff\xfe|(\0\0)?\xfe\xff|(\xef\xbb\xbf)?[\x07-\x0d\x20-\x7e\x80-\xfe]*$)',
979
983
  TIFF => '(II|MM)', # don't test magic number (some raw formats are different)
980
- VCard=> '(?i)BEGIN:(VCARD|VCALENDAR)\r\n',
984
+ VCard=> '(?i)BEGIN:(VCARD|VCALENDAR|VNOTE)\r\n',
981
985
  VRD => 'CANON OPTIONAL DATA\0',
982
986
  WMF => '(\xd7\xcd\xc6\x9a\0\0|\x01\0\x09\0\0\x03)',
983
987
  WTV => '\xb7\xd8\x00\x20\x37\x49\xda\x11\xa6\x4e\x00\x07\xe9\x5e\xad\x8d',
@@ -1819,6 +1823,17 @@ my %systemTagsNotes = (
1819
1823
  if specifically requested
1820
1824
  },
1821
1825
  },
1826
+ ImageDataMD5 => {
1827
+ Notes => q{
1828
+ MD5 of image data. Generated only if specifically requested for JPEG and
1829
+ TIFF-based images, PNG, CRW, CR3, MRW, RAF, X3F and AVIF images, MOV/MP4
1830
+ videos, and some RIFF-based files. The MD5 includes the main image data,
1831
+ plus JpgFromRaw/OtherImage for some formats, but does not include
1832
+ ThumbnailImage or PreviewImage. Includes video and audio data for MOV/MP4.
1833
+ The L<XMP-et:OriginalImageMD5 tag|XMP.html#ExifTool> provides a place to
1834
+ store these values in the file.
1835
+ },
1836
+ },
1822
1837
  );
1823
1838
 
1824
1839
  # tags defined by UserParam option (added at runtime)
@@ -2041,7 +2056,9 @@ sub new
2041
2056
  $$self{DEL_GROUP} = { }; # lookup for groups to delete when writing
2042
2057
  $$self{SAVE_COUNT} = 0; # count calls to SaveNewValues()
2043
2058
  $$self{FILE_SEQUENCE} = 0; # sequence number for files when reading
2059
+ $$self{FILES_WRITTEN} = 0; # count of files successfully written
2044
2060
  $$self{INDENT2} = ''; # indentation of verbose messages from SetNewValue
2061
+ $$self{ALT_EXIFTOOL} = { }; # alternate exiftool objects
2045
2062
 
2046
2063
  # initialize our new groups for writing
2047
2064
  $self->SetNewGroups(@defaultWriteGroups);
@@ -2476,8 +2493,33 @@ sub ExtractInfo($;@)
2476
2493
  $self->WarnOnce('Install Time::HiRes to generate ProcessingTime');
2477
2494
  }
2478
2495
  }
2479
-
2496
+
2497
+ # create MD5 object if ImageDataMD5 is requested
2498
+ if ($$req{imagedatamd5} and not $$self{ImageDataMD5}) {
2499
+ if (require Digest::MD5) {
2500
+ $$self{ImageDataMD5} = Digest::MD5->new;
2501
+ } else {
2502
+ $self->WarnOnce('Install Digest::MD5 to calculate image data MD5');
2503
+ }
2504
+ }
2480
2505
  ++$$self{FILE_SEQUENCE}; # count files read
2506
+ # extract information from alternate files if necessary
2507
+ my ($g8, $altExifTool);
2508
+ foreach $g8 (keys %{$$self{ALT_EXIFTOOL}}) {
2509
+ $altExifTool = $$self{ALT_EXIFTOOL}{$g8};
2510
+ next if $$altExifTool{DID_EXTRACT}; # avoid extracting twice
2511
+ $$altExifTool{OPTIONS} = $$self{OPTIONS};
2512
+ $$altExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
2513
+ $$altExifTool{REQ_TAG_LOOKUP} = $$self{REQ_TAG_LOOKUP};
2514
+ $altExifTool->ExtractInfo($$altExifTool{ALT_FILE});
2515
+ # set family 8 group name for all tags
2516
+ foreach (keys %{$$altExifTool{VALUE}}) {
2517
+ my $ex = $$altExifTool{TAG_EXTRA}{$_};
2518
+ $ex or $ex = $$altExifTool{TAG_EXTRA}{$_} = { };
2519
+ $$ex{G8} = $g8;
2520
+ }
2521
+ $$altExifTool{DID_EXTRACT} = 1;
2522
+ }
2481
2523
  }
2482
2524
 
2483
2525
  my $filename = $$self{FILENAME}; # image file name ('' if already open)
@@ -2867,6 +2909,10 @@ sub ExtractInfo($;@)
2867
2909
  # restore necessary members when exiting re-entrant code
2868
2910
  $$self{$_} = $$reEntry{$_} foreach keys %$reEntry;
2869
2911
  SetByteOrder($saveOrder);
2912
+ } elsif ($$self{ImageDataMD5}) {
2913
+ my $digest = $$self{ImageDataMD5}->hexdigest;
2914
+ # (don't store empty digest)
2915
+ $self->FoundTag(ImageDataMD5 => $digest) unless $digest eq 'd41d8cd98f00b204e9800998ecf8427e';
2870
2916
  }
2871
2917
 
2872
2918
  # ($type may be undef without an Error when processing sub-documents)
@@ -3498,6 +3544,10 @@ sub GetGroup($$;$)
3498
3544
  $groups[6] = $$ex{G6};
3499
3545
  }
3500
3546
  }
3547
+ if ($$ex{G8}) {
3548
+ $groups[7] = '';
3549
+ $groups[8] = $$ex{G8};
3550
+ }
3501
3551
  # generate tag ID group names unless obviously not needed
3502
3552
  unless ($noID) {
3503
3553
  my $id = $$tagInfo{KeysID} || $$tagInfo{TagID};
@@ -3683,13 +3733,21 @@ COMPOSITE_TAG:
3683
3733
  next COMPOSITE_TAG;
3684
3734
  }
3685
3735
  }
3736
+ my ($i, $key, @keys, $altFile);
3737
+ my $et = $self;
3738
+ # get tags from alternate file if a family 8 group was specified
3739
+ if ($reqTag =~ /\b(File\d+):/i and $$self{ALT_EXIFTOOL}{$1}) {
3740
+ $et = $$self{ALT_EXIFTOOL}{$1};
3741
+ $altFile = $1;
3742
+ }
3686
3743
  # (CAREFUL! keys may not be sequential if one was deleted)
3687
- my ($i, $key, @keys);
3688
- for ($key=$name, $i=$$self{DUPL_TAG}{$name} || 0; ; --$i) {
3689
- push @keys, $key if defined $$rawValue{$key};
3744
+ for ($key=$name, $i=$$et{DUPL_TAG}{$name} || 0; ; --$i) {
3745
+ push @keys, $key if defined $$et{VALUE}{$key};
3690
3746
  last if $i <= 0;
3691
3747
  $key = "$name ($i)";
3692
3748
  }
3749
+ # make sure the necessary information is available from the alternate file
3750
+ $self->CopyAltInfo($altFile, \@keys) if $altFile;
3693
3751
  # find first matching tag
3694
3752
  $key = $self->GroupMatches($reqGroup, \@keys);
3695
3753
  $reqTag = $key || "$name (0)";
@@ -4131,7 +4189,11 @@ sub SplitFileName($)
4131
4189
  } else {
4132
4190
  ($name = $file) =~ tr/\\/\//;
4133
4191
  # remove path
4134
- $dir = length($1) ? $1 : '/' if $name =~ s/(.*)\///;
4192
+ if ($name =~ s/(.*)\///) {
4193
+ $dir = length($1) ? $1 : '/';
4194
+ } else {
4195
+ $dir = '.';
4196
+ }
4135
4197
  }
4136
4198
  return ($dir, $name);
4137
4199
  }
@@ -4294,9 +4356,9 @@ sub GetFileTime($$)
4294
4356
  # on Windows, try to work around incorrect file times when daylight saving time is in effect
4295
4357
  if ($^O eq 'MSWin32') {
4296
4358
  if (not eval { require Win32::API }) {
4297
- $self->WarnOnce('Install Win32::API for proper handling of Windows file times');
4359
+ $self->WarnOnce('Install Win32::API for proper handling of Windows file times', 1);
4298
4360
  } elsif (not eval { require Win32API::File }) {
4299
- $self->WarnOnce('Install Win32API::File for proper handling of Windows file times');
4361
+ $self->WarnOnce('Install Win32API::File for proper handling of Windows file times', 1);
4300
4362
  } else {
4301
4363
  # get Win32 handle, needed for GetFileTime
4302
4364
  my $win32Handle = eval { Win32API::File::GetOsFHandle($file) };
@@ -4546,6 +4608,29 @@ sub RemoveTagsFromList($$$$;$)
4546
4608
  $_[0] = \@filteredTags; # update tag list
4547
4609
  }
4548
4610
 
4611
+ #------------------------------------------------------------------------------
4612
+ # Copy tags from alternate input file
4613
+ # Inputs: 0) ExifTool ref, 1) family 8 group, 2) list ref for tag keys to copy
4614
+ # - updates tag key list to match keys newly added to $self
4615
+ sub CopyAltInfo($$$)
4616
+ {
4617
+ my ($self, $g8, $tags) = @_;
4618
+ my ($tag, $vtag);
4619
+ return unless $g8 =~ /(\d+)/;
4620
+ my $et = $$self{ALT_EXIFTOOL}{$g8} or return;
4621
+ my $altOrder = ($1 + 1) * 100000; # increment file order
4622
+ foreach $tag (@$tags) {
4623
+ ($vtag = $tag) =~ s/( |$)/ #[$g8]/;
4624
+ unless (defined $$self{VALUE}{$vtag}) {
4625
+ $$self{VALUE}{$vtag} = $$et{VALUE}{$tag};
4626
+ $$self{TAG_INFO}{$vtag} = $$et{TAG_INFO}{$tag};
4627
+ $$self{TAG_EXTRA}{$vtag} = $$et{TAG_EXTRA}{$tag} || { };
4628
+ $$self{FILE_ORDER}{$vtag} = ($$et{FILE_ORDER}{$tag} || 0) + $altOrder;
4629
+ }
4630
+ $tag = $vtag;
4631
+ }
4632
+ }
4633
+
4549
4634
  #------------------------------------------------------------------------------
4550
4635
  # Set list of found tags from previously requested tags
4551
4636
  # Inputs: 0) ExifTool object reference
@@ -4572,11 +4657,17 @@ sub SetFoundTags($)
4572
4657
  my $tagHash = $$self{VALUE};
4573
4658
  my $reqTag;
4574
4659
  foreach $reqTag (@$reqTags) {
4575
- my (@matches, $group, $allGrp, $allTag, $byValue);
4660
+ my (@matches, $group, $allGrp, $allTag, $byValue, $g8);
4661
+ my $et = $self;
4576
4662
  if ($reqTag =~ /^(.*):(.+)/) {
4577
4663
  ($group, $tag) = ($1, $2);
4578
4664
  if ($group =~ /^(\*|all)$/i) {
4579
4665
  $allGrp = 1;
4666
+ } elsif ($reqTag =~ /\bfile(\d+):/i) {
4667
+ $g8 = "File$1";
4668
+ $et = $$self{ALT_EXIFTOOL}{$g8} || $self;
4669
+ $fileOrder = $$et{FILE_ORDER};
4670
+ $tagHash = $$et{VALUE};
4580
4671
  } elsif ($group !~ /^[-\w:]*$/) {
4581
4672
  $self->Warn("Invalid group name '${group}'");
4582
4673
  $group = 'invalid';
@@ -4618,7 +4709,7 @@ sub SetFoundTags($)
4618
4709
  }
4619
4710
  if (defined $group and not $allGrp) {
4620
4711
  # keep only specified group
4621
- @matches = $self->GroupMatches($group, \@matches);
4712
+ @matches = $et->GroupMatches($group, \@matches);
4622
4713
  next unless @matches or not $allTag;
4623
4714
  }
4624
4715
  if (@matches > 1) {
@@ -4627,9 +4718,9 @@ sub SetFoundTags($)
4627
4718
  # return only the highest priority tag unless duplicates wanted
4628
4719
  unless ($doDups or $allTag or $allGrp) {
4629
4720
  $tag = shift @matches;
4630
- my $oldPriority = $$self{PRIORITY}{$tag} || 1;
4721
+ my $oldPriority = $$et{PRIORITY}{$tag} || 1;
4631
4722
  foreach (@matches) {
4632
- my $priority = $$self{PRIORITY}{$_};
4723
+ my $priority = $$et{PRIORITY}{$_};
4633
4724
  $priority = 1 unless defined $priority;
4634
4725
  next unless $priority >= $oldPriority;
4635
4726
  $tag = $_;
@@ -4643,6 +4734,13 @@ sub SetFoundTags($)
4643
4734
  # bogus file order entry to avoid warning if sorting in file order
4644
4735
  $$self{FILE_ORDER}{$matches[0]} = 9999;
4645
4736
  }
4737
+ # copy over necessary information for tags from alternate files
4738
+ if ($g8) {
4739
+ $self->CopyAltInfo($g8, \@matches);
4740
+ # restore variables to original values for main file
4741
+ $fileOrder = $$self{FILE_ORDER};
4742
+ $tagHash = $$self{VALUE};
4743
+ }
4646
4744
  # save indices of tags extracted by value
4647
4745
  push @byValue, scalar(@$rtnTags) .. (scalar(@$rtnTags)+scalar(@matches)-1) if $byValue;
4648
4746
  # save indices of wildcard tags
@@ -5865,7 +5963,8 @@ sub ConvertTimeSpan($;$)
5865
5963
  #------------------------------------------------------------------------------
5866
5964
  # Patched timelocal() that fixes ActivePerl timezone bug
5867
5965
  # Inputs/Returns: same as timelocal()
5868
- # Notes: must 'require Time::Local' before calling this routine
5966
+ # Notes: must 'require Time::Local' before calling this routine.
5967
+ # Also note that year should be full year, and not relative to 1900 as with localtime
5869
5968
  sub TimeLocal(@)
5870
5969
  {
5871
5970
  my $tm = Time::Local::timelocal(@_);
@@ -6324,7 +6423,6 @@ sub ProcessJPEG($$)
6324
6423
  {
6325
6424
  local $_;
6326
6425
  my ($self, $dirInfo) = @_;
6327
- my ($ch, $s, $length);
6328
6426
  my $options = $$self{OPTIONS};
6329
6427
  my $verbose = $$options{Verbose};
6330
6428
  my $out = $$options{TextOut};
@@ -6333,10 +6431,17 @@ sub ProcessJPEG($$)
6333
6431
  my $req = $$self{REQ_TAG_LOOKUP};
6334
6432
  my $htmlDump = $$self{HTML_DUMP};
6335
6433
  my %dumpParms = ( Out => $out );
6434
+ my ($ch, $s, $length, $md5, $md5size);
6336
6435
  my ($success, $wantTrailer, $trailInfo, $foundSOS, %jumbfChunk);
6337
6436
  my (@iccChunk, $iccChunkCount, $iccChunksTotal, @flirChunk, $flirCount, $flirTotal);
6338
6437
  my ($preview, $scalado, @dqt, $subSampling, $dumpEnd, %extendedXMP);
6339
6438
 
6439
+ # get pointer to MD5 object if it exists and we are the top-level JPEG
6440
+ if ($$self{FILE_TYPE} eq 'JPEG' and not $$self{DOC_NUM}) {
6441
+ $md5 = $$self{ImageDataMD5};
6442
+ $md5size = 0;
6443
+ }
6444
+
6340
6445
  # check to be sure this is a valid JPG (or J2C, or EXV) file
6341
6446
  return 0 unless $raf->Read($s, 2) == 2 and $s =~ /^\xff[\xd8\x4f\x01]/;
6342
6447
  if ($s eq "\xff\x01") {
@@ -6383,7 +6488,9 @@ sub ProcessJPEG($$)
6383
6488
  #
6384
6489
  # read ahead to the next segment unless we have reached EOI, SOS or SOD
6385
6490
  #
6386
- unless ($marker and ($marker==0xd9 or ($marker==0xda and not $wantTrailer) or $marker==0x93)) {
6491
+ unless ($marker and ($marker==0xd9 or ($marker==0xda and not $wantTrailer and not $md5) or
6492
+ $marker==0x93))
6493
+ {
6387
6494
  # read up to next marker (JPEG markers begin with 0xff)
6388
6495
  my $buff;
6389
6496
  $raf->ReadLine($buff) or last;
@@ -6413,6 +6520,19 @@ sub ProcessJPEG($$)
6413
6520
  $nextSegPos = $raf->Tell();
6414
6521
  $len -= 4; # subtract size of length word
6415
6522
  last unless $raf->Seek($len, 1);
6523
+ } elsif ($md5 and defined $marker and ($marker == 0x00 or $marker == 0xda or
6524
+ ($marker >= 0xd0 and $marker <= 0xd7)))
6525
+ {
6526
+ # calculate MD5 for image data (includes leading ff d9 but not trailing ff da)
6527
+ $md5->add("\xff" . chr($marker));
6528
+ my $n = $skipped - (length($buff) - 1); # number of extra 0xff's
6529
+ if (not $n) {
6530
+ $buff = substr($buff, 0, -1); # remove trailing 0xff
6531
+ } elsif ($n > 1) {
6532
+ $buff .= "\xff" x ($n - 1); # add back extra 0xff's
6533
+ }
6534
+ $md5->add($buff);
6535
+ $md5size += $skipped + 2;
6416
6536
  }
6417
6537
  # read second segment too if this was the first
6418
6538
  next unless defined $marker;
@@ -6623,7 +6743,7 @@ sub ProcessJPEG($$)
6623
6743
  next if $trailInfo or $wantTrailer or $verbose > 2 or $htmlDump;
6624
6744
  }
6625
6745
  # must scan to EOI if Validate or JpegCompressionFactor used
6626
- next if $$options{Validate} or $calcImageLen or $$req{trailer};
6746
+ next if $$options{Validate} or $calcImageLen or $$req{trailer} or $md5;
6627
6747
  # nothing interesting to parse after start of scan (SOS)
6628
6748
  $success = 1;
6629
6749
  last; # all done parsing file
@@ -6705,7 +6825,7 @@ sub ProcessJPEG($$)
6705
6825
  } elsif ($marker == 0xe1) { # APP1 (EXIF, XMP, QVCI, PARROT)
6706
6826
  # (some Kodak cameras don't put a second "\0", and I have seen an
6707
6827
  # example where there was a second 4-byte APP1 segment header)
6708
- if ($$segDataPt =~ /^(.{0,4})Exif\0/is) {
6828
+ if ($$segDataPt =~ /^(.{0,4})Exif\0./is) {
6709
6829
  undef $dumpType; # (will be dumped here)
6710
6830
  # this is EXIF data --
6711
6831
  # get the data block (into a common variable)
@@ -6871,7 +6991,7 @@ sub ProcessJPEG($$)
6871
6991
  $self->Warn("Ignored APP1 segment length $length (unknown header)");
6872
6992
  }
6873
6993
  }
6874
- } elsif ($marker == 0xe2) { # APP2 (ICC Profile, FPXR, MPF, PreviewImage)
6994
+ } elsif ($marker == 0xe2) { # APP2 (ICC Profile, FPXR, MPF, InfiRay, PreviewImage)
6875
6995
  if ($$segDataPt =~ /^ICC_PROFILE\0/ and $length >= 14) {
6876
6996
  $dumpType = 'ICC_Profile';
6877
6997
  # must concatenate profile chunks (note: handle the case where
@@ -6933,6 +7053,12 @@ sub ProcessJPEG($$)
6933
7053
  # extract the MPF information (it is in standard TIFF format)
6934
7054
  my $tagTablePtr = GetTagTable('Image::ExifTool::MPF::Main');
6935
7055
  $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
7056
+ } elsif ($$segDataPt =~ /^....IJPEG\0/s) {
7057
+ $dumpType = 'InfiRay Version';
7058
+ $$self{HasIJPEG} = 1;
7059
+ SetByteOrder('II');
7060
+ my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Version');
7061
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
6936
7062
  } elsif ($$segDataPt =~ /^(|QVGA\0|BGTH)\xff\xd8\xff[\xdb\xe0\xe1]/) {
6937
7063
  # Samsung/GE/GoPro="", BenQ DC C1220/Pentacon/Polaroid="QVGA\0",
6938
7064
  # Digilife DDC-690/Rollei="BGTH"
@@ -6973,8 +7099,8 @@ sub ProcessJPEG($$)
6973
7099
  SetByteOrder('MM');
6974
7100
  my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::JPS');
6975
7101
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
6976
- } elsif ($$self{Make} eq 'DJI') {
6977
- $dumpType = 'DJI ThermalData';
7102
+ } elsif ($$self{HasIJPEG} or $$self{Make} eq 'DJI') {
7103
+ $dumpType = $$self{HasIJPEG} ? 'InfiRay ImagingData' : 'DJI ThermalData';
6978
7104
  # add this data to the combined data if it exists
6979
7105
  my $dataPt = $segDataPt;
6980
7106
  if (defined $combinedSegData) {
@@ -6984,11 +7110,14 @@ sub ProcessJPEG($$)
6984
7110
  if ($nextMarker == $marker) {
6985
7111
  $combinedSegData = $$segDataPt unless defined $combinedSegData;
6986
7112
  } else {
6987
- # process DJI FLIR thermal data
7113
+ # process InfiRay/DJI thermal data
6988
7114
  my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::Main');
6989
7115
  $self->HandleTag($tagTablePtr, 'APP3', $$dataPt);
6990
7116
  undef $combinedSegData;
6991
7117
  }
7118
+ } elsif ($$self{HasIJPEG}) {
7119
+ $dumpType = 'InfiRay Data',
7120
+
6992
7121
  } elsif ($$segDataPt =~ /^\xff\xd8\xff\xdb/) {
6993
7122
  $dumpType = 'PreviewImage'; # (Samsung, HP, BenQ)
6994
7123
  $preview = $$segDataPt;
@@ -6997,7 +7126,7 @@ sub ProcessJPEG($$)
6997
7126
  $self->FoundTag('PreviewImage', $preview);
6998
7127
  undef $preview;
6999
7128
  }
7000
- } elsif ($marker == 0xe4) { # APP4 ("SCALADO", FPXR, PreviewImage)
7129
+ } elsif ($marker == 0xe4) { # APP4 (InfiRay, "SCALADO", FPXR, DJI, PreviewImage)
7001
7130
  if ($$segDataPt =~ /^SCALADO\0/ and $length >= 16) {
7002
7131
  $dumpType = 'SCALADO';
7003
7132
  my ($num, $idx, $len) = unpack('x8n2N', $$segDataPt);
@@ -7028,6 +7157,21 @@ sub ProcessJPEG($$)
7028
7157
  DirStart(\%dirInfo, 0, 0);
7029
7158
  my $tagTablePtr = GetTagTable('Image::ExifTool::DJI::ThermalParams');
7030
7159
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7160
+ } elsif ($$self{Make} eq 'DJI' and $$segDataPt =~ /^(.{32})?.{32}\x2c\x01\x20\0/s) {
7161
+ $dumpType = 'DJI ThermalParams2';
7162
+ DirStart(\%dirInfo, $1 ? 32 : 0, 0);
7163
+ my $tagTablePtr = GetTagTable('Image::ExifTool::DJI::ThermalParams2');
7164
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7165
+ } elsif ($$self{Make} eq 'DJI' and $$segDataPt =~ /^.{32}\xaa\x55\x38\0/s) {
7166
+ $dumpType = 'DJI ThermalParams3';
7167
+ DirStart(\%dirInfo, 32, 0);
7168
+ my $tagTablePtr = GetTagTable('Image::ExifTool::DJI::ThermalParams3');
7169
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7170
+ } elsif ($$self{HasIJPEG} and $length >= 120) {
7171
+ $dumpType = 'InfiRay Factory';
7172
+ SetByteOrder('II');
7173
+ my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Factory');
7174
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7031
7175
  } elsif ($preview) {
7032
7176
  # continued Samsung S1060 preview from APP3
7033
7177
  $dumpType = 'PreviewImage';
@@ -7039,7 +7183,7 @@ sub ProcessJPEG($$)
7039
7183
  $self->FoundTag('PreviewImage', $preview);
7040
7184
  undef $preview;
7041
7185
  }
7042
- } elsif ($marker == 0xe5) { # APP5 (Ricoh "RMETA")
7186
+ } elsif ($marker == 0xe5) { # APP5 (InfiRay, Ricoh "RMETA")
7043
7187
  if ($$segDataPt =~ /^RMETA\0/) {
7044
7188
  # (NOTE: apparently these may span multiple segments, but I haven't seen
7045
7189
  # a sample like this, so multi-segment support hasn't yet been implemented)
@@ -7054,13 +7198,18 @@ sub ProcessJPEG($$)
7054
7198
  $dumpType = 'DJI ThermalCal';
7055
7199
  my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::Main');
7056
7200
  $self->HandleTag($tagTablePtr, 'APP5', $$segDataPt);
7201
+ } elsif ($$self{HasIJPEG} and $length >= 38) {
7202
+ $dumpType = 'InfiRay Picture';
7203
+ SetByteOrder('II');
7204
+ my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Picture');
7205
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7057
7206
  } elsif ($preview) {
7058
7207
  $dumpType = 'PreviewImage';
7059
7208
  $preview .= $$segDataPt;
7060
7209
  $self->FoundTag('PreviewImage', $preview);
7061
7210
  undef $preview;
7062
7211
  }
7063
- } elsif ($marker == 0xe6) { # APP6 (Toshiba EPPIM, NITF, HP_TDHD)
7212
+ } elsif ($marker == 0xe6) { # APP6 (InfiRay, Toshiba EPPIM, NITF, HP_TDHD)
7064
7213
  if ($$segDataPt =~ /^EPPIM\0/) {
7065
7214
  undef $dumpType; # (will be dumped here)
7066
7215
  DirStart(\%dirInfo, 6, 6);
@@ -7093,8 +7242,13 @@ sub ProcessJPEG($$)
7093
7242
  $dumpType = 'DJI_DTAT';
7094
7243
  my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::Main');
7095
7244
  $self->HandleTag($tagTablePtr, 'APP6', $$segDataPt);
7245
+ } elsif ($$self{HasIJPEG} and $length >= 129) {
7246
+ $dumpType = 'InfiRay MixMode';
7247
+ SetByteOrder('II');
7248
+ my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::MixMode');
7249
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7096
7250
  }
7097
- } elsif ($marker == 0xe7) { # APP7 (Pentax, Huawei, Qualcomm)
7251
+ } elsif ($marker == 0xe7) { # APP7 (InfiRay, Pentax, Huawei, Qualcomm)
7098
7252
  if ($$segDataPt =~ /^PENTAX \0(II|MM)/) {
7099
7253
  # found in K-3 images (is this multi-segment??)
7100
7254
  SetByteOrder($1);
@@ -7126,6 +7280,13 @@ sub ProcessJPEG($$)
7126
7280
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7127
7281
  delete $$self{SET_GROUP0};
7128
7282
  delete $$self{SET_GROUP1};
7283
+ } elsif ($$segDataPt =~ /^DJI-DBG\0/) {
7284
+ $dumpType = 'DJI Info';
7285
+ my $tagTablePtr = GetTagTable('Image::ExifTool::DJI::Info');
7286
+ DirStart(\%dirInfo, 8, 0);
7287
+ $$self{SET_GROUP0} = 'APP7';
7288
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7289
+ delete $$self{SET_GROUP0};
7129
7290
  } elsif ($$segDataPt =~ /^\x1aQualcomm Camera Attributes/) {
7130
7291
  # found in HP iPAQ_VoiceMessenger
7131
7292
  $dumpType = 'Qualcomm';
@@ -7133,16 +7294,26 @@ sub ProcessJPEG($$)
7133
7294
  DirStart(\%dirInfo, 27);
7134
7295
  $dirInfo{DirName} = 'Qualcomm';
7135
7296
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7297
+ } elsif ($$self{HasIJPEG} and $length >= 32) {
7298
+ $dumpType = 'InfiRay OpMode';
7299
+ SetByteOrder('II');
7300
+ my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::OpMode');
7301
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7136
7302
  }
7137
- } elsif ($marker == 0xe8) { # APP8 (SPIFF)
7303
+ } elsif ($marker == 0xe8) { # APP8 (InfiRay, SPIFF)
7138
7304
  # my sample SPIFF has 32 bytes of data, but spec states 30
7139
7305
  if ($$segDataPt =~ /^SPIFF\0/ and $length == 32) {
7140
7306
  $dumpType = 'SPIFF';
7141
7307
  DirStart(\%dirInfo, 6);
7142
7308
  my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::SPIFF');
7143
7309
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7310
+ } elsif ($$self{HasIJPEG} and $length >= 32) {
7311
+ $dumpType = 'InfiRay Isothermal';
7312
+ SetByteOrder('II');
7313
+ my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Isothermal');
7314
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7144
7315
  }
7145
- } elsif ($marker == 0xe9) { # APP9 (Media Jukebox)
7316
+ } elsif ($marker == 0xe9) { # APP9 (InfiRay, Media Jukebox)
7146
7317
  if ($$segDataPt =~ /^Media Jukebox\0/ and $length > 22) {
7147
7318
  $dumpType = 'MediaJukebox';
7148
7319
  # (start parsing after the "<MJMD>")
@@ -7151,6 +7322,11 @@ sub ProcessJPEG($$)
7151
7322
  require Image::ExifTool::XMP;
7152
7323
  my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::MediaJukebox');
7153
7324
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr, \&Image::ExifTool::XMP::ProcessXMP);
7325
+ } elsif ($$self{HasIJPEG} and $length >= 768) {
7326
+ $dumpType = 'InfiRay Sensor';
7327
+ SetByteOrder('II');
7328
+ my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Sensor');
7329
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7154
7330
  }
7155
7331
  } elsif ($marker == 0xea) { # APP10 (PhotoStudio Unicode comments)
7156
7332
  if ($$segDataPt =~ /^UNICODE\0/) {
@@ -7371,6 +7547,8 @@ sub ProcessJPEG($$)
7371
7547
  delete $extendedXMP{$guid};
7372
7548
  }
7373
7549
  }
7550
+ # print verbose MD5 message if necessary
7551
+ print $out "$$self{INDENT}(ImageDataMD5: $md5size bytes of JPEG image data)\n" if $md5size and $verbose;
7374
7552
  # calculate JPEGDigest if requested
7375
7553
  if (@dqt) {
7376
7554
  require Image::ExifTool::JPEGDigest;
@@ -7639,7 +7817,7 @@ sub DoProcessTIFF($$;$)
7639
7817
  }
7640
7818
  }
7641
7819
  # update FileType if necessary now that we know more about the file
7642
- if ($$self{DNGVersion} and $$self{VALUE}{FileType} !~ /^(DNG|GPR)$/) {
7820
+ if ($$self{DNGVersion} and $$self{FileType} !~ /^(DNG|GPR)$/) {
7643
7821
  # override whatever FileType we set since we now know it is DNG
7644
7822
  $self->OverrideFileType($$self{TIFF_TYPE} = 'DNG');
7645
7823
  }
@@ -7947,7 +8125,7 @@ sub ProcessDirectory($$$;$)
7947
8125
  # patch for bug in Windows phone 7.5 O/S that writes incorrect InteropIFD pointer
7948
8126
  return 0 unless $dirName eq 'GPS' and $$self{PROCESSED}{$addr} eq 'InteropIFD';
7949
8127
  }
7950
- $$self{PROCESSED}{$addr} = $dirName;
8128
+ $$self{PROCESSED}{$addr} = $dirName unless $$tagTablePtr{VARS} and $$tagTablePtr{VARS}{ALLOW_REPROCESS};
7951
8129
  }
7952
8130
  my $oldOrder = GetByteOrder();
7953
8131
  my @save = @$self{'INDENT','DIR_NAME','Compression','SubfileType'};
@@ -8525,7 +8703,7 @@ sub DoEscape($$)
8525
8703
  sub SetFileType($;$$$)
8526
8704
  {
8527
8705
  my ($self, $fileType, $mimeType, $normExt) = @_;
8528
- unless ($$self{VALUE}{FileType} and not $$self{DOC_NUM}) {
8706
+ unless ($$self{FileType} and not $$self{DOC_NUM}) {
8529
8707
  my $baseType = $$self{FILE_TYPE};
8530
8708
  my $ext = $$self{FILE_EXT};
8531
8709
  $fileType or $fileType = $baseType;
@@ -8544,7 +8722,8 @@ sub SetFileType($;$$$)
8544
8722
  $normExt = $fileTypeExt{$fileType};
8545
8723
  $normExt = $fileType unless defined $normExt;
8546
8724
  }
8547
- $$self{FileType} = $fileType;
8725
+ # ($$self{FileType} is the file type of the main document)
8726
+ $$self{FileType} = $fileType unless $$self{DOC_NUM};
8548
8727
  $self->FoundTag('FileType', $fileType);
8549
8728
  $self->FoundTag('FileTypeExtension', uc $normExt);
8550
8729
  $self->FoundTag('MIMEType', $mimeType || 'application/unknown');
@@ -8708,13 +8887,16 @@ sub ProcessBinaryData($$$)
8708
8887
  {
8709
8888
  my ($self, $dirInfo, $tagTablePtr) = @_;
8710
8889
  my $dataPt = $$dirInfo{DataPt};
8711
- my $offset = $$dirInfo{DirStart} || 0;
8712
- my $size = $$dirInfo{DirLen} || (length($$dataPt) - $offset);
8890
+ my $dataLen = length $$dataPt;
8891
+ my $dirStart = $$dirInfo{DirStart} || 0;
8892
+ my $maxLen = $dataLen - $dirStart;
8893
+ my $size = $$dirInfo{DirLen};
8713
8894
  my $base = $$dirInfo{Base} || 0;
8714
8895
  my $verbose = $$self{OPTIONS}{Verbose};
8715
8896
  my $unknown = $$self{OPTIONS}{Unknown};
8716
8897
  my $dataPos = $$dirInfo{DataPos} || 0;
8717
8898
 
8899
+ $size = $maxLen if not defined $size or $size > $maxLen;
8718
8900
  # get default format ('int8u' unless specified)
8719
8901
  my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
8720
8902
  my $increment = $formatSize{$defaultFormat};
@@ -8756,6 +8938,7 @@ sub ProcessBinaryData($$$)
8756
8938
  $tagInfo = $self->GetTagInfo($tagTablePtr, $index);
8757
8939
  unless ($tagInfo) {
8758
8940
  next unless defined $tagInfo;
8941
+ # $entry = offset of value relative to directory start (or end if negative)
8759
8942
  my $entry = int($index) * $increment + $varSize;
8760
8943
  if ($entry < 0) {
8761
8944
  $entry += $size;
@@ -8764,7 +8947,7 @@ sub ProcessBinaryData($$$)
8764
8947
  next if $entry >= $size;
8765
8948
  my $more = $size - $entry;
8766
8949
  $more = 128 if $more > 128;
8767
- my $v = substr($$dataPt, $entry+$offset, $more);
8950
+ my $v = substr($$dataPt, $entry+$dirStart, $more);
8768
8951
  $tagInfo = $self->GetTagInfo($tagTablePtr, $index, \$v);
8769
8952
  next unless $tagInfo;
8770
8953
  }
@@ -8797,7 +8980,7 @@ sub ProcessBinaryData($$$)
8797
8980
  $count = $more;
8798
8981
  } elsif ($format eq 'pstring') {
8799
8982
  $format = 'string';
8800
- $count = Get8u($dataPt, ($entry++)+$offset);
8983
+ $count = Get8u($dataPt, ($entry++)+$dirStart);
8801
8984
  --$more;
8802
8985
  } elsif (not $formatSize{$format}) {
8803
8986
  if ($format =~ /(.*)\[(.*)\]/) {
@@ -8826,17 +9009,17 @@ sub ProcessBinaryData($$$)
8826
9009
  } elsif ($format =~ /^var_/) {
8827
9010
  # handle variable-length string formats
8828
9011
  $format = substr($format, 4);
8829
- pos($$dataPt) = $entry + $offset;
9012
+ pos($$dataPt) = $entry + $dirStart;
8830
9013
  undef $count;
8831
9014
  if ($format eq 'ustring') {
8832
- $count = pos($$dataPt) - ($entry+$offset) if $$dataPt =~ /\G(..)*?\0\0/sg;
9015
+ $count = pos($$dataPt) - ($entry+$dirStart) if $$dataPt =~ /\G(..)*?\0\0/sg;
8833
9016
  $varSize -= 2; # ($count includes base size of 2 bytes)
8834
9017
  } elsif ($format eq 'pstring') {
8835
- $count = Get8u($dataPt, ($entry++)+$offset);
9018
+ $count = Get8u($dataPt, ($entry++)+$dirStart);
8836
9019
  --$more;
8837
9020
  } elsif ($format eq 'pstr32' or $format eq 'ustr32') {
8838
9021
  last if $more < 4;
8839
- $count = Get32u($dataPt, $entry + $offset);
9022
+ $count = Get32u($dataPt, $entry + $dirStart);
8840
9023
  $count *= 2 if $format eq 'ustr32';
8841
9024
  $entry += 4;
8842
9025
  $more -= 4;
@@ -8844,22 +9027,22 @@ sub ProcessBinaryData($$$)
8844
9027
  } elsif ($format eq 'int16u') {
8845
9028
  # int16u size of binary data to follow
8846
9029
  last if $more < 2;
8847
- $count = Get16u($dataPt, $entry + $offset) + 2;
9030
+ $count = Get16u($dataPt, $entry + $dirStart) + 2;
8848
9031
  $varSize -= 2; # ($count includes size word)
8849
9032
  $format = 'undef';
8850
9033
  } elsif ($format eq 'ue7') {
8851
9034
  require Image::ExifTool::BPG;
8852
- ($val, $count) = Image::ExifTool::BPG::Get_ue7($dataPt, $entry + $offset);
9035
+ ($val, $count) = Image::ExifTool::BPG::Get_ue7($dataPt, $entry + $dirStart);
8853
9036
  last unless defined $val;
8854
9037
  --$varSize; # ($count includes base size of 1 byte)
8855
9038
  } elsif ($$dataPt =~ /\0/g) {
8856
- $count = pos($$dataPt) - ($entry+$offset);
9039
+ $count = pos($$dataPt) - ($entry+$dirStart);
8857
9040
  --$varSize; # ($count includes base size of 1 byte)
8858
9041
  }
8859
9042
  $count = $more if not defined $count or $count > $more;
8860
9043
  $varSize += $count; # shift subsequent indices
8861
9044
  unless (defined $val) {
8862
- $val = substr($$dataPt, $entry+$offset, $count);
9045
+ $val = substr($$dataPt, $entry+$dirStart, $count);
8863
9046
  $val = $self->Decode($val, 'UCS2') if $format eq 'ustring' or $format eq 'ustr32';
8864
9047
  $val =~ s/\0.*//s unless $format eq 'undef'; # truncate at null
8865
9048
  }
@@ -8873,7 +9056,7 @@ sub ProcessBinaryData($$$)
8873
9056
  # hook to allow format, etc to be set dynamically
8874
9057
  if (defined $$tagInfo{Hook}) {
8875
9058
  my $oldVarSize = $varSize;
8876
- my $pos = $entry + $offset;
9059
+ my $pos = $entry + $dirStart;
8877
9060
  #### eval Hook ($format, $varSize, $size, $dataPt, $pos)
8878
9061
  eval $$tagInfo{Hook};
8879
9062
  # save variable size data if required for writing (in case changed by Hook)
@@ -8898,7 +9081,7 @@ sub ProcessBinaryData($$$)
8898
9081
  next if $$tagInfo{LargeTag} and $$self{EXCL_TAG_LOOKUP}{lc $$tagInfo{Name}};
8899
9082
  # read value now if necessary
8900
9083
  unless (defined $val and not $$tagInfo{SubDirectory}) {
8901
- $val = ReadValue($dataPt, $entry+$offset, $format, $count, $more, \$rational);
9084
+ $val = ReadValue($dataPt, $entry+$dirStart, $format, $count, $more, \$rational);
8902
9085
  next unless defined $val;
8903
9086
  $mask = $$tagInfo{Mask};
8904
9087
  $val = ($val & $mask) >> $$tagInfo{BitShift} if $mask;
@@ -8915,8 +9098,8 @@ sub ProcessBinaryData($$$)
8915
9098
  Value => $val,
8916
9099
  DataPt => $dataPt,
8917
9100
  Size => $len,
8918
- Start => $entry+$offset,
8919
- Addr => $entry+$offset+$base+$dataPos,
9101
+ Start => $entry+$dirStart,
9102
+ Addr => $entry+$dirStart+$base+$dataPos,
8920
9103
  Format => $format,
8921
9104
  Count => $count,
8922
9105
  Extra => $mask ? sprintf(', mask 0x%.2x',$mask) : undef,
@@ -8942,16 +9125,27 @@ sub ProcessBinaryData($$$)
8942
9125
  my $subdirBase = $base;
8943
9126
  if (defined $$subdir{Base}) {
8944
9127
  #### eval Base ($start,$base)
8945
- my $start = $entry + $offset + $dataPos;
9128
+ my $start = $entry + $dirStart + $dataPos;
8946
9129
  $subdirBase = eval($$subdir{Base}) + $base;
8947
9130
  }
8948
9131
  my $start = $$subdir{Start} || 0;
9132
+ if ($start =~ /\$/) {
9133
+ # ignore directories with a zero offset (ie. missing Nikon ShotInfo entries)
9134
+ next unless $val;
9135
+ #### eval Start ($val, $dirStart)
9136
+ $start = eval($start);
9137
+ next if $start < $dirStart or $start > $dataLen;
9138
+ $len = $$subdir{DirLen};
9139
+ $len = $dataLen - $start unless $len and $len <= $dataLen - $start;
9140
+ } else {
9141
+ $start += $dirStart + $entry;
9142
+ }
8949
9143
  my %subdirInfo = (
8950
9144
  DataPt => $dataPt,
8951
9145
  DataPos => $dataPos,
8952
- DataLen => length $$dataPt,
8953
- DirStart => $entry + $offset + $start,
8954
- DirLen => $len - $start,
9146
+ DataLen => $dataLen,
9147
+ DirStart => $start,
9148
+ DirLen => $len,
8955
9149
  Base => $subdirBase,
8956
9150
  );
8957
9151
  delete $$self{NO_UNKNOWN};