exiftool_vendored 13.02.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 +23 -3
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/exiftool +53 -24
- data/bin/lib/Image/ExifTool/Apple.pm +2 -2
- data/bin/lib/Image/ExifTool/Exif.pm +8 -1
- data/bin/lib/Image/ExifTool/GIF.pm +143 -92
- data/bin/lib/Image/ExifTool/JPEG.pm +9 -1
- data/bin/lib/Image/ExifTool/Matroska.pm +10 -2
- data/bin/lib/Image/ExifTool/PDF.pm +29 -2
- data/bin/lib/Image/ExifTool/PNG.pm +14 -3
- data/bin/lib/Image/ExifTool/PPM.pm +11 -2
- data/bin/lib/Image/ExifTool/QuickTime.pm +6 -1
- data/bin/lib/Image/ExifTool/RIFF.pm +7 -2
- data/bin/lib/Image/ExifTool/TagLookup.pm +5594 -5583
- data/bin/lib/Image/ExifTool/TagNames.pod +43 -4
- data/bin/lib/Image/ExifTool/WriteRIFF.pl +13 -4
- data/bin/lib/Image/ExifTool/Writer.pl +16 -11
- data/bin/lib/Image/ExifTool/XMP.pm +7 -2
- data/bin/lib/Image/ExifTool/XMP2.pl +60 -0
- data/bin/lib/Image/ExifTool.pm +94 -67
- data/bin/lib/Image/ExifTool.pod +16 -3
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +2 -2
@@ -12,7 +12,7 @@ meta information extracted from or written to a file.
|
|
12
12
|
=head1 TAG TABLES
|
13
13
|
|
14
14
|
The tables listed below give the names of all tags recognized by ExifTool.
|
15
|
-
They contain a total of
|
15
|
+
They contain a total of 28175 tags, with 17499 unique tag names.
|
16
16
|
|
17
17
|
B<Tag ID>, B<Index#> or B<Sequence> is given in the first column of each
|
18
18
|
table. A B<Tag ID> is the computer-readable equivalent of a tag name, and
|
@@ -147,8 +147,10 @@ L<https://www.w3.org/Graphics/JPEG/jfif3.pdf> for the JPEG specification.
|
|
147
147
|
DJI-DBG DJI Info
|
148
148
|
'APP8' SPIFF JPEG SPIFF
|
149
149
|
InfiRayIsothermal InfiRay Isothermal
|
150
|
+
SEAL XMP SEAL
|
150
151
|
'APP9' MediaJukebox JPEG MediaJukebox
|
151
152
|
InfiRaySensor InfiRay Sensor
|
153
|
+
SEAL XMP SEAL
|
152
154
|
'APP10' Comment no
|
153
155
|
'APP11' JPEG-HDR JPEG HDR
|
154
156
|
JUMBF Jpeg2000
|
@@ -1053,6 +1055,7 @@ for the official EXIF 2.32 specification.
|
|
1053
1055
|
0xcd49 JXLDistance IFD0 float
|
1054
1056
|
0xcd4a JXLEffort IFD0 int32u
|
1055
1057
|
0xcd4b JXLDecodeSpeed IFD0 int32u
|
1058
|
+
0xcea1 SEAL IFD0 XMP SEAL
|
1056
1059
|
0xea1c Padding ExifIFD undef!
|
1057
1060
|
0xea1d OffsetSchema ExifIFD int32s!
|
1058
1061
|
0xfde8 OwnerName ExifIFD string/
|
@@ -1427,6 +1430,7 @@ L<http://www.adobe.com/devnet/xmp/> for the official XMP specification.
|
|
1427
1430
|
pur XMP pur
|
1428
1431
|
rdf XMP rdf
|
1429
1432
|
sdc Nikon sdc
|
1433
|
+
seal XMP seal
|
1430
1434
|
swf XMP swf
|
1431
1435
|
tiff XMP tiff
|
1432
1436
|
x XMP x
|
@@ -6152,6 +6156,37 @@ These tags belong to the ExifTool XMP-rdf family 1 group.
|
|
6152
6156
|
-------- --------
|
6153
6157
|
About string!
|
6154
6158
|
|
6159
|
+
=head3 XMP seal Tags
|
6160
|
+
|
6161
|
+
SEAL embedded in XMP.
|
6162
|
+
|
6163
|
+
These tags belong to the ExifTool XMP-seal family 1 group.
|
6164
|
+
|
6165
|
+
Tag Name Writable
|
6166
|
+
-------- --------
|
6167
|
+
Seal XMP SEAL
|
6168
|
+
|
6169
|
+
=head3 XMP SEAL Tags
|
6170
|
+
|
6171
|
+
These tags are used in SEAL content authentification, which is actually XML
|
6172
|
+
format, not XMP. ExifTool has read/delete support for SEAL information in
|
6173
|
+
JPG, TIFF, XMP, PNG, WEBP, HEIC, PPM, MOV and MP4 files, and read-only
|
6174
|
+
support in PDF, MKV and WAV. Use C<-seal:all=> on the command line to
|
6175
|
+
delete SEAL information in supported formats.
|
6176
|
+
|
6177
|
+
Tag ID Tag Name Writable
|
6178
|
+
------ -------- --------
|
6179
|
+
'b' ByteRange no
|
6180
|
+
'copyright' Copyright no
|
6181
|
+
'd' Domain no
|
6182
|
+
'da' DigestAlgorithm no
|
6183
|
+
'info' SEALComment no
|
6184
|
+
'ka' KeyAlgorithm no
|
6185
|
+
'kv' KeyVersion no
|
6186
|
+
's' Signature no
|
6187
|
+
'seal' SEALVersion no
|
6188
|
+
'sf' SignatureFormat no
|
6189
|
+
|
6155
6190
|
=head3 XMP swf Tags
|
6156
6191
|
|
6157
6192
|
Adobe SWF namespace tags.
|
@@ -28129,6 +28164,7 @@ check if speed is more of a concern.
|
|
28129
28164
|
'sPLT' SuggestedPalette no
|
28130
28165
|
'sRGB' SRGBRendering yes!
|
28131
28166
|
'sTER' StereoImage PNG StereoImage
|
28167
|
+
'seAl' SEAL XMP SEAL
|
28132
28168
|
'tEXt' TextualData PNG TextualData
|
28133
28169
|
'tIME' ModifyDate yes
|
28134
28170
|
'tRNS' Transparency no
|
@@ -29232,7 +29268,7 @@ C2PA JUMBF metadata extracted from "/C2PA_Manifest" file.
|
|
29232
29268
|
|
29233
29269
|
Tag ID Tag Name Writable
|
29234
29270
|
------ -------- --------
|
29235
|
-
'_stream'
|
29271
|
+
'_stream' PhotoshopStream Photoshop
|
29236
29272
|
|
29237
29273
|
=head3 PDF Illustrator Tags
|
29238
29274
|
|
@@ -29255,7 +29291,7 @@ C2PA JUMBF metadata extracted from "/C2PA_Manifest" file.
|
|
29255
29291
|
|
29256
29292
|
Tag ID Tag Name Writable
|
29257
29293
|
------ -------- --------
|
29258
|
-
'_stream'
|
29294
|
+
'_stream' AIStream PostScript
|
29259
29295
|
|
29260
29296
|
=head3 PDF Resources Tags
|
29261
29297
|
|
@@ -29284,7 +29320,7 @@ C2PA JUMBF metadata extracted from "/C2PA_Manifest" file.
|
|
29284
29320
|
|
29285
29321
|
Tag ID Tag Name Writable
|
29286
29322
|
------ -------- --------
|
29287
|
-
'_stream'
|
29323
|
+
'_stream' ICC_Profile ICC_Profile
|
29288
29324
|
|
29289
29325
|
=head3 PDF Properties Tags
|
29290
29326
|
|
@@ -29852,6 +29888,7 @@ for the official QuickTime specification.
|
|
29852
29888
|
'GPS ' GPSDataList2? no
|
29853
29889
|
'IDIT' DateTimeOriginal string
|
29854
29890
|
'PICT' PreviewPICT no
|
29891
|
+
'SEAL' SEAL XMP SEAL
|
29855
29892
|
'_htc' HTCInfo QuickTime HTCInfo
|
29856
29893
|
'ardt' ARDroneFile no
|
29857
29894
|
'cust' CustomInfo no
|
@@ -31651,6 +31688,7 @@ metadata to WEBP images, but can't yet write to other RIFF-based formats.
|
|
31651
31688
|
'LIST_hydt' PentaxData Pentax AVI
|
31652
31689
|
'LIST_ncdt' NikonData Nikon AVI
|
31653
31690
|
'LIST_pntx' PentaxData2 Pentax AVI
|
31691
|
+
'SEAL' SEAL XMP SEAL
|
31654
31692
|
'SGLT' BikeBroAccel QuickTime Stream
|
31655
31693
|
'SLLT' BikeBroGPS QuickTime Stream
|
31656
31694
|
'VP8 ' VP8Bitstream RIFF VP8
|
@@ -33258,6 +33296,7 @@ Matroska specification.
|
|
33258
33296
|
0xb538667 SignatureSlot Matroska
|
33259
33297
|
0xc53bb6b Cues Matroska
|
33260
33298
|
0xf43b675 Cluster Matroska
|
33299
|
+
0x5345414c SEAL XMP SEAL
|
33261
33300
|
|
33262
33301
|
=head3 Matroska Projection Tags
|
33263
33302
|
|
@@ -34,6 +34,11 @@ my %webpMap = (
|
|
34
34
|
MakerNotes => 'ExifIFD',
|
35
35
|
);
|
36
36
|
|
37
|
+
my %deletableGroup = (
|
38
|
+
"XMP\0" => 'XMP', # delete incorrectly written "XMP\0" tag with XMP group
|
39
|
+
SEAL => 'SEAL', # delete SEAL tag with SEAL group
|
40
|
+
);
|
41
|
+
|
37
42
|
#------------------------------------------------------------------------------
|
38
43
|
# Write RIFF file (currently WebP-type only)
|
39
44
|
# Inputs: 0) ExifTool object ref, 1) dirInfo ref
|
@@ -46,6 +51,7 @@ sub WriteRIFF($$)
|
|
46
51
|
my $outfile = $$dirInfo{OutFile};
|
47
52
|
my $outsize = 0;
|
48
53
|
my $raf = $$dirInfo{RAF};
|
54
|
+
my $verbose = $et->Options('Verbose');
|
49
55
|
my ($buff, $err, $pass, %has, %dirDat, $imageWidth, $imageHeight);
|
50
56
|
|
51
57
|
# do this in 2 passes so we can set the size of the containing RIFF chunk
|
@@ -65,6 +71,7 @@ sub WriteRIFF($$)
|
|
65
71
|
SetByteOrder('II');
|
66
72
|
|
67
73
|
# determine which directories we must write for this file type
|
74
|
+
$et->Options(Verbose => 0) if $pass; # (avoid duplicate Verbose options here)
|
68
75
|
$et->InitWriteDirs(\%webpMap);
|
69
76
|
my $addDirs = $$et{ADD_DIRS};
|
70
77
|
my $editDirs = $$et{EDIT_DIRS};
|
@@ -73,6 +80,7 @@ sub WriteRIFF($$)
|
|
73
80
|
|
74
81
|
# write header
|
75
82
|
if ($pass) {
|
83
|
+
$et->Options(Verbose => $verbose);
|
76
84
|
my $needsVP8X = ($has{ANIM} or $has{'XMP '} or $has{EXIF} or
|
77
85
|
$has{ALPH} or $has{ICCP});
|
78
86
|
if ($has{VP8X} and not $needsVP8X and $$et{CHANGED}) {
|
@@ -146,13 +154,14 @@ sub WriteRIFF($$)
|
|
146
154
|
# RIFF chunks are padded to an even number of bytes
|
147
155
|
my $len2 = $len + ($len & 0x01);
|
148
156
|
# handle incorrect "XMP\0" chunk ID written by Google software
|
149
|
-
if ($tag
|
150
|
-
if ($$et{DEL_GROUP}{
|
151
|
-
# just ignore this chunk if deleting
|
157
|
+
if ($deletableGroup{$tag}) {
|
158
|
+
if ($$et{DEL_GROUP}{$deletableGroup{$tag}}) {
|
159
|
+
# just ignore this chunk if deleting the associated group
|
152
160
|
$raf->Seek($len2, 1) or $et->Error('Seek error'), last;
|
161
|
+
$et->VPrint(0, " Deleting $deletableGroup{$tag}\n") if $pass;
|
153
162
|
++$$et{CHANGED};
|
154
163
|
next;
|
155
|
-
}
|
164
|
+
} elsif ($tag eq "XMP\0") {
|
156
165
|
$et->Warn('Incorrect XMP tag ID',1) if $pass;
|
157
166
|
}
|
158
167
|
}
|
@@ -43,6 +43,7 @@ my %tiffMap = (
|
|
43
43
|
PrintIM => 'IFD0',
|
44
44
|
IPTC => 'IFD0',
|
45
45
|
Photoshop => 'IFD0',
|
46
|
+
SEAL => 'IFD0',
|
46
47
|
InteropIFD => 'ExifIFD',
|
47
48
|
MakerNotes => 'ExifIFD',
|
48
49
|
CanonVRD => 'MakerNotes', # (so VRDOffset will get updated)
|
@@ -74,6 +75,7 @@ my %jpegMap = (
|
|
74
75
|
Meta => 'APP3',
|
75
76
|
MetaIFD => 'Meta',
|
76
77
|
RMETA => 'APP5',
|
78
|
+
SEAL => ['APP8','APP9'], # (note: add 'IFD0' if this is a possibility)
|
77
79
|
Ducky => 'APP12',
|
78
80
|
Photoshop => 'APP13',
|
79
81
|
Adobe => 'APP14',
|
@@ -140,7 +142,7 @@ my @delGroups = qw(
|
|
140
142
|
GlobParamIFD GPS ICC_Profile IFD0 IFD1 Insta360 InteropIFD IPTC ItemList JFIF
|
141
143
|
Jpeg2000 JUMBF Keys MakerNotes Meta MetaIFD Microsoft MIE MPF Nextbase NikonApp
|
142
144
|
NikonCapture PDF PDF-update PhotoMechanic Photoshop PNG PNG-pHYs PrintIM
|
143
|
-
QuickTime RMETA RSRC SubIFD Trailer UserData XML XML-* XMP XMP-*
|
145
|
+
QuickTime RMETA RSRC SEAL SubIFD Trailer UserData XML XML-* XMP XMP-*
|
144
146
|
);
|
145
147
|
# family 2 group names that we can delete
|
146
148
|
my @delGroup2 = qw(
|
@@ -152,6 +154,7 @@ my %delMore = (
|
|
152
154
|
QuickTime => [ qw(ItemList UserData Keys) ],
|
153
155
|
XMP => [ 'XMP-*' ],
|
154
156
|
XML => [ 'XML-*' ],
|
157
|
+
SEAL => [ 'XMP-SEAL' ],
|
155
158
|
);
|
156
159
|
|
157
160
|
# family 0 groups where directories should never be deleted
|
@@ -1311,7 +1314,7 @@ sub SetNewValuesFromFile($$;@)
|
|
1311
1314
|
LimitLongValues => 10000000, # (10 MB)
|
1312
1315
|
List => 1,
|
1313
1316
|
MakerNotes => $$options{FastScan} && $$options{FastScan} > 1 ? undef : 1,
|
1314
|
-
RequestAll => $$options{RequestAll} || 1, # (
|
1317
|
+
RequestAll => $$options{RequestAll} || 1, # (must request all because reqTags doesn't cover wildcards)
|
1315
1318
|
StrictDate => defined $$options{StrictDate} ? $$options{StrictDate} : 1,
|
1316
1319
|
Struct => $structOpt,
|
1317
1320
|
);
|
@@ -1324,11 +1327,8 @@ sub SetNewValuesFromFile($$;@)
|
|
1324
1327
|
$$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL};
|
1325
1328
|
foreach $tag (@setTags) {
|
1326
1329
|
next if ref $tag;
|
1327
|
-
|
1328
|
-
|
1329
|
-
push @exclude, $1;
|
1330
|
-
next;
|
1331
|
-
}
|
1330
|
+
# avoid extracting tags that are excluded
|
1331
|
+
$tag =~ /^-(.*)/ and push(@exclude, $1), next;
|
1332
1332
|
# add specified tags to list of requested tags
|
1333
1333
|
$_ = $tag;
|
1334
1334
|
if (/(.+?)\s*(>|<)\s*(.+)/) {
|
@@ -2983,7 +2983,7 @@ Conv: for (;;) {
|
|
2983
2983
|
$err2 = eval $$tagInfo{WriteCheck};
|
2984
2984
|
$@ and warn($@), $err2 = 'Error evaluating WriteCheck';
|
2985
2985
|
}
|
2986
|
-
unless ($err2) {
|
2986
|
+
unless (defined $err2) {
|
2987
2987
|
my $table = $$tagInfo{Table};
|
2988
2988
|
if ($table and $$table{CHECK_PROC} and not $$tagInfo{RawConvInv}) {
|
2989
2989
|
my $checkProc = $$table{CHECK_PROC};
|
@@ -6621,6 +6621,11 @@ sub WriteJPEG($$)
|
|
6621
6621
|
$segType = 'Ricoh RMETA';
|
6622
6622
|
$$delGroup{RMETA} and $del = 1;
|
6623
6623
|
}
|
6624
|
+
} elsif ($marker == 0xe8 or $marker == 0xe9) { # APP8/9 (SEAL)
|
6625
|
+
if ($$segDataPt =~ /^SEAL\0/) {
|
6626
|
+
$segType = 'SEAL';
|
6627
|
+
$$delGroup{SEAL} and $del = 1;
|
6628
|
+
}
|
6624
6629
|
} elsif ($marker == 0xeb) { # APP10 (JUMBF)
|
6625
6630
|
if ($$segDataPt =~ /^JP/) {
|
6626
6631
|
$segType = 'JUMBF';
|
@@ -6987,7 +6992,7 @@ sub SetFileTime($$;$$$$)
|
|
6987
6992
|
# get Win32 handle, needed for SetFileTime
|
6988
6993
|
my $win32Handle = eval { Win32API::File::GetOsFHandle($file) };
|
6989
6994
|
unless ($win32Handle) {
|
6990
|
-
$self->Warn('Win32API::File
|
6995
|
+
$self->Warn('Win32API::File GetOsFHandle returned invalid handle');
|
6991
6996
|
return 0;
|
6992
6997
|
}
|
6993
6998
|
# convert Unix seconds to FILETIME structs
|
@@ -7005,13 +7010,13 @@ sub SetFileTime($$;$$$$)
|
|
7005
7010
|
return 0 if defined $k32SetFileTime;
|
7006
7011
|
$k32SetFileTime = Win32::API->new('KERNEL32', 'SetFileTime', 'NPPP', 'I');
|
7007
7012
|
unless ($k32SetFileTime) {
|
7008
|
-
$self->Warn('Error
|
7013
|
+
$self->Warn('Error loading Win32::API SetFileTime');
|
7009
7014
|
$k32SetFileTime = 0;
|
7010
7015
|
return 0;
|
7011
7016
|
}
|
7012
7017
|
}
|
7013
7018
|
unless ($k32SetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) {
|
7014
|
-
$self->Warn('Win32::API
|
7019
|
+
$self->Warn('Win32::API SetFileTime returned ' . Win32::GetLastError());
|
7015
7020
|
return 0;
|
7016
7021
|
}
|
7017
7022
|
return 1;
|
@@ -50,7 +50,7 @@ use Image::ExifTool::Exif;
|
|
50
50
|
use Image::ExifTool::GPS;
|
51
51
|
require Exporter;
|
52
52
|
|
53
|
-
$VERSION = '3.
|
53
|
+
$VERSION = '3.69';
|
54
54
|
@ISA = qw(Exporter);
|
55
55
|
@EXPORT_OK = qw(EscapeXML UnescapeXML);
|
56
56
|
|
@@ -203,6 +203,7 @@ my %xmpNS = (
|
|
203
203
|
hdr_metadata => 'http://ns.adobe.com/hdr-metadata/1.0/',
|
204
204
|
hdrgm => 'http://ns.adobe.com/hdr-gain-map/1.0/',
|
205
205
|
xmpDSA => 'http://leica-camera.com/digital-shift-assistant/1.0/',
|
206
|
+
seal => 'http://ns.seal/2024/1.0/',
|
206
207
|
# Note: Google uses a prefix of 'Container', but this conflicts with the
|
207
208
|
# Device Container namespace, also by Google. So call this one GContainer
|
208
209
|
GContainer=> 'http://ns.google.com/photos/1.0/container/',
|
@@ -933,6 +934,10 @@ my %sRangeMask = (
|
|
933
934
|
Name => 'xmpDSA',
|
934
935
|
SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::DSA' },
|
935
936
|
},
|
937
|
+
seal => {
|
938
|
+
Name => 'seal',
|
939
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::seal' },
|
940
|
+
},
|
936
941
|
GContainer => {
|
937
942
|
Name => 'GContainer',
|
938
943
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::GContainer' },
|
@@ -4142,7 +4147,7 @@ sub ParseXMPElement($$$;$$$$)
|
|
4142
4147
|
|
4143
4148
|
#------------------------------------------------------------------------------
|
4144
4149
|
# Process XMP data
|
4145
|
-
# Inputs: 0) ExifTool
|
4150
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
4146
4151
|
# Returns: 1 on success
|
4147
4152
|
# Notes: The following flavours of XMP files are currently recognized:
|
4148
4153
|
# - standard XMP with xpacket, x:xmpmeta and rdf:RDF elements
|
@@ -25,6 +25,7 @@ use strict;
|
|
25
25
|
use Image::ExifTool qw(:Utils);
|
26
26
|
use Image::ExifTool::XMP;
|
27
27
|
|
28
|
+
sub ProcessSEAL($$;$);
|
28
29
|
sub Init_crd($);
|
29
30
|
|
30
31
|
#------------------------------------------------------------------------------
|
@@ -2290,6 +2291,65 @@ my %sACDSeeRegionStruct = (
|
|
2290
2291
|
},
|
2291
2292
|
);
|
2292
2293
|
|
2294
|
+
%Image::ExifTool::XMP::seal = (
|
2295
|
+
GROUPS => { 0 => 'XMP', 1 => 'XMP-seal', 2 => 'Image' },
|
2296
|
+
NAMESPACE => 'seal',
|
2297
|
+
WRITABLE => 'string',
|
2298
|
+
NOTES => 'SEAL embedded in XMP.',
|
2299
|
+
seal => {
|
2300
|
+
Name => 'Seal',
|
2301
|
+
Binary => 1,
|
2302
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::SEAL' },
|
2303
|
+
},
|
2304
|
+
);
|
2305
|
+
|
2306
|
+
%Image::ExifTool::XMP::SEAL = (
|
2307
|
+
GROUPS => { 0 => 'XML', 1 => 'SEAL', 2 => 'Document' },
|
2308
|
+
PROCESS_PROC => \&ProcessSEAL,
|
2309
|
+
NOTES => q{
|
2310
|
+
These tags are used in SEAL content authentification, which is actually XML
|
2311
|
+
format, not XMP. ExifTool has read/delete support for SEAL information in
|
2312
|
+
JPG, TIFF, XMP, PNG, WEBP, HEIC, PPM, MOV and MP4 files, and read-only
|
2313
|
+
support in PDF, MKV and WAV. Use C<-seal:all=> on the command line to
|
2314
|
+
delete SEAL information in supported formats.
|
2315
|
+
},
|
2316
|
+
seal=> 'SEALVersion',
|
2317
|
+
kv => 'KeyVersion',
|
2318
|
+
ka => 'KeyAlgorithm',
|
2319
|
+
da => 'DigestAlgorithm',
|
2320
|
+
sf => 'SignatureFormat',
|
2321
|
+
d => 'Domain',
|
2322
|
+
b => 'ByteRange',
|
2323
|
+
's' => 'Signature',
|
2324
|
+
info=> 'SEALComment',
|
2325
|
+
copyright => { Name => 'Copyright', Groups => { 2 => 'Author' } },
|
2326
|
+
);
|
2327
|
+
|
2328
|
+
#------------------------------------------------------------------------------
|
2329
|
+
# We found a SEAL property name/value
|
2330
|
+
# Inputs: 0) ExifTool ref, 1) tag table ref, 2) xmp property list ref
|
2331
|
+
# 3) property value, 4) attribute hash ref
|
2332
|
+
# Returns: 1 if valid tag was found
|
2333
|
+
sub FoundSEAL($$$$;$)
|
2334
|
+
{
|
2335
|
+
my ($et, $tagTablePtr, $props, $val, $attrs) = @_;
|
2336
|
+
# remove 'seal' container property from name
|
2337
|
+
my @sealProps = @$props;
|
2338
|
+
shift @sealProps if @sealProps and $sealProps[0] eq 'seal';
|
2339
|
+
return FoundXMP($et, $tagTablePtr, \@sealProps, $val, $attrs);
|
2340
|
+
}
|
2341
|
+
|
2342
|
+
#------------------------------------------------------------------------------
|
2343
|
+
# Process SEAL XML
|
2344
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
2345
|
+
# Returns: 1 on success
|
2346
|
+
sub ProcessSEAL($$;$)
|
2347
|
+
{
|
2348
|
+
my ($et, $dirInfo, $tagTablePtr) = @_;
|
2349
|
+
$$dirInfo{XMPParseOpts}{FoundProc} = \&FoundSEAL;
|
2350
|
+
return ProcessXMP($et, $dirInfo, $tagTablePtr);
|
2351
|
+
}
|
2352
|
+
|
2293
2353
|
#------------------------------------------------------------------------------
|
2294
2354
|
# Generate crd tags
|
2295
2355
|
# Inputs: 0) tag table ref
|
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 = '13.
|
32
|
+
$VERSION = '13.03';
|
33
33
|
$RELEASE = '';
|
34
34
|
@ISA = qw(Exporter);
|
35
35
|
%EXPORT_TAGS = (
|
@@ -4660,74 +4660,56 @@ sub EncodeFileName($$;$)
|
|
4660
4660
|
# References:
|
4661
4661
|
# - https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
|
4662
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;
|
4663
4672
|
sub WindowsLongPath($$)
|
4664
4673
|
{
|
4665
4674
|
my ($self, $path) = @_;
|
4666
4675
|
my $debug = $$self{OPTIONS}{Debug};
|
4667
4676
|
my $out = $$self{OPTIONS}{TextOut};
|
4668
|
-
my @fullParts;
|
4669
|
-
my $prefixLen = 0;
|
4670
4677
|
|
4671
4678
|
$debug and print $out "WindowsLongPath input : $path\n";
|
4672
|
-
$path =~ tr(/)(\\); # convert slashes to backslashes
|
4673
|
-
my @pathParts = split /\\/, $path;
|
4674
4679
|
|
4675
|
-
|
4676
|
-
$path =~
|
4677
|
-
|
4678
|
-
|
4679
|
-
$
|
4680
|
-
|
4681
|
-
|
4682
|
-
|
4683
|
-
|
4684
|
-
|
4685
|
-
|
4686
|
-
|
4687
|
-
|
4688
|
-
|
4689
|
-
|
4690
|
-
|
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
|
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
|
+
}
|
4705
4696
|
}
|
4706
|
-
|
4707
|
-
|
4708
|
-
|
4709
|
-
|
4710
|
-
$
|
4711
|
-
|
4712
|
-
|
4713
|
-
|
4714
|
-
|
4715
|
-
if ($
|
4716
|
-
|
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;
|
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);
|
4723
4708
|
} else {
|
4724
|
-
|
4709
|
+
$path = '\\\\?\\' . $path;
|
4725
4710
|
}
|
4711
|
+
last;
|
4726
4712
|
}
|
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
4713
|
$debug and print $out "WindowsLongPath return: $path\n";
|
4732
4714
|
return $path;
|
4733
4715
|
}
|
@@ -4861,16 +4843,16 @@ sub CreateDirectory($$)
|
|
4861
4843
|
my $d2 = $dir; # (must make a copy in case EncodeFileName recodes it)
|
4862
4844
|
if ($self->EncodeFileName($d2)) {
|
4863
4845
|
# handle Windows Unicode directory names
|
4864
|
-
unless (eval { require Win32::API }) {
|
4865
|
-
$err = 'Install Win32::API to create directories with Unicode names';
|
4866
|
-
last;
|
4867
|
-
}
|
4868
4846
|
unless (defined $k32CreateDir) {
|
4847
|
+
unless (eval { require Win32::API }) {
|
4848
|
+
$err = 'Install Win32::API to create directories with Unicode names';
|
4849
|
+
last;
|
4850
|
+
}
|
4869
4851
|
$k32CreateDir = Win32::API->new('KERNEL32', 'CreateDirectoryW', 'PP', 'I');
|
4870
4852
|
unless ($k32CreateDir) {
|
4871
4853
|
$k32CreateDir = 0;
|
4872
4854
|
# give this error once, then just "Error creating" for subsequent attempts
|
4873
|
-
return 'Error
|
4855
|
+
return 'Error loading Win32::API CreateDirectoryW';
|
4874
4856
|
}
|
4875
4857
|
}
|
4876
4858
|
$success = $k32CreateDir->Call($d2, 0) if $k32CreateDir;
|
@@ -4928,13 +4910,13 @@ sub GetFileTime($$)
|
|
4928
4910
|
return () if defined $k32GetFileTime;
|
4929
4911
|
$k32GetFileTime = Win32::API->new('KERNEL32', 'GetFileTime', 'NPPP', 'I');
|
4930
4912
|
unless ($k32GetFileTime) {
|
4931
|
-
$self->Warn('Error
|
4913
|
+
$self->Warn('Error loading Win32::API GetFileTime');
|
4932
4914
|
$k32GetFileTime = 0;
|
4933
4915
|
return ();
|
4934
4916
|
}
|
4935
4917
|
}
|
4936
4918
|
unless ($k32GetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) {
|
4937
|
-
$self->Warn("Win32::API
|
4919
|
+
$self->Warn("Win32::API GetFileTime returned " . Win32::GetLastError());
|
4938
4920
|
return ();
|
4939
4921
|
}
|
4940
4922
|
# convert FILETIME structs to Unix seconds
|
@@ -5085,7 +5067,7 @@ sub IsSameID($$)
|
|
5085
5067
|
|
5086
5068
|
#------------------------------------------------------------------------------
|
5087
5069
|
# Get list of tags in specified group
|
5088
|
-
# 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
|
5089
5071
|
# Returns: list of matching tags in list context, or first match in scalar context
|
5090
5072
|
# Notes: Group spec may contain multiple groups separated by colons, each
|
5091
5073
|
# possibly with a leading family number
|
@@ -6450,9 +6432,45 @@ sub ConvertDateTime($$)
|
|
6450
6432
|
my $fmt = $$self{OPTIONS}{DateFormat};
|
6451
6433
|
my $shift = $$self{OPTIONS}{GlobalTimeShift};
|
6452
6434
|
if ($shift) {
|
6453
|
-
my $dir = ($shift =~ s/^([-+])// and $1 eq '-') ? -1 : 1;
|
6454
6435
|
my $offset = $$self{GLOBAL_TIME_OFFSET};
|
6455
|
-
$
|
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
|
+
}
|
6456
6474
|
ShiftTime($date, $shift, $dir, $offset);
|
6457
6475
|
}
|
6458
6476
|
# only convert date if a format was specified and the date is recognizable
|
@@ -7952,6 +7970,10 @@ sub ProcessJPEG($$;$)
|
|
7952
7970
|
SetByteOrder('II');
|
7953
7971
|
my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Isothermal');
|
7954
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"));
|
7955
7977
|
}
|
7956
7978
|
} elsif ($marker == 0xe9) { # APP9 (InfiRay, Media Jukebox)
|
7957
7979
|
if ($$segDataPt =~ /^Media Jukebox\0/ and $length > 22) {
|
@@ -7967,6 +7989,10 @@ sub ProcessJPEG($$;$)
|
|
7967
7989
|
SetByteOrder('II');
|
7968
7990
|
my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Sensor');
|
7969
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"));
|
7970
7996
|
}
|
7971
7997
|
} elsif ($marker == 0xea) { # APP10 (PhotoStudio Unicode comments)
|
7972
7998
|
if ($$segDataPt =~ /^UNICODE\0/) {
|
@@ -9102,6 +9128,7 @@ sub HandleTag($$$$;%)
|
|
9102
9128
|
Base => $parms{Base},
|
9103
9129
|
Multi => $$subdir{Multi},
|
9104
9130
|
TagInfo => $tagInfo,
|
9131
|
+
IgnoreProp => $$subdir{IgnoreProp},
|
9105
9132
|
RAF => $parms{RAF},
|
9106
9133
|
);
|
9107
9134
|
my $oldOrder = GetByteOrder();
|