exiftool_vendored 13.30.0 → 13.33.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +57 -1
  3. data/bin/MANIFEST +5 -0
  4. data/bin/META.json +4 -3
  5. data/bin/META.yml +3 -2
  6. data/bin/README +47 -46
  7. data/bin/exiftool +88 -60
  8. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +7 -5
  9. data/bin/lib/Image/ExifTool/Canon.pm +16 -5
  10. data/bin/lib/Image/ExifTool/Exif.pm +7 -4
  11. data/bin/lib/Image/ExifTool/FlashPix.pm +4 -159
  12. data/bin/lib/Image/ExifTool/FujiFilm.pm +13 -3
  13. data/bin/lib/Image/ExifTool/Geotag.pm +5 -3
  14. data/bin/lib/Image/ExifTool/GoPro.pm +23 -4
  15. data/bin/lib/Image/ExifTool/LNK.pm +24 -3
  16. data/bin/lib/Image/ExifTool/Lang/cs.pm +0 -1
  17. data/bin/lib/Image/ExifTool/Lang/de.pm +2 -2
  18. data/bin/lib/Image/ExifTool/Lang/fr.pm +2 -2
  19. data/bin/lib/Image/ExifTool/Lang/it.pm +0 -1
  20. data/bin/lib/Image/ExifTool/Lang/ja.pm +0 -1
  21. data/bin/lib/Image/ExifTool/Lang/nl.pm +0 -1
  22. data/bin/lib/Image/ExifTool/Lang/pl.pm +0 -1
  23. data/bin/lib/Image/ExifTool/Lang/zh_cn.pm +0 -1
  24. data/bin/lib/Image/ExifTool/LigoGPS.pm +14 -6
  25. data/bin/lib/Image/ExifTool/Microsoft.pm +158 -1
  26. data/bin/lib/Image/ExifTool/Minolta.pm +1 -1
  27. data/bin/lib/Image/ExifTool/Nikon.pm +80 -39
  28. data/bin/lib/Image/ExifTool/NikonCustom.pm +40 -10
  29. data/bin/lib/Image/ExifTool/Olympus.pm +240 -35
  30. data/bin/lib/Image/ExifTool/PDF.pm +1 -0
  31. data/bin/lib/Image/ExifTool/Panasonic.pm +4 -4
  32. data/bin/lib/Image/ExifTool/Parrot.pm +1 -1
  33. data/bin/lib/Image/ExifTool/Pentax.pm +276 -56
  34. data/bin/lib/Image/ExifTool/Plot.pm +2 -3
  35. data/bin/lib/Image/ExifTool/QuickTime.pm +13 -5
  36. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +41 -17
  37. data/bin/lib/Image/ExifTool/Sigma.pm +19 -1
  38. data/bin/lib/Image/ExifTool/Sony.pm +5 -2
  39. data/bin/lib/Image/ExifTool/TNEF.pm +487 -0
  40. data/bin/lib/Image/ExifTool/TagLookup.pm +4374 -4262
  41. data/bin/lib/Image/ExifTool/TagNames.pod +259 -22
  42. data/bin/lib/Image/ExifTool/WriteExif.pl +14 -12
  43. data/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
  44. data/bin/lib/Image/ExifTool/Writer.pl +142 -139
  45. data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -1
  46. data/bin/lib/Image/ExifTool.pm +16 -7
  47. data/bin/lib/Image/ExifTool.pod +45 -44
  48. data/bin/perl-Image-ExifTool.spec +46 -45
  49. data/lib/exiftool_vendored/version.rb +1 -1
  50. metadata +3 -2
@@ -1907,7 +1907,7 @@ sub RestoreNewValues($)
1907
1907
  #------------------------------------------------------------------------------
1908
1908
  # Set alternate file for extracting information
1909
1909
  # Inputs: 0) ExifTool ref, 1) family 8 group name (of the form "File#" where # is any number)
1910
- # 2) alternate file name, or undef to reset
1910
+ # 2) alternate file name (may contain tag names with leading "$"), or undef to reset
1911
1911
  # Returns: 1 on success, or 0 on invalid group name
1912
1912
  sub SetAlternateFile($$$)
