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.
- checksums.yaml +4 -4
- data/bin/Changes +76 -3
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/arg_files/exif2xmp.args +4 -0
- data/bin/arg_files/xmp2exif.args +4 -0
- data/bin/exiftool +121 -50
- data/bin/lib/Image/ExifTool/Apple.pm +2 -2
- data/bin/lib/Image/ExifTool/CBOR.pm +4 -1
- data/bin/lib/Image/ExifTool/Canon.pm +35 -26
- data/bin/lib/Image/ExifTool/Exif.pm +15 -9
- data/bin/lib/Image/ExifTool/FlashPix.pm +5 -9
- data/bin/lib/Image/ExifTool/GIF.pm +143 -92
- data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
- data/bin/lib/Image/ExifTool/Geotag.pm +6 -5
- data/bin/lib/Image/ExifTool/GoPro.pm +2 -2
- data/bin/lib/Image/ExifTool/JPEG.pm +9 -1
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -2
- data/bin/lib/Image/ExifTool/LNK.pm +1 -1
- data/bin/lib/Image/ExifTool/M2TS.pm +2 -2
- data/bin/lib/Image/ExifTool/MIE.pm +9 -3
- data/bin/lib/Image/ExifTool/MacOS.pm +2 -1
- data/bin/lib/Image/ExifTool/Matroska.pm +10 -2
- data/bin/lib/Image/ExifTool/Nikon.pm +5 -2
- data/bin/lib/Image/ExifTool/PDF.pm +35 -4
- data/bin/lib/Image/ExifTool/PNG.pm +14 -3
- data/bin/lib/Image/ExifTool/PPM.pm +11 -2
- data/bin/lib/Image/ExifTool/PhaseOne.pm +2 -1
- data/bin/lib/Image/ExifTool/QuickTime.pm +6 -1
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +69 -7
- data/bin/lib/Image/ExifTool/RIFF.pm +7 -2
- data/bin/lib/Image/ExifTool/TagLookup.pm +5596 -5582
- data/bin/lib/Image/ExifTool/TagNames.pod +75 -21
- data/bin/lib/Image/ExifTool/Text.pm +3 -2
- data/bin/lib/Image/ExifTool/Validate.pm +2 -2
- data/bin/lib/Image/ExifTool/WriteRIFF.pl +13 -4
- data/bin/lib/Image/ExifTool/Writer.pl +42 -66
- data/bin/lib/Image/ExifTool/XMP.pm +19 -4
- data/bin/lib/Image/ExifTool/XMP2.pl +60 -0
- data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -2
- data/bin/lib/Image/ExifTool.pm +204 -86
- data/bin/lib/Image/ExifTool.pod +58 -31
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +2 -2
data/bin/lib/Image/ExifTool.pm
CHANGED
@@ -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 = '
|
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/
|
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/
|
802
|
-
TTF => 'application/
|
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
|
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{
|
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(
|
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(
|
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(
|
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
|
3744
|
-
|
3745
|
-
|
3746
|
-
|
3747
|
-
|
3748
|
-
|
3749
|
-
|
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
|
-
|
3925
|
-
|
3926
|
-
|
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
|
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
|
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
|
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
|
-
$
|
4625
|
-
if ($
|
4626
|
-
|
4627
|
-
|
4628
|
-
if (
|
4629
|
-
|
4630
|
-
|
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
|
-
|
4638
|
-
|
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
|
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
|
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
|
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
|
-
|
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 ?
|
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
|
-
$
|
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
|
-
|
9034
|
-
|
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}
|
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
|
-
|
9176
|
-
|
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{
|
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;
|