exiftool_vendored 12.98.0 → 13.02.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 +73 -1
- 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/config_files/example.config +2 -1
- data/bin/exiftool +297 -66
- data/bin/lib/Image/ExifTool/APP12.pm +3 -2
- data/bin/lib/Image/ExifTool/CBOR.pm +4 -1
- data/bin/lib/Image/ExifTool/Canon.pm +36 -26
- data/bin/lib/Image/ExifTool/Exif.pm +8 -9
- data/bin/lib/Image/ExifTool/FlashPix.pm +5 -9
- 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/Import.pm +7 -3
- data/bin/lib/Image/ExifTool/JSON.pm +3 -4
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -2
- data/bin/lib/Image/ExifTool/LNK.pm +1 -1
- data/bin/lib/Image/ExifTool/Lytro.pm +2 -2
- 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/Nikon.pm +5 -2
- data/bin/lib/Image/ExifTool/PDF.pm +7 -3
- data/bin/lib/Image/ExifTool/PhaseOne.pm +2 -1
- data/bin/lib/Image/ExifTool/QuickTime.pm +11 -1
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +88 -9
- data/bin/lib/Image/ExifTool/TagLookup.pm +14 -9
- data/bin/lib/Image/ExifTool/TagNames.pod +37 -20
- data/bin/lib/Image/ExifTool/Text.pm +3 -2
- data/bin/lib/Image/ExifTool/Validate.pm +2 -2
- data/bin/lib/Image/ExifTool/WriteXMP.pl +16 -4
- data/bin/lib/Image/ExifTool/Writer.pl +42 -61
- data/bin/lib/Image/ExifTool/XMP.pm +13 -3
- data/bin/lib/Image/ExifTool/XMPStruct.pl +16 -9
- data/bin/lib/Image/ExifTool.pm +190 -77
- data/bin/lib/Image/ExifTool.pod +73 -36
- 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.02';
|
33
33
|
$RELEASE = '';
|
34
34
|
@ISA = qw(Exporter);
|
35
35
|
%EXPORT_TAGS = (
|
@@ -37,7 +37,7 @@ $RELEASE = '';
|
|
37
37
|
Public => [qw(
|
38
38
|
ImageInfo AvailableOptions GetTagName GetShortcuts GetAllTags
|
39
39
|
GetWritableTags GetAllGroups GetDeleteGroups GetFileType CanWrite
|
40
|
-
CanCreate AddUserDefinedTags
|
40
|
+
CanCreate AddUserDefinedTags OrderedKeys
|
41
41
|
)],
|
42
42
|
# exports not part of the public API, but used by ExifTool modules:
|
43
43
|
DataAccess => [qw(
|
@@ -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
|
@@ -2292,6 +2301,7 @@ sub new
|
|
2292
2301
|
$$self{PATH} = [ ]; # (this too)
|
2293
2302
|
$$self{DEL_GROUP} = { }; # lookup for groups to delete when writing
|
2294
2303
|
$$self{SAVE_COUNT} = 0; # count calls to SaveNewValues()
|
2304
|
+
$$self{NV_COUNT} = 0; # count of NEW_VALUE entries
|
2295
2305
|
$$self{FILE_SEQUENCE} = 0; # sequence number for files when reading
|
2296
2306
|
$$self{FILES_WRITTEN} = 0; # count of files successfully written
|
2297
2307
|
$$self{INDENT2} = ''; # indentation of verbose messages from SetNewValue
|
@@ -2517,6 +2527,8 @@ sub Options($$;@)
|
|
2517
2527
|
# set Compact and XMPShorthand options, preserving backward compatibility
|
2518
2528
|
my ($p, %compact);
|
2519
2529
|
foreach $p ('Compact','XMPShorthand') {
|
2530
|
+
# (allow setting from a HASH (undocumented)
|
2531
|
+
ref $newVal eq 'HASH' and %compact = %{$newVal}, next;
|
2520
2532
|
my $val = $param eq $p ? $newVal : $$options{Compact}{$p};
|
2521
2533
|
if (defined $val) {
|
2522
2534
|
my @v = ($val =~ /\w+/g);
|
@@ -3325,8 +3337,8 @@ sub GetRequestedTags($)
|
|
3325
3337
|
# Inputs: 0) ExifTool object reference
|
3326
3338
|
# 1) tag key or tag name with optional group names (case sensitive)
|
3327
3339
|
# (or flattened tagInfo for getting field values, not part of public API)
|
3328
|
-
# 2) [optional] Value type: PrintConv, ValueConv, Both, Raw or Rational, the
|
3329
|
-
# 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
|
3330
3342
|
# 3) raw field value (not part of public API)
|
3331
3343
|
# Returns: Scalar context: tag value or undefined
|
3332
3344
|
# List context: list of values or empty list
|
@@ -3355,7 +3367,8 @@ sub GetValue($$;$)
|
|
3355
3367
|
}
|
3356
3368
|
# figure out what conversions to do
|
3357
3369
|
if ($type) {
|
3358
|
-
return $$self{
|
3370
|
+
return $$self{TAG_EXTRA}{$tag}{Rational} if $type eq 'Rational';
|
3371
|
+
return $$self{TAG_EXTRA}{$tag}{BinVal} if $type eq 'Bin';
|
3359
3372
|
} else {
|
3360
3373
|
$type = $$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv';
|
3361
3374
|
}
|
@@ -3699,9 +3712,10 @@ sub GetGroup($$;$)
|
|
3699
3712
|
$tag = $$tagInfo{Name};
|
3700
3713
|
# set flag so we don't get extra information for an extracted tag
|
3701
3714
|
$byTagInfo = 1;
|
3715
|
+
$ex = { };
|
3702
3716
|
} else {
|
3703
3717
|
$tagInfo = $$self{TAG_INFO}{$tag} || { };
|
3704
|
-
$ex = $$self{TAG_EXTRA}{$tag};
|
3718
|
+
$ex = $$self{TAG_EXTRA}{$tag} || { };
|
3705
3719
|
}
|
3706
3720
|
my $groups = $$tagInfo{Groups};
|
3707
3721
|
# fill in default groups unless already done
|
@@ -3720,32 +3734,30 @@ sub GetGroup($$;$)
|
|
3720
3734
|
if (defined $family and $family ne '-1') {
|
3721
3735
|
if ($family =~ /[^\d]/) {
|
3722
3736
|
@families = ($family =~ /\d+/g);
|
3723
|
-
return(
|
3737
|
+
return($$ex{G0} || $$groups{0}) unless @families;
|
3724
3738
|
$simplify = 1 unless $family =~ /^:/;
|
3725
3739
|
undef $family;
|
3726
3740
|
foreach (0..2) { $groups[$_] = $$groups{$_}; }
|
3727
3741
|
$noID = 1 if @families == 1 and $families[0] != 7;
|
3728
3742
|
} else {
|
3729
|
-
return(
|
3743
|
+
return($$ex{"G$family"} || $$groups{$family}) if $family == 0 or $family == 2;
|
3730
3744
|
$groups[1] = $$groups{1};
|
3731
3745
|
}
|
3732
3746
|
} else {
|
3733
|
-
return(
|
3747
|
+
return($$ex{G0} || $$groups{0}) unless wantarray;
|
3734
3748
|
foreach (0..2) { $groups[$_] = $$groups{$_}; }
|
3735
3749
|
}
|
3736
3750
|
$groups[3] = 'Main';
|
3737
|
-
$groups[4] = ($tag =~ /\((\d+)\)$/) ? "Copy$1" : '';
|
3751
|
+
$groups[4] = ($tag =~ /\((\d+)\)$/ and $1 ne '0') ? "Copy$1" : '';
|
3738
3752
|
# handle dynamic group names if necessary
|
3739
3753
|
unless ($byTagInfo) {
|
3740
|
-
if
|
3741
|
-
|
3742
|
-
|
3743
|
-
|
3744
|
-
|
3745
|
-
|
3746
|
-
|
3747
|
-
$groups[6] = $$ex{G6};
|
3748
|
-
}
|
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};
|
3749
3761
|
}
|
3750
3762
|
if ($$ex{G8}) {
|
3751
3763
|
$groups[7] = '';
|
@@ -3918,12 +3930,9 @@ COMPOSITE_TAG:
|
|
3918
3930
|
$key = "$reqTag ($i)";
|
3919
3931
|
}
|
3920
3932
|
@keys = $self->GroupMatches($reqGroup, \@keys) if defined $reqGroup;
|
3921
|
-
|
3922
|
-
|
3923
|
-
|
3924
|
-
# priority tag will win in the case of duplicates within a doc
|
3925
|
-
$$cacheTag[$$ex{$_} ? $$ex{$_}{G3} || 0 : 0] = $_ foreach reverse @keys;
|
3926
|
-
}
|
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;
|
3927
3936
|
}
|
3928
3937
|
# (set $reqTag to a bogus key if not found)
|
3929
3938
|
$reqTag = $$cacheTag[$doc] || "$reqTag (0)";
|
@@ -4195,6 +4204,16 @@ sub CanCreate($)
|
|
4195
4204
|
return 0;
|
4196
4205
|
}
|
4197
4206
|
|
4207
|
+
#------------------------------------------------------------------------------
|
4208
|
+
# Return list of ordered keys if available, otherwise just sort alphabetically
|
4209
|
+
# Inputs: 0) hash ref
|
4210
|
+
# Returns: List of ordered/sorted keys
|
4211
|
+
sub OrderedKeys($)
|
4212
|
+
{
|
4213
|
+
my $hash = shift;
|
4214
|
+
return $$hash{_ordered_keys_} ? @{$$hash{_ordered_keys_}} : sort keys %$hash;
|
4215
|
+
}
|
4216
|
+
|
4198
4217
|
#==============================================================================
|
4199
4218
|
# Functions below this are not part of the public API
|
4200
4219
|
|
@@ -4205,9 +4224,7 @@ sub Init($)
|
|
4205
4224
|
local $_;
|
4206
4225
|
my $self = shift;
|
4207
4226
|
# delete all DataMember variables (lower-case names)
|
4208
|
-
foreach
|
4209
|
-
/[a-z]/ and delete $$self{$_};
|
4210
|
-
}
|
4227
|
+
delete $$self{$_} foreach grep /[a-z]/, keys %$self;
|
4211
4228
|
undef %static_vars; # clear all static variables
|
4212
4229
|
delete $$self{FOUND_TAGS}; # list of found tags
|
4213
4230
|
delete $$self{EXIF_DATA}; # the EXIF data block
|
@@ -4222,7 +4239,6 @@ sub Init($)
|
|
4222
4239
|
$$self{FILE_ORDER} = { }; # * hash of tag order in file ('*' = based on tag key)
|
4223
4240
|
$$self{VALUE} = { }; # * hash of raw tag values
|
4224
4241
|
$$self{BOTH} = { }; # * hash for Value/PrintConv values of Require'd tags
|
4225
|
-
$$self{RATIONAL} = { }; # * hash of original rational components
|
4226
4242
|
$$self{TAG_INFO} = { }; # * hash of tag information
|
4227
4243
|
$$self{TAG_EXTRA} = { }; # * hash of extra tag information (dynamic group names)
|
4228
4244
|
$$self{PRIORITY} = { }; # * priority of current tags
|
@@ -4455,11 +4471,7 @@ sub DoneExtract($)
|
|
4455
4471
|
my $err = $$altExifTool{VALUE}{Error};
|
4456
4472
|
$err and $self->Warn(qq{$err "$fileName"});
|
4457
4473
|
# set family 8 group name for all tags
|
4458
|
-
foreach
|
4459
|
-
my $ex = $$altExifTool{TAG_EXTRA}{$_};
|
4460
|
-
$ex or $ex = $$altExifTool{TAG_EXTRA}{$_} = { };
|
4461
|
-
$$ex{G8} = $g8;
|
4462
|
-
}
|
4474
|
+
$$altExifTool{TAG_EXTRA}{$_}{G8} = $g8 foreach keys %{$$altExifTool{VALUE}};
|
4463
4475
|
# prepare our sorted list of found tags
|
4464
4476
|
$$altExifTool{FoundTags} = [ reverse sort keys %{$$altExifTool{VALUE}} ];
|
4465
4477
|
$$altExifTool{DID_EXTRACT} = 1;
|
@@ -4601,36 +4613,125 @@ sub SplitFileName($)
|
|
4601
4613
|
|
4602
4614
|
#------------------------------------------------------------------------------
|
4603
4615
|
# Encode file name for calls to system i/o routines
|
4604
|
-
# Inputs: 0) ExifTool ref, 1) file name in
|
4616
|
+
# Inputs: 0) ExifTool ref, 1) file name in CharsetFileName encoding, 2) flag to force conversion
|
4605
4617
|
# Returns: true if Windows Unicode routines should be used (in which case
|
4606
4618
|
# the file name will be encoded as a null-terminated UTF-16LE string)
|
4607
4619
|
sub EncodeFileName($$;$)
|
4608
4620
|
{
|
4609
4621
|
my ($self, $file, $force) = @_;
|
4610
4622
|
my $enc = $$self{OPTIONS}{CharsetFileName};
|
4611
|
-
$
|
4612
|
-
if ($
|
4613
|
-
|
4614
|
-
|
4615
|
-
if (
|
4616
|
-
|
4617
|
-
|
4618
|
-
# recode as UTF-16LE and add null terminator
|
4619
|
-
$_[1] = $self->Decode($file, $enc, undef, 'UTF16', 'II') . "\0\0";
|
4620
|
-
return 1;
|
4621
|
-
}
|
4622
|
-
$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;
|
4623
4630
|
} else {
|
4624
|
-
|
4625
|
-
$_[1] = $self->Decode($file, $enc, undef, 'UTF8') unless $enc eq 'UTF8';
|
4631
|
+
$enc = 'UTF8'; # assume UTF8
|
4626
4632
|
}
|
4627
4633
|
}
|
4628
|
-
}
|
4629
|
-
|
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;
|
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');
|
4650
|
+
}
|
4630
4651
|
}
|
4631
4652
|
return 0;
|
4632
4653
|
}
|
4633
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
|
+
sub WindowsLongPath($$)
|
4664
|
+
{
|
4665
|
+
my ($self, $path) = @_;
|
4666
|
+
my $debug = $$self{OPTIONS}{Debug};
|
4667
|
+
my $out = $$self{OPTIONS}{TextOut};
|
4668
|
+
my @fullParts;
|
4669
|
+
my $prefixLen = 0;
|
4670
|
+
|
4671
|
+
$debug and print $out "WindowsLongPath input : $path\n";
|
4672
|
+
$path =~ tr(/)(\\); # convert slashes to backslashes
|
4673
|
+
my @pathParts = split /\\/, $path;
|
4674
|
+
|
4675
|
+
if ($path =~ /^\\\\\?\\/ or # already a device path in the format we want
|
4676
|
+
$path =~ s/^\\\\\.\\/\\\\?\\/) # convert //./ to //?/
|
4677
|
+
{
|
4678
|
+
# path is already long-path compatible
|
4679
|
+
$prefixLen = 3; # path already contains prefix of 3 parts ('', '' and '?')
|
4680
|
+
} elsif ($path =~ /[*?]/) {
|
4681
|
+
return $path; # do nothing because we don't support wildcards
|
4682
|
+
} elsif ($path =~ /^\\\\/) {
|
4683
|
+
# UNC path starts with two slashes change to "\\?\UNC\"
|
4684
|
+
splice @pathParts, 2, 0, '?', 'UNC';
|
4685
|
+
$prefixLen = (@pathParts > 6 ? 6 : @pathParts); # ('', '', '?', 'UNC', <server>, <share>)
|
4686
|
+
} elsif ($path =~ /^[a-z]:\\/i) {
|
4687
|
+
# path is already absolute but we need to add the device path prefix
|
4688
|
+
unshift @pathParts, '', '', '?';
|
4689
|
+
$prefixLen = 4;
|
4690
|
+
} elsif ({ eval { require Cwd } }) {
|
4691
|
+
my $drive;
|
4692
|
+
$drive = $1 if $pathParts[0] =~ s/^([a-z]:)//;
|
4693
|
+
my $cwd = Cwd::getdcwd($drive); # ($drive is undef for current working drive)
|
4694
|
+
$debug and print $out "WindowsLongPath getcwd: $cwd\n";
|
4695
|
+
@fullParts = split /[\\\/]/, $cwd;
|
4696
|
+
# UNC path starts with "\\", so first 2 elements are empty
|
4697
|
+
# --> shift and put UNC in first element.
|
4698
|
+
if (@fullParts > 1 and $fullParts[0] eq '' and $fullParts[1] eq '') {
|
4699
|
+
shift @fullParts;
|
4700
|
+
$fullParts[0] = 'UNC';
|
4701
|
+
unshift @fullParts, '', '', '?';
|
4702
|
+
$prefixLen = (@fullParts > 6 ? 6 : @fullParts);
|
4703
|
+
} else {
|
4704
|
+
$prefixLen = 1; # drive designator only
|
4705
|
+
}
|
4706
|
+
# if absolute path on current drive starts with "\"
|
4707
|
+
# just keep prefix and drop the rest of the cwd
|
4708
|
+
$#fullParts = $prefixLen - 1 if $pathParts[0] eq '';
|
4709
|
+
} else {
|
4710
|
+
$prefixLen = @pathParts; # (nothing more we can do)
|
4711
|
+
}
|
4712
|
+
# remove "." and ".." from path (not handled for device paths)
|
4713
|
+
my $part;
|
4714
|
+
foreach $part (@pathParts) {
|
4715
|
+
if ($part eq '.') {
|
4716
|
+
next;
|
4717
|
+
} elsif ($part eq '') {
|
4718
|
+
# only allow double slashes at start of path name (max 2)
|
4719
|
+
push @fullParts, $part if not @fullParts or (@fullParts == 1 and $fullParts[0] eq '');
|
4720
|
+
} elsif ($part eq '..') {
|
4721
|
+
# step up one directory, but not into the prefix
|
4722
|
+
pop @fullParts if @fullParts > $prefixLen;
|
4723
|
+
} else {
|
4724
|
+
push @fullParts, $part;
|
4725
|
+
}
|
4726
|
+
}
|
4727
|
+
$path = join '\\', @fullParts;
|
4728
|
+
# add device path prefix ("\\?\") if path length near the limit (the most
|
4729
|
+
# conservative limit I can find is 247, which is the limit on the directory name)
|
4730
|
+
$path = '\\\\?\\' . $path unless $prefixLen > 1 or length($path) <= 247;
|
4731
|
+
$debug and print $out "WindowsLongPath return: $path\n";
|
4732
|
+
return $path;
|
4733
|
+
}
|
4734
|
+
|
4634
4735
|
#------------------------------------------------------------------------------
|
4635
4736
|
# Modified perl open() routine to properly handle special characters in file names
|
4636
4737
|
# Inputs: 0) ExifTool ref, 1) filehandle, 2) filename,
|
@@ -4897,11 +4998,13 @@ sub ParseArguments($;@)
|
|
4897
4998
|
next if defined $$self{RAF};
|
4898
4999
|
# convert image data from UTF-8 to character stream if necessary
|
4899
5000
|
# (patches RHEL 3 UTF8 LANG problem)
|
4900
|
-
if (ref $arg eq 'SCALAR' and $] >= 5.006 and
|
4901
|
-
|
5001
|
+
if (ref $arg eq 'SCALAR' and $] >= 5.006 and ($$self{OPTIONS}{EncodeHangs} or
|
5002
|
+
eval { require Encode; Encode::is_utf8($$arg) } or $@))
|
4902
5003
|
{
|
5004
|
+
local $SIG{'__WARN__'} = \&SetWarning;
|
4903
5005
|
# repack by hand if Encode isn't available
|
4904
|
-
my $buff = $@ ? pack('C*',unpack($] < 5.010000 ?
|
5006
|
+
my $buff = ($$self{OPTIONS}{EncodeHangs} or $@) ? pack('C*',unpack($] < 5.010000 ?
|
5007
|
+
'U0C*' : 'C0C*', $$arg)) : Encode::encode('utf8', $$arg);
|
4905
5008
|
$arg = \$buff;
|
4906
5009
|
}
|
4907
5010
|
$$self{RAF} = File::RandomAccess->new($arg);
|
@@ -6309,10 +6412,12 @@ sub Filter($$$)
|
|
6309
6412
|
# Return printable value
|
6310
6413
|
# Inputs: 0) ExifTool object reference
|
6311
6414
|
# 1) value to print, 2) line length limit (undef defaults to 60, 0=unlimited)
|
6415
|
+
# Returns: Printable string
|
6312
6416
|
sub Printable($;$)
|
6313
6417
|
{
|
6314
6418
|
my ($self, $outStr, $maxLen) = @_;
|
6315
6419
|
return '(undef)' unless defined $outStr;
|
6420
|
+
ref $outStr eq 'SCALAR' and return '(Binary data '.length($$outStr).' bytes)';
|
6316
6421
|
$outStr =~ tr/\x01-\x1f\x7f-\xff/./;
|
6317
6422
|
$outStr =~ s/\x00//g;
|
6318
6423
|
my $verbose = $$self{OPTIONS}{Verbose};
|
@@ -7904,8 +8009,17 @@ sub ProcessJPEG($$;$)
|
|
7904
8009
|
my $seq = Get32u($segDataPt, 4);
|
7905
8010
|
my $len = Get32u($segDataPt, 8);
|
7906
8011
|
my $type = substr($$segDataPt, 12, 4);
|
8012
|
+
# a Microsoft bug writes $len and $type incorrectly as little-endian
|
8013
|
+
if ($type eq 'bmuj') {
|
8014
|
+
$self->WarnOnce('Wrong byte order in C2PA APP11 JUMBF header');
|
8015
|
+
$type = 'jumb';
|
8016
|
+
$len = unpack('x8V', $$segDataPt);
|
8017
|
+
# fix the header
|
8018
|
+
substr($$segDataPt, 8, 8) = Set32u($len) . $type;
|
8019
|
+
}
|
7907
8020
|
my $hdrLen;
|
7908
8021
|
if ($len == 1 and length($$segDataPt) >= 24) {
|
8022
|
+
# (haven't seen this with the Microsoft bug)
|
7909
8023
|
$len = Get64u($$segDataPt, 16);
|
7910
8024
|
$hdrLen = 16;
|
7911
8025
|
} else {
|
@@ -8902,7 +9016,7 @@ sub HandleTag($$$$;%)
|
|
8902
9016
|
my $pfmt = $parms{Format};
|
8903
9017
|
my $tagInfo = $parms{TagInfo} || $self->GetTagInfo($tagTablePtr, $tag, \$val, $pfmt, $parms{Count});
|
8904
9018
|
my $dataPt = $parms{DataPt};
|
8905
|
-
my ($subdir, $format, $noTagInfo, $rational);
|
9019
|
+
my ($subdir, $format, $noTagInfo, $rational, $binVal);
|
8906
9020
|
|
8907
9021
|
if ($tagInfo) {
|
8908
9022
|
$subdir = $$tagInfo{SubDirectory};
|
@@ -8926,6 +9040,7 @@ sub HandleTag($$$$;%)
|
|
8926
9040
|
} else {
|
8927
9041
|
$val = substr($$dataPt, $start, $size);
|
8928
9042
|
}
|
9043
|
+
$binVal = substr($$dataPt, $start, $size) if $$self{OPTIONS}{SaveBin};
|
8929
9044
|
} else {
|
8930
9045
|
$self->Warn("Error extracting value for $$tagInfo{Name}");
|
8931
9046
|
return undef;
|
@@ -9008,8 +9123,11 @@ sub HandleTag($$$$;%)
|
|
9008
9123
|
return undef unless $$tagInfo{Writable};
|
9009
9124
|
}
|
9010
9125
|
my $key = $self->FoundTag($tagInfo, $val);
|
9011
|
-
|
9012
|
-
|
9126
|
+
if (defined $key) {
|
9127
|
+
# save original components of rational numbers and original binary value
|
9128
|
+
$$self{TAG_EXTRA}{$key}{Rational} = $rational if defined $rational;
|
9129
|
+
$$self{TAG_EXTRA}{$key}{BinVal} = $binVal if defined $binVal;
|
9130
|
+
}
|
9013
9131
|
return $key;
|
9014
9132
|
}
|
9015
9133
|
return undef;
|
@@ -9120,9 +9238,7 @@ sub FoundTag($$$;@)
|
|
9120
9238
|
# a Warning tag because they may be added by ValueConv, which could be confusing)
|
9121
9239
|
my $oldPriority = $$self{PRIORITY}{$tag};
|
9122
9240
|
unless ($oldPriority) {
|
9123
|
-
if ($$self{DOC_NUM} or not $$self{TAG_EXTRA}{$tag}
|
9124
|
-
not $$self{TAG_EXTRA}{$tag}{G3})
|
9125
|
-
{
|
9241
|
+
if ($$self{DOC_NUM} or $tag eq 'Warning' or not $$self{TAG_EXTRA}{$tag}{G3}) {
|
9126
9242
|
$oldPriority = 1;
|
9127
9243
|
} else {
|
9128
9244
|
$oldPriority = 0; # don't promote sub-document tag over main document
|
@@ -9140,8 +9256,7 @@ sub FoundTag($$$;@)
|
|
9140
9256
|
} else {
|
9141
9257
|
$priority = 1; # the normal default
|
9142
9258
|
}
|
9143
|
-
if ($priority >= $oldPriority and (not $$self{DOC_NUM} or
|
9144
|
-
($$self{TAG_EXTRA}{$tag} and $$self{TAG_EXTRA}{$tag}{G3} and
|
9259
|
+
if ($priority >= $oldPriority and (not $$self{DOC_NUM} or ($$self{TAG_EXTRA}{$tag}{G3} and
|
9145
9260
|
$$self{DOC_NUM} eq $$self{TAG_EXTRA}{$tag}{G3})) and not $noListDel)
|
9146
9261
|
{
|
9147
9262
|
# move existing tag out of the way since this tag is higher priority
|
@@ -9150,12 +9265,8 @@ sub FoundTag($$$;@)
|
|
9150
9265
|
$$valueHash{$nextTag} = $$valueHash{$tag};
|
9151
9266
|
$$self{FILE_ORDER}{$nextTag} = $$self{FILE_ORDER}{$tag};
|
9152
9267
|
my $oldInfo = $$self{TAG_INFO}{$nextTag} = $$self{TAG_INFO}{$tag};
|
9153
|
-
|
9154
|
-
|
9155
|
-
$$self{$_}{$nextTag} = $$self{$_}{$tag};
|
9156
|
-
delete $$self{$_}{$tag};
|
9157
|
-
}
|
9158
|
-
}
|
9268
|
+
$$self{TAG_EXTRA}{$nextTag} = $$self{TAG_EXTRA}{$tag};
|
9269
|
+
$$self{TAG_EXTRA}{$tag} = { };
|
9159
9270
|
delete $$self{BOTH}{$tag};
|
9160
9271
|
# update tag key for list if necessary
|
9161
9272
|
$$self{LIST_TAGS}{$oldInfo} = $nextTag if $$self{LIST_TAGS}{$oldInfo};
|
@@ -9180,6 +9291,7 @@ sub FoundTag($$$;@)
|
|
9180
9291
|
$$valueHash{$tag} = $value;
|
9181
9292
|
$$self{FILE_ORDER}{$tag} = ++$$self{NUM_FOUND};
|
9182
9293
|
$$self{TAG_INFO}{$tag} = $tagInfo;
|
9294
|
+
$$self{TAG_EXTRA}{$tag} = { } unless $$self{TAG_EXTRA}{$tag};
|
9183
9295
|
# set dynamic groups 0, 1 and 3 if necessary
|
9184
9296
|
$$self{TAG_EXTRA}{$tag}{G0} = $grps[0] if $grps[0];
|
9185
9297
|
$$self{TAG_EXTRA}{$tag}{G1} = $grps[1] if $grps[1];
|
@@ -9238,7 +9350,6 @@ sub DeleteTag($$)
|
|
9238
9350
|
delete $$self{TAG_INFO}{$tag};
|
9239
9351
|
delete $$self{TAG_EXTRA}{$tag};
|
9240
9352
|
delete $$self{PRIORITY}{$tag};
|
9241
|
-
delete $$self{RATIONAL}{$tag};
|
9242
9353
|
delete $$self{BOTH}{$tag};
|
9243
9354
|
}
|
9244
9355
|
|
@@ -9474,7 +9585,7 @@ sub ProcessBinaryData($$$)
|
|
9474
9585
|
$increment = $formatSize{$defaultFormat};
|
9475
9586
|
}
|
9476
9587
|
# prepare list of tag numbers to extract
|
9477
|
-
my (@tags, $topIndex);
|
9588
|
+
my (@tags, $topIndex, $binVal);
|
9478
9589
|
if ($unknown > 1 and defined $$tagTablePtr{FIRST_ENTRY}) {
|
9479
9590
|
# don't create a stupid number of tags if data is huge
|
9480
9591
|
my $sizeLimit = $size < 65536 ? $size : 65536;
|
@@ -9614,6 +9725,7 @@ sub ProcessBinaryData($$$)
|
|
9614
9725
|
$val = $self->Decode($val, 'UCS2') if $format eq 'ustring' or $format eq 'ustr32';
|
9615
9726
|
$val =~ s/\0.*//s unless $format eq 'undef'; # truncate at null
|
9616
9727
|
}
|
9728
|
+
$binVal = substr($$dataPt, $entry+$dirStart, $count) if $$self{OPTIONS}{SaveBin};
|
9617
9729
|
$wasVar = 1;
|
9618
9730
|
# save variable size data if required for writing
|
9619
9731
|
if ($$dirInfo{VarFormatData}) {
|
@@ -9735,7 +9847,8 @@ sub ProcessBinaryData($$$)
|
|
9735
9847
|
my $key = $self->FoundTag($tagInfo,$val);
|
9736
9848
|
$$self{BASE} = $oldBase if defined $oldBase;
|
9737
9849
|
if ($key) {
|
9738
|
-
$$self{
|
9850
|
+
$$self{TAG_EXTRA}{$key}{Rational} = $rational if defined $rational;
|
9851
|
+
$$self{TAG_EXTRA}{$key}{BinVal} = $binVal if defined $binVal;
|
9739
9852
|
} else {
|
9740
9853
|
# don't increment nextIndex if we didn't extract a tag
|
9741
9854
|
$nextIndex = $saveNextIndex if defined $saveNextIndex;
|