exiftool-vendored.exe 12.62.0 → 12.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +254 -254
- package/bin/exiftool_files/Changes +96 -1
- package/bin/exiftool_files/Makefile.PL +7 -1
- package/bin/exiftool_files/README +50 -46
- package/bin/exiftool_files/config_files/guano.config +161 -0
- package/bin/exiftool_files/exiftool.pl +113 -78
- package/bin/exiftool_files/lib/Image/ExifTool/7Z.pm +793 -0
- package/bin/exiftool_files/lib/Image/ExifTool/Apple.pm +19 -8
- package/bin/exiftool_files/lib/Image/ExifTool/BigTIFF.pm +8 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +33 -12
- package/bin/exiftool_files/lib/Image/ExifTool/CanonRaw.pm +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/CanonVRD.pm +4 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +31 -14
- package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +8 -2
- package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +6 -3
- package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +5 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +5 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +226 -28
- package/bin/exiftool_files/lib/Image/ExifTool/Lang/fr.pm +1467 -202
- package/bin/exiftool_files/lib/Image/ExifTool/MPF.pm +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Matroska.pm +16 -1
- package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +948 -31
- package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +874 -63
- package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +23 -5
- package/bin/exiftool_files/lib/Image/ExifTool/PLIST.pm +8 -1
- package/bin/exiftool_files/lib/Image/ExifTool/PLUS.pm +19 -4
- package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +6 -6
- package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +3 -1
- package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +5 -5
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +91 -30
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +20 -19
- package/bin/exiftool_files/lib/Image/ExifTool/README +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +11 -9
- package/bin/exiftool_files/lib/Image/ExifTool/Samsung.pm +227 -227
- package/bin/exiftool_files/lib/Image/ExifTool/Shortcuts.pm +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +229 -30
- package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +4758 -4633
- package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +715 -23
- package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +17 -1
- package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +9 -7
- package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +21 -9
- package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +36 -12
- package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +14 -2
- package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +33 -1
- package/bin/exiftool_files/lib/Image/ExifTool/XMPStruct.pl +96 -28
- package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +5 -5
- package/bin/exiftool_files/lib/Image/ExifTool.pm +184 -132
- package/bin/exiftool_files/lib/Image/ExifTool.pod +124 -58
- package/package.json +3 -2
|
@@ -17,7 +17,7 @@ package Image::ExifTool::Validate;
|
|
|
17
17
|
use strict;
|
|
18
18
|
use vars qw($VERSION %exifSpec);
|
|
19
19
|
|
|
20
|
-
$VERSION = '1.
|
|
20
|
+
$VERSION = '1.21';
|
|
21
21
|
|
|
22
22
|
use Image::ExifTool qw(:Utils);
|
|
23
23
|
use Image::ExifTool::Exif;
|
|
@@ -56,6 +56,15 @@ use Image::ExifTool::Exif;
|
|
|
56
56
|
0x212 => 1, 0x9204 => 1, 0xa210 => 1, 0xa500 => 221,
|
|
57
57
|
0x213 => 1, 0x9205 => 1, 0xa214 => 1,
|
|
58
58
|
0x214 => 1, 0x9206 => 1, 0xa215 => 1,
|
|
59
|
+
|
|
60
|
+
# new Exif 3.0 tags
|
|
61
|
+
0xa436 => 300,
|
|
62
|
+
0xa437 => 300,
|
|
63
|
+
0xa438 => 300,
|
|
64
|
+
0xa439 => 300,
|
|
65
|
+
0xa43a => 300,
|
|
66
|
+
0xa43b => 300,
|
|
67
|
+
0xa43c => 300,
|
|
59
68
|
);
|
|
60
69
|
|
|
61
70
|
# GPSVersionID numbers when each tag was introduced
|
|
@@ -147,6 +156,13 @@ my %stdFormat = (
|
|
|
147
156
|
0xc68d => 'int(16|32)u', 0xc791 => 'int(16|32)u',
|
|
148
157
|
0xc68e => 'int(16|32)u', 0xc792 => 'int(16|32)u',
|
|
149
158
|
0xc6d2 => '', 0xc793 => '(int16u|int32u|rational64u)',
|
|
159
|
+
# Exif 3.0 spec
|
|
160
|
+
0x10e => 'string|utf8', 0xa430 => 'string|utf8', 0xa439 => 'string|utf8',
|
|
161
|
+
0x10f => 'string|utf8', 0xa433 => 'string|utf8', 0xa43a => 'string|utf8',
|
|
162
|
+
0x110 => 'string|utf8', 0xa434 => 'string|utf8', 0xa43b => 'string|utf8',
|
|
163
|
+
0x131 => 'string|utf8', 0xa436 => 'string|utf8', 0xa43c => 'string|utf8',
|
|
164
|
+
0x13b => 'string|utf8', 0xa437 => 'string|utf8', 0xa43a => 'string|utf8',
|
|
165
|
+
0x8298 => 'string|utf8', 0xa438 => 'string|utf8',
|
|
150
166
|
},
|
|
151
167
|
);
|
|
152
168
|
|
|
@@ -420,15 +420,15 @@ sub ValidateImageData($$$;$)
|
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
#------------------------------------------------------------------------------
|
|
423
|
-
# Add specified image data to
|
|
423
|
+
# Add specified image data to ImageDataHash hash
|
|
424
424
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) lookup for [tagInfo,value] based on tagID
|
|
425
|
-
sub
|
|
425
|
+
sub AddImageDataHash($$$)
|
|
426
426
|
{
|
|
427
427
|
my ($et, $dirInfo, $offsetInfo) = @_;
|
|
428
428
|
my ($tagID, $offset, $buff);
|
|
429
429
|
|
|
430
430
|
my $verbose = $et->Options('Verbose');
|
|
431
|
-
my $
|
|
431
|
+
my $hash = $$et{ImageDataHash};
|
|
432
432
|
my $raf = $$dirInfo{RAF};
|
|
433
433
|
|
|
434
434
|
foreach $tagID (sort keys %$offsetInfo) {
|
|
@@ -451,12 +451,12 @@ sub AddImageDataMD5($$$)
|
|
|
451
451
|
my $size = shift @sizes;
|
|
452
452
|
next unless $offset =~ /^\d+$/ and $size and $size =~ /^\d+$/ and $size;
|
|
453
453
|
next unless $raf->Seek($offset, 0); # (offset is absolute)
|
|
454
|
-
$total += $et->
|
|
454
|
+
$total += $et->ImageDataHash($raf, $size);
|
|
455
455
|
}
|
|
456
456
|
if ($verbose) {
|
|
457
457
|
my $name = "$$dirInfo{DirName}:$$tagInfo{Name}";
|
|
458
458
|
$name =~ s/Offsets?|Start$//;
|
|
459
|
-
$et->VPrint(0, "$$et{INDENT}(
|
|
459
|
+
$et->VPrint(0, "$$et{INDENT}(ImageDataHash: $total bytes of $name data)\n");
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
}
|
|
@@ -780,7 +780,7 @@ Entry: for (;;) {
|
|
|
780
780
|
$readFormat = $oldFormat = Get16u($dataPt, $entry+2);
|
|
781
781
|
$readCount = $oldCount = Get32u($dataPt, $entry+4);
|
|
782
782
|
undef $oldImageData;
|
|
783
|
-
if ($oldFormat < 1 or $oldFormat > 13 and not ($oldFormat == 16 and $$et{Make} eq 'Apple' and $inMakerNotes)) {
|
|
783
|
+
if (($oldFormat < 1 or $oldFormat > 13) and $oldFormat != 129 and not ($oldFormat == 16 and $$et{Make} eq 'Apple' and $inMakerNotes)) {
|
|
784
784
|
my $msg = "Bad format ($oldFormat) for $name entry $index";
|
|
785
785
|
# patch to preserve invalid directory entries in SubIFD3 of
|
|
786
786
|
# various Kodak Z-series cameras (Z812, Z1085IS, Z1275)
|
|
@@ -1277,7 +1277,9 @@ NoWrite: next if $isNew > 0;
|
|
|
1277
1277
|
$et->Warn("Writing large value for $name",1);
|
|
1278
1278
|
}
|
|
1279
1279
|
# re-code if necessary
|
|
1280
|
-
if ($
|
|
1280
|
+
if ($newFormName eq 'utf8') {
|
|
1281
|
+
$newValue = $et->Encode($newValue, 'UTF8');
|
|
1282
|
+
} elsif ($strEnc and $newFormName eq 'string') {
|
|
1281
1283
|
$newValue = $et->Encode($newValue, $strEnc);
|
|
1282
1284
|
}
|
|
1283
1285
|
} else {
|
|
@@ -973,16 +973,14 @@ sub WriteQuickTime($$$)
|
|
|
973
973
|
}
|
|
974
974
|
} elsif ($tag eq 'CTBO' or $tag eq 'uuid') { # hack for updating CR3 CTBO offsets
|
|
975
975
|
push @{$$dirInfo{ChunkOffset}}, [ $tag, length($$outfile), length($hdr) + $size ];
|
|
976
|
-
} elsif (not $flg) {
|
|
977
|
-
|
|
978
|
-
$
|
|
979
|
-
|
|
976
|
+
} elsif (not $flg or $flg == 1) {
|
|
977
|
+
# assume "1" if stsd is yet to be read
|
|
978
|
+
$flg or $$et{AssumedDataRef} = 1;
|
|
979
|
+
# must update offsets since the data is in this file
|
|
980
|
+
push @{$$dirInfo{ChunkOffset}}, [ $tag, length($$outfile) + length($hdr), $size ];
|
|
980
981
|
} elsif ($flg == 3) {
|
|
981
982
|
$et->Error("Can't write files with mixed internal/external media data");
|
|
982
983
|
return $rtnVal;
|
|
983
|
-
} elsif ($flg == 1) {
|
|
984
|
-
# must update offsets since the data is in this file
|
|
985
|
-
push @{$$dirInfo{ChunkOffset}}, [ $tag, length($$outfile) + length($hdr), $size ];
|
|
986
984
|
}
|
|
987
985
|
}
|
|
988
986
|
|
|
@@ -1036,8 +1034,10 @@ sub WriteQuickTime($$$)
|
|
|
1036
1034
|
|
|
1037
1035
|
if ($subdir) { # process atoms in this container from a buffer in memory
|
|
1038
1036
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1037
|
+
if ($tag eq 'trak') {
|
|
1038
|
+
undef $$et{HandlerType}; # init handler type for this track
|
|
1039
|
+
delete $$et{AssumedDataRef};
|
|
1040
|
+
}
|
|
1041
1041
|
my $subName = $$subdir{DirName} || $$tagInfo{Name};
|
|
1042
1042
|
my $start = $$subdir{Start} || 0;
|
|
1043
1043
|
my $base = ($$dirInfo{Base} || 0) + $raf->Tell() - $size;
|
|
@@ -1103,6 +1103,11 @@ sub WriteQuickTime($$$)
|
|
|
1103
1103
|
$$et{CHANGED} = $oldChanged;
|
|
1104
1104
|
undef $newData;
|
|
1105
1105
|
}
|
|
1106
|
+
if ($tag eq 'trak' and $$et{AssumedDataRef}) {
|
|
1107
|
+
my $grp = $$et{CUR_WRITE_GROUP} || $dirName;
|
|
1108
|
+
$et->Error("Can't locate data reference to update offsets for $grp");
|
|
1109
|
+
delete $$et{AssumedDataRef};
|
|
1110
|
+
}
|
|
1106
1111
|
$$et{CUR_WRITE_GROUP} = $oldWriteGroup;
|
|
1107
1112
|
SetByteOrder('MM');
|
|
1108
1113
|
# add back header if necessary
|
|
@@ -1405,6 +1410,13 @@ sub WriteQuickTime($$$)
|
|
|
1405
1410
|
$flg = 1; # (this seems to be the case)
|
|
1406
1411
|
}
|
|
1407
1412
|
$$et{QtDataFlg} = $flg;
|
|
1413
|
+
if ($$et{AssumedDataRef}) {
|
|
1414
|
+
if ($flg != $$et{AssumedDataRef}) {
|
|
1415
|
+
my $grp = $$et{CUR_WRITE_GROUP} || $parent;
|
|
1416
|
+
$et->Error("Assumed incorrect data reference for $grp (was $flg)");
|
|
1417
|
+
}
|
|
1418
|
+
delete $$et{AssumedDataRef};
|
|
1419
|
+
}
|
|
1408
1420
|
}
|
|
1409
1421
|
if ($tagInfo and $$tagInfo{WriteLast}) {
|
|
1410
1422
|
$writeLast = ($writeLast || '') . $hdr . $buff;
|
|
@@ -176,7 +176,7 @@ sub CheckXMP($$$;$)
|
|
|
176
176
|
require 'Image/ExifTool/XMPStruct.pl';
|
|
177
177
|
my ($item, $err, $w, $warn);
|
|
178
178
|
unless (ref $$valPtr) {
|
|
179
|
-
($$valPtr, $warn) = InflateStruct($valPtr);
|
|
179
|
+
($$valPtr, $warn) = InflateStruct($et, $valPtr);
|
|
180
180
|
# expect a structure HASH ref or ARRAY of structures
|
|
181
181
|
unless (ref $$valPtr) {
|
|
182
182
|
$$valPtr eq '' and $$valPtr = { }, return undef; # allow empty structures
|
|
@@ -189,7 +189,7 @@ sub CheckXMP($$$;$)
|
|
|
189
189
|
$$valPtr = \@copy; # return the copy
|
|
190
190
|
foreach $item (@copy) {
|
|
191
191
|
unless (ref $item eq 'HASH') {
|
|
192
|
-
($item, $w) = InflateStruct(\$item); # deserialize structure
|
|
192
|
+
($item, $w) = InflateStruct($et, \$item); # deserialize structure
|
|
193
193
|
$w and $warn = $w;
|
|
194
194
|
next if ref $item eq 'HASH';
|
|
195
195
|
$err = 'Improperly formed structure';
|
|
@@ -139,7 +139,7 @@ my @delGroups = qw(
|
|
|
139
139
|
Adobe AFCP APP0 APP1 APP2 APP3 APP4 APP5 APP6 APP7 APP8 APP9 APP10 APP11
|
|
140
140
|
APP12 APP13 APP14 APP15 CanonVRD CIFF Ducky EXIF ExifIFD File FlashPix
|
|
141
141
|
FotoStation GlobParamIFD GPS ICC_Profile IFD0 IFD1 Insta360 InteropIFD IPTC
|
|
142
|
-
ItemList JFIF Jpeg2000 Keys MakerNotes Meta MetaIFD Microsoft MIE MPF
|
|
142
|
+
ItemList JFIF Jpeg2000 JUMBF Keys MakerNotes Meta MetaIFD Microsoft MIE MPF
|
|
143
143
|
NikonApp NikonCapture PDF PDF-update PhotoMechanic Photoshop PNG PNG-pHYs
|
|
144
144
|
PrintIM QuickTime RMETA RSRC SubIFD Trailer UserData XML XML-* XMP XMP-*
|
|
145
145
|
);
|
|
@@ -1028,7 +1028,7 @@ TAG: foreach $tagInfo (@matchingTags) {
|
|
|
1028
1028
|
foreach (@vals) {
|
|
1029
1029
|
if (ref $_ eq 'HASH') {
|
|
1030
1030
|
require 'Image/ExifTool/XMPStruct.pl';
|
|
1031
|
-
$_ = Image::ExifTool::XMP::SerializeStruct($_);
|
|
1031
|
+
$_ = Image::ExifTool::XMP::SerializeStruct($self, $_);
|
|
1032
1032
|
}
|
|
1033
1033
|
print $out "$$self{INDENT2}$verb $wgrp1:$tag$fromList if value is '${_}'\n";
|
|
1034
1034
|
}
|
|
@@ -1293,6 +1293,7 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1293
1293
|
HexTagIDs => $$options{HexTagIDs},
|
|
1294
1294
|
IgnoreMinorErrors=>$$options{IgnoreMinorErrors},
|
|
1295
1295
|
IgnoreTags => $$options{IgnoreTags},
|
|
1296
|
+
ImageHashType => $$options{ImageHashType},
|
|
1296
1297
|
Lang => $$options{Lang},
|
|
1297
1298
|
LargeFileSupport=> $$options{LargeFileSupport},
|
|
1298
1299
|
List => 1,
|
|
@@ -1313,11 +1314,13 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1313
1314
|
ScanForXMP => $$options{ScanForXMP},
|
|
1314
1315
|
StrictDate => defined $$options{StrictDate} ? $$options{StrictDate} : 1,
|
|
1315
1316
|
Struct => $structOpt,
|
|
1317
|
+
StructFormat => $$options{StructFormat},
|
|
1316
1318
|
SystemTags => $$options{SystemTags},
|
|
1317
1319
|
TimeZone => $$options{TimeZone},
|
|
1318
1320
|
Unknown => $$options{Unknown},
|
|
1319
1321
|
UserParam => $$options{UserParam},
|
|
1320
1322
|
Validate => $$options{Validate},
|
|
1323
|
+
WindowsWideFile => $$options{WindowsWideFile},
|
|
1321
1324
|
XAttrTags => $$options{XAttrTags},
|
|
1322
1325
|
XMPAutoConv => $$options{XMPAutoConv},
|
|
1323
1326
|
);
|
|
@@ -1763,7 +1766,14 @@ GNV_TagInfo: foreach $tagInfo (@tagInfoList) {
|
|
|
1763
1766
|
}
|
|
1764
1767
|
}
|
|
1765
1768
|
# return our value(s)
|
|
1766
|
-
|
|
1769
|
+
if (wantarray) {
|
|
1770
|
+
# remove duplicates if requested
|
|
1771
|
+
if (@$vals > 1 and $self->Options('NoDups')) {
|
|
1772
|
+
my %seen;
|
|
1773
|
+
@$vals = grep { !$seen{$_}++ } @$vals;
|
|
1774
|
+
}
|
|
1775
|
+
return @$vals;
|
|
1776
|
+
}
|
|
1767
1777
|
return $$vals[0];
|
|
1768
1778
|
}
|
|
1769
1779
|
|
|
@@ -2016,7 +2026,7 @@ sub SetFileName($$;$$$)
|
|
|
2016
2026
|
# protect against empty file name
|
|
2017
2027
|
length $newName or $self->Warn('New file name is empty'), return -1;
|
|
2018
2028
|
# don't replace existing file
|
|
2019
|
-
if ($self->Exists($newName) and (not defined $usedFlag or $usedFlag)) {
|
|
2029
|
+
if ($self->Exists($newName, 1) and (not defined $usedFlag or $usedFlag)) {
|
|
2020
2030
|
if ($file ne $newName or $opt =~ /Link$/) {
|
|
2021
2031
|
# allow for case-insensitive filesystem
|
|
2022
2032
|
if ($opt =~ /Link$/ or not $self->IsSameFile($file, $newName)) {
|
|
@@ -2395,7 +2405,7 @@ sub WriteInfo($$;$$)
|
|
|
2395
2405
|
$outBuff = '';
|
|
2396
2406
|
$outRef = \$outBuff;
|
|
2397
2407
|
$outPos = 0;
|
|
2398
|
-
} elsif ($self->Exists($outfile)) {
|
|
2408
|
+
} elsif ($self->Exists($outfile, 1)) {
|
|
2399
2409
|
$self->Error("File already exists: $outfile");
|
|
2400
2410
|
} elsif ($self->Open(\*EXIFTOOL_OUTFILE, $outfile, '>')) {
|
|
2401
2411
|
$outRef = \*EXIFTOOL_OUTFILE;
|
|
@@ -3283,7 +3293,7 @@ sub InsertTagValues($$$;$$$)
|
|
|
3283
3293
|
}
|
|
3284
3294
|
} elsif (ref $val eq 'HASH') {
|
|
3285
3295
|
require 'Image/ExifTool/XMPStruct.pl';
|
|
3286
|
-
$val = Image::ExifTool::XMP::SerializeStruct($val);
|
|
3296
|
+
$val = Image::ExifTool::XMP::SerializeStruct($self, $val);
|
|
3287
3297
|
} elsif (not defined $val) {
|
|
3288
3298
|
$val = $$self{OPTIONS}{MissingTagValue} if $asList;
|
|
3289
3299
|
}
|
|
@@ -3767,6 +3777,8 @@ sub GetNewValueHash($$;$$$$)
|
|
|
3767
3777
|
# this is a bit tricky: we want to add to a protected nvHash only if we
|
|
3768
3778
|
# are adding a conditional delete ($_[5] true or DelValue with no Shift)
|
|
3769
3779
|
# or accumulating List items (NoReplace true)
|
|
3780
|
+
# (NOTE: this should be looked into --> lists may be accumulated instead of being replaced
|
|
3781
|
+
# as expected when copying to the same list from different dynamic -tagsFromFile source files)
|
|
3770
3782
|
if ($protect and not ($opts{create} and ($$nvHash{NoReplace} or $_[5] or
|
|
3771
3783
|
($$nvHash{DelValue} and not defined $$nvHash{Shift}))))
|
|
3772
3784
|
{
|
|
@@ -4908,6 +4920,11 @@ sub InverseDateTime($$;$$)
|
|
|
4908
4920
|
my $fs = ($fmt =~ s/%f$// and $val =~ s/(\.\d+)\s*$//) ? $1 : '';
|
|
4909
4921
|
my ($lib, $wrn, @a);
|
|
4910
4922
|
TryLib: for ($lib=$strptimeLib; ; $lib='') {
|
|
4923
|
+
# handle %s format ourself (not supported in Fedora, see forum15032)
|
|
4924
|
+
if ($fmt eq '%s') {
|
|
4925
|
+
$val = ConvertUnixTime($val, 1);
|
|
4926
|
+
last;
|
|
4927
|
+
}
|
|
4911
4928
|
if (not $lib) {
|
|
4912
4929
|
last unless $$self{OPTIONS}{StrictDate};
|
|
4913
4930
|
warn $wrn || "Install POSIX::strptime or Time::Piece for inverse date/time conversions\n";
|
|
@@ -5598,6 +5615,8 @@ sub WriteJPEG($$)
|
|
|
5598
5615
|
$s =~ /^(Meta|META|Exif)\0\0/ and $dirName = 'Meta';
|
|
5599
5616
|
} elsif ($marker == 0xe5) {
|
|
5600
5617
|
$s =~ /^RMETA\0/ and $dirName = 'RMETA';
|
|
5618
|
+
} elsif ($marker == 0xeb) {
|
|
5619
|
+
$s =~ /^JP/ and $dirName = 'JUMBF';
|
|
5601
5620
|
} elsif ($marker == 0xec) {
|
|
5602
5621
|
$s =~ /^Ducky/ and $dirName = 'Ducky';
|
|
5603
5622
|
} elsif ($marker == 0xed) {
|
|
@@ -6463,6 +6482,11 @@ sub WriteJPEG($$)
|
|
|
6463
6482
|
$segType = 'Ricoh RMETA';
|
|
6464
6483
|
$$delGroup{RMETA} and $del = 1;
|
|
6465
6484
|
}
|
|
6485
|
+
} elsif ($marker == 0xeb) { # APP10 (JUMBF)
|
|
6486
|
+
if ($$segDataPt =~ /^JP/) {
|
|
6487
|
+
$segType = 'JUMBF';
|
|
6488
|
+
$$delGroup{JUMBF} and $del = 1;
|
|
6489
|
+
}
|
|
6466
6490
|
} elsif ($marker == 0xec) { # APP12 (Ducky)
|
|
6467
6491
|
if ($$segDataPt =~ /^Ducky/) {
|
|
6468
6492
|
$segType = 'Ducky';
|
|
@@ -6880,14 +6904,14 @@ sub SetFileTime($$;$$$$)
|
|
|
6880
6904
|
}
|
|
6881
6905
|
|
|
6882
6906
|
#------------------------------------------------------------------------------
|
|
6883
|
-
# Add data to
|
|
6907
|
+
# Add data to hash checksum
|
|
6884
6908
|
# Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file),
|
|
6885
6909
|
# 3) data name (or undef for no warnings or messages), 4) flag for no verbose message
|
|
6886
|
-
# Returns: number of bytes read and
|
|
6887
|
-
sub
|
|
6910
|
+
# Returns: number of bytes read and hashed
|
|
6911
|
+
sub ImageDataHash($$$;$$)
|
|
6888
6912
|
{
|
|
6889
6913
|
my ($self, $raf, $size, $type, $noMsg) = @_;
|
|
6890
|
-
my $
|
|
6914
|
+
my $hash = $$self{ImageDataHash} or return;
|
|
6891
6915
|
my ($bytesRead, $n) = (0, 65536);
|
|
6892
6916
|
my $buff;
|
|
6893
6917
|
for (;;) {
|
|
@@ -6900,11 +6924,11 @@ sub ImageDataMD5($$$;$$)
|
|
|
6900
6924
|
$self->Warn("Error reading $type data") if $type and defined $size;
|
|
6901
6925
|
last;
|
|
6902
6926
|
}
|
|
6903
|
-
$
|
|
6927
|
+
$hash->add($buff);
|
|
6904
6928
|
$bytesRead += length $buff;
|
|
6905
6929
|
}
|
|
6906
6930
|
if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) {
|
|
6907
|
-
$self->VPrint(0, "$$self{INDENT}(
|
|
6931
|
+
$self->VPrint(0, "$$self{INDENT}(ImageDataHash: $bytesRead bytes of $type data)\n");
|
|
6908
6932
|
}
|
|
6909
6933
|
return $bytesRead;
|
|
6910
6934
|
}
|
|
@@ -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.60';
|
|
54
54
|
@ISA = qw(Exporter);
|
|
55
55
|
@EXPORT_OK = qw(EscapeXML UnescapeXML);
|
|
56
56
|
|
|
@@ -144,6 +144,7 @@ my %xmpNS = (
|
|
|
144
144
|
xmpTPg => 'http://ns.adobe.com/xap/1.0/t/pg/',
|
|
145
145
|
xmpidq => 'http://ns.adobe.com/xmp/Identifier/qual/1.0/',
|
|
146
146
|
xmpPLUS => 'http://ns.adobe.com/xap/1.0/PLUS/',
|
|
147
|
+
panorama => 'http://ns.adobe.com/photoshop/1.0/panorama-profile',
|
|
147
148
|
dex => 'http://ns.optimasc.com/dex/1.0/',
|
|
148
149
|
mediapro => 'http://ns.iview-multimedia.com/mediapro/1.0/',
|
|
149
150
|
expressionmedia => 'http://ns.microsoft.com/expressionmedia/1.0/',
|
|
@@ -199,6 +200,7 @@ my %xmpNS = (
|
|
|
199
200
|
ast => 'http://ns.nikon.com/asteroid/1.0/',
|
|
200
201
|
nine => 'http://ns.nikon.com/nine/1.0/',
|
|
201
202
|
hdr_metadata => 'http://ns.adobe.com/hdr-metadata/1.0/',
|
|
203
|
+
hdrgm => 'http://ns.adobe.com/hdr-gain-map/1.0/',
|
|
202
204
|
);
|
|
203
205
|
|
|
204
206
|
# build reverse namespace lookup
|
|
@@ -709,6 +711,10 @@ my %sRangeMask = (
|
|
|
709
711
|
Name => 'xmpPLUS',
|
|
710
712
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpPLUS' },
|
|
711
713
|
},
|
|
714
|
+
panorama => {
|
|
715
|
+
Name => 'panorama',
|
|
716
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::panorama' },
|
|
717
|
+
},
|
|
712
718
|
plus => {
|
|
713
719
|
Name => 'plus',
|
|
714
720
|
SubDirectory => { TagTable => 'Image::ExifTool::PLUS::XMP' },
|
|
@@ -909,6 +915,10 @@ my %sRangeMask = (
|
|
|
909
915
|
Name => 'hdr',
|
|
910
916
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::hdr' },
|
|
911
917
|
},
|
|
918
|
+
hdrgm => {
|
|
919
|
+
Name => 'hdrgm',
|
|
920
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::hdrgm' },
|
|
921
|
+
},
|
|
912
922
|
);
|
|
913
923
|
|
|
914
924
|
# hack to allow XML containing Dublin Core metadata to be handled like XMP (eg. EPUB - see ZIP.pm)
|
|
@@ -2566,7 +2576,9 @@ my %sPantryItem = (
|
|
|
2566
2576
|
%xmpTableDefaults,
|
|
2567
2577
|
GROUPS => { 1 => 'XMP-et', 2 => 'Image' },
|
|
2568
2578
|
NAMESPACE => 'et',
|
|
2569
|
-
|
|
2579
|
+
OriginalImageHash => { Notes => 'used to store ExifTool ImageDataHash digest' },
|
|
2580
|
+
OriginalImageHashType => { Notes => "ImageHashType API setting, default 'MD5'" },
|
|
2581
|
+
OriginalImageMD5 => { Notes => 'deprecated' },
|
|
2570
2582
|
);
|
|
2571
2583
|
|
|
2572
2584
|
# table to add tags in other namespaces
|
|
@@ -1063,7 +1063,7 @@ my %prismPublicationDate = (
|
|
|
1063
1063
|
NOTES => q{
|
|
1064
1064
|
PRISM Rights Language 2.1 namespace tags. These tags have been deprecated
|
|
1065
1065
|
since the release of the PRISM Usage Rights 3.0. (see
|
|
1066
|
-
L<
|
|
1066
|
+
L<https://www.w3.org/submissions/2020/SUBM-prism-20200910/prism-image.html>)
|
|
1067
1067
|
},
|
|
1068
1068
|
geography => { List => 'Bag' },
|
|
1069
1069
|
industry => { List => 'Bag' },
|
|
@@ -1388,6 +1388,17 @@ my %sSubVersion = (
|
|
|
1388
1388
|
ReuseAllowed => { Writable => 'boolean' },
|
|
1389
1389
|
);
|
|
1390
1390
|
|
|
1391
|
+
%Image::ExifTool::XMP::panorama = (
|
|
1392
|
+
%xmpTableDefaults,
|
|
1393
|
+
GROUPS => { 1 => 'XMP-panorama', 2 => 'Image' },
|
|
1394
|
+
NAMESPACE => 'panorama',
|
|
1395
|
+
NOTES => 'Adobe Photoshop Panorama-profile tags.',
|
|
1396
|
+
Transformation => { },
|
|
1397
|
+
VirtualFocalLength => { Writable => 'real' },
|
|
1398
|
+
VirtualImageXCenter => { Writable => 'real' },
|
|
1399
|
+
VirtualImageYCenter => { Writable => 'real' },
|
|
1400
|
+
);
|
|
1401
|
+
|
|
1391
1402
|
# Creative Commons namespace properties (cc) (ref 5)
|
|
1392
1403
|
%Image::ExifTool::XMP::cc = (
|
|
1393
1404
|
%xmpTableDefaults,
|
|
@@ -2094,6 +2105,27 @@ my %sSubVersion = (
|
|
|
2094
2105
|
scene_referred => { Name => 'SceneReferred', Writable => 'boolean' },
|
|
2095
2106
|
);
|
|
2096
2107
|
|
|
2108
|
+
# HDR Gain Map metadata namespace
|
|
2109
|
+
%Image::ExifTool::XMP::hdrgm = (
|
|
2110
|
+
%xmpTableDefaults,
|
|
2111
|
+
GROUPS => { 1 => 'XMP-hdrgm', 2 => 'Image' },
|
|
2112
|
+
NAMESPACE => 'hdrgm',
|
|
2113
|
+
TABLE_DESC => 'XMP HDR Gain Map Metadata',
|
|
2114
|
+
NOTES => 'Tags used in Adobe gain map images.',
|
|
2115
|
+
Version => { Avoid => 1 },
|
|
2116
|
+
BaseRenditionIsHDR => { Writable => 'boolean' },
|
|
2117
|
+
# this is a pain in the ass: List items below may or may not be lists
|
|
2118
|
+
# according to the Adobe specification -- I don't know how to handle tags
|
|
2119
|
+
# with a variable format like this, so just make them lists here for now
|
|
2120
|
+
OffsetSDR => { Writable => 'real', List => 'Seq' },
|
|
2121
|
+
OffsetHDR => { Writable => 'real', List => 'Seq' },
|
|
2122
|
+
HDRCapacityMin => { Writable => 'real' },
|
|
2123
|
+
HDRCapacityMax => { Writable => 'real' },
|
|
2124
|
+
GainMapMin => { Writable => 'real', List => 'Seq' },
|
|
2125
|
+
GainMapMax => { Writable => 'real', List => 'Seq' },
|
|
2126
|
+
Gamma => { Writable => 'real', List => 'Seq', Avoid => 1 },
|
|
2127
|
+
);
|
|
2128
|
+
|
|
2097
2129
|
# SVG namespace properties (ref 9)
|
|
2098
2130
|
%Image::ExifTool::XMP::SVG = (
|
|
2099
2131
|
GROUPS => { 0 => 'SVG', 1 => 'SVG', 2 => 'Image' },
|
|
@@ -14,42 +14,55 @@ use vars qw(%specialStruct %stdXlatNS);
|
|
|
14
14
|
use Image::ExifTool qw(:Utils);
|
|
15
15
|
use Image::ExifTool::XMP;
|
|
16
16
|
|
|
17
|
-
sub SerializeStruct(
|
|
18
|
-
sub InflateStruct(
|
|
17
|
+
sub SerializeStruct($$;$);
|
|
18
|
+
sub InflateStruct($$;$);
|
|
19
19
|
sub DumpStruct($;$);
|
|
20
20
|
sub CheckStruct($$$);
|
|
21
21
|
sub AddNewStruct($$$$$$);
|
|
22
22
|
sub ConvertStruct($$$$;$);
|
|
23
|
+
sub EscapeJSON($;$);
|
|
24
|
+
|
|
25
|
+
# lookups for JSON characters that we escape specially
|
|
26
|
+
my %jsonChar = ( '"'=>'"', '\\'=>'\\', "\b"=>'b', "\f"=>'f', "\n"=>'n', "\r"=>'r', "\t"=>'t' );
|
|
27
|
+
my %jsonEsc = ( '"'=>'"', '\\'=>'\\', 'b'=>"\b", 'f'=>"\f", 'n'=>"\n", 'r'=>"\r", 't'=>"\t" );
|
|
23
28
|
|
|
24
29
|
#------------------------------------------------------------------------------
|
|
25
30
|
# Serialize a structure (or other object) into a simple string
|
|
26
|
-
# Inputs: 0) HASH ref, ARRAY ref, or SCALAR,
|
|
27
|
-
# Returns: serialized structure string
|
|
31
|
+
# Inputs: 0) ExifTool ref, 1) HASH ref, ARRAY ref, or SCALAR, 2) closing bracket (or undef)
|
|
32
|
+
# Returns: serialized structure string (in format specified by StructFormat option)
|
|
28
33
|
# eg) "{field=text with {braces|}|, and a comma, field2=val2,field3={field4=[a,b]}}"
|
|
29
|
-
sub SerializeStruct(
|
|
34
|
+
sub SerializeStruct($$;$)
|
|
30
35
|
{
|
|
31
|
-
my ($obj, $ket) = @_;
|
|
36
|
+
my ($et, $obj, $ket) = @_;
|
|
32
37
|
my ($key, $val, @vals, $rtnVal);
|
|
38
|
+
my $sfmt = $et->Options('StructFormat');
|
|
33
39
|
|
|
34
40
|
if (ref $obj eq 'HASH') {
|
|
35
41
|
# support hashes with ordered keys
|
|
36
42
|
my @keys = $$obj{_ordered_keys_} ? @{$$obj{_ordered_keys_}} : sort keys %$obj;
|
|
37
43
|
foreach $key (@keys) {
|
|
38
|
-
|
|
44
|
+
my $hdr = $sfmt ? EscapeJSON($key) . ':' : $key . '=';
|
|
45
|
+
push @vals, $hdr . SerializeStruct($et, $$obj{$key}, '}');
|
|
39
46
|
}
|
|
40
47
|
$rtnVal = '{' . join(',', @vals) . '}';
|
|
41
48
|
} elsif (ref $obj eq 'ARRAY') {
|
|
42
49
|
foreach $val (@$obj) {
|
|
43
|
-
push @vals, SerializeStruct($val, ']');
|
|
50
|
+
push @vals, SerializeStruct($et, $val, ']');
|
|
44
51
|
}
|
|
45
52
|
$rtnVal = '[' . join(',', @vals) . ']';
|
|
46
53
|
} elsif (defined $obj) {
|
|
47
54
|
$obj = $$obj if ref $obj eq 'SCALAR';
|
|
48
55
|
# escape necessary characters in string (closing bracket plus "," and "|")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
if ($sfmt) {
|
|
57
|
+
$rtnVal = EscapeJSON($obj, $sfmt eq 'JSONQ');
|
|
58
|
+
} else {
|
|
59
|
+
my $pat = $ket ? "\\$ket|,|\\|" : ',|\\|';
|
|
60
|
+
($rtnVal = $obj) =~ s/($pat)/|$1/g;
|
|
61
|
+
# also must escape opening bracket or whitespace at start of string
|
|
62
|
+
$rtnVal =~ s/^([\s\[\{])/|$1/;
|
|
63
|
+
}
|
|
64
|
+
} elsif ($sfmt) {
|
|
65
|
+
$rtnVal = 'null';
|
|
53
66
|
} else {
|
|
54
67
|
$rtnVal = ''; # allow undefined list items
|
|
55
68
|
}
|
|
@@ -58,21 +71,25 @@ sub SerializeStruct($;$)
|
|
|
58
71
|
|
|
59
72
|
#------------------------------------------------------------------------------
|
|
60
73
|
# Inflate structure (or other object) from a serialized string
|
|
61
|
-
# Inputs: 0) reference to object in string form
|
|
62
|
-
#
|
|
74
|
+
# Inputs: 0) ExifTool ref, 1) reference to object in string form
|
|
75
|
+
# (serialized using the '|' escape, or JSON)
|
|
76
|
+
# 2) extra delimiter for scalar values delimiters
|
|
63
77
|
# Returns: 0) object as a SCALAR, HASH ref, or ARRAY ref (or undef on error),
|
|
64
78
|
# 1) warning string (or undef)
|
|
65
79
|
# Notes: modifies input string to remove parsed objects
|
|
66
|
-
sub InflateStruct(
|
|
80
|
+
sub InflateStruct($$;$)
|
|
67
81
|
{
|
|
68
|
-
my ($obj, $delim) = @_;
|
|
82
|
+
my ($et, $obj, $delim) = @_;
|
|
69
83
|
my ($val, $warn, $part);
|
|
84
|
+
my $sfmt = $et->Options('StructFormat');
|
|
70
85
|
|
|
71
86
|
if ($$obj =~ s/^\s*\{//) {
|
|
72
87
|
my %struct;
|
|
73
|
-
|
|
88
|
+
for (;;) {
|
|
89
|
+
last unless $sfmt ? $$obj =~ s/^\s*"(.*?)"\s*://s :
|
|
90
|
+
$$obj =~ s/^\s*([-\w:]+#?)\s*=//s;
|
|
74
91
|
my $tag = $1;
|
|
75
|
-
my ($v, $w) = InflateStruct($obj, '}');
|
|
92
|
+
my ($v, $w) = InflateStruct($et, $obj, '}');
|
|
76
93
|
$warn = $w if $w and not $warn;
|
|
77
94
|
return(undef, $warn) unless defined $v;
|
|
78
95
|
$struct{$tag} = $v;
|
|
@@ -94,7 +111,7 @@ sub InflateStruct($;$)
|
|
|
94
111
|
} elsif ($$obj =~ s/^\s*\[//) {
|
|
95
112
|
my @list;
|
|
96
113
|
for (;;) {
|
|
97
|
-
my ($v, $w) = InflateStruct($obj, ']');
|
|
114
|
+
my ($v, $w) = InflateStruct($et, $obj, ']');
|
|
98
115
|
$warn = $w if $w and not $warn;
|
|
99
116
|
return(undef, $warn) unless defined $v;
|
|
100
117
|
push @list, $v;
|
|
@@ -105,20 +122,71 @@ sub InflateStruct($;$)
|
|
|
105
122
|
$val = \@list;
|
|
106
123
|
} else {
|
|
107
124
|
$$obj =~ s/^\s+//s; # remove leading whitespace
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
if ($sfmt) {
|
|
126
|
+
if ($$obj =~ s/^"//) {
|
|
127
|
+
$val = '';
|
|
128
|
+
while ($$obj =~ s/(.*?)"//) {
|
|
129
|
+
$val .= $1;
|
|
130
|
+
last unless $val =~ /([\\]+)$/ and length($1) & 0x01;
|
|
131
|
+
substr($val, -1, 1) = '"'; # (was an escaped quote)
|
|
132
|
+
}
|
|
133
|
+
if ($val =~ s/^base64://) {
|
|
134
|
+
$val = DecodeBase64($val);
|
|
135
|
+
} else {
|
|
136
|
+
# un-escape characters in JSON string
|
|
137
|
+
$val =~ s/\\(.)/$jsonEsc{$1}||'\\'.$1/egs;
|
|
138
|
+
}
|
|
139
|
+
} elsif ($$obj =~ s/^(true|false)\b//) {
|
|
140
|
+
$val = '"' . ucfirst($1) . '"';
|
|
141
|
+
} elsif ($$obj =~ s/^([+-]?(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)//) {
|
|
142
|
+
$val = $1;
|
|
143
|
+
} else {
|
|
144
|
+
$warn or $warn = 'Unknown JSON object';
|
|
145
|
+
$val = '""';
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
# read scalar up to specified delimiter (or "," if not defined)
|
|
149
|
+
$delim = $delim ? "\\$delim|,|\\||\$" : ',|\\||$';
|
|
150
|
+
$val = '';
|
|
151
|
+
for (;;) {
|
|
152
|
+
$$obj =~ s/^(.*?)($delim)//s or last;
|
|
153
|
+
$val .= $1;
|
|
154
|
+
last unless $2;
|
|
155
|
+
$2 eq '|' or $$obj = $2 . $$obj, last;
|
|
156
|
+
$$obj =~ s/^(.)//s and $val .= $1; # add escaped character
|
|
157
|
+
}
|
|
117
158
|
}
|
|
118
159
|
}
|
|
119
160
|
return($val, $warn);
|
|
120
161
|
}
|
|
121
162
|
|
|
163
|
+
#------------------------------------------------------------------------------
|
|
164
|
+
# Escape string for JSON
|
|
165
|
+
# Inputs: 0) string, 1) flag to force numbers to be quoted too
|
|
166
|
+
# Returns: Escaped string (quoted if necessary)
|
|
167
|
+
sub EscapeJSON($;$)
|
|
168
|
+
{
|
|
169
|
+
my ($str, $quote) = @_;
|
|
170
|
+
unless ($quote) {
|
|
171
|
+
return 'null' unless defined $str;
|
|
172
|
+
# JSON boolean (true or false)
|
|
173
|
+
return lc($str) if $str =~ /^(true|false)$/i;
|
|
174
|
+
# JSON number (see json.org for numerical format)
|
|
175
|
+
# return $str if $str =~ /^-?(\d|[1-9]\d+)(\.\d+)?(e[-+]?\d+)?$/i;
|
|
176
|
+
# (these big numbers caused problems for some JSON parsers, so be more conservative)
|
|
177
|
+
return $str if $str =~ /^-?(\d|[1-9]\d{1,14})(\.\d{1,16})?(e[-+]?\d{1,3})?$/i;
|
|
178
|
+
}
|
|
179
|
+
return '""' unless defined $str;
|
|
180
|
+
# encode JSON string in base64 if necessary
|
|
181
|
+
return '"base64:' . EncodeBase64($str, 1) . '"' if Image::ExifTool::IsUTF8(\$str) < 0;
|
|
182
|
+
# escape special characters
|
|
183
|
+
$str =~ s/(["\t\n\r\\])/\\$jsonChar{$1}/sg;
|
|
184
|
+
$str =~ tr/\0//d; # remove all nulls
|
|
185
|
+
# escape other control characters with \u
|
|
186
|
+
$str =~ s/([\0-\x1f])/sprintf("\\u%.4X",ord $1)/sge;
|
|
187
|
+
return '"' . $str . '"'; # return the quoted string
|
|
188
|
+
}
|
|
189
|
+
|
|
122
190
|
#------------------------------------------------------------------------------
|
|
123
191
|
# Get XMP language code from tag name string
|
|
124
192
|
# Inputs: 0) tag name string
|