exiftool_vendored 12.22.0 → 12.34.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 +201 -5
- data/bin/MANIFEST +22 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +45 -43
- data/bin/arg_files/xmp2exif.args +2 -1
- data/bin/config_files/acdsee.config +193 -6
- data/bin/config_files/convert_regions.config +25 -14
- data/bin/config_files/cuepointlist.config +70 -0
- data/bin/config_files/example.config +1 -1
- data/bin/exiftool +89 -70
- data/bin/fmt_files/gpx.fmt +1 -1
- data/bin/fmt_files/gpx_wpt.fmt +1 -1
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +17 -4
- data/bin/lib/Image/ExifTool/CBOR.pm +331 -0
- data/bin/lib/Image/ExifTool/Canon.pm +53 -21
- data/bin/lib/Image/ExifTool/Charset.pm +2 -0
- data/bin/lib/Image/ExifTool/DPX.pm +13 -2
- data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
- data/bin/lib/Image/ExifTool/Exif.pm +120 -12
- data/bin/lib/Image/ExifTool/FlashPix.pm +35 -10
- data/bin/lib/Image/ExifTool/FujiFilm.pm +19 -8
- data/bin/lib/Image/ExifTool/Geotag.pm +13 -2
- data/bin/lib/Image/ExifTool/GoPro.pm +16 -1
- data/bin/lib/Image/ExifTool/ICC_Profile.pm +96 -4
- data/bin/lib/Image/ExifTool/ID3.pm +15 -3
- data/bin/lib/Image/ExifTool/JPEG.pm +74 -4
- data/bin/lib/Image/ExifTool/JSON.pm +30 -5
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +395 -16
- data/bin/lib/Image/ExifTool/LIF.pm +153 -0
- data/bin/lib/Image/ExifTool/Lang/nl.pm +60 -59
- data/bin/lib/Image/ExifTool/M2TS.pm +103 -7
- data/bin/lib/Image/ExifTool/MIE.pm +2 -1
- data/bin/lib/Image/ExifTool/MRC.pm +341 -0
- data/bin/lib/Image/ExifTool/MWG.pm +3 -3
- data/bin/lib/Image/ExifTool/MXF.pm +1 -1
- data/bin/lib/Image/ExifTool/MacOS.pm +3 -3
- data/bin/lib/Image/ExifTool/Microsoft.pm +5 -3
- data/bin/lib/Image/ExifTool/Nikon.pm +17 -5
- data/bin/lib/Image/ExifTool/NikonSettings.pm +19 -2
- data/bin/lib/Image/ExifTool/Olympus.pm +9 -2
- data/bin/lib/Image/ExifTool/Other.pm +93 -0
- data/bin/lib/Image/ExifTool/PDF.pm +11 -12
- data/bin/lib/Image/ExifTool/PNG.pm +8 -7
- data/bin/lib/Image/ExifTool/Panasonic.pm +28 -3
- data/bin/lib/Image/ExifTool/Pentax.pm +28 -5
- data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
- data/bin/lib/Image/ExifTool/Photoshop.pm +6 -0
- data/bin/lib/Image/ExifTool/QuickTime.pm +210 -65
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +280 -139
- data/bin/lib/Image/ExifTool/README +9 -2
- data/bin/lib/Image/ExifTool/RIFF.pm +89 -12
- data/bin/lib/Image/ExifTool/Samsung.pm +48 -10
- data/bin/lib/Image/ExifTool/Sony.pm +204 -61
- data/bin/lib/Image/ExifTool/TagLookup.pm +206 -19
- data/bin/lib/Image/ExifTool/TagNames.pod +634 -195
- data/bin/lib/Image/ExifTool/Torrent.pm +18 -11
- data/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
- data/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
- data/bin/lib/Image/ExifTool/WritePNG.pl +2 -0
- data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +58 -16
- data/bin/lib/Image/ExifTool/WriteXMP.pl +7 -3
- data/bin/lib/Image/ExifTool/Writer.pl +44 -0
- data/bin/lib/Image/ExifTool/XMP.pm +51 -18
- data/bin/lib/Image/ExifTool/XMP2.pl +4 -1
- data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
- data/bin/lib/Image/ExifTool/ZISRAW.pm +121 -2
- data/bin/lib/Image/ExifTool.pm +188 -61
- data/bin/lib/Image/ExifTool.pod +89 -68
- data/bin/perl-Image-ExifTool.spec +43 -42
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +10 -9
@@ -13,10 +13,11 @@ package Image::ExifTool::Torrent;
|
|
13
13
|
use strict;
|
14
14
|
use vars qw($VERSION);
|
15
15
|
use Image::ExifTool qw(:DataAccess :Utils);
|
16
|
+
use Image::ExifTool::XMP;
|
16
17
|
|
17
|
-
$VERSION = '1.
|
18
|
+
$VERSION = '1.05';
|
18
19
|
|
19
|
-
sub ReadBencode(
|
20
|
+
sub ReadBencode($$$);
|
20
21
|
sub ExtractTags($$$;$$@);
|
21
22
|
|
22
23
|
# tags extracted from BitTorrent files
|
@@ -99,12 +100,12 @@ sub ReadMore($$)
|
|
99
100
|
|
100
101
|
#------------------------------------------------------------------------------
|
101
102
|
# Read bencoded value
|
102
|
-
# Inputs: 0) input file,
|
103
|
+
# Inputs: 0) ExifTool ref, 1) input file, 2) buffer (pos must be set to current position)
|
103
104
|
# Returns: HASH ref, ARRAY ref, SCALAR ref, SCALAR, or undef on error or end of data
|
104
105
|
# Notes: Sets BencodeError element of RAF on any error
|
105
|
-
sub ReadBencode(
|
106
|
+
sub ReadBencode($$$)
|
106
107
|
{
|
107
|
-
my ($raf, $dataPt) = @_;
|
108
|
+
my ($et, $raf, $dataPt) = @_;
|
108
109
|
|
109
110
|
# read more if necessary (keep a minimum of 64 bytes in the buffer)
|
110
111
|
my $pos = pos($$dataPt);
|
@@ -123,21 +124,21 @@ sub ReadBencode($$)
|
|
123
124
|
} elsif ($tok eq 'd') { # dictionary
|
124
125
|
$val = { };
|
125
126
|
for (;;) {
|
126
|
-
my $k = ReadBencode($raf, $dataPt);
|
127
|
+
my $k = ReadBencode($et, $raf, $dataPt);
|
127
128
|
last unless defined $k;
|
128
129
|
# the key must be a byte string
|
129
130
|
if (ref $k) {
|
130
131
|
ref $k ne 'SCALAR' and $$raf{BencodeError} = 'Bad dictionary key', last;
|
131
132
|
$k = $$k;
|
132
133
|
}
|
133
|
-
my $v = ReadBencode($raf, $dataPt);
|
134
|
+
my $v = ReadBencode($et, $raf, $dataPt);
|
134
135
|
last unless defined $v;
|
135
136
|
$$val{$k} = $v;
|
136
137
|
}
|
137
138
|
} elsif ($tok eq 'l') { # list
|
138
139
|
$val = [ ];
|
139
140
|
for (;;) {
|
140
|
-
my $v = ReadBencode($raf, $dataPt);
|
141
|
+
my $v = ReadBencode($et, $raf, $dataPt);
|
141
142
|
last unless defined $v;
|
142
143
|
push @$val, $v;
|
143
144
|
}
|
@@ -165,8 +166,14 @@ sub ReadBencode($$)
|
|
165
166
|
}
|
166
167
|
if (defined $value) {
|
167
168
|
# return as binary data unless it is a reasonable-length ASCII string
|
168
|
-
if (length($value) > 256
|
169
|
+
if (length($value) > 256) {
|
169
170
|
$val = \$value;
|
171
|
+
} elsif ($value =~ /[^\t\x20-\x7e]/) {
|
172
|
+
if (Image::ExifTool::XMP::IsUTF8(\$value) >= 0) {
|
173
|
+
$val = $et->Decode($value, 'UTF8');
|
174
|
+
} else {
|
175
|
+
$val = \$value;
|
176
|
+
}
|
170
177
|
} else {
|
171
178
|
$val = $value;
|
172
179
|
}
|
@@ -206,7 +213,7 @@ sub ExtractTags($$$;$$@)
|
|
206
213
|
my $tagInfo = $et->GetTagInfo($tagTablePtr, $id) or next;
|
207
214
|
if (ref $val eq 'ARRAY') {
|
208
215
|
if ($$tagInfo{JoinPath}) {
|
209
|
-
$val = join '/', @$val;
|
216
|
+
$val = join '/', map { ref $_ ? '(Binary data)' : $_ } @$val;
|
210
217
|
} else {
|
211
218
|
push @more, @$val;
|
212
219
|
next if ref $more[0] eq 'ARRAY'; # continue expanding nested lists
|
@@ -273,7 +280,7 @@ sub ProcessTorrent($$)
|
|
273
280
|
my $raf = $$dirInfo{RAF};
|
274
281
|
my $buff = '';
|
275
282
|
pos($buff) = 0;
|
276
|
-
my $dict = ReadBencode($raf, \$buff);
|
283
|
+
my $dict = ReadBencode($et, $raf, \$buff);
|
277
284
|
my $err = $$raf{BencodeError};
|
278
285
|
$et->Warn("Bencode error: $err") if $err;
|
279
286
|
if (ref $dict eq 'HASH' and ($$dict{announce} or $$dict{'created by'})) {
|
@@ -170,7 +170,7 @@ sub FormatIPTC($$$$$;$)
|
|
170
170
|
} else {
|
171
171
|
my $len = int(($1 || 0) / 8);
|
172
172
|
if ($len == 1) { # 1 byte
|
173
|
-
$$valPtr = chr($$valPtr);
|
173
|
+
$$valPtr = chr($$valPtr & 0xff);
|
174
174
|
} elsif ($len == 2) { # 2-byte integer
|
175
175
|
$$valPtr = pack('n', $$valPtr);
|
176
176
|
} else { # 4-byte integer
|
@@ -293,6 +293,7 @@ sub WritePDF($$)
|
|
293
293
|
my $newTool = new Image::ExifTool;
|
294
294
|
$newTool->Options(List => 1);
|
295
295
|
$newTool->Options(Password => $et->Options('Password'));
|
296
|
+
$newTool->Options(NoPDFList => $et->Options('NoPDFList'));
|
296
297
|
$$newTool{PDF_CAPTURE} = \%capture;
|
297
298
|
my $info = $newTool->ImageInfo($raf, 'XMP', 'PDF:*', 'Error', 'Warning');
|
298
299
|
# not a valid PDF file unless we got a version number
|
@@ -179,6 +179,8 @@ sub BuildTextChunk($$$$$)
|
|
179
179
|
$tag =~ s/-$lang$//; # remove language code from tagID
|
180
180
|
} elsif ($$et{OPTIONS}{Charset} ne 'Latin' and $val =~ /[\x80-\xff]/) {
|
181
181
|
$iTXt = 1; # write as iTXt if it contains non-Latin special characters
|
182
|
+
} elsif ($$tagInfo{iTXt}) {
|
183
|
+
$iTXt = 1; # write as iTXt if specified in user-defined tag
|
182
184
|
}
|
183
185
|
}
|
184
186
|
if ($comp) {
|
@@ -163,6 +163,9 @@ sub ConvInvISO6709($)
|
|
163
163
|
# latitude must have 2 digits before the decimal, and longitude 3,
|
164
164
|
# and all values must start with a "+" or "-", and Google Photos
|
165
165
|
# requires at least 3 digits after the decimal point
|
166
|
+
# (and as of Apr 2021, Google Photos doesn't accept coordinats
|
167
|
+
# with more than 5 digits after the decimal place:
|
168
|
+
# https://exiftool.org/forum/index.php?topic=11055.msg67171#msg67171 )
|
166
169
|
my @fmt = ('%s%02d.%s%s','%s%03d.%s%s','%s%d.%s%s');
|
167
170
|
foreach (@a) {
|
168
171
|
return undef unless Image::ExifTool::IsFloat($_);
|
@@ -314,7 +317,7 @@ sub FormatQTValue($$;$$)
|
|
314
317
|
if ($writable and $qtFormat{$writable}) {
|
315
318
|
$flags = $qtFormat{$writable};
|
316
319
|
} else {
|
317
|
-
$flags = $qtFormat{$format} || 0;
|
320
|
+
$flags = $qtFormat{$format || 0} || 0;
|
318
321
|
}
|
319
322
|
} elsif ($$valPt =~ /^\xff\xd8\xff/) {
|
320
323
|
$flags = 0x0d; # JPG
|
@@ -846,7 +849,7 @@ sub WriteQuickTime($$$)
|
|
846
849
|
# --> hold this terminator to the end
|
847
850
|
$term = $hdr;
|
848
851
|
} elsif ($n != 0) {
|
849
|
-
$et->Error(
|
852
|
+
$et->Error("Unknown $n bytes at end of file", 1);
|
850
853
|
}
|
851
854
|
last;
|
852
855
|
}
|
@@ -1064,6 +1067,9 @@ sub WriteQuickTime($$$)
|
|
1064
1067
|
# 3=optional base offset, 4=optional item ID)
|
1065
1068
|
ChunkOffset => \@chunkOffset,
|
1066
1069
|
);
|
1070
|
+
# set InPlace flag so XMP will be padded properly when
|
1071
|
+
# QuickTimePad is used if this is an XMP directory
|
1072
|
+
$subdirInfo{InPlace} = 2 if $et->Options('QuickTimePad');
|
1067
1073
|
# pass the header pointer if necessary (for EXIF IFD's
|
1068
1074
|
# where the Base offset is at the end of the header)
|
1069
1075
|
if ($hdrLen and $hdrLen < $size) {
|
@@ -1086,7 +1092,9 @@ sub WriteQuickTime($$$)
|
|
1086
1092
|
$$et{CHANGED} = $oldChanged if $$et{DemoteErrors} > 1;
|
1087
1093
|
delete $$et{DemoteErrors};
|
1088
1094
|
}
|
1089
|
-
if (defined $newData and not length $newData and $$
|
1095
|
+
if (defined $newData and not length $newData and ($$tagInfo{Permanent} or
|
1096
|
+
($$tagTablePtr{PERMANENT} and not defined $$tagInfo{Permanent})))
|
1097
|
+
{
|
1090
1098
|
# do nothing if trying to delete tag from a PERMANENT table
|
1091
1099
|
$$et{CHANGED} = $oldChanged;
|
1092
1100
|
undef $newData;
|
@@ -1094,7 +1102,9 @@ sub WriteQuickTime($$$)
|
|
1094
1102
|
$$et{CUR_WRITE_GROUP} = $oldWriteGroup;
|
1095
1103
|
SetByteOrder('MM');
|
1096
1104
|
# add back header if necessary
|
1097
|
-
if ($start and defined $newData and length $newData
|
1105
|
+
if ($start and defined $newData and (length $newData or
|
1106
|
+
(defined $$tagInfo{Permanent} and not $$tagInfo{Permanent})))
|
1107
|
+
{
|
1098
1108
|
$newData = substr($buff,0,$start) . $newData;
|
1099
1109
|
$$_[1] += $start foreach @chunkOffset;
|
1100
1110
|
}
|
@@ -1232,10 +1242,14 @@ sub WriteQuickTime($$$)
|
|
1232
1242
|
} elsif ($format) {
|
1233
1243
|
$val = ReadValue(\$buff, 0, $format, undef, $size);
|
1234
1244
|
} elsif (($tag =~ /^\xa9/ or $$tagInfo{IText}) and $size >= ($$tagInfo{IText} || 4)) {
|
1235
|
-
|
1236
|
-
|
1237
|
-
$
|
1238
|
-
$
|
1245
|
+
my $hdr;
|
1246
|
+
if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
|
1247
|
+
my $iText = $$tagInfo{IText};
|
1248
|
+
my $pos = $iText - 2;
|
1249
|
+
$lang = unpack("x${pos}n", $buff);
|
1250
|
+
$hdr = substr($buff,4,$iText-6);
|
1251
|
+
$len = $size - $iText;
|
1252
|
+
$val = substr($buff, $iText, $len);
|
1239
1253
|
} else {
|
1240
1254
|
($len, $lang) = unpack('nn', $buff);
|
1241
1255
|
$len -= 4 if 4 + $len > $size; # (see QuickTime.pm for explanation)
|
@@ -1243,14 +1257,18 @@ sub WriteQuickTime($$$)
|
|
1243
1257
|
$val = substr($buff, 4, $len);
|
1244
1258
|
}
|
1245
1259
|
$lang or $lang = $undLang; # treat both 0 and 'und' as 'und'
|
1260
|
+
my $enc;
|
1246
1261
|
if ($lang < 0x400 and $val !~ /^\xfe\xff/) {
|
1247
1262
|
$charsetQuickTime = $et->Options('CharsetQuickTime');
|
1248
|
-
$
|
1263
|
+
$enc = $charsetQuickTime;
|
1249
1264
|
} else {
|
1250
|
-
|
1265
|
+
$enc = $val=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
|
1266
|
+
}
|
1267
|
+
unless ($$tagInfo{NoDecode}) {
|
1251
1268
|
$val = $et->Decode($val, $enc);
|
1269
|
+
$val =~ s/\0+$//; # remove trailing nulls if they exist
|
1252
1270
|
}
|
1253
|
-
$val
|
1271
|
+
$val = $hdr . $val if defined $hdr;
|
1254
1272
|
my $langCode = UnpackLang($lang, 1);
|
1255
1273
|
$langInfo = GetLangInfo($tagInfo, $langCode);
|
1256
1274
|
$nvHash = $et->GetNewValueHash($langInfo);
|
@@ -1267,6 +1285,9 @@ sub WriteQuickTime($$$)
|
|
1267
1285
|
}
|
1268
1286
|
} else {
|
1269
1287
|
$val = $buff;
|
1288
|
+
if ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
|
1289
|
+
$et->Warn("Corrupted $$tagInfo{Name} value");
|
1290
|
+
}
|
1270
1291
|
}
|
1271
1292
|
if ($nvHash and defined $val) {
|
1272
1293
|
if ($et->IsOverwriting($nvHash, $val)) {
|
@@ -1279,12 +1300,23 @@ sub WriteQuickTime($$$)
|
|
1279
1300
|
$et->VerboseValue("+ $grp:$$langInfo{Name}", $newData);
|
1280
1301
|
# add back necessary header and encode as necessary
|
1281
1302
|
if (defined $lang) {
|
1282
|
-
$
|
1303
|
+
my $iText = $$tagInfo{IText} || 0;
|
1304
|
+
my $hdr;
|
1305
|
+
if ($iText > 6) {
|
1306
|
+
$newData .= ' 'x($iText-6) if length($newData) < $iText-6;
|
1307
|
+
$hdr = substr($newData, 0, $iText-6);
|
1308
|
+
$newData = substr($newData, $iText-6);
|
1309
|
+
}
|
1310
|
+
unless ($$tagInfo{NoDecode}) {
|
1311
|
+
$newData = $et->Encode($newData, $lang < 0x400 ? $charsetQuickTime : 'UTF8');
|
1312
|
+
}
|
1283
1313
|
my $wLang = $lang eq $undLang ? 0 : $lang;
|
1284
|
-
if (
|
1314
|
+
if ($iText < 6) {
|
1315
|
+
$newData = pack('nn', length($newData), $wLang) . $newData;
|
1316
|
+
} elsif ($iText == 6) {
|
1285
1317
|
$newData = pack('Nn', 0, $wLang) . $newData . "\0";
|
1286
1318
|
} else {
|
1287
|
-
$newData = pack('
|
1319
|
+
$newData = "\0\0\0\0" . $hdr . pack('n', $wLang) . $newData . "\0";
|
1288
1320
|
}
|
1289
1321
|
} elsif (not $format or $format =~ /^string/ and
|
1290
1322
|
not $$tagInfo{Binary} and not $$tagInfo{ValueConv})
|
@@ -1302,6 +1334,13 @@ sub WriteQuickTime($$$)
|
|
1302
1334
|
}
|
1303
1335
|
# write the new atom if it was modified
|
1304
1336
|
if (defined $newData) {
|
1337
|
+
my $sizeDiff = length($buff) - length($newData);
|
1338
|
+
if ($sizeDiff > 0 and $$tagInfo{PreservePadding} and $et->Options('QuickTimePad')) {
|
1339
|
+
$newData .= "\0" x $sizeDiff;
|
1340
|
+
$et->VPrint(1, " ($$tagInfo{Name} padded to original size)");
|
1341
|
+
} elsif ($sizeDiff) {
|
1342
|
+
$et->VPrint(1, " ($$tagInfo{Name} changed size)");
|
1343
|
+
}
|
1305
1344
|
my $len = length($newData) + 8;
|
1306
1345
|
$len > 0x7fffffff and $et->Error("$$tagInfo{Name} to large to write"), last;
|
1307
1346
|
# update size in ChunkOffset list for modified 'uuid' atom
|
@@ -1443,9 +1482,12 @@ sub WriteQuickTime($$$)
|
|
1443
1482
|
my $grp = $et->GetGroup($tagInfo,1);
|
1444
1483
|
$et->Warn("Can't use country code for $grp:$$tagInfo{Name}");
|
1445
1484
|
next;
|
1446
|
-
} elsif ($$tagInfo{IText} and $$tagInfo{IText}
|
1485
|
+
} elsif ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
|
1447
1486
|
# add 6-byte langText header and trailing null
|
1448
|
-
|
1487
|
+
# (with extra junk before language code if IText > 6)
|
1488
|
+
my $n = $$tagInfo{IText} - 6;
|
1489
|
+
$newVal .= ' ' x $n if length($newVal) < $n;
|
1490
|
+
$newVal = "\0\0\0\0" . substr($newVal,0,$n) . pack('n',0,$lang) . substr($newVal,$n) . "\0";
|
1449
1491
|
} else {
|
1450
1492
|
# add IText header
|
1451
1493
|
$newVal = pack('nn',length($newVal),$lang) . $newVal;
|
@@ -506,7 +506,7 @@ sub ConformPathToNamespace($$)
|
|
506
506
|
my $prop;
|
507
507
|
foreach $prop (@propList) {
|
508
508
|
my ($ns, $tag) = $prop =~ /(.+?):(.*)/;
|
509
|
-
next if $$nsUsed{$ns};
|
509
|
+
next if not defined $ns or $$nsUsed{$ns};
|
510
510
|
my $uri = $nsURI{$ns};
|
511
511
|
unless ($uri) {
|
512
512
|
warn "No URI for namespace prefix $ns!\n";
|
@@ -1417,7 +1417,11 @@ sub WriteXMP($$;$)
|
|
1417
1417
|
my $uri = $nsUsed{$1};
|
1418
1418
|
unless ($uri) {
|
1419
1419
|
$uri = $nsURI{$1}; # we must have added a namespace
|
1420
|
-
$uri
|
1420
|
+
unless ($uri) {
|
1421
|
+
# (namespace may be empty if trying to write empty XMP structure, forum12384)
|
1422
|
+
$xmpErr = "Undefined XMP namespace: $1" if length $uri;
|
1423
|
+
next;
|
1424
|
+
}
|
1421
1425
|
}
|
1422
1426
|
$nsNew{$1} = $uri;
|
1423
1427
|
# need a new description if any new namespaces
|
@@ -1465,7 +1469,7 @@ sub WriteXMP($$;$)
|
|
1465
1469
|
$long[-2] .= "$nl$sp<$prop rdf:about='${about}'";
|
1466
1470
|
# generate et:toolkit attribute if this is an exiftool RDF/XML output file
|
1467
1471
|
if (@ns and $nsCur{$ns[0]} =~ m{^http://ns.exiftool.(?:ca|org)/}) {
|
1468
|
-
$long[-2] .= "\n$sp${sp}xmlns:et='http://ns.exiftool.
|
1472
|
+
$long[-2] .= "\n$sp${sp}xmlns:et='http://ns.exiftool.org/1.0/'" .
|
1469
1473
|
" et:toolkit='Image::ExifTool $Image::ExifTool::VERSION'";
|
1470
1474
|
}
|
1471
1475
|
$long[-2] .= "\n$sp${sp}xmlns:$_='$nsCur{$_}'" foreach @ns;
|
@@ -105,6 +105,7 @@ my %writableType = (
|
|
105
105
|
ICC => [ 'ICC_Profile', 'WriteICC' ],
|
106
106
|
IND => 'InDesign',
|
107
107
|
JP2 => 'Jpeg2000',
|
108
|
+
JXL => 'Jpeg2000',
|
108
109
|
MIE => undef,
|
109
110
|
MOV => [ 'QuickTime', 'WriteMOV' ],
|
110
111
|
MRW => 'MinoltaRaw',
|
@@ -573,6 +574,9 @@ sub SetNewValue($;$$%)
|
|
573
574
|
my $pre = $wantGroup ? $wantGroup . ':' : '';
|
574
575
|
$err = "Tag '$pre${origTag}' is not defined";
|
575
576
|
$err .= ' or has a bad language code' if $origTag =~ /-/;
|
577
|
+
if (not $pre and uc($origTag) eq 'TAG') {
|
578
|
+
$err .= " (specify a writable tag name, not '${origTag}' literally)"
|
579
|
+
}
|
576
580
|
} else {
|
577
581
|
$err = "Invalid tag name '${tag}'";
|
578
582
|
$err .= " (remove the leading '\$')" if $tag =~ /^\$/;
|
@@ -2070,6 +2074,46 @@ sub SetSystemTags($$)
|
|
2070
2074
|
last;
|
2071
2075
|
}
|
2072
2076
|
}
|
2077
|
+
# delete Windows Zone.Identifier if specified
|
2078
|
+
my $zhash = $self->GetNewValueHash($Image::ExifTool::Extra{ZoneIdentifier});
|
2079
|
+
if ($zhash) {
|
2080
|
+
my $res = -1;
|
2081
|
+
if ($^O ne 'MSWin32') {
|
2082
|
+
$self->Warn('ZoneIdentifer is a Windows-only tag');
|
2083
|
+
} elsif (ref $file) {
|
2084
|
+
$self->Warn('Writing ZoneIdentifer requires a file name');
|
2085
|
+
} elsif (defined $self->GetNewValue('ZoneIdentifier', \$zhash)) {
|
2086
|
+
$self->Warn('ZoneIndentifier may only be delted');
|
2087
|
+
} elsif (not eval { require Win32API::File }) {
|
2088
|
+
$self->Warn('Install Win32API::File to write ZoneIdentifier');
|
2089
|
+
} else {
|
2090
|
+
my ($wattr, $wide);
|
2091
|
+
my $zfile = "${file}:Zone.Identifier";
|
2092
|
+
if ($self->EncodeFileName($zfile)) {
|
2093
|
+
$wide = 1;
|
2094
|
+
$wattr = eval { Win32API::File::GetFileAttributesW($zfile) };
|
2095
|
+
} else {
|
2096
|
+
$wattr = eval { Win32API::File::GetFileAttributes($zfile) };
|
2097
|
+
}
|
2098
|
+
if ($wattr == Win32API::File::INVALID_FILE_ATTRIBUTES()) {
|
2099
|
+
$res = 0; # file doesn't exist, nothing to do
|
2100
|
+
} elsif ($wattr & Win32API::File::FILE_ATTRIBUTE_READONLY()) {
|
2101
|
+
$self->Warn('Zone.Identifier stream is read-only');
|
2102
|
+
} else {
|
2103
|
+
if ($wide) {
|
2104
|
+
$res = 1 if eval { Win32API::File::DeleteFileW($zfile) };
|
2105
|
+
} else {
|
2106
|
+
$res = 1 if eval { Win32API::File::DeleteFile($zfile) };
|
2107
|
+
}
|
2108
|
+
if ($res > 0) {
|
2109
|
+
$self->VPrint(0, " Deleting Zone.Identifier stream\n");
|
2110
|
+
} else {
|
2111
|
+
$self->Warn('Error deleting Zone.Identifier stream');
|
2112
|
+
}
|
2113
|
+
}
|
2114
|
+
}
|
2115
|
+
$result = $res if $res == 1 or not $result;
|
2116
|
+
}
|
2073
2117
|
return $result;
|
2074
2118
|
}
|
2075
2119
|
|
@@ -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.47';
|
54
54
|
@ISA = qw(Exporter);
|
55
55
|
@EXPORT_OK = qw(EscapeXML UnescapeXML);
|
56
56
|
|
@@ -71,6 +71,13 @@ sub ConvertRational($);
|
|
71
71
|
sub ConvertRationalList($);
|
72
72
|
sub WriteGSpherical($$$);
|
73
73
|
|
74
|
+
# standard path locations for XMP in major file types
|
75
|
+
my %stdPath = (
|
76
|
+
JPEG => 'JPEG-APP1-XMP',
|
77
|
+
TIFF => 'TIFF-IFD0-XMP',
|
78
|
+
PSD => 'PSD-XMP',
|
79
|
+
);
|
80
|
+
|
74
81
|
# lookup for translating to ExifTool namespaces (and family 1 group names)
|
75
82
|
%stdXlatNS = (
|
76
83
|
# shorten ugly namespace prefixes
|
@@ -148,7 +155,7 @@ my %xmpNS = (
|
|
148
155
|
DICOM => 'http://ns.adobe.com/DICOM/',
|
149
156
|
'drone-dji'=> 'http://www.dji.com/drone-dji/1.0/',
|
150
157
|
svg => 'http://www.w3.org/2000/svg',
|
151
|
-
et => 'http://ns.exiftool.
|
158
|
+
et => 'http://ns.exiftool.org/1.0/',
|
152
159
|
#
|
153
160
|
# namespaces defined in XMP2.pl:
|
154
161
|
#
|
@@ -188,7 +195,7 @@ my %xmpNS = (
|
|
188
195
|
);
|
189
196
|
|
190
197
|
# build reverse namespace lookup
|
191
|
-
my %uri2ns = ( 'http://ns.exiftool.
|
198
|
+
my %uri2ns = ( 'http://ns.exiftool.ca/1.0/' => 'et' ); # (allow exiftool.ca as well as exiftool.org)
|
192
199
|
{
|
193
200
|
my $ns;
|
194
201
|
foreach $ns (keys %nsURI) {
|
@@ -1526,6 +1533,9 @@ my %sPantryItem = (
|
|
1526
1533
|
CameraProfile => { },
|
1527
1534
|
LookTable => { },
|
1528
1535
|
ToneCurvePV2012 => { List => 'Seq' },
|
1536
|
+
ToneCurvePV2012Red => { List => 'Seq' },
|
1537
|
+
ToneCurvePV2012Green => { List => 'Seq' },
|
1538
|
+
ToneCurvePV2012Blue => { List => 'Seq' },
|
1529
1539
|
},
|
1530
1540
|
},
|
1531
1541
|
}
|
@@ -2010,6 +2020,11 @@ my %sPantryItem = (
|
|
2010
2020
|
Groups => { 2 => 'Location' },
|
2011
2021
|
Writable => 'integer',
|
2012
2022
|
PrintConv => {
|
2023
|
+
OTHER => sub {
|
2024
|
+
my ($val, $inv) = @_;
|
2025
|
+
return undef unless $inv and $val =~ /^([-+0-9])/;
|
2026
|
+
return($1 eq '-' ? 1 : 0);
|
2027
|
+
},
|
2013
2028
|
0 => 'Above Sea Level',
|
2014
2029
|
1 => 'Below Sea Level',
|
2015
2030
|
},
|
@@ -2127,8 +2142,8 @@ my %sPantryItem = (
|
|
2127
2142
|
NAMESPACE => 'exifEX',
|
2128
2143
|
PRIORITY => 0, # not as reliable as actual EXIF tags
|
2129
2144
|
NOTES => q{
|
2130
|
-
EXIF tags added by the EXIF 2.
|
2131
|
-
L<
|
2145
|
+
EXIF tags added by the EXIF 2.32 for XMP specification (see
|
2146
|
+
L<https://cipa.jp/std/documents/download_e.html?DC-010-2020_E>).
|
2132
2147
|
},
|
2133
2148
|
Gamma => { Writable => 'rational' },
|
2134
2149
|
PhotographicSensitivity => { Writable => 'integer' },
|
@@ -2334,6 +2349,9 @@ my %sPantryItem = (
|
|
2334
2349
|
Scene => { Groups => { 2 => 'Other' }, List => 'Bag' },
|
2335
2350
|
SubjectCode => { Groups => { 2 => 'Other' }, List => 'Bag' },
|
2336
2351
|
# Copyright - have seen this in a sample (Jan 2021), but I think it is non-standard
|
2352
|
+
# new IPTC Core 1.3 properties
|
2353
|
+
AltTextAccessibility => { Groups => { 2 => 'Other' }, Writable => 'lang-alt' },
|
2354
|
+
ExtDescrAccessibility => { Groups => { 2 => 'Other' }, Writable => 'lang-alt' },
|
2337
2355
|
);
|
2338
2356
|
|
2339
2357
|
# Adobe Lightroom namespace properties (lr) (ref PH)
|
@@ -3251,8 +3269,14 @@ NoLoop:
|
|
3251
3269
|
}
|
3252
3270
|
}
|
3253
3271
|
# generate a default tagInfo hash if necessary
|
3254
|
-
|
3255
|
-
|
3272
|
+
unless ($tagInfo) {
|
3273
|
+
# shorten tag name if necessary
|
3274
|
+
if ($$et{ShortenXmpTags}) {
|
3275
|
+
my $shorten = $$et{ShortenXmpTags};
|
3276
|
+
$name = &$shorten($name);
|
3277
|
+
}
|
3278
|
+
$tagInfo = { Name => $name, IsDefault => 1, Priority => 0 };
|
3279
|
+
}
|
3256
3280
|
# add tag Namespace entry for tags in variable-namespace tables
|
3257
3281
|
$$tagInfo{Namespace} = $xns if $xns;
|
3258
3282
|
if ($$et{curURI}{$ns} and $$et{curURI}{$ns} =~ m{^http://ns.exiftool.(?:ca|org)/(.*?)/(.*?)/}) {
|
@@ -3771,6 +3795,7 @@ sub ParseXMPElement($$$;$$$$)
|
|
3771
3795
|
# (unless we already extracted shorthand values from this element)
|
3772
3796
|
if (length $val or not $shorthand) {
|
3773
3797
|
my $lastProp = $$propList[-1];
|
3798
|
+
$lastProp = '' unless defined $lastProp;
|
3774
3799
|
if (defined $nodeID) {
|
3775
3800
|
SaveBlankInfo($blankInfo, $propList, $val);
|
3776
3801
|
} elsif ($lastProp eq 'rdf:type' and $wasEmpty) {
|
@@ -3844,6 +3869,7 @@ sub ProcessXMP($$;$)
|
|
3844
3869
|
my ($buff, $fmt, $hasXMP, $isXML, $isRDF, $isSVG);
|
3845
3870
|
my $rtnVal = 0;
|
3846
3871
|
my $bom = 0;
|
3872
|
+
my $path = $et->MetadataPath();
|
3847
3873
|
|
3848
3874
|
# namespaces and prefixes currently in effect while parsing the file,
|
3849
3875
|
# and lookup to translate brain-dead-Microsoft-Photo-software prefixes
|
@@ -3861,11 +3887,7 @@ sub ProcessXMP($$;$)
|
|
3861
3887
|
(($$dirInfo{DirName} || '') eq 'XMP' or $$et{FILE_TYPE} eq 'XMP'))
|
3862
3888
|
{
|
3863
3889
|
$$et{XmpValidate} = { } if $$et{OPTIONS}{Validate};
|
3864
|
-
my $
|
3865
|
-
my $nonStd;
|
3866
|
-
if ($$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/ and $path !~ /^(JPEG-APP1-XMP|TIFF-IFD0-XMP|PSD-XMP)$/) {
|
3867
|
-
$nonStd = 1;
|
3868
|
-
}
|
3890
|
+
my $nonStd = ($stdPath{$$et{FILE_TYPE}} and $path ne $stdPath{$$et{FILE_TYPE}});
|
3869
3891
|
if ($nonStd and $Image::ExifTool::MWG::strict) {
|
3870
3892
|
$et->Warn("Ignored non-standard XMP at $path");
|
3871
3893
|
return 1;
|
@@ -3944,7 +3966,7 @@ sub ProcessXMP($$;$)
|
|
3944
3966
|
} elsif ($1 eq 'REDXIF') {
|
3945
3967
|
$type = 'RMD';
|
3946
3968
|
$mime = 'application/xml';
|
3947
|
-
}
|
3969
|
+
} elsif ($1 ne 'fcpxml') { # Final Cut Pro XML
|
3948
3970
|
return 0;
|
3949
3971
|
}
|
3950
3972
|
} elsif ($buf2 =~ /<svg[\s>]/) {
|
@@ -3954,14 +3976,16 @@ sub ProcessXMP($$;$)
|
|
3954
3976
|
} elsif ($buf2 =~ /<plist[\s>]/) {
|
3955
3977
|
$type = 'PLIST';
|
3956
3978
|
}
|
3957
|
-
if ($isSVG and $$et{XMP_CAPTURE}) {
|
3958
|
-
$et->Error("ExifTool does not yet support writing of SVG images");
|
3959
|
-
return 0;
|
3960
|
-
}
|
3961
3979
|
}
|
3962
3980
|
$isXML = 1;
|
3963
3981
|
} elsif ($2 eq '<rdf:RDF') {
|
3964
3982
|
$isRDF = 1; # recognize XMP without x:xmpmeta element
|
3983
|
+
} elsif ($2 eq '<svg') {
|
3984
|
+
$isSVG = $isXML = 1;
|
3985
|
+
}
|
3986
|
+
if ($isSVG and $$et{XMP_CAPTURE}) {
|
3987
|
+
$et->Error("ExifTool does not yet support writing of SVG images");
|
3988
|
+
return 0;
|
3965
3989
|
}
|
3966
3990
|
if ($buff =~ /^\0\0/) {
|
3967
3991
|
$fmt = 'N'; # UTF-32 MM with or without BOM
|
@@ -4063,12 +4087,14 @@ sub ProcessXMP($$;$)
|
|
4063
4087
|
|
4064
4088
|
# extract XMP/XML as a block if specified
|
4065
4089
|
my $blockName = $$dirInfo{BlockInfo} ? $$dirInfo{BlockInfo}{Name} : 'XMP';
|
4090
|
+
my $blockExtract = $et->Options('BlockExtract');
|
4066
4091
|
if (($$et{REQ_TAG_LOOKUP}{lc $blockName} or ($$et{TAGS_FROM_FILE} and
|
4067
|
-
not $$et{EXCL_TAG_LOOKUP}{lc $blockName})) and
|
4092
|
+
not $$et{EXCL_TAG_LOOKUP}{lc $blockName}) or $blockExtract) and
|
4068
4093
|
(($$et{FileType} eq 'XMP' and $blockName eq 'XMP') or
|
4069
4094
|
($$dirInfo{DirName} and $$dirInfo{DirName} eq $blockName)))
|
4070
4095
|
{
|
4071
4096
|
$et->FoundTag($$dirInfo{BlockInfo} || 'XMP', substr($$dataPt, $dirStart, $dirLen));
|
4097
|
+
return 1 if $blockExtract and $blockExtract > 1;
|
4072
4098
|
}
|
4073
4099
|
|
4074
4100
|
$tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
|
@@ -4123,6 +4149,13 @@ sub ProcessXMP($$;$)
|
|
4123
4149
|
}
|
4124
4150
|
defined $fmt or $et->Warn('XMP character encoding error');
|
4125
4151
|
}
|
4152
|
+
# warn if standard XMP is missing xpacket wrapper
|
4153
|
+
if ($$et{XMP_NO_XPACKET} and $$et{OPTIONS}{Validate} and
|
4154
|
+
$stdPath{$$et{FILE_TYPE}} and $path eq $stdPath{$$et{FILE_TYPE}} and
|
4155
|
+
not $$dirInfo{IsExtended} and not $$et{DOC_NUM})
|
4156
|
+
{
|
4157
|
+
$et->Warn('XMP is missing xpacket wrapper', 1);
|
4158
|
+
}
|
4126
4159
|
if ($fmt) {
|
4127
4160
|
# trim if necessary to avoid converting non-UTF data
|
4128
4161
|
if ($dirStart or $dirEnd != length($$dataPt)) {
|
@@ -539,7 +539,8 @@ my %sImageRegion = ( # new in 1.5
|
|
539
539
|
NOTES => q{
|
540
540
|
This table contains tags defined by the IPTC Extension schema version 1.5.
|
541
541
|
The actual namespace prefix is "Iptc4xmpExt", but ExifTool shortens this for
|
542
|
-
the family 1 group name. (see
|
542
|
+
the family 1 group name. (see
|
543
|
+
L<http://www.iptc.org/standards/photo-metadata/iptc-standard/>)
|
543
544
|
},
|
544
545
|
AboutCvTerm => {
|
545
546
|
Struct => \%sCVTermDetails,
|
@@ -796,6 +797,8 @@ my %sImageRegion = ( # new in 1.5
|
|
796
797
|
audioBitsPerSample => { Groups => { 2 => 'Audio' }, Writable => 'integer' },
|
797
798
|
# new IPTC Extension schema 1.5 property
|
798
799
|
ImageRegion => { Groups => { 2 => 'Image' }, List => 'Bag', Struct => \%sImageRegion },
|
800
|
+
# new Extension 1.6 property
|
801
|
+
EventId => { Name => 'EventID', List => 'Bag' },
|
799
802
|
);
|
800
803
|
|
801
804
|
#------------------------------------------------------------------------------
|
@@ -32,7 +32,9 @@ sub SerializeStruct($;$)
|
|
32
32
|
my ($key, $val, @vals, $rtnVal);
|
33
33
|
|
34
34
|
if (ref $obj eq 'HASH') {
|
35
|
-
|
35
|
+
# support hashes with ordered keys
|
36
|
+
my @keys = $$obj{_ordered_keys_} ? @{$$obj{_ordered_keys_}} : sort keys %$obj;
|
37
|
+
foreach $key (@keys) {
|
36
38
|
push @vals, $key . '=' . SerializeStruct($$obj{$key}, '}');
|
37
39
|
}
|
38
40
|
$rtnVal = '{' . join(',', @vals) . '}';
|