1913
1913
  {
@@ -1917,7 +1917,9 @@ sub SetAlternateFile($$$)
1917
1917
  # keep the same file if already initialized (possibly has metadata extracted)
1918
1918
  if (not defined $file) {
1919
1919
  delete $$self{ALT_EXIFTOOL}{$g8};
1920
- } elsif (not ($$self{ALT_EXIFTOOL}{$g8} and $$self{ALT_EXIFTOOL}{$g8}{ALT_FILE} eq $file)) {
1920
+ } elsif (not ($$self{ALT_EXIFTOOL}{$g8} and $file !~ /\$/ and
1921
+ $$self{ALT_EXIFTOOL}{$g8}{ALT_FILE} eq $file))
1922
+ {
1921
1923
  my $altExifTool = Image::ExifTool->new;
1922
1924
  $$altExifTool{ALT_FILE} = $file;
1923
1925
  $$self{ALT_EXIFTOOL}{$g8} = $altExifTool;
@@ -3201,7 +3203,7 @@ sub InsertTagValues($$;$$$$)
3201
3203
  my ($docNum, $tag);
3202
3204
 
3203
3205
  if ($docGrp) {
3204
- $docNum = $docGrp =~ /(\d+)$/ ? $1 : 0;
3206
+ $docNum = $docGrp =~ /(\d+(-\d+)*)$/ ? $1 : 0;
3205
3207
  } else {
3206
3208
  undef $cache; # no cache if no document groups
3207
3209
  }
@@ -3269,25 +3271,25 @@ sub InsertTagValues($$;$$$$)
3269
3271
  # (similar to code in BuildCompositeTags(), but this is case-insensitive)
3270
3272
  my $cacheTag = $$cache{$lcTag};
3271
3273
  unless ($cacheTag) {
3272
- $cacheTag = $$cache{$lcTag} = [ ];
3274
+ $cacheTag = $$cache{$lcTag} = { };
3273
3275
  # find all matching keys, organize into groups, and store in cache
3274
3276
  my $ex = $$self{TAG_EXTRA};
3275
3277
  my @matches = grep /^$tag(\s|$)/i, @$foundTags;
3276
3278
  @matches = $self->GroupMatches($group, \@matches) if defined $group;
3277
3279
  foreach (@matches) {
3278
3280
  my $doc = $$ex{$_}{G3} || 0;
3279
- if (defined $$cacheTag[$doc]) {
3280
- next unless $$cacheTag[$doc] =~ / \((\d+)\)$/;
3281
+ if (defined $$cacheTag{$doc}) {
3282
+ next unless $$cacheTag{$doc} =~ / \((\d+)\)$/;
3281
3283
  my $cur = $1;
3282
3284
  # keep the most recently extracted tag
3283
3285
  next if / \((\d+)\)$/ and $1 < $cur;
3284
3286
  }
3285
- $$cacheTag[$doc] = $_;
3287
+ $$cacheTag{$doc} = $_;
3286
3288
  }
3287
3289
  }
3288
- my $doc = $lcTag =~ /\b(main|doc(\d+)):/ ? ($2 || 0) : $docNum;
3289
- if ($$cacheTag[$doc]) {
3290
- $tag = $$cacheTag[$doc];
3290
+ my $doc = $lcTag =~ /\b(main|doc(\d+(-\d+)*)):/ ? ($2 || 0) : $docNum;
3291
+ if ($$cacheTag{$doc}) {
3292
+ $tag = $$cacheTag{$doc};
3291
3293
  $val = $self->GetValue($tag, $type);
3292
3294
  }
3293
3295
  } else {
@@ -4741,10 +4743,11 @@ sub DumpUnknownTrailer($$)
4741
4743
  my $pos = $$dirInfo{DataPos};
4742
4744
  my $endPos = $pos + $$dirInfo{DirLen};
4743
4745
  # account for preview/MPF image trailer
4744
- my $prePos = $$self{VALUE}{PreviewImageStart} || $$self{PreviewImageStart};
4745
- my $preLen = $$self{VALUE}{PreviewImageLength} || $$self{PreviewImageLength};
4746
- my $hidPos = $$self{VALUE}{HiddenDataOffset};
4747
- my $hidLen = $$self{VALUE}{HiddenDataLength};
4746
+ my $value = $$self{VALUE};
4747
+ my $prePos = $$value{PreviewImageStart} || $$self{PreviewImageStart};
4748
+ my $preLen = $$value{PreviewImageLength} || $$self{PreviewImageLength};
4749
+ my $hidPos = $$value{HiddenDataOffset};
4750
+ my $hidLen = $$value{HiddenDataLength};
4748
4751
  my $tag = 'PreviewImage';
4749
4752
  my $mpImageNum = 0;
4750
4753
  my (%image, $lastOne);
@@ -4761,12 +4764,12 @@ sub DumpUnknownTrailer($$)
4761
4764
  last if $lastOne; # checked all images
4762
4765
  # look for MPF images (in the proper order)
4763
4766
  ++$mpImageNum;
4764
- $prePos = $$self{VALUE}{"MPImageStart ($mpImageNum)"};
4767
+ $prePos = $$value{"MPImageStart ($mpImageNum)"};
4765
4768
  if (defined $prePos) {
4766
- $preLen = $$self{VALUE}{"MPImageLength ($mpImageNum)"};
4769
+ $preLen = $$value{"MPImageLength ($mpImageNum)"};
4767
4770
  } else {
4768
- $prePos = $$self{VALUE}{'MPImageStart'};
4769
- $preLen = $$self{VALUE}{'MPImageLength'};
4771
+ $prePos = $$value{MPImageStart};
4772
+ $preLen = $$value{MPImageLength};
4770
4773
  $lastOne = 1;
4771
4774
  }
4772
4775
  $tag = "MPImage$mpImageNum";
@@ -5887,10 +5890,10 @@ sub WriteJPEG($$)
5887
5890
  $writeBuffer = '';
5888
5891
  $oldOutfile = $outfile;
5889
5892
  $outfile = \$writeBuffer;
5890
- # account for segment, EXIF and TIFF headers
5891
- $$self{PREVIEW_INFO}{Fixup}{Start} += 18 if $$self{PREVIEW_INFO};
5892
- $$self{LeicaTrailer}{Fixup}{Start} += 18 if $$self{LeicaTrailer};
5893
- $$self{HiddenData}{Fixup}{Start} += 18 if $$self{HiddenData};
5893
+ # must account for segment, EXIF and TIFF headers
5894
+ foreach (qw(PREVIEW_INFO LeicaTrailer HiddenData)) {
5895
+ $$self{$_}{Fixup}{Start} += 18 if $$self{$_};
5896
+ }
5894
5897
  }
5895
5898
  # write as multi-segment
5896
5899
  my $n = WriteMultiSegment($outfile, 0xe1, $exifAPP1hdr, \$buff, 'EXIF');
@@ -6036,8 +6039,8 @@ sub WriteJPEG($$)
6036
6039
  my $delPreview = $$self{DEL_PREVIEW};
6037
6040
  $trailInfo = $self->IdentifyTrailer($raf) unless $$delGroup{Trailer};
6038
6041
  my $nvTrail = $self->GetNewValueHash($Image::ExifTool::Extra{Trailer});
6039
- unless ($oldOutfile or $delPreview or $trailInfo or $$delGroup{Trailer} or $nvTrail or
6040
- $$self{HiddenData})
6042
+ unless ($oldOutfile or $delPreview or $trailInfo or $$delGroup{Trailer} or
6043
+ $nvTrail or $$self{HiddenData})
6041
6044
  {
6042
6045
  # blindly copy the rest of the file
6043
6046
  while ($raf->Read($buff, 65536)) {
@@ -6082,35 +6085,7 @@ sub WriteJPEG($$)
6082
6085
  }
6083
6086
  last; # all done
6084
6087
  }
6085
- # copy HiddenData if necessary
6086
- if ($$self{HiddenData}) {
6087
- my $pad;
6088
- my $hd = $$self{HiddenData};
6089
- my $hdOff = $$hd{Offset} + $$hd{Base};
6090
- require Image::ExifTool::Sony;
6091
- # read HiddenData, updating $hdOff with actual offset if necessary
6092
- my $dataPt = Image::ExifTool::Sony::ReadHiddenData($self, $hdOff, $$hd{Size});
6093
- if ($dataPt) {
6094
- # preserve padding to avoid invalidating MPF pointers (yuk!)
6095
- my $padLen = $hdOff - $endPos;
6096
- unless ($padLen >= 0 and $raf->Seek($endPos,0) and $raf->Read($pad,$padLen)==$padLen) {
6097
- $self->Error('Error reading HiddenData padding',1);
6098
- $pad = '';
6099
- }
6100
- $endPos += length($pad) + length($$dataPt); # update end position
6101
- } else {
6102
- $$dataPt = $pad = '';
6103
- }
6104
- my $fixup = $$self{HiddenData}{Fixup};
6105
- # set MakerNote pointer and size (subtract 10 for segment and EXIF headers)
6106
- $fixup->SetMarkerPointers($outfile, 'HiddenData', length($$outfile) + length($pad) - 10);
6107
- # clean up and write the buffered data
6108
- $outfile = $oldOutfile;
6109
- undef $oldOutfile;
6110
- Write($outfile, $writeBuffer, $pad, $$dataPt) or $err = 1;
6111
- undef $writeBuffer;
6112
- }
6113
- # rewrite existing trailers
6088
+ # rewrite existing trailers into buffer
6114
6089
  if ($trailInfo) {
6115
6090
  my $tbuf = '';
6116
6091
  $raf->Seek(-length($buff), 1); # seek back to just after EOI
@@ -6118,100 +6093,126 @@ sub WriteJPEG($$)
6118
6093
  $$trailInfo{ScanForTrailer} = 1;# scan if necessary
6119
6094
  $self->ProcessTrailers($trailInfo) or undef $trailInfo;
6120
6095
  }
6121
- if (not $oldOutfile) {
6122
- # do nothing special
6123
- } elsif ($$self{LeicaTrailer}) {
6124
- my $trailLen;
6125
- if ($trailInfo) {
6126
- $trailLen = $$trailInfo{DataPos} - $endPos;
6127
- } else {
6128
- $raf->Seek(0, 2) or $err = 1;
6129
- $trailLen = $raf->Tell() - $endPos;
6130
- }
6131
- my $fixup = $$self{LeicaTrailer}{Fixup};
6132
- $$self{LeicaTrailer}{TrailPos} = $endPos;
6133
- $$self{LeicaTrailer}{TrailLen} = $trailLen;
6134
- # get _absolute_ position of new Leica trailer
6135
- my $absPos = Tell($oldOutfile) + length($$outfile);
6136
- require Image::ExifTool::Panasonic;
6137
- my $dat = Image::ExifTool::Panasonic::ProcessLeicaTrailer($self, $absPos);
6138
- # allow some junk before Leica trailer (just in case)
6139
- my $junk = $$self{LeicaTrailerPos} - $endPos;
6140
- # set MakerNote pointer and size (subtract 10 for segment and EXIF headers)
6141
- $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', length($$outfile) - 10 + $junk);
6142
- # use this fixup to set the size too (sneaky)
6143
- my $trailSize = defined($dat) ? length($dat) - $junk : $$self{LeicaTrailer}{Size};
6144
- $$fixup{Start} -= 4; $$fixup{Shift} += 4;
6145
- $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', $trailSize) if defined $trailSize;
6146
- $$fixup{Start} += 4; $$fixup{Shift} -= 4;
6147
- # clean up and write the buffered data
6148
- $outfile = $oldOutfile;
6149
- undef $oldOutfile;
6150
- Write($outfile, $writeBuffer) or $err = 1;
6151
- undef $writeBuffer;
6152
- if (defined $dat) {
6153
- Write($outfile, $dat) or $err = 1; # write new Leica trailer
6154
- $delPreview = 1; # delete existing Leica trailer
6096
+ if ($oldOutfile) {
6097
+ my $previewInfo;
6098
+ # copy HiddenData if necessary
6099
+ if ($$self{HiddenData}) {
6100
+ my $pad;
6101
+ my $hd = $$self{HiddenData};
6102
+ my $hdOff = $$hd{Offset} + $$hd{Base};
6103
+ require Image::ExifTool::Sony;
6104
+ # read HiddenData, updating $hdOff with actual offset if necessary
6105
+ my $dataPt = Image::ExifTool::Sony::ReadHiddenData($self, $hdOff, $$hd{Size});
6106
+ if ($dataPt) {
6107
+ # preserve padding to avoid invalidating MPF pointers (yuk!)
6108
+ my $padLen = $hdOff - $endPos;
6109
+ unless ($padLen >= 0 and $raf->Seek($endPos,0) and $raf->Read($pad,$padLen)==$padLen) {
6110
+ $self->Error('Error reading HiddenData padding',1);
6111
+ $pad = '';
6112
+ }
6113
+ $endPos += length($pad) + length($$dataPt); # update end position
6114
+ } else {
6115
+ $$dataPt = $pad = '';
6116
+ }
6117
+ my $fixup = $$self{HiddenData}{Fixup};
6118
+ # set MakerNote pointer and size (subtract 10 for segment and EXIF headers)
6119
+ $fixup->SetMarkerPointers($outfile, 'HiddenData', length($$outfile) + length($pad) - 10);
6120
+ $writeBuffer .= $pad . $$dataPt; # keep padding for now
6155
6121
  }
6156
- } else {
6157
- # locate preview image and fix up preview offsets
6158
- my $scanLen = $$self{Make} =~ /^SONY/i ? 65536 : 1024;
6159
- if (length($buff) < $scanLen) { # make sure we have enough trailer to scan
6160
- my $buf2;
6161
- $buff .= $buf2 if $raf->Read($buf2, $scanLen - length($buff));
6122
+ if ($$self{LeicaTrailer}) {
6123
+ my $trailLen;
6124
+ if ($trailInfo) {
6125
+ $trailLen = $$trailInfo{DataPos} - $endPos;
6126
+ } else {
6127
+ $raf->Seek(0, 2) or $err = 1;
6128
+ $trailLen = $raf->Tell() - $endPos;
6129
+ }
6130
+ my $fixup = $$self{LeicaTrailer}{Fixup};
6131
+ $$self{LeicaTrailer}{TrailPos} = $endPos;
6132
+ $$self{LeicaTrailer}{TrailLen} = $trailLen;
6133
+ # get _absolute_ position of new Leica trailer
6134
+ my $absPos = Tell($oldOutfile) + length($$outfile);
6135
+ require Image::ExifTool::Panasonic;
6136
+ my $dat = Image::ExifTool::Panasonic::ProcessLeicaTrailer($self, $absPos);
6137
+ # allow some junk before Leica trailer (just in case)
6138
+ my $junk = $$self{LeicaTrailerPos} - $endPos;
6139
+ # set MakerNote pointer and size (subtract 10 for segment and EXIF headers)
6140
+ $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', length($$outfile) - 10 + $junk);
6141
+ # use this fixup to set the size too (sneaky)
6142
+ my $trailSize = defined($dat) ? length($dat) - $junk : $$self{LeicaTrailer}{Size};
6143
+ $$fixup{Start} -= 4; $$fixup{Shift} += 4;
6144
+ $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', $trailSize) if defined $trailSize;
6145
+ $$fixup{Start} += 4; $$fixup{Shift} -= 4;
6146
+ if (defined $dat) {
6147
+ Write($outfile, $dat) or $err = 1; # write new Leica trailer
6148
+ $delPreview = 1; # delete existing Leica trailer
6149
+ }
6162
6150
  }
6163
- # get new preview image position, relative to EXIF base
6164
- my $newPos = length($$outfile) - 10; # (subtract 10 for segment and EXIF headers)
6165
- my $junkLen;
6166
- # adjust position if image isn't at the start (eg. Olympus E-1/E-300)
6167
- if ($buff =~ /(\xff\xd8\xff.|.\xd8\xff\xdb)(..)/sg) {
6168
- my ($jpegHdr, $segLen) = ($1, $2);
6169
- $junkLen = pos($buff) - 6;
6170
- # Sony previewimage trailer has a 32 byte header
6171
- if ($$self{Make} =~ /^SONY/i and $junkLen > 32) {
6172
- # with some newer Sony models, the makernotes preview pointer
6173
- # points to JPEG at end of EXIF inside MPImage preview (what a pain!)
6174
- if ($jpegHdr eq "\xff\xd8\xff\xe1") { # is the first segment EXIF?
6175
- $segLen = unpack('n', $segLen); # the EXIF segment length
6176
- # Sony PreviewImage starts with last 2 bytes of EXIF segment
6177
- # (and first byte is usually "\0", not "\xff", so don't check this)
6178
- if (length($buff) > $junkLen + $segLen + 6 and
6179
- substr($buff, $junkLen + $segLen + 3, 3) eq "\xd8\xff\xdb")
6180
- {
6181
- $junkLen += $segLen + 2;
6182
- # (note: this will not copy the trailer after PreviewImage,
6183
- # which is a 14kB block full of zeros for the A77)
6151
+ # handle preview image last
6152
+ if ($$self{PREVIEW_INFO}) {
6153
+ # locate preview image and fix up preview offsets
6154
+ my $scanLen = $$self{Make} =~ /^SONY/i ? 65536 : 1024;
6155
+ if (length($buff) < $scanLen) { # make sure we have enough trailer to scan
6156
+ my $buf2;
6157
+ $buff .= $buf2 if $raf->Read($buf2, $scanLen - length($buff));
6158
+ }
6159
+ # get new preview image position, relative to EXIF base
6160
+ my $newPos = length($$outfile) - 10; # (subtract 10 for segment and EXIF headers)
6161
+ my $junkLen;
6162
+ # adjust position if image isn't at the start (eg. Olympus E-1/E-300)
6163
+ if ($buff =~ /(\xff\xd8\xff.|.\xd8\xff\xdb)(..)/sg) {
6164
+ my ($jpegHdr, $segLen) = ($1, $2);
6165
+ $junkLen = pos($buff) - 6;
6166
+ # Sony previewimage trailer has a 32 byte header
6167
+ if ($$self{Make} =~ /^SONY/i and $junkLen > 32) {
6168
+ # with some newer Sony models, the makernotes preview pointer
6169
+ # points to JPEG at end of EXIF inside MPImage preview (what a pain!)
6170
+ if ($jpegHdr eq "\xff\xd8\xff\xe1") { # is the first segment EXIF?
6171
+ $segLen = unpack('n', $segLen); # the EXIF segment length
6172
+ # Sony PreviewImage starts with last 2 bytes of EXIF segment
6173
+ # (and first byte is usually "\0", not "\xff", so don't check this)
6174
+ if (length($buff) > $junkLen + $segLen + 6 and
6175
+ substr($buff, $junkLen + $segLen + 3, 3) eq "\xd8\xff\xdb")
6176
+ {
6177
+ $junkLen += $segLen + 2;
6178
+ # (note: this will not copy the trailer after PreviewImage,
6179
+ # which is a 14kB block full of zeros for the A77)
6180
+ }
6184
6181
  }
6182
+ $junkLen -= 32;
6185
6183
  }
6186
- $junkLen -= 32;
6184
+ $newPos += $junkLen;
6185
+ }
6186
+ # fix up the preview offsets to point to the start of the new image
6187
+ $previewInfo = $$self{PREVIEW_INFO};
6188
+ delete $$self{PREVIEW_INFO};
6189
+ my $fixup = $$previewInfo{Fixup};
6190
+ $newPos += ($$previewInfo{BaseShift} || 0);
6191
+ # adjust to absolute file offset if necessary (Samsung STMN)
6192
+ $newPos += Tell($oldOutfile) + 10 if $$previewInfo{Absolute};
6193
+ if ($$previewInfo{Relative}) {
6194
+ # adjust for our base by looking at how far the pointer got shifted
6195
+ $newPos -= ($fixup->GetMarkerPointers($outfile, 'PreviewImage') || 0);
6196
+ } elsif ($$previewInfo{ChangeBase}) {
6197
+ # Leica S2 uses relative offsets for the preview only (leica sucks)
6198
+ my $makerOffset = $fixup->GetMarkerPointers($outfile, 'LeicaTrailer');
6199
+ $newPos -= $makerOffset if $makerOffset;
6200
+ }
6201
+ $fixup->SetMarkerPointers($outfile, 'PreviewImage', $newPos);
6202
+ if ($$previewInfo{Data} ne 'LOAD_PREVIEW') {
6203
+ # write any junk that existed before the preview image
6204
+ $$previewInfo{Junk} = substr($buff,0,$junkLen) if $junkLen;
6187
6205
  }
6188
- $newPos += $junkLen;
6189
- }
6190
- # fix up the preview offsets to point to the start of the new image
6191
- my $previewInfo = $$self{PREVIEW_INFO};
6192
- delete $$self{PREVIEW_INFO};
6193
- my $fixup = $$previewInfo{Fixup};
6194
- $newPos += ($$previewInfo{BaseShift} || 0);
6195
- # adjust to absolute file offset if necessary (Samsung STMN)
6196
- $newPos += Tell($oldOutfile) + 10 if $$previewInfo{Absolute};
6197
- if ($$previewInfo{Relative}) {
6198
- # adjust for our base by looking at how far the pointer got shifted
6199
- $newPos -= ($fixup->GetMarkerPointers($outfile, 'PreviewImage') || 0);
6200
- } elsif ($$previewInfo{ChangeBase}) {
6201
- # Leica S2 uses relative offsets for the preview only (leica sucks)
6202
- my $makerOffset = $fixup->GetMarkerPointers($outfile, 'LeicaTrailer');
6203
- $newPos -= $makerOffset if $makerOffset;
6204
6206
  }
6205
- $fixup->SetMarkerPointers($outfile, 'PreviewImage', $newPos);
6206
6207
  # clean up and write the buffered data
6207
6208
  $outfile = $oldOutfile;
6208
6209
  undef $oldOutfile;
6209
6210
  Write($outfile, $writeBuffer) or $err = 1;
6210
6211
  undef $writeBuffer;
6211
6212
  # write preview image
6212
- if ($$previewInfo{Data} ne 'LOAD_PREVIEW') {
6213
+ if ($previewInfo and $$previewInfo{Data} ne 'LOAD_PREVIEW') {
6213
6214
  # write any junk that existed before the preview image
6214
- Write($outfile, substr($buff,0,$junkLen)) or $err = 1 if $junkLen;
6215
+ Write($outfile, $$previewInfo{Junk}) or $err = 1 if defined $$previewInfo{Junk};
6215
6216
  # write the saved preview image
6216
6217
  Write($outfile, $$previewInfo{Data}) or $err = 1;
6217
6218
  delete $$previewInfo{Data};
@@ -6224,7 +6225,7 @@ sub WriteJPEG($$)
6224
6225
  my $extra;
6225
6226
  if ($trailInfo) {
6226
6227
  # copy everything up to start of first processed trailer
6227
- $extra = $$trailInfo{DataPos} - $endPos;
6228
+ $extra = defined $$trailInfo{DataPos} ? ($$trailInfo{DataPos} - $endPos) : 0;
6228
6229
  } else {
6229
6230
  # copy everything up to end of file
6230
6231
  $raf->Seek(0, 2) or $err = 1;
@@ -6393,9 +6394,9 @@ sub WriteJPEG($$)
6393
6394
  $oldOutfile = $outfile;
6394
6395
  $outfile = \$writeBuffer;
6395
6396
  # must account for segment, EXIF and TIFF headers
6396
- $$self{PREVIEW_INFO}{Fixup}{Start} += 18 if $$self{PREVIEW_INFO};
6397
- $$self{LeicaTrailer}{Fixup}{Start} += 18 if $$self{LeicaTrailer};
6398
- $$self{HiddenData}{Fixup}{Start} += 18 if $$self{HiddenData};
6397
+ foreach (qw(PREVIEW_INFO LeicaTrailer HiddenData)) {
6398
+ $$self{$_}{Fixup}{Start} += 18 if $$self{$_};
6399
+ }
6399
6400
  }
6400
6401
  # write as multi-segment
6401
6402
  my $n = WriteMultiSegment($outfile, $marker, $exifAPP1hdr, $segDataPt, 'EXIF');
@@ -6922,7 +6923,7 @@ sub CheckBinaryData($$$)
6922
6923
  $format = $1;
6923
6924
  $count = $2;
6924
6925
  # can't evaluate $count now because we don't know $size yet
6925
- undef $count if $count =~ /\$size/;
6926
+ $count = -1 if $count =~ /\$size/; # (-1 = any count allowed)
6926
6927
  }
6927
6928
  return CheckValue($valPtr, $format, $count);
6928
6929
  }
@@ -7248,6 +7249,8 @@ sub WriteBinaryData($$$)
7248
7249
  $self->VerboseValue("- $dirName:$$tagInfo{Name}", $val);
7249
7250
  $self->VerboseValue("+ $dirName:$$tagInfo{Name}", $newVal);
7250
7251
  ++$$self{CHANGED};
7252
+ } else {
7253
+ $self->Warn("Error packing $$tagInfo{Name} value");
7251
7254
  }
7252
7255
  }
7253
7256
  # add necessary fixups for any offsets
@@ -86,7 +86,7 @@ sub InflateStruct($$;$)
86
86
  my %struct;
87
87
  for (;;) {
88
88
  last unless $sfmt ? $$obj =~ s/^\s*"(.*?)"\s*://s :
89
- $$obj =~ s/^\s*([-\w:]+#?)\s*=//s;
89
+ $$obj =~ s/^\s*([-\w:.]+#?)\s*=//s;
90
90
  my $tag = $1;
91
91
  my ($v, $w) = InflateStruct($et, $obj, '}');
92
92
  $warn = $w if $w and not $warn;
@@ -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 $advFmtSelf $configFile @configFiles $noConfig);
31
31
 
32
- $VERSION = '13.30';
32
+ $VERSION = '13.33';
33
33
  $RELEASE = '';
34
34
  @ISA = qw(Exporter);
35
35
  %EXPORT_TAGS = (
@@ -42,7 +42,7 @@ $RELEASE = '';
42
42
  # exports not part of the public API, but used by ExifTool modules:
43
43
  DataAccess => [qw(
44
44
  ReadValue GetByteOrder SetByteOrder ToggleByteOrder Get8u Get8s Get16u
45
- Get16s Get32u Get32s Get64u GetFloat GetDouble GetFixed32s Write
45
+ Get16s Get32u Get32s Get64u Get64s GetFloat GetDouble GetFixed32s Write
46
46
  WriteValue Tell Set8u Set8s Set16u Set32u Set64u Set64s
47
47
  )],
48
48
  Utils => [qw(GetTagTable TagTableKeys GetTagInfoList AddTagToTable HexDump)],
@@ -152,8 +152,8 @@ sub ReadValue($$$;$$$);
152
152
  APE::OldHeader Audible MPC MPEG::Audio MPEG::Video MPEG::Xing M2TS QuickTime
153
153
  QuickTime::ImageFile QuickTime::Stream QuickTime::Tags360Fly Matroska
154
154
  Matroska::StdTag MOI MXF DV Flash Flash::FLV Real::Media Real::Audio
155
- Real::Metafile Red RIFF AIFF ASF WTV DICOM FITS XISF MIE JSON HTML XMP::SVG
156
- Palm Palm::MOBI Palm::EXTH Torrent EXE EXE::PEVersion EXE::PEString
155
+ Real::Metafile Red RIFF AIFF ASF TNEF WTV DICOM FITS XISF MIE JSON HTML
156
+ XMP::SVG Palm Palm::MOBI Palm::EXTH Torrent EXE EXE::PEVersion EXE::PEString
157
157
  EXE::DebugRSDS EXE::DebugNB10 EXE::Misc EXE::MachO EXE::PEF EXE::ELF EXE::AR
158
158
  EXE::CHM LNK PCAP Font VCard Text VCard::VCalendar VCard::VNote RSRC Rawzor
159
159
  ZIP ZIP::GZIP ZIP::RAR ZIP::RAR5 RTF OOXML iWork ISO FLIR::AFF FLIR::FPF
@@ -200,7 +200,7 @@ $defaultLang = 'en'; # default language
200
200
  RAR 7Z BZ2 CZI TAR EXE EXR HDR CHM LNK WMF AVC DEX DPX RAW Font
201
201
  JUMBF RSRC M2TS MacOS PHP PCX DCX DWF DWG DXF WTV Torrent VCard
202
202
  LRI R3D AA PDB PFM2 MRC LIF JXL MOI ISO ALIAS PCAP JSON MP3
203
- DICOM PCD NKA ICO TXT AAC);
203
+ TNEF DICOM PCD NKA ICO TXT AAC);
204
204
 
205
205
  # file types that we can write (edit)
206
206
  my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF RAF RAW PNG MIE PSD XMP PPM EPS
@@ -536,6 +536,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
536
536
  THMX => [['ZIP','FPX'], 'Office Open XML Theme'],
537
537
  TIF => 'TIFF',
538
538
  TIFF => ['TIFF', 'Tagged Image File Format'],
539
+ TNEF => ['TNEF', 'Transport Neural Encapsulation Format'], # (actual extension is .DAT)
539
540
  TORRENT => ['Torrent', 'BitTorrent description file'],
540
541
  TS => 'M2TS',
541
542
  TTC => ['Font', 'True Type Font Collection'],
@@ -802,6 +803,7 @@ my %fileDescription = (
802
803
  TAR => 'application/x-tar',
803
804
  THMX => 'application/vnd.ms-officetheme',
804
805
  TIFF => 'image/tiff',
806
+ TNEF => 'application/vnd.ms-tnef',
805
807
  Torrent => 'application/x-bittorrent',
806
808
  TTC => 'application/font-ttf',
807
809
  TTF => 'application/font-ttf',
@@ -1012,6 +1014,7 @@ $testLen = 1024; # number of bytes to read when testing for magic number
1012
1014
  RWZ => 'rawzor',
1013
1015
  SWF => '[FC]WS[^\0]',
1014
1016
  TAR => '.{257}ustar( )?\0', # (this doesn't catch old-style tar files)
1017
+ TNEF => '\x78\x9f\x3e\x22..\x01\x06\x90\x08\0',
1015
1018
  TXT => '(\xff\xfe|(\0\0)?\xfe\xff|(\xef\xbb\xbf)?[\x07-\x0d\x20-\x7e\x80-\xfe]*$)',
1016
1019
  TIFF => '(II|MM)', # don't test magic number (some raw formats are different)
1017
1020
  VCard=> '(?i)BEGIN:(VCARD|VCALENDAR|VNOTE)\r\n',
@@ -4292,6 +4295,7 @@ sub Init($)
4292
4295
  $$self{LOW_PRIORITY_DIR} = { PreviewIFD => 1 }; # names of priority 0 directories
4293
4296
  $$self{TIFF_TYPE} = ''; # type of TIFF data (APP1, TIFF, NEF, etc...)
4294
4297
  $$self{FMT_EXPR} = undef; # current advanced formatting expression
4298
+ $$self{HAS_DOC} = { }; # lookup for all document numbers in this file
4295
4299
  $$self{Make} = ''; # camera make
4296
4300
  $$self{Model} = ''; # camera model
4297
4301
  $$self{CameraType} = ''; # Olympus camera type
@@ -6582,6 +6586,8 @@ sub ConvertDateTime($$)
6582
6586
  }
6583
6587
  $a[5] -= 1900; # strftime year starts from 1900
6584
6588
  $date = POSIX::strftime($fmt, @a); # generate the formatted date/time
6589
+ # apparently strftime can set the UTF-8 flag (argh!), so reset this if necessary
6590
+ $self->Sanitize(\$date) if $fmt =~ /[\x80-\xff]/;
6585
6591
  } elsif ($$self{OPTIONS}{StrictDate}) {
6586
6592
  undef $date;
6587
6593
  }
@@ -9480,6 +9486,7 @@ sub FoundTag($$$;@)
9480
9486
  $$self{TAG_EXTRA}{$tag}{G1} = $grps[1] if $grps[1];
9481
9487
  if ($$self{DOC_NUM}) {
9482
9488
  $$self{TAG_EXTRA}{$tag}{G3} = $$self{DOC_NUM};
9489
+ $$self{HAS_DOC}{$$self{DOC_NUM}} = 1;
9483
9490
  if ($$self{DOC_NUM} =~ /^(\d+)/) {
9484
9491
  # keep track of maximum 1st-level sub-document number
9485
9492
  $$self{DOC_COUNT} = $1 unless $$self{DOC_COUNT} >= $1;
@@ -9719,8 +9726,10 @@ sub ExtractBinary($$$;$)
9719
9726
  $isPreview = 1;
9720
9727
  }
9721
9728
  my $lcTag = lc $tag;
9722
- if ((not $$self{OPTIONS}{Binary} or $$self{EXCL_TAG_LOOKUP}{$lcTag}) and
9723
- not $$self{OPTIONS}{Verbose} and not $$self{REQ_TAG_LOOKUP}{$lcTag})
9729
+ my $options = $$self{OPTIONS};
9730
+ if ((not $$options{Binary} or $$self{EXCL_TAG_LOOKUP}{$lcTag}) and
9731
+ not $$options{Verbose} and not $$options{Validate} and
9732
+ not $$self{REQ_TAG_LOOKUP}{$lcTag})
9724
9733
  {
9725
9734
  return "Binary data $length bytes";
9726
9735
  }
@@ -65,50 +65,51 @@ supported by ExifTool (r = read, w = write, c = create):
65
65
 
66
66
  File Types
67
67
  ------------+-------------+-------------+-------------+------------
68
- 360 r/w | DOCX r | ITC r | NUMBERS r | RAM r
69
- 3FR r | DPX r | J2C r | NXD r | RAR r
70
- 3G2 r/w | DR4 r/w/c | JNG r/w | O r | RAW r/w
71
- 3GP r/w | DSS r | JP2 r/w | ODP r | RIFF r
72
- 7Z r | DV r | JPEG r/w | ODS r | RSRC r
73
- A r | DVB r/w | JSON r | ODT r | RTF r
74
- AA r | DVR-MS r | JXL r/w | OFR r | RW2 r/w
75
- AAC r | DYLIB r | K25 r | OGG r | RWL r/w
76
- AAE r | EIP r | KDC r | OGV r | RWZ r
77
- AAX r/w | EPS r/w | KEY r | ONP r | RM r
78
- ACR r | EPUB r | LA r | OPUS r | SEQ r
79
- AFM r | ERF r/w | LFP r | ORF r/w | SKETCH r
80
- AI r/w | EXE r | LIF r | ORI r/w | SO r
81
- AIFF r | EXIF r/w/c | LNK r | OTF r | SR2 r/w
82
- APE r | EXR r | LRV r/w | PAC r | SRF r
83
- ARQ r/w | EXV r/w/c | M2TS r | PAGES r | SRW r/w
84
- ARW r/w | F4A/V r/w | M4A/V r/w | PBM r/w | SVG r
85
- ASF r | FFF r/w | MACOS r | PCAP r | SWF r
86
- AVI r | FITS r | MAX r | PCAPNG r | THM r/w
87
- AVIF r/w | FLA r | MEF r/w | PCD r | TIFF r/w
88
- AZW r | FLAC r | MIE r/w/c | PCX r | TORRENT r
89
- BMP r | FLIF r/w | MIFF r | PDB r | TTC r
90
- BPG r | FLV r | MKA r | PDF r/w | TTF r
91
- BTF r | FPF r | MKS r | PEF r/w | TXT r
92
- C2PA r | FPX r | MKV r | PFA r | VCF r
93
- CHM r | GIF r/w | MNG r/w | PFB r | VNT r
94
- COS r | GLV r/w | MOBI r | PFM r | VRD r/w/c
95
- CR2 r/w | GPR r/w | MODD r | PGF r | VSD r
96
- CR3 r/w | GZ r | MOI r | PGM r/w | WAV r
97
- CRM r/w | HDP r/w | MOS r/w | PLIST r | WDP r/w
98
- CRW r/w | HDR r | MOV r/w | PICT r | WEBP r/w
99
- CS1 r/w | HEIC r/w | MP3 r | PMP r | WEBM r
100
- CSV r | HEIF r/w | MP4 r/w | PNG r/w | WMA r
101
- CUR r | HTML r | MPC r | PPM r/w | WMV r
102
- CZI r | ICC r/w/c | MPG r | PPT r | WPG r
103
- DCM r | ICO r | MPO r/w | PPTX r | WTV r
104
- DCP r/w | ICS r | MQV r/w | PS r/w | WV r
105
- DCR r | IDML r | MRC r | PSB r/w | X3F r/w
106
- DFONT r | IIQ r/w | MRW r/w | PSD r/w | XCF r
107
- DIVX r | IND r/w | MXF r | PSP r | XISF r
108
- DJVU r | INSP r/w | NEF r/w | QTIF r/w | XLS r
109
- DLL r | INSV r | NKA r | R3D r | XLSX r
110
- DNG r/w | INX r | NKSC r/w | RA r | XMP r/w/c
111
- DOC r | ISO r | NRW r/w | RAF r/w | ZIP r
68
+ 360 r/w | DPX r | JNG r/w | ODP r | RSRC r
69
+ 3FR r | DR4 r/w/c | JP2 r/w | ODS r | RTF r
70
+ 3G2 r/w | DSS r | JPEG r/w | ODT r | RW2 r/w
71
+ 3GP r/w | DV r | JSON r | OFR r | RWL r/w
72
+ 7Z r | DVB r/w | JXL r/w | OGG r | RWZ r
73
+ A r | DVR-MS r | K25 r | OGV r | RM r
74
+ AA r | DYLIB r | KDC r | ONP r | SEQ r
75
+ AAC r | EIP r | KEY r | OPUS r | SKETCH r
76
+ AAE r | EPS r/w | LA r | ORF r/w | SO r
77
+ AAX r/w | EPUB r | LFP r | ORI r/w | SR2 r/w
78
+ ACR r | ERF r/w | LIF r | OTF r | SRF r
79
+ AFM r | EXE r | LNK r | PAC r | SRW r/w
80
+ AI r/w | EXIF r/w/c | LRV r/w | PAGES r | SVG r
81
+ AIFF r | EXR r | M2TS r | PBM r/w | SWF r
82
+ APE r | EXV r/w/c | M4A/V r/w | PCAP r | THM r/w
83
+ ARQ r/w | F4A/V r/w | MACOS r | PCAPNG r | TIFF r/w
84
+ ARW r/w | FFF r/w | MAX r | PCD r | TNEF r
85
+ ASF r | FITS r | MEF r/w | PCX r | TORRENT r
86
+ AVI r | FLA r | MIE r/w/c | PDB r | TTC r
87
+ AVIF r/w | FLAC r | MIFF r | PDF r/w | TTF r
88
+ AZW r | FLIF r/w | MKA r | PEF r/w | TXT r
89
+ BMP r | FLV r | MKS r | PFA r | VCF r
90
+ BPG r | FPF r | MKV r | PFB r | VNT r
91
+ BTF r | FPX r | MNG r/w | PFM r | VRD r/w/c
92
+ C2PA r | GIF r/w | MOBI r | PGF r | VSD r
93
+ CHM r | GLV r/w | MODD r | PGM r/w | WAV r
94
+ COS r | GPR r/w | MOI r | PLIST r | WDP r/w
95
+ CR2 r/w | GZ r | MOS r/w | PICT r | WEBP r/w
96
+ CR3 r/w | HDP r/w | MOV r/w | PMP r | WEBM r
97
+ CRM r/w | HDR r | MP3 r | PNG r/w | WMA r
98
+ CRW r/w | HEIC r/w | MP4 r/w | PPM r/w | WMV r
99
+ CS1 r/w | HEIF r/w | MPC r | PPT r | WPG r
100
+ CSV r | HTML r | MPG r | PPTX r | WTV r
101
+ CUR r | ICC r/w/c | MPO r/w | PS r/w | WV r
102
+ CZI r | ICO r | MQV r/w | PSB r/w | X3F r/w
103
+ DCM r | ICS r | MRC r | PSD r/w | XCF r
104
+ DCP r/w | IDML r | MRW r/w | PSP r | XISF r
105
+ DCR r | IIQ r/w | MXF r | QTIF r/w | XLS r
106
+ DFONT r | IND r/w | NEF r/w | R3D r | XLSX r
107
+ DIVX r | INSP r/w | NKA r | RA r | XMP r/w/c
108
+ DJVU r | INSV r | NKSC r/w | RAF r/w | ZIP r
109
+ DLL r | INX r | NRW r/w | RAM r |
110
+ DNG r/w | ISO r | NUMBERS r | RAR r |
111
+ DOC r | ITC r | NXD r | RAW r/w |
112
+ DOCX r | J2C r | O r | RIFF r |
112
113
 
113
114
  Meta Information
114
115
  ----------------------+----------------------+---------------------