exiftool_vendored 12.99.0 → 13.03.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +76 -3
  3. data/bin/META.json +1 -1
  4. data/bin/META.yml +1 -1
  5. data/bin/README +2 -2
  6. data/bin/arg_files/exif2xmp.args +4 -0
  7. data/bin/arg_files/xmp2exif.args +4 -0
  8. data/bin/exiftool +121 -50
  9. data/bin/lib/Image/ExifTool/Apple.pm +2 -2
  10. data/bin/lib/Image/ExifTool/CBOR.pm +4 -1
  11. data/bin/lib/Image/ExifTool/Canon.pm +35 -26
  12. data/bin/lib/Image/ExifTool/Exif.pm +15 -9
  13. data/bin/lib/Image/ExifTool/FlashPix.pm +5 -9
  14. data/bin/lib/Image/ExifTool/GIF.pm +143 -92
  15. data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  16. data/bin/lib/Image/ExifTool/Geotag.pm +6 -5
  17. data/bin/lib/Image/ExifTool/GoPro.pm +2 -2
  18. data/bin/lib/Image/ExifTool/JPEG.pm +9 -1
  19. data/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -2
  20. data/bin/lib/Image/ExifTool/LNK.pm +1 -1
  21. data/bin/lib/Image/ExifTool/M2TS.pm +2 -2
  22. data/bin/lib/Image/ExifTool/MIE.pm +9 -3
  23. data/bin/lib/Image/ExifTool/MacOS.pm +2 -1
  24. data/bin/lib/Image/ExifTool/Matroska.pm +10 -2
  25. data/bin/lib/Image/ExifTool/Nikon.pm +5 -2
  26. data/bin/lib/Image/ExifTool/PDF.pm +35 -4
  27. data/bin/lib/Image/ExifTool/PNG.pm +14 -3
  28. data/bin/lib/Image/ExifTool/PPM.pm +11 -2
  29. data/bin/lib/Image/ExifTool/PhaseOne.pm +2 -1
  30. data/bin/lib/Image/ExifTool/QuickTime.pm +6 -1
  31. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +69 -7
  32. data/bin/lib/Image/ExifTool/RIFF.pm +7 -2
  33. data/bin/lib/Image/ExifTool/TagLookup.pm +5596 -5582
  34. data/bin/lib/Image/ExifTool/TagNames.pod +75 -21
  35. data/bin/lib/Image/ExifTool/Text.pm +3 -2
  36. data/bin/lib/Image/ExifTool/Validate.pm +2 -2
  37. data/bin/lib/Image/ExifTool/WriteRIFF.pl +13 -4
  38. data/bin/lib/Image/ExifTool/Writer.pl +42 -66
  39. data/bin/lib/Image/ExifTool/XMP.pm +19 -4
  40. data/bin/lib/Image/ExifTool/XMP2.pl +60 -0
  41. data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -2
  42. data/bin/lib/Image/ExifTool.pm +204 -86
  43. data/bin/lib/Image/ExifTool.pod +58 -31
  44. data/bin/perl-Image-ExifTool.spec +1 -1
  45. data/lib/exiftool_vendored/version.rb +1 -1
  46. metadata +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 $advFmtSelf);
31
31
 
32
- $VERSION = '12.99';
32
+ $VERSION = '13.03';
33
33
  $RELEASE = '';
34
34
  @ISA = qw(Exporter);
