exiftool-vendored.pl 12.60.0 → 12.65.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/bin/Changes +110 -0
- package/bin/LICENSE +674 -0
- package/bin/MANIFEST +11 -0
- package/bin/META.json +5 -3
- package/bin/META.yml +5 -3
- package/bin/Makefile.PL +7 -1
- package/bin/README +50 -45
- package/bin/config_files/guano.config +161 -0
- package/bin/exiftool +163 -103
- package/bin/lib/Image/ExifTool/7Z.pm +793 -0
- package/bin/lib/Image/ExifTool/Apple.pm +14 -7
- package/bin/lib/Image/ExifTool/BMP.pm +0 -1
- package/bin/lib/Image/ExifTool/BigTIFF.pm +8 -1
- package/bin/lib/Image/ExifTool/BuildTagLookup.pm +4 -4
- package/bin/lib/Image/ExifTool/Canon.pm +4 -1
- package/bin/lib/Image/ExifTool/CanonRaw.pm +4 -4
- package/bin/lib/Image/ExifTool/CanonVRD.pm +4 -1
- package/bin/lib/Image/ExifTool/Exif.pm +31 -14
- package/bin/lib/Image/ExifTool/FlashPix.pm +9 -2
- package/bin/lib/Image/ExifTool/FujiFilm.pm +3 -3
- package/bin/lib/Image/ExifTool/GPS.pm +5 -2
- package/bin/lib/Image/ExifTool/Geotag.pm +4 -1
- package/bin/lib/Image/ExifTool/Jpeg2000.pm +243 -20
- package/bin/lib/Image/ExifTool/Lang/fr.pm +1467 -202
- package/bin/lib/Image/ExifTool/MPF.pm +2 -1
- package/bin/lib/Image/ExifTool/Matroska.pm +16 -1
- package/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
- package/bin/lib/Image/ExifTool/Nikon.pm +941 -33
- package/bin/lib/Image/ExifTool/NikonCustom.pm +874 -63
- package/bin/lib/Image/ExifTool/PDF.pm +39 -12
- package/bin/lib/Image/ExifTool/PLIST.pm +8 -1
- package/bin/lib/Image/ExifTool/PNG.pm +6 -6
- package/bin/lib/Image/ExifTool/PhaseOne.pm +5 -5
- package/bin/lib/Image/ExifTool/QuickTime.pm +96 -32
- package/bin/lib/Image/ExifTool/QuickTimeStream.pl +68 -37
- package/bin/lib/Image/ExifTool/README +2 -2
- package/bin/lib/Image/ExifTool/RIFF.pm +11 -9
- package/bin/lib/Image/ExifTool/Samsung.pm +227 -227
- package/bin/lib/Image/ExifTool/Shortcuts.pm +2 -1
- package/bin/lib/Image/ExifTool/SigmaRaw.pm +4 -4
- package/bin/lib/Image/ExifTool/Sony.pm +237 -32
- package/bin/lib/Image/ExifTool/TagLookup.pm +4762 -4629
- package/bin/lib/Image/ExifTool/TagNames.pod +737 -20
- package/bin/lib/Image/ExifTool/Validate.pm +17 -1
- package/bin/lib/Image/ExifTool/WPG.pm +296 -0
- package/bin/lib/Image/ExifTool/WriteExif.pl +9 -7
- package/bin/lib/Image/ExifTool/WritePDF.pl +7 -8
- package/bin/lib/Image/ExifTool/WriteQuickTime.pl +21 -9
- package/bin/lib/Image/ExifTool/WriteXMP.pl +2 -2
- package/bin/lib/Image/ExifTool/Writer.pl +47 -16
- package/bin/lib/Image/ExifTool/XMP.pm +30 -6
- package/bin/lib/Image/ExifTool/XMP2.pl +32 -0
- package/bin/lib/Image/ExifTool/XMPStruct.pl +96 -28
- package/bin/lib/Image/ExifTool/ZIP.pm +159 -41
- package/bin/lib/Image/ExifTool.pm +280 -164
- package/bin/lib/Image/ExifTool.pod +117 -52
- package/bin/perl-Image-ExifTool.spec +44 -43
- package/bin/pp_build_exe.args +8 -4
- package/package.json +3 -3
|
@@ -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,
|
|
@@ -1302,6 +1303,7 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1302
1303
|
MDItemTags => $$options{MDItemTags},
|
|
1303
1304
|
MissingTagValue => $$options{MissingTagValue},
|
|
1304
1305
|
NoPDFList => $$options{NoPDFList},
|
|
1306
|
+
NoWarning => $$options{NoWarning},
|
|
1305
1307
|
Password => $$options{Password},
|
|
1306
1308
|
PrintConv => $$options{PrintConv},
|
|
1307
1309
|
QuickTimeUTC => $$options{QuickTimeUTC},
|
|
@@ -1312,6 +1314,7 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1312
1314
|
ScanForXMP => $$options{ScanForXMP},
|
|
1313
1315
|
StrictDate => defined $$options{StrictDate} ? $$options{StrictDate} : 1,
|
|
1314
1316
|
Struct => $structOpt,
|
|
1317
|
+
StructFormat => $$options{StructFormat},
|
|
1315
1318
|
SystemTags => $$options{SystemTags},
|
|
1316
1319
|
TimeZone => $$options{TimeZone},
|
|
1317
1320
|
Unknown => $$options{Unknown},
|
|
@@ -1569,10 +1572,17 @@ SET: foreach $set (@setList) {
|
|
|
1569
1572
|
# handle expressions
|
|
1570
1573
|
if ($$opts{EXPR}) {
|
|
1571
1574
|
my $val = $srcExifTool->InsertTagValues(\@tags, $$set[1], 'Error');
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
$
|
|
1575
|
+
my $err = $$srcExifTool{VALUE}{Error};
|
|
1576
|
+
if ($err) {
|
|
1577
|
+
# pass on any error as a warning unless it is suppressed
|
|
1578
|
+
my $noWarn = $$srcExifTool{OPTIONS}{NoWarning};
|
|
1579
|
+
unless ($noWarn and (eval { $err =~ /$noWarn/ } or
|
|
1580
|
+
# (also apply expression to warning without "[minor] " prefix)
|
|
1581
|
+
($err =~ s/^\[minor\] //i and eval { $err =~ /$noWarn/ })))
|
|
1582
|
+
{
|
|
1583
|
+
$tag = NextFreeTagKey(\%rtnInfo, 'Warning');
|
|
1584
|
+
$rtnInfo{$tag} = $$srcExifTool{VALUE}{Error};
|
|
1585
|
+
}
|
|
1576
1586
|
delete $$srcExifTool{VALUE}{Error};
|
|
1577
1587
|
next unless defined $val;
|
|
1578
1588
|
}
|
|
@@ -1755,7 +1765,14 @@ GNV_TagInfo: foreach $tagInfo (@tagInfoList) {
|
|
|
1755
1765
|
}
|
|
1756
1766
|
}
|
|
1757
1767
|
# return our value(s)
|
|
1758
|
-
|
|
1768
|
+
if (wantarray) {
|
|
1769
|
+
# remove duplicates if requested
|
|
1770
|
+
if (@$vals > 1 and $self->Options('NoDups')) {
|
|
1771
|
+
my %seen;
|
|
1772
|
+
@$vals = grep { !$seen{$_}++ } @$vals;
|
|
1773
|
+
}
|
|
1774
|
+
return @$vals;
|
|
1775
|
+
}
|
|
1759
1776
|
return $$vals[0];
|
|
1760
1777
|
}
|
|
1761
1778
|
|
|
@@ -2008,7 +2025,7 @@ sub SetFileName($$;$$$)
|
|
|
2008
2025
|
# protect against empty file name
|
|
2009
2026
|
length $newName or $self->Warn('New file name is empty'), return -1;
|
|
2010
2027
|
# don't replace existing file
|
|
2011
|
-
if ($self->Exists($newName) and (not defined $usedFlag or $usedFlag)) {
|
|
2028
|
+
if ($self->Exists($newName, 1) and (not defined $usedFlag or $usedFlag)) {
|
|
2012
2029
|
if ($file ne $newName or $opt =~ /Link$/) {
|
|
2013
2030
|
# allow for case-insensitive filesystem
|
|
2014
2031
|
if ($opt =~ /Link$/ or not $self->IsSameFile($file, $newName)) {
|
|
@@ -2387,7 +2404,7 @@ sub WriteInfo($$;$$)
|
|
|
2387
2404
|
$outBuff = '';
|
|
2388
2405
|
$outRef = \$outBuff;
|
|
2389
2406
|
$outPos = 0;
|
|
2390
|
-
} elsif ($self->Exists($outfile)) {
|
|
2407
|
+
} elsif ($self->Exists($outfile, 1)) {
|
|
2391
2408
|
$self->Error("File already exists: $outfile");
|
|
2392
2409
|
} elsif ($self->Open(\*EXIFTOOL_OUTFILE, $outfile, '>')) {
|
|
2393
2410
|
$outRef = \*EXIFTOOL_OUTFILE;
|
|
@@ -3275,7 +3292,7 @@ sub InsertTagValues($$$;$$$)
|
|
|
3275
3292
|
}
|
|
3276
3293
|
} elsif (ref $val eq 'HASH') {
|
|
3277
3294
|
require 'Image/ExifTool/XMPStruct.pl';
|
|
3278
|
-
$val = Image::ExifTool::XMP::SerializeStruct($val);
|
|
3295
|
+
$val = Image::ExifTool::XMP::SerializeStruct($self, $val);
|
|
3279
3296
|
} elsif (not defined $val) {
|
|
3280
3297
|
$val = $$self{OPTIONS}{MissingTagValue} if $asList;
|
|
3281
3298
|
}
|
|
@@ -3759,6 +3776,8 @@ sub GetNewValueHash($$;$$$$)
|
|
|
3759
3776
|
# this is a bit tricky: we want to add to a protected nvHash only if we
|
|
3760
3777
|
# are adding a conditional delete ($_[5] true or DelValue with no Shift)
|
|
3761
3778
|
# or accumulating List items (NoReplace true)
|
|
3779
|
+
# (NOTE: this should be looked into --> lists may be accumulated instead of being replaced
|
|
3780
|
+
# as expected when copying to the same list from different dynamic -tagsFromFile source files)
|
|
3762
3781
|
if ($protect and not ($opts{create} and ($$nvHash{NoReplace} or $_[5] or
|
|
3763
3782
|
($$nvHash{DelValue} and not defined $$nvHash{Shift}))))
|
|
3764
3783
|
{
|
|
@@ -4900,6 +4919,11 @@ sub InverseDateTime($$;$$)
|
|
|
4900
4919
|
my $fs = ($fmt =~ s/%f$// and $val =~ s/(\.\d+)\s*$//) ? $1 : '';
|
|
4901
4920
|
my ($lib, $wrn, @a);
|
|
4902
4921
|
TryLib: for ($lib=$strptimeLib; ; $lib='') {
|
|
4922
|
+
# handle %s format ourself (not supported in Fedora, see forum15032)
|
|
4923
|
+
if ($fmt eq '%s') {
|
|
4924
|
+
$val = ConvertUnixTime($val, 1);
|
|
4925
|
+
last;
|
|
4926
|
+
}
|
|
4903
4927
|
if (not $lib) {
|
|
4904
4928
|
last unless $$self{OPTIONS}{StrictDate};
|
|
4905
4929
|
warn $wrn || "Install POSIX::strptime or Time::Piece for inverse date/time conversions\n";
|
|
@@ -5590,6 +5614,8 @@ sub WriteJPEG($$)
|
|
|
5590
5614
|
$s =~ /^(Meta|META|Exif)\0\0/ and $dirName = 'Meta';
|
|
5591
5615
|
} elsif ($marker == 0xe5) {
|
|
5592
5616
|
$s =~ /^RMETA\0/ and $dirName = 'RMETA';
|
|
5617
|
+
} elsif ($marker == 0xeb) {
|
|
5618
|
+
$s =~ /^JP/ and $dirName = 'JUMBF';
|
|
5593
5619
|
} elsif ($marker == 0xec) {
|
|
5594
5620
|
$s =~ /^Ducky/ and $dirName = 'Ducky';
|
|
5595
5621
|
} elsif ($marker == 0xed) {
|
|
@@ -6455,6 +6481,11 @@ sub WriteJPEG($$)
|
|
|
6455
6481
|
$segType = 'Ricoh RMETA';
|
|
6456
6482
|
$$delGroup{RMETA} and $del = 1;
|
|
6457
6483
|
}
|
|
6484
|
+
} elsif ($marker == 0xeb) { # APP10 (JUMBF)
|
|
6485
|
+
if ($$segDataPt =~ /^JP/) {
|
|
6486
|
+
$segType = 'JUMBF';
|
|
6487
|
+
$$delGroup{JUMBF} and $del = 1;
|
|
6488
|
+
}
|
|
6458
6489
|
} elsif ($marker == 0xec) { # APP12 (Ducky)
|
|
6459
6490
|
if ($$segDataPt =~ /^Ducky/) {
|
|
6460
6491
|
$segType = 'Ducky';
|
|
@@ -6872,14 +6903,14 @@ sub SetFileTime($$;$$$$)
|
|
|
6872
6903
|
}
|
|
6873
6904
|
|
|
6874
6905
|
#------------------------------------------------------------------------------
|
|
6875
|
-
# Add data to
|
|
6906
|
+
# Add data to hash checksum
|
|
6876
6907
|
# Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file),
|
|
6877
6908
|
# 3) data name (or undef for no warnings or messages), 4) flag for no verbose message
|
|
6878
|
-
# Returns: number of bytes read and
|
|
6879
|
-
sub
|
|
6909
|
+
# Returns: number of bytes read and hashed
|
|
6910
|
+
sub ImageDataHash($$$;$$)
|
|
6880
6911
|
{
|
|
6881
6912
|
my ($self, $raf, $size, $type, $noMsg) = @_;
|
|
6882
|
-
my $
|
|
6913
|
+
my $hash = $$self{ImageDataHash} or return;
|
|
6883
6914
|
my ($bytesRead, $n) = (0, 65536);
|
|
6884
6915
|
my $buff;
|
|
6885
6916
|
for (;;) {
|
|
@@ -6892,11 +6923,11 @@ sub ImageDataMD5($$$;$$)
|
|
|
6892
6923
|
$self->Warn("Error reading $type data") if $type and defined $size;
|
|
6893
6924
|
last;
|
|
6894
6925
|
}
|
|
6895
|
-
$
|
|
6926
|
+
$hash->add($buff);
|
|
6896
6927
|
$bytesRead += length $buff;
|
|
6897
6928
|
}
|
|
6898
6929
|
if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) {
|
|
6899
|
-
$self->VPrint(0, "$$self{INDENT}(
|
|
6930
|
+
$self->VPrint(0, "$$self{INDENT}(ImageDataHash: $bytesRead bytes of $type data)\n");
|
|
6900
6931
|
}
|
|
6901
6932
|
return $bytesRead;
|
|
6902
6933
|
}
|
|
@@ -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
|
|
@@ -248,7 +250,11 @@ my %boolConv = (
|
|
|
248
250
|
|
|
249
251
|
# XMP namespaces which we don't want to contribute to generated EXIF tag names
|
|
250
252
|
# (Note: namespaces with non-standard prefixes aren't currently ignored)
|
|
251
|
-
my %ignoreNamespace = ( 'x'=>1, rdf=>1, xmlns=>1, xml=>1, svg=>1,
|
|
253
|
+
my %ignoreNamespace = ( 'x'=>1, rdf=>1, xmlns=>1, xml=>1, svg=>1, office=>1 );
|
|
254
|
+
|
|
255
|
+
# ExifTool properties that don't generate tag names (et:tagid is historic)
|
|
256
|
+
my %ignoreEtProp = ( 'et:desc'=>1, 'et:prt'=>1, 'et:val'=>1 , 'et:id'=>1, 'et:tagid'=>1,
|
|
257
|
+
'et:toolkit'=>1, 'et:table'=>1, 'et:index'=>1 );
|
|
252
258
|
|
|
253
259
|
# XMP properties to ignore (set dynamically via dirInfo IgnoreProp)
|
|
254
260
|
my %ignoreProp;
|
|
@@ -705,6 +711,10 @@ my %sRangeMask = (
|
|
|
705
711
|
Name => 'xmpPLUS',
|
|
706
712
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpPLUS' },
|
|
707
713
|
},
|
|
714
|
+
panorama => {
|
|
715
|
+
Name => 'panorama',
|
|
716
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::panorama' },
|
|
717
|
+
},
|
|
708
718
|
plus => {
|
|
709
719
|
Name => 'plus',
|
|
710
720
|
SubDirectory => { TagTable => 'Image::ExifTool::PLUS::XMP' },
|
|
@@ -905,6 +915,10 @@ my %sRangeMask = (
|
|
|
905
915
|
Name => 'hdr',
|
|
906
916
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::hdr' },
|
|
907
917
|
},
|
|
918
|
+
hdrgm => {
|
|
919
|
+
Name => 'hdrgm',
|
|
920
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::hdrgm' },
|
|
921
|
+
},
|
|
908
922
|
);
|
|
909
923
|
|
|
910
924
|
# hack to allow XML containing Dublin Core metadata to be handled like XMP (eg. EPUB - see ZIP.pm)
|
|
@@ -2485,6 +2499,9 @@ my %sPantryItem = (
|
|
|
2485
2499
|
EnhanceSuperResolutionAlreadyApplied => { Writable => 'boolean' },
|
|
2486
2500
|
EnhanceSuperResolutionVersion => { }, # integer?
|
|
2487
2501
|
EnhanceSuperResolutionScale => { Writable => 'rational' },
|
|
2502
|
+
EnhanceDenoiseAlreadyApplied => { Writable => 'boolean' }, #forum14760
|
|
2503
|
+
EnhanceDenoiseVersion => { }, #forum14760 integer?
|
|
2504
|
+
EnhanceDenoiseLumaAmount => { }, #forum14760 integer?
|
|
2488
2505
|
);
|
|
2489
2506
|
|
|
2490
2507
|
# IPTC Core namespace properties (Iptc4xmpCore) (ref 4)
|
|
@@ -2559,7 +2576,9 @@ my %sPantryItem = (
|
|
|
2559
2576
|
%xmpTableDefaults,
|
|
2560
2577
|
GROUPS => { 1 => 'XMP-et', 2 => 'Image' },
|
|
2561
2578
|
NAMESPACE => 'et',
|
|
2562
|
-
|
|
2579
|
+
OriginalImageHash => { Notes => 'used to store ExifTool ImageDataHash digest' },
|
|
2580
|
+
OriginalImageHashType => { Notes => "ImageHashType API setting, default 'MD5'" },
|
|
2581
|
+
OriginalImageMD5 => { Notes => 'deprecated' },
|
|
2563
2582
|
);
|
|
2564
2583
|
|
|
2565
2584
|
# table to add tags in other namespaces
|
|
@@ -2850,7 +2869,7 @@ sub GetXMPTagID($;$$)
|
|
|
2850
2869
|
# split name into namespace and property name
|
|
2851
2870
|
# (Note: namespace can be '' for property qualifiers)
|
|
2852
2871
|
my ($ns, $nm) = ($prop =~ /(.*?):(.*)/) ? ($1, $2) : ('', $prop);
|
|
2853
|
-
if ($ignoreNamespace{$ns} or $ignoreProp{$prop}) {
|
|
2872
|
+
if ($ignoreNamespace{$ns} or $ignoreProp{$prop} or $ignoreEtProp{$prop}) {
|
|
2854
2873
|
# special case: don't ignore rdf numbered items
|
|
2855
2874
|
# (not technically allowed in XMP, but used in RDF/XML)
|
|
2856
2875
|
unless ($prop =~ /^rdf:(_\d+)$/) {
|
|
@@ -3420,7 +3439,10 @@ NoLoop:
|
|
|
3420
3439
|
my %grps = ( 0 => $1, 1 => $2 );
|
|
3421
3440
|
# apply a little magic to recover original group names
|
|
3422
3441
|
# from this exiftool-written RDF/XML file
|
|
3423
|
-
if ($grps{1}
|
|
3442
|
+
if ($grps{1} eq 'System') {
|
|
3443
|
+
$grps{1} = 'XML-System';
|
|
3444
|
+
$grps{0} = 'XML';
|
|
3445
|
+
} elsif ($grps{1} =~ /^\d/) {
|
|
3424
3446
|
# URI's with only family 0 are internal tags from the source file,
|
|
3425
3447
|
# so change the group name to avoid confusion with tags from this file
|
|
3426
3448
|
$grps{1} = "XML-$grps{0}";
|
|
@@ -3888,7 +3910,9 @@ sub ParseXMPElement($$$;$$$$)
|
|
|
3888
3910
|
}
|
|
3889
3911
|
}
|
|
3890
3912
|
my $shortVal = $attrs{$shortName};
|
|
3891
|
-
|
|
3913
|
+
# Note: $prop is the containing property in this loop (not the shorthand property)
|
|
3914
|
+
# so $ignoreProp ignores all attributes of the ignored property
|
|
3915
|
+
if ($ignoreNamespace{$ns} or $ignoreProp{$prop} or $ignoreEtProp{$propName}) {
|
|
3892
3916
|
$ignored = $propName;
|
|
3893
3917
|
# handle special attributes (extract as tags only once if not empty)
|
|
3894
3918
|
if (ref $recognizedAttrs{$propName} and $shortVal) {
|
|
@@ -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
|