35
35
  %EXPORT_TAGS = (
@@ -126,6 +126,7 @@ sub MakeTiffHeader($$$$;$$);
126
126
  # other subroutine definitions
127
127
  sub SplitFileName($);
128
128
  sub EncodeFileName($$;$);
129
+ sub WindowsLongPath($$);
129
130
  sub Open($*$;$);
130
131
  sub Exists($$;$);
131
132
  sub IsDirectory($$);
@@ -740,7 +741,7 @@ my %fileDescription = (
740
741
  OGV => 'video/ogg',
741
742
  ONP => 'application/on1',
742
743
  ORF => 'image/x-olympus-orf',
743
- OTF => 'application/x-font-otf',
744
+ OTF => 'application/font-otf',
744
745
  PAGES=> 'application/x-iwork-pages-sffpages',
745
746
  PBM => 'image/x-portable-bitmap',
746
747
  PCD => 'image/x-photo-cd',
@@ -798,8 +799,8 @@ my %fileDescription = (
798
799
  THMX => 'application/vnd.ms-officetheme',
799
800
  TIFF => 'image/tiff',
800
801
  Torrent => 'application/x-bittorrent',
801
- TTC => 'application/x-font-ttf',
802
- TTF => 'application/x-font-ttf',
802
+ TTC => 'application/font-ttf',
803
+ TTF => 'application/font-ttf',
803
804
  TXT => 'text/plain',
804
805
  VCard=> 'text/vcard',
805
806
  VRD => 'application/octet-stream', #PH (NC)
@@ -1081,6 +1082,7 @@ my %xmpShorthandOpt = ( 0 => 'None', 1 => 'Shorthand', 2 => ['Shorthand','OneDes
1081
1082
  # +-----------------------------------------------------+
1082
1083
  # (Note: All options must exist in this lookup, even if undefined,
1083
1084
  # to facilitate case-insensitive options. 'Group#' is handled specially)
1085
+ # (item 3 is a flag indicating the option is undocumented)
1084
1086
  my @availableOptions = (
1085
1087
  [ 'Binary', undef, 'flag to extract binary values even if tag not specified' ],
1086
1088
  [ 'ByteOrder', undef, 'default byte order when creating EXIF information' ],
@@ -1098,7 +1100,12 @@ my @availableOptions = (
1098
1100
  [ 'Compress', undef, 'flag to write new values as compressed if possible' ],
1099
1101
  [ 'CoordFormat', undef, 'GPS lat/long coordinate format' ],
1100
1102
  [ 'DateFormat', undef, 'format for date/time' ],
1103
+ [ 'Debug', undef, 'enable debugging output', 1 ], # (undocumented)
1101
1104
  [ 'Duplicates', 1, 'flag to save duplicate tag values' ],
1105
+ # ("require Encode" hangs on my Windows 10 virtual machine running under MacOS if
1106
+ # the current working directory has a long path name. This problem hasn't been
1107
+ # seen on other Windows systems, so I'm leaving this option undocumented for now)
1108
+ [ 'EncodeHangs', undef, 'flag set to avoid using Encode if it hangs on your system', 1 ], # (undocumented)
1102
1109
  [ 'Escape', undef, 'escape special characters' ],
1103
1110
  [ 'Exclude', undef, 'tags to exclude' ],
1104
1111
  [ 'ExtendedXMP', 1, 'strategy for reading extended XMP' ],
@@ -1130,10 +1137,10 @@ my @availableOptions = (
1130
1137
  [ 'Lang', $defaultLang, 'localized language for descriptions etc' ],
1131
1138
  [ 'LargeFileSupport', 1, 'flag indicating support of 64-bit file offsets' ],
1132
1139
  [ 'LimitLongValues', 60, 'length limit for long values' ],
1133
- [ 'List', undef, '[deprecated, use ListSplit and ListJoin instead]' ],
1140
+ [ 'List', undef, '[deprecated, use ListSplit and ListJoin instead]', 1 ],
1134
1141
  [ 'ListItem', undef, 'used to return a specific item from lists' ],
1135
1142
  [ 'ListJoin', ', ', 'join lists together with this separator' ],
1136
- [ 'ListSep', ', ', '[deprecated, use ListSplit and ListJoin instead]' ],
1143
+ [ 'ListSep', ', ', '[deprecated, use ListSplit and ListJoin instead]', 1 ],
1137
1144
  [ 'ListSplit', undef, 'regex for splitting list-type tag values when writing' ],
1138
1145
  [ 'MakerNotes', undef, 'extract maker notes as a block' ],
1139
1146
  [ 'MDItemTags', undef, 'extract MacOS metadata item tags' ],
@@ -1150,6 +1157,7 @@ my @availableOptions = (
1150
1157
  [ 'QuickTimeUTC', undef, 'assume that QuickTime date/time tags are stored as UTC' ],
1151
1158
  [ 'RequestAll', undef, 'extract all tags that must be specifically requested' ],
1152
1159
  [ 'RequestTags', undef, 'extra tags to request (on top of those in the tag list)' ],
1160
+ [ 'SaveBin', undef, 'save binary values of tags' ],
1153
1161
  [ 'SaveFormat', undef, 'save family 6 tag TIFF format' ],
1154
1162
  [ 'SavePath', undef, 'save family 5 location path' ],
1155
1163
  [ 'ScanForXMP', undef, 'flag to scan for XMP information in all files' ],
@@ -1165,11 +1173,12 @@ my @availableOptions = (
1165
1173
  [ 'UserParam', { }, 'user parameters for additional user-defined tag values' ],
1166
1174
  [ 'Validate', undef, 'perform additional validation' ],
1167
1175
  [ 'Verbose', 0, 'print verbose messages (0-5, higher # = more verbose)' ],
1176
+ [ 'WindowsLongPath', undef, 'enable support for long pathnames (enables WindowsWideFile)' ],
1168
1177
  [ 'WindowsWideFile', undef, 'force the use of Windows wide-character file routines' ], # (see forum15208)
1169
1178
  [ 'WriteMode', 'wcg', 'enable all write modes by default' ],
1170
1179
  [ 'XAttrTags', undef, 'extract MacOS extended attribute tags' ],
1171
1180
  [ 'XMPAutoConv', 1, 'automatic conversion of unknown XMP tag values' ],
1172
- [ 'XMPShorthand', 0, '[deprecated, use Compact=Shorthand instead]' ],
1181
+ [ 'XMPShorthand', 0, '[deprecated, use Compact=Shorthand instead]', 1 ],
1173
1182
  );
1174
1183
 
1175
1184
  # default family 0 group priority for writing
@@ -3328,8 +3337,8 @@ sub GetRequestedTags($)
3328
3337
  # Inputs: 0) ExifTool object reference
3329
3338
  # 1) tag key or tag name with optional group names (case sensitive)
3330
3339
  # (or flattened tagInfo for getting field values, not part of public API)
3331
- # 2) [optional] Value type: PrintConv, ValueConv, Both, Raw or Rational, the default
3332
- # is PrintConv or ValueConv, depending on the PrintConv option setting
3340
+ # 2) [optional] Value type: PrintConv, ValueConv, Both, Raw, Bin or Rational, the
3341
+ # default is PrintConv or ValueConv, depending on the PrintConv option setting
3333
3342
  # 3) raw field value (not part of public API)
3334
3343
  # Returns: Scalar context: tag value or undefined
3335
3344
  # List context: list of values or empty list
@@ -3358,7 +3367,8 @@ sub GetValue($$;$)
3358
3367
  }
3359
3368
  # figure out what conversions to do
3360
3369
  if ($type) {
3361
- return $$self{RATIONAL}{$tag} if $type eq 'Rational';
3370
+ return $$self{TAG_EXTRA}{$tag}{Rational} if $type eq 'Rational';
3371
+ return $$self{TAG_EXTRA}{$tag}{BinVal} if $type eq 'Bin';
3362
3372
  } else {
3363
3373
  $type = $$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv';
3364
3374
  }
@@ -3702,9 +3712,10 @@ sub GetGroup($$;$)
3702
3712
  $tag = $$tagInfo{Name};
3703
3713
  # set flag so we don't get extra information for an extracted tag
3704
3714
  $byTagInfo = 1;
3715
+ $ex = { };
3705
3716
  } else {
3706
3717
  $tagInfo = $$self{TAG_INFO}{$tag} || { };
3707
- $ex = $$self{TAG_EXTRA}{$tag};
3718
+ $ex = $$self{TAG_EXTRA}{$tag} || { };
3708
3719
  }
3709
3720
  my $groups = $$tagInfo{Groups};
3710
3721
  # fill in default groups unless already done
@@ -3723,32 +3734,30 @@ sub GetGroup($$;$)
3723
3734
  if (defined $family and $family ne '-1') {
3724
3735
  if ($family =~ /[^\d]/) {
3725
3736
  @families = ($family =~ /\d+/g);
3726
- return(($ex && $$ex{G0}) || $$groups{0}) unless @families;
3737
+ return($$ex{G0} || $$groups{0}) unless @families;
3727
3738
  $simplify = 1 unless $family =~ /^:/;
3728
3739
  undef $family;
3729
3740
  foreach (0..2) { $groups[$_] = $$groups{$_}; }
3730
3741
  $noID = 1 if @families == 1 and $families[0] != 7;
3731
3742
  } else {
3732
- return(($ex && $$ex{"G$family"}) || $$groups{$family}) if $family == 0 or $family == 2;
3743
+ return($$ex{"G$family"} || $$groups{$family}) if $family == 0 or $family == 2;
3733
3744
  $groups[1] = $$groups{1};
3734
3745
  }
3735
3746
  } else {
3736
- return(($ex && $$ex{G0}) || $$groups{0}) unless wantarray;
3747
+ return($$ex{G0} || $$groups{0}) unless wantarray;
3737
3748
  foreach (0..2) { $groups[$_] = $$groups{$_}; }
3738
3749
  }
3739
3750
  $groups[3] = 'Main';
3740
- $groups[4] = ($tag =~ /\((\d+)\)$/) ? "Copy$1" : '';
3751
+ $groups[4] = ($tag =~ /\((\d+)\)$/ and $1 ne '0') ? "Copy$1" : '';
3741
3752
  # handle dynamic group names if necessary
3742
3753
  unless ($byTagInfo) {
3743
- if ($ex) {
3744
- $groups[0] = $$ex{G0} if $$ex{G0};
3745
- $groups[1] = $$ex{G1} =~ /^\+(.*)/ ? "$groups[1]$1" : $$ex{G1} if $$ex{G1};
3746
- $groups[3] = 'Doc' . $$ex{G3} if $$ex{G3};
3747
- $groups[5] = $$ex{G5} || $groups[1] if defined $$ex{G5};
3748
- if (defined $$ex{G6}) {
3749
- $groups[5] = '' unless defined $groups[5]; # (can't leave a hole in the array)
3750
- $groups[6] = $$ex{G6};
3751
- }
3754
+ $groups[0] = $$ex{G0} if $$ex{G0};
3755
+ $groups[1] = $$ex{G1} =~ /^\+(.*)/ ? "$groups[1]$1" : $$ex{G1} if $$ex{G1};
3756
+ $groups[3] = 'Doc' . $$ex{G3} if $$ex{G3};
3757
+ $groups[5] = $$ex{G5} || $groups[1] if defined $$ex{G5};
3758
+ if (defined $$ex{G6}) {
3759
+ $groups[5] = '' unless defined $groups[5]; # (can't leave a hole in the array)
3760
+ $groups[6] = $$ex{G6};
3752
3761
  }
3753
3762
  if ($$ex{G8}) {
3754
3763
  $groups[7] = '';
@@ -3921,12 +3930,9 @@ COMPOSITE_TAG:
3921
3930
  $key = "$reqTag ($i)";
3922
3931
  }
3923
3932
  @keys = $self->GroupMatches($reqGroup, \@keys) if defined $reqGroup;
3924
- if (@keys) {
3925
- my $ex = $$self{TAG_EXTRA};
3926
- # loop through tags in reverse order of precedence so the higher
3927
- # priority tag will win in the case of duplicates within a doc
3928
- $$cacheTag[$$ex{$_} ? $$ex{$_}{G3} || 0 : 0] = $_ foreach reverse @keys;
3929
- }
3933
+ # loop through tags in reverse order of precedence so the higher
3934
+ # priority tag will win in the case of duplicates within a doc
3935
+ $$cacheTag[$$self{TAG_EXTRA}{$_}{G3} || 0] = $_ foreach reverse @keys;
3930
3936
  }
3931
3937
  # (set $reqTag to a bogus key if not found)
3932
3938
  $reqTag = $$cacheTag[$doc] || "$reqTag (0)";
@@ -4218,9 +4224,7 @@ sub Init($)
4218
4224
  local $_;
4219
4225
  my $self = shift;
4220
4226
  # delete all DataMember variables (lower-case names)
4221
- foreach (keys %$self) {
4222
- /[a-z]/ and delete $$self{$_};
4223
- }
4227
+ delete $$self{$_} foreach grep /[a-z]/, keys %$self;
4224
4228
  undef %static_vars; # clear all static variables
4225
4229
  delete $$self{FOUND_TAGS}; # list of found tags
4226
4230
  delete $$self{EXIF_DATA}; # the EXIF data block
@@ -4235,7 +4239,6 @@ sub Init($)
4235
4239
  $$self{FILE_ORDER} = { }; # * hash of tag order in file ('*' = based on tag key)
4236
4240
  $$self{VALUE} = { }; # * hash of raw tag values
4237
4241
  $$self{BOTH} = { }; # * hash for Value/PrintConv values of Require'd tags
4238
- $$self{RATIONAL} = { }; # * hash of original rational components
4239
4242
  $$self{TAG_INFO} = { }; # * hash of tag information
4240
4243
  $$self{TAG_EXTRA} = { }; # * hash of extra tag information (dynamic group names)
4241
4244
  $$self{PRIORITY} = { }; # * priority of current tags
@@ -4468,11 +4471,7 @@ sub DoneExtract($)
4468
4471
  my $err = $$altExifTool{VALUE}{Error};
4469
4472
  $err and $self->Warn(qq{$err "$fileName"});
4470
4473
  # set family 8 group name for all tags
4471
- foreach (keys %{$$altExifTool{VALUE}}) {
4472
- my $ex = $$altExifTool{TAG_EXTRA}{$_};
4473
- $ex or $ex = $$altExifTool{TAG_EXTRA}{$_} = { };
4474
- $$ex{G8} = $g8;
4475
- }
4474
+ $$altExifTool{TAG_EXTRA}{$_}{G8} = $g8 foreach keys %{$$altExifTool{VALUE}};
4476
4475
  # prepare our sorted list of found tags
4477
4476
  $$altExifTool{FoundTags} = [ reverse sort keys %{$$altExifTool{VALUE}} ];
4478
4477
  $$altExifTool{DID_EXTRACT} = 1;
@@ -4614,36 +4613,107 @@ sub SplitFileName($)
4614
4613
 
4615
4614
  #------------------------------------------------------------------------------
4616
4615
  # Encode file name for calls to system i/o routines
4617
- # Inputs: 0) ExifTool ref, 1) file name in CharSetFileName, 2) flag to force conversion
4616
+ # Inputs: 0) ExifTool ref, 1) file name in CharsetFileName encoding, 2) flag to force conversion
4618
4617
  # Returns: true if Windows Unicode routines should be used (in which case
4619
4618
  # the file name will be encoded as a null-terminated UTF-16LE string)
4620
4619
  sub EncodeFileName($$;$)
4621
4620
  {
4622
4621
  my ($self, $file, $force) = @_;
4623
4622
  my $enc = $$self{OPTIONS}{CharsetFileName};
4624
- $force = 1 if $$self{OPTIONS}{WindowsWideFile};
4625
- if ($enc) {
4626
- if ($file =~ /[\x80-\xff]/ or $force) {
4627
- # encode for use in Windows Unicode functions if necessary
4628
- if ($^O eq 'MSWin32') {
4629
- local $SIG{'__WARN__'} = \&SetWarning;
4630
- if (eval { require Win32API::File }) {
4631
- # recode as UTF-16LE and add null terminator
4632
- $_[1] = $self->Decode($file, $enc, undef, 'UTF16', 'II') . "\0\0";
4633
- return 1;
4634
- }
4635
- $self->WarnOnce('Install Win32API::File for Windows Unicode file support');
4623
+ my $hasSpecialChars;
4624
+ if ($file =~ /[\x80-\xff]/) {
4625
+ $hasSpecialChars = 1;
4626
+ if (not $enc and $^O eq 'MSWin32') {
4627
+ if (IsUTF8(\$file) < 0) {
4628
+ $self->WarnOnce('FileName encoding must be specified') if not defined $enc;
4629
+ return 0;
4636
4630
  } else {
4637
- # recode as UTF-8 for other platforms if necessary
4638
- $_[1] = $self->Decode($file, $enc, undef, 'UTF8') unless $enc eq 'UTF8';
4631
+ $enc = 'UTF8'; # assume UTF8
4632
+ }
4633
+ }
4634
+ }
4635
+ $force = 1 if $$self{OPTIONS}{WindowsWideFile} or $$self{OPTIONS}{WindowsLongPath};
4636
+ if ($hasSpecialChars or $force) {
4637
+ $enc or $enc = 'UTF8';
4638
+ if ($^O eq 'MSWin32') {
4639
+ local $SIG{'__WARN__'} = \&SetWarning;
4640
+ if (eval { require Win32API::File }) {
4641
+ $file = $self->WindowsLongPath($file) if $$self{OPTIONS}{WindowsLongPath};
4642
+ # recode as UTF-16LE and add null terminator
4643
+ $_[1] = $self->Decode($file, $enc, undef, 'UTF16', 'II') . "\0\0";
4644
+ return 1;
4639
4645
  }
4646
+ $self->WarnOnce('Install Win32API::File for Windows wide/long file name support');
4647
+ } elsif ($enc ne 'UTF8') {
4648
+ # recode as UTF-8 for other platforms if necessary
4649
+ $_[1] = $self->Decode($file, $enc, undef, 'UTF8');
4640
4650
  }
4641
- } elsif ($^O eq 'MSWin32' and $file =~ /[\x80-\xff]/ and not defined $enc) {
4642
- $self->WarnOnce('FileName encoding not specified') if IsUTF8(\$file) < 0;
4643
4651
  }
4644
4652
  return 0;
4645
4653
  }
4646
4654
 
4655
+ #------------------------------------------------------------------------------
4656
+ # Rebuild a path as an absolute long path to be usable in Windows system calls
4657
+ # Inputs: 0) ExifTool ref, 1) path string
4658
+ # Returns: normalized long path
4659
+ # Note: this should only be called for Windows systems
4660
+ # References:
4661
+ # - https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
4662
+ # - https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
4663
+ # GetFullPathName supported by Windows XP and later. It handles:
4664
+ # full path names EG: c:\foto\sub\abc.jpg
4665
+ # relative EG: .\abc.jpg, ..\abc.jpg
4666
+ # full UNC paths EG: \\server\share\abc.jpg
4667
+ # relative UNC paths EG: .\abc.jpg, ..\abc.jpg
4668
+ # Dos device paths EG: \\.\c:\fotoabc.jpg
4669
+ # relative path on other drives EG: z:abc.jpg (working dir on z: z:\foto called from c:\foto)
4670
+ # Wide chars EG: Chars that need UTF8.
4671
+ my $k32GetFullPathName;
4672
+ sub WindowsLongPath($$)
4673
+ {
4674
+ my ($self, $path) = @_;
4675
+ my $debug = $$self{OPTIONS}{Debug};
4676
+ my $out = $$self{OPTIONS}{TextOut};
4677
+
4678
+ $debug and print $out "WindowsLongPath input : $path\n";
4679
+
4680
+ for (;;) { # (cheap goto)
4681
+ $path =~ tr(/)(\\); # convert slashes to backslashes
4682
+ last if $path =~ /^\\\\\?\\/; # already a device path in the format we want
4683
+
4684
+ unless ($k32GetFullPathName) { # need to import (once) GetFullPathNameW
4685
+ last if defined $k32GetFullPathName;
4686
+ unless (eval { require Win32::API }) {
4687
+ $self->WarnOnce('Install Win32::API to use WindowsLongPath option');
4688
+ last;
4689
+ }
4690
+ $k32GetFullPathName = Win32::API->new('KERNEL32', 'GetFullPathNameW', 'PNPP', 'I');
4691
+ unless ($k32GetFullPathName) {
4692
+ $k32GetFullPathName = 0;
4693
+ $self->Warn('Error loading Win32::API GetFullPathNameW');
4694
+ last;
4695
+ }
4696
+ }
4697
+ my $enc = $$self{OPTIONS}{CharsetFileName};
4698
+ my $encPath = $self->Encode($path, 'UTF16', 'II', $enc); # need to encode to UTF16
4699
+ my $lenReq = $k32GetFullPathName->Call($encPath,0,0,0) + 1; # first pass gets length required, +1 for safety (null?)
4700
+ my $fullPath = "\0" x $lenReq x 2; # create buffer to hold full path
4701
+ $k32GetFullPathName->Call($encPath, $lenReq, $fullPath, 0); # fullPath is UTF16 now
4702
+ $path = $self->Decode($fullPath, 'UTF16', 'II', $enc);
4703
+
4704
+ last if length($path) <= 247;
4705
+
4706
+ if ($path =~ /^\\\\/) {
4707
+ $path = '\\\\?\\UNC' . substr($path, 1);
4708
+ } else {
4709
+ $path = '\\\\?\\' . $path;
4710
+ }
4711
+ last;
4712
+ }
4713
+ $debug and print $out "WindowsLongPath return: $path\n";
4714
+ return $path;
4715
+ }
4716
+
4647
4717
  #------------------------------------------------------------------------------
4648
4718
  # Modified perl open() routine to properly handle special characters in file names
4649
4719
  # Inputs: 0) ExifTool ref, 1) filehandle, 2) filename,
@@ -4773,16 +4843,16 @@ sub CreateDirectory($$)
4773
4843
  my $d2 = $dir; # (must make a copy in case EncodeFileName recodes it)
4774
4844
  if ($self->EncodeFileName($d2)) {
4775
4845
  # handle Windows Unicode directory names
4776
- unless (eval { require Win32::API }) {
4777
- $err = 'Install Win32::API to create directories with Unicode names';
4778
- last;
4779
- }
4780
4846
  unless (defined $k32CreateDir) {
4847
+ unless (eval { require Win32::API }) {
4848
+ $err = 'Install Win32::API to create directories with Unicode names';
4849
+ last;
4850
+ }
4781
4851
  $k32CreateDir = Win32::API->new('KERNEL32', 'CreateDirectoryW', 'PP', 'I');
4782
4852
  unless ($k32CreateDir) {
4783
4853
  $k32CreateDir = 0;
4784
4854
  # give this error once, then just "Error creating" for subsequent attempts
4785
- return 'Error accessing Win32::API::CreateDirectoryW';
4855
+ return 'Error loading Win32::API CreateDirectoryW';
4786
4856
  }
4787
4857
  }
4788
4858
  $success = $k32CreateDir->Call($d2, 0) if $k32CreateDir;
@@ -4840,13 +4910,13 @@ sub GetFileTime($$)
4840
4910
  return () if defined $k32GetFileTime;
4841
4911
  $k32GetFileTime = Win32::API->new('KERNEL32', 'GetFileTime', 'NPPP', 'I');
4842
4912
  unless ($k32GetFileTime) {
4843
- $self->Warn('Error calling Win32::API::GetFileTime');
4913
+ $self->Warn('Error loading Win32::API GetFileTime');
4844
4914
  $k32GetFileTime = 0;
4845
4915
  return ();
4846
4916
  }
4847
4917
  }
4848
4918
  unless ($k32GetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) {
4849
- $self->Warn("Win32::API::GetFileTime returned " . Win32::GetLastError());
4919
+ $self->Warn("Win32::API GetFileTime returned " . Win32::GetLastError());
4850
4920
  return ();
4851
4921
  }
4852
4922
  # convert FILETIME structs to Unix seconds
@@ -4910,11 +4980,13 @@ sub ParseArguments($;@)
4910
4980
  next if defined $$self{RAF};
4911
4981
  # convert image data from UTF-8 to character stream if necessary
4912
4982
  # (patches RHEL 3 UTF8 LANG problem)
4913
- if (ref $arg eq 'SCALAR' and $] >= 5.006 and
4914
- (eval { require Encode; Encode::is_utf8($$arg) } or $@))
4983
+ if (ref $arg eq 'SCALAR' and $] >= 5.006 and ($$self{OPTIONS}{EncodeHangs} or
4984
+ eval { require Encode; Encode::is_utf8($$arg) } or $@))
4915
4985
  {
4986
+ local $SIG{'__WARN__'} = \&SetWarning;
4916
4987
  # repack by hand if Encode isn't available
4917
- my $buff = $@ ? pack('C*',unpack($] < 5.010000 ? 'U0C*' : 'C0C*',$$arg)) : Encode::encode('utf8',$$arg);
4988
+ my $buff = ($$self{OPTIONS}{EncodeHangs} or $@) ? pack('C*',unpack($] < 5.010000 ?
4989
+ 'U0C*' : 'C0C*', $$arg)) : Encode::encode('utf8', $$arg);
4918
4990
  $arg = \$buff;
4919
4991
  }
4920
4992
  $$self{RAF} = File::RandomAccess->new($arg);
@@ -4995,7 +5067,7 @@ sub IsSameID($$)
4995
5067
 
4996
5068
  #------------------------------------------------------------------------------
4997
5069
  # Get list of tags in specified group
4998
- # Inputs: 0) ExifTool ref, 1) group spec, 2) tag key or reference to list of tag keys
5070
+ # Inputs: 0) ExifTool ref, 1) group spec (case insensitive), 2) tag key or reference to list of tag keys
4999
5071
  # Returns: list of matching tags in list context, or first match in scalar context
5000
5072
  # Notes: Group spec may contain multiple groups separated by colons, each
5001
5073
  # possibly with a leading family number
@@ -6322,10 +6394,12 @@ sub Filter($$$)
6322
6394
  # Return printable value
6323
6395
  # Inputs: 0) ExifTool object reference
6324
6396
  # 1) value to print, 2) line length limit (undef defaults to 60, 0=unlimited)
6397
+ # Returns: Printable string
6325
6398
  sub Printable($;$)
6326
6399
  {
6327
6400
  my ($self, $outStr, $maxLen) = @_;
6328
6401
  return '(undef)' unless defined $outStr;
6402
+ ref $outStr eq 'SCALAR' and return '(Binary data '.length($$outStr).' bytes)';
6329
6403
  $outStr =~ tr/\x01-\x1f\x7f-\xff/./;
6330
6404
  $outStr =~ s/\x00//g;
6331
6405
  my $verbose = $$self{OPTIONS}{Verbose};
@@ -6358,9 +6432,45 @@ sub ConvertDateTime($$)
6358
6432
  my $fmt = $$self{OPTIONS}{DateFormat};
6359
6433
  my $shift = $$self{OPTIONS}{GlobalTimeShift};
6360
6434
  if ($shift) {
6361
- my $dir = ($shift =~ s/^([-+])// and $1 eq '-') ? -1 : 1;
6362
6435
  my $offset = $$self{GLOBAL_TIME_OFFSET};
6363
- $offset or $offset = $$self{GLOBAL_TIME_OFFSET} = { };
6436
+ my ($g, $t, $dir, @matches);
6437
+ if ($shift =~ s/^((\d?[A-Z][-\w]*\w:)*)([A-Z][-\w]*\w)([-+])//i) {
6438
+ ($g, $t, $dir) = ($1, $3, ($4 eq '-' ? -1 : 1));
6439
+ } else {
6440
+ $dir = ($shift =~ s/^([-+])// and $1 eq '-') ? -1 : 1;
6441
+ }
6442
+ unless ($offset) {
6443
+ $offset = $$self{GLOBAL_TIME_OFFSET} = { };
6444
+ # (see forum16692 for a discussion about why this code was added)
6445
+ if ($t) {
6446
+ # determine initial shift from specified tag
6447
+ @matches = sort grep(/^$t( \(|$)/i, keys %{$$self{VALUE}});
6448
+ if ($g and @matches) {
6449
+ $g =~ s/:$//;
6450
+ @matches = $self->GroupMatches($g, \@matches);
6451
+ }
6452
+ }
6453
+ if (not @matches and $$self{TAGS_FROM_FILE} and $$self{OPTIONS}{RequestTags}) {
6454
+ # determine initial shift from first requested date/time tag
6455
+ my @reqDate = grep /date/i, @{$$self{OPTIONS}{RequestTags}};
6456
+ while (@reqDate) {
6457
+ $t = shift @reqDate;
6458
+ @matches = sort grep(/^$t( \(|$)/i, keys %{$$self{VALUE}});
6459
+ my $ti = $$self{TAG_INFO};
6460
+ for (; @matches; shift @matches) {
6461
+ # select the first tag that calls this routine in its PrintConv
6462
+ next unless $$ti{$matches[0]}{PrintConv};
6463
+ next unless $$ti{$matches[0]}{PrintConv} =~ /ConvertDateTime/;
6464
+ undef @reqDate;
6465
+ last;
6466
+ }
6467
+ }
6468
+ }
6469
+ if (@matches) {
6470
+ my $val = $self->GetValue($matches[0], 'ValueConv');
6471
+ ShiftTime($val, $shift, $dir, $offset) if defined $val;
6472
+ }
6473
+ }
6364
6474
  ShiftTime($date, $shift, $dir, $offset);
6365
6475
  }
6366
6476
  # only convert date if a format was specified and the date is recognizable
@@ -7860,6 +7970,10 @@ sub ProcessJPEG($$;$)
7860
7970
  SetByteOrder('II');
7861
7971
  my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Isothermal');
7862
7972
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7973
+ } elsif ($$segDataPt =~ /^SEAL\0/) {
7974
+ $dumpType = 'SEAL';
7975
+ DirStart(\%dirInfo, 5);
7976
+ $self->ProcessDirectory(\%dirInfo, GetTagTable("Image::ExifTool::XMP::SEAL"));
7863
7977
  }
7864
7978
  } elsif ($marker == 0xe9) { # APP9 (InfiRay, Media Jukebox)
7865
7979
  if ($$segDataPt =~ /^Media Jukebox\0/ and $length > 22) {
@@ -7875,6 +7989,10 @@ sub ProcessJPEG($$;$)
7875
7989
  SetByteOrder('II');
7876
7990
  my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Sensor');
7877
7991
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7992
+ } elsif ($$segDataPt =~ /^SEAL\0/) {
7993
+ $dumpType = 'SEAL';
7994
+ DirStart(\%dirInfo, 5);
7995
+ $self->ProcessDirectory(\%dirInfo, GetTagTable("Image::ExifTool::XMP::SEAL"));
7878
7996
  }
7879
7997
  } elsif ($marker == 0xea) { # APP10 (PhotoStudio Unicode comments)
7880
7998
  if ($$segDataPt =~ /^UNICODE\0/) {
@@ -8924,7 +9042,7 @@ sub HandleTag($$$$;%)
8924
9042
  my $pfmt = $parms{Format};
8925
9043
  my $tagInfo = $parms{TagInfo} || $self->GetTagInfo($tagTablePtr, $tag, \$val, $pfmt, $parms{Count});
8926
9044
  my $dataPt = $parms{DataPt};
8927
- my ($subdir, $format, $noTagInfo, $rational);
9045
+ my ($subdir, $format, $noTagInfo, $rational, $binVal);
8928
9046
 
8929
9047
  if ($tagInfo) {
8930
9048
  $subdir = $$tagInfo{SubDirectory};
@@ -8948,6 +9066,7 @@ sub HandleTag($$$$;%)
8948
9066
  } else {
8949
9067
  $val = substr($$dataPt, $start, $size);
8950
9068
  }
9069
+ $binVal = substr($$dataPt, $start, $size) if $$self{OPTIONS}{SaveBin};
8951
9070
  } else {
8952
9071
  $self->Warn("Error extracting value for $$tagInfo{Name}");
8953
9072
  return undef;
@@ -9009,6 +9128,7 @@ sub HandleTag($$$$;%)
9009
9128
  Base => $parms{Base},
9010
9129
  Multi => $$subdir{Multi},
9011
9130
  TagInfo => $tagInfo,
9131
+ IgnoreProp => $$subdir{IgnoreProp},
9012
9132
  RAF => $parms{RAF},
9013
9133
  );
9014
9134
  my $oldOrder = GetByteOrder();
@@ -9030,8 +9150,11 @@ sub HandleTag($$$$;%)
9030
9150
  return undef unless $$tagInfo{Writable};
9031
9151
  }
9032
9152
  my $key = $self->FoundTag($tagInfo, $val);
9033
- # save original components of rational numbers
9034
- $$self{RATIONAL}{$key} = $rational if defined $rational and defined $key;
9153
+ if (defined $key) {
9154
+ # save original components of rational numbers and original binary value
9155
+ $$self{TAG_EXTRA}{$key}{Rational} = $rational if defined $rational;
9156
+ $$self{TAG_EXTRA}{$key}{BinVal} = $binVal if defined $binVal;
9157
+ }
9035
9158
  return $key;
9036
9159
  }
9037
9160
  return undef;
@@ -9142,9 +9265,7 @@ sub FoundTag($$$;@)
9142
9265
  # a Warning tag because they may be added by ValueConv, which could be confusing)
9143
9266
  my $oldPriority = $$self{PRIORITY}{$tag};
9144
9267
  unless ($oldPriority) {
9145
- if ($$self{DOC_NUM} or not $$self{TAG_EXTRA}{$tag} or $tag eq 'Warning' or
9146
- not $$self{TAG_EXTRA}{$tag}{G3})
9147
- {
9268
+ if ($$self{DOC_NUM} or $tag eq 'Warning' or not $$self{TAG_EXTRA}{$tag}{G3}) {
9148
9269
  $oldPriority = 1;
9149
9270
  } else {
9150
9271
  $oldPriority = 0; # don't promote sub-document tag over main document
@@ -9162,8 +9283,7 @@ sub FoundTag($$$;@)
9162
9283
  } else {
9163
9284
  $priority = 1; # the normal default
9164
9285
  }
9165
- if ($priority >= $oldPriority and (not $$self{DOC_NUM} or
9166
- ($$self{TAG_EXTRA}{$tag} and $$self{TAG_EXTRA}{$tag}{G3} and
9286
+ if ($priority >= $oldPriority and (not $$self{DOC_NUM} or ($$self{TAG_EXTRA}{$tag}{G3} and
9167
9287
  $$self{DOC_NUM} eq $$self{TAG_EXTRA}{$tag}{G3})) and not $noListDel)
9168
9288
  {
9169
9289
  # move existing tag out of the way since this tag is higher priority
@@ -9172,12 +9292,8 @@ sub FoundTag($$$;@)
9172
9292
  $$valueHash{$nextTag} = $$valueHash{$tag};
9173
9293
  $$self{FILE_ORDER}{$nextTag} = $$self{FILE_ORDER}{$tag};
9174
9294
  my $oldInfo = $$self{TAG_INFO}{$nextTag} = $$self{TAG_INFO}{$tag};
9175
- foreach ('TAG_EXTRA','RATIONAL') {
9176
- if ($$self{$_}{$tag}) {
9177
- $$self{$_}{$nextTag} = $$self{$_}{$tag};
9178
- delete $$self{$_}{$tag};
9179
- }
9180
- }
9295
+ $$self{TAG_EXTRA}{$nextTag} = $$self{TAG_EXTRA}{$tag};
9296
+ $$self{TAG_EXTRA}{$tag} = { };
9181
9297
  delete $$self{BOTH}{$tag};
9182
9298
  # update tag key for list if necessary
9183
9299
  $$self{LIST_TAGS}{$oldInfo} = $nextTag if $$self{LIST_TAGS}{$oldInfo};
@@ -9202,6 +9318,7 @@ sub FoundTag($$$;@)
9202
9318
  $$valueHash{$tag} = $value;
9203
9319
  $$self{FILE_ORDER}{$tag} = ++$$self{NUM_FOUND};
9204
9320
  $$self{TAG_INFO}{$tag} = $tagInfo;
9321
+ $$self{TAG_EXTRA}{$tag} = { } unless $$self{TAG_EXTRA}{$tag};
9205
9322
  # set dynamic groups 0, 1 and 3 if necessary
9206
9323
  $$self{TAG_EXTRA}{$tag}{G0} = $grps[0] if $grps[0];
9207
9324
  $$self{TAG_EXTRA}{$tag}{G1} = $grps[1] if $grps[1];
@@ -9260,7 +9377,6 @@ sub DeleteTag($$)
9260
9377
  delete $$self{TAG_INFO}{$tag};
9261
9378
  delete $$self{TAG_EXTRA}{$tag};
9262
9379
  delete $$self{PRIORITY}{$tag};
9263
- delete $$self{RATIONAL}{$tag};
9264
9380
  delete $$self{BOTH}{$tag};
9265
9381
  }
9266
9382
 
@@ -9496,7 +9612,7 @@ sub ProcessBinaryData($$$)
9496
9612
  $increment = $formatSize{$defaultFormat};
9497
9613
  }
9498
9614
  # prepare list of tag numbers to extract
9499
- my (@tags, $topIndex);
9615
+ my (@tags, $topIndex, $binVal);
9500
9616
  if ($unknown > 1 and defined $$tagTablePtr{FIRST_ENTRY}) {
9501
9617
  # don't create a stupid number of tags if data is huge
9502
9618
  my $sizeLimit = $size < 65536 ? $size : 65536;
@@ -9636,6 +9752,7 @@ sub ProcessBinaryData($$$)
9636
9752
  $val = $self->Decode($val, 'UCS2') if $format eq 'ustring' or $format eq 'ustr32';
9637
9753
  $val =~ s/\0.*//s unless $format eq 'undef'; # truncate at null
9638
9754
  }
9755
+ $binVal = substr($$dataPt, $entry+$dirStart, $count) if $$self{OPTIONS}{SaveBin};
9639
9756
  $wasVar = 1;
9640
9757
  # save variable size data if required for writing
9641
9758
  if ($$dirInfo{VarFormatData}) {
@@ -9757,7 +9874,8 @@ sub ProcessBinaryData($$$)
9757
9874
  my $key = $self->FoundTag($tagInfo,$val);
9758
9875
  $$self{BASE} = $oldBase if defined $oldBase;
9759
9876
  if ($key) {
9760
- $$self{RATIONAL}{$key} = $rational if defined $rational;
9877
+ $$self{TAG_EXTRA}{$key}{Rational} = $rational if defined $rational;
9878
+ $$self{TAG_EXTRA}{$key}{BinVal} = $binVal if defined $binVal;
9761
9879
  } else {
9762
9880
  # don't increment nextIndex if we didn't extract a tag
9763
9881
  $nextIndex = $saveNextIndex if defined $saveNextIndex;