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
|
@@ -16,7 +16,7 @@ use strict;
|
|
|
16
16
|
use vars qw($VERSION);
|
|
17
17
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
18
18
|
|
|
19
|
-
$VERSION = '1.
|
|
19
|
+
$VERSION = '1.36';
|
|
20
20
|
|
|
21
21
|
sub ProcessJpeg2000Box($$$);
|
|
22
22
|
sub ProcessJUMD($$$);
|
|
@@ -34,6 +34,9 @@ my %resolutionUnit = (
|
|
|
34
34
|
6 => 'um',
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
+
# top-level boxes containing image data
|
|
38
|
+
my %isImageData = ( jp2c=>1, jbrd=>1, jxlp=>1, jxlc=>1 );
|
|
39
|
+
|
|
37
40
|
# map of where information is written in JPEG2000 image
|
|
38
41
|
my %jp2Map = (
|
|
39
42
|
IPTC => 'UUID-IPTC',
|
|
@@ -127,7 +130,10 @@ my %j2cMarker = (
|
|
|
127
130
|
NOTES => q{
|
|
128
131
|
The tags below are found in JPEG 2000 images and the JUMBF metadata in JPEG
|
|
129
132
|
images, but not all of these are extracted. Note that ExifTool currently
|
|
130
|
-
writes only EXIF, IPTC and XMP tags in Jpeg2000 images
|
|
133
|
+
writes only EXIF, IPTC and XMP tags in Jpeg2000 images, and EXIF and XMP in
|
|
134
|
+
JXL images. ExifTool will read/write Brotli-compressed EXIF and XMP in JXL
|
|
135
|
+
images, but the API L<Compress|../ExifTool.html#Compress> option must be set to create new EXIF and XMP
|
|
136
|
+
in compressed format.
|
|
131
137
|
},
|
|
132
138
|
#
|
|
133
139
|
# NOTE: ONLY TAGS WITH "Format" DEFINED ARE EXTRACTED!
|
|
@@ -259,7 +265,7 @@ my %j2cMarker = (
|
|
|
259
265
|
uuid => [
|
|
260
266
|
{
|
|
261
267
|
Name => 'UUID-EXIF',
|
|
262
|
-
# (this is the EXIF that we create)
|
|
268
|
+
# (this is the EXIF that we create in JP2)
|
|
263
269
|
Condition => '$$valPt=~/^JpgTiffExif->JP2(?!Exif\0\0)/',
|
|
264
270
|
SubDirectory => {
|
|
265
271
|
TagTable => 'Image::ExifTool::Exif::Main',
|
|
@@ -295,7 +301,7 @@ my %j2cMarker = (
|
|
|
295
301
|
},
|
|
296
302
|
{
|
|
297
303
|
Name => 'UUID-IPTC',
|
|
298
|
-
# (this is the IPTC that we create)
|
|
304
|
+
# (this is the IPTC that we create in JP2)
|
|
299
305
|
Condition => '$$valPt=~/^\x33\xc7\xa4\xd2\xb8\x1d\x47\x23\xa0\xba\xf1\xa3\xe0\x97\xad\x38/',
|
|
300
306
|
SubDirectory => {
|
|
301
307
|
TagTable => 'Image::ExifTool::IPTC::Main',
|
|
@@ -437,6 +443,15 @@ my %j2cMarker = (
|
|
|
437
443
|
},
|
|
438
444
|
RawConv => 'Image::ExifTool::Jpeg2000::ProcessJXLCodestream($self,\$val); undef',
|
|
439
445
|
},
|
|
446
|
+
jxlp => {
|
|
447
|
+
Name => 'PartialJXLCodestream',
|
|
448
|
+
Format => 'undef',
|
|
449
|
+
Notes => q{
|
|
450
|
+
Partial codestreams in JPEG XL image. Currently processed only to determine
|
|
451
|
+
ImageSize
|
|
452
|
+
},
|
|
453
|
+
RawConv => 'Image::ExifTool::Jpeg2000::ProcessJXLCodestream($self,\$val); undef',
|
|
454
|
+
},
|
|
440
455
|
Exif => {
|
|
441
456
|
Name => 'EXIF',
|
|
442
457
|
SubDirectory => {
|
|
@@ -447,6 +462,38 @@ my %j2cMarker = (
|
|
|
447
462
|
Start => '$valuePtr + 4 + (length($$dataPt)-$valuePtr > 4 ? unpack("N", $$dataPt) : 0)',
|
|
448
463
|
},
|
|
449
464
|
},
|
|
465
|
+
hrgm => {
|
|
466
|
+
Name => 'GainMapImage',
|
|
467
|
+
Groups => { 2 => 'Preview' },
|
|
468
|
+
Format => 'undef',
|
|
469
|
+
Binary => 1,
|
|
470
|
+
},
|
|
471
|
+
brob => [{ # Brotli-encoded metadata (see https://libjxl.readthedocs.io/en/latest/api_decoder.html)
|
|
472
|
+
Name => 'BrotliXMP',
|
|
473
|
+
Condition => '$$valPt =~ /^xml /i',
|
|
474
|
+
SubDirectory => {
|
|
475
|
+
TagTable => 'Image::ExifTool::XMP::Main',
|
|
476
|
+
ProcessProc => \&ProcessBrotli,
|
|
477
|
+
WriteProc => \&ProcessBrotli,
|
|
478
|
+
# (don't set DirName to 'XMP' because this would enable a block write of raw XMP)
|
|
479
|
+
},
|
|
480
|
+
},{
|
|
481
|
+
Name => 'BrotliEXIF',
|
|
482
|
+
Condition => '$$valPt =~ /^exif/i',
|
|
483
|
+
SubDirectory => {
|
|
484
|
+
TagTable => 'Image::ExifTool::Exif::Main',
|
|
485
|
+
ProcessProc => \&ProcessBrotli,
|
|
486
|
+
WriteProc => \&ProcessBrotli,
|
|
487
|
+
# (don't set DirName to 'EXIF' because this would enable a block write of raw EXIF)
|
|
488
|
+
},
|
|
489
|
+
},{
|
|
490
|
+
Name => 'BrotliJUMB',
|
|
491
|
+
Condition => '$$valPt =~ /^jumb/i',
|
|
492
|
+
SubDirectory => {
|
|
493
|
+
TagTable => 'Image::ExifTool::Jpeg2000::Main',
|
|
494
|
+
ProcessProc => \&ProcessBrotli,
|
|
495
|
+
},
|
|
496
|
+
}],
|
|
450
497
|
);
|
|
451
498
|
|
|
452
499
|
%Image::ExifTool::Jpeg2000::ImageHeader = (
|
|
@@ -836,12 +883,31 @@ sub CreateNewBoxes($$)
|
|
|
836
883
|
$tagTable = GetTagTable('Image::ExifTool::XMP::Main') if $dir eq 'XMP';
|
|
837
884
|
my %dirInfo = (
|
|
838
885
|
DirName => $dir,
|
|
839
|
-
Parent =>
|
|
886
|
+
Parent => $tag,
|
|
840
887
|
);
|
|
888
|
+
my $compress = $et->Options('Compress');
|
|
889
|
+
$dirInfo{Compact} = 1 if $$et{IsJXL} and $compress;
|
|
841
890
|
my $newdir = $et->WriteDirectory(\%dirInfo, $tagTable, $$subdir{WriteProc});
|
|
842
891
|
if (defined $newdir and length $newdir) {
|
|
843
892
|
# not sure why, but EXIF box is padded with leading 0's in my sample
|
|
844
893
|
my $pad = $dirName eq 'Exif' ? "\0\0\0\0" : '';
|
|
894
|
+
if ($$et{IsJXL} and $compress) {
|
|
895
|
+
# create as Brotli-compressed metadata
|
|
896
|
+
if (eval { require IO::Compress::Brotli }) {
|
|
897
|
+
my $compressed;
|
|
898
|
+
eval { $compressed = IO::Compress::Brotli::bro($pad . $newdir) };
|
|
899
|
+
if ($@ or not $compressed) {
|
|
900
|
+
$et->Warn("Error encoding $dirName brob box");
|
|
901
|
+
} else {
|
|
902
|
+
$et->VPrint(0, " Writing Brotli-compressed $dir\n");
|
|
903
|
+
$newdir = $compressed;
|
|
904
|
+
$pad = $tag;
|
|
905
|
+
$tag = 'brob';
|
|
906
|
+
}
|
|
907
|
+
} else {
|
|
908
|
+
$et->WarnOnce('Install IO::Compress::Brotli to create Brotli-compressed metadata');
|
|
909
|
+
}
|
|
910
|
+
}
|
|
845
911
|
my $boxhdr = pack('N', length($newdir) + length($pad) + 8) . $tag;
|
|
846
912
|
Write($outfile, $boxhdr, $pad, $newdir) or return 0;
|
|
847
913
|
next;
|
|
@@ -930,7 +996,7 @@ sub ProcessJpeg2000Box($$$)
|
|
|
930
996
|
my $raf = $$dirInfo{RAF};
|
|
931
997
|
my $outfile = $$dirInfo{OutFile};
|
|
932
998
|
my $dirEnd = $dirStart + $dirLen;
|
|
933
|
-
my ($err, $outBuff, $verbose, $doColour);
|
|
999
|
+
my ($err, $outBuff, $verbose, $doColour, $hash);
|
|
934
1000
|
|
|
935
1001
|
if ($outfile) {
|
|
936
1002
|
unless ($raf) {
|
|
@@ -948,6 +1014,8 @@ sub ProcessJpeg2000Box($$$)
|
|
|
948
1014
|
# (must not set verbose flag when writing!)
|
|
949
1015
|
$verbose = $$et{OPTIONS}{Verbose};
|
|
950
1016
|
$et->VerboseDir($$dirInfo{DirName}) if $verbose;
|
|
1017
|
+
# do hash if requested, but only for top-level image data
|
|
1018
|
+
$hash = $$et{ImageDataHash} if $raf;
|
|
951
1019
|
}
|
|
952
1020
|
# loop through all contained boxes
|
|
953
1021
|
my ($pos, $boxLen, $lastBox);
|
|
@@ -971,6 +1039,11 @@ sub ProcessJpeg2000Box($$$)
|
|
|
971
1039
|
}
|
|
972
1040
|
$boxLen = unpack("x$pos N",$$dataPt); # (length includes header and data)
|
|
973
1041
|
$boxID = substr($$dataPt, $pos+4, 4);
|
|
1042
|
+
# (ftbl box contains flst boxes with absolute file offsets, not currently handled)
|
|
1043
|
+
if ($outfile and $boxID eq 'ftbl') {
|
|
1044
|
+
$et->Error("Can't yet handle fragmented JPX files");
|
|
1045
|
+
return -1;
|
|
1046
|
+
}
|
|
974
1047
|
# remove old colr boxes if necessary
|
|
975
1048
|
if ($doColour and $boxID eq 'colr') {
|
|
976
1049
|
if ($doColour == 1) { # did we successfully write the new colr box?
|
|
@@ -1007,9 +1080,14 @@ sub ProcessJpeg2000Box($$$)
|
|
|
1007
1080
|
while ($raf->Read($buff, 65536)) {
|
|
1008
1081
|
Write($outfile, $buff) or $err = 1;
|
|
1009
1082
|
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1083
|
+
} else {
|
|
1084
|
+
if ($verbose) {
|
|
1085
|
+
my $msg = sprintf("offset 0x%.4x to end of file", $dataPos + $base + $pos);
|
|
1086
|
+
$et->VPrint(0, "$$et{INDENT}- Tag '${boxID}' ($msg)\n");
|
|
1087
|
+
}
|
|
1088
|
+
if ($hash and $isImageData{$boxID}) {
|
|
1089
|
+
$et->ImageDataHash($raf, undef, $boxID);
|
|
1090
|
+
}
|
|
1013
1091
|
}
|
|
1014
1092
|
last; # (ignore the rest of the file when reading)
|
|
1015
1093
|
}
|
|
@@ -1026,6 +1104,8 @@ sub ProcessJpeg2000Box($$$)
|
|
|
1026
1104
|
Write($outfile, $$dataPt) or $err = 1;
|
|
1027
1105
|
$raf->Read($buff,$boxLen) == $boxLen or $err = '', last;
|
|
1028
1106
|
Write($outfile, $buff) or $err = 1;
|
|
1107
|
+
} elsif ($hash and $isImageData{$boxID}) {
|
|
1108
|
+
$et->ImageDataHash($raf, $boxLen, $boxID);
|
|
1029
1109
|
} else {
|
|
1030
1110
|
$raf->Seek($boxLen, 1) or $err = 'Seek error', last;
|
|
1031
1111
|
}
|
|
@@ -1038,6 +1118,10 @@ sub ProcessJpeg2000Box($$$)
|
|
|
1038
1118
|
# read the box data
|
|
1039
1119
|
$dataPos = $raf->Tell() - $base;
|
|
1040
1120
|
$raf->Read($buff,$boxLen) == $boxLen or $err = '', last;
|
|
1121
|
+
if ($hash and $isImageData{$boxID}) {
|
|
1122
|
+
$hash->add($buff);
|
|
1123
|
+
$et->VPrint(0, "$$et{INDENT}(ImageDataHash: $boxLen bytes of $boxID data)\n");
|
|
1124
|
+
}
|
|
1041
1125
|
$valuePtr = 0;
|
|
1042
1126
|
$dataLen = $boxLen;
|
|
1043
1127
|
} elsif ($pos + $boxLen > $dirEnd) {
|
|
@@ -1122,19 +1206,66 @@ sub ProcessJpeg2000Box($$$)
|
|
|
1122
1206
|
$subdirInfo{DirName} =~ s/^UUID-//;
|
|
1123
1207
|
my $subTable = GetTagTable($$subdir{TagTable}) || $tagTablePtr;
|
|
1124
1208
|
if ($outfile) {
|
|
1125
|
-
#
|
|
1126
|
-
|
|
1127
|
-
|
|
1209
|
+
# (special case for brob box, which may be EXIF or XMP)
|
|
1210
|
+
my $fakeID = $boxID;
|
|
1211
|
+
if ($boxID eq 'brob') {
|
|
1212
|
+
# I have seen 'brob' ID's with funny cases, so standardize these
|
|
1213
|
+
$fakeID = 'xml ' if $$dataPt =~ /^xml /i;
|
|
1214
|
+
$fakeID = 'Exif' if $$dataPt =~ /^Exif/i;
|
|
1215
|
+
}
|
|
1128
1216
|
my $newdir;
|
|
1129
1217
|
# only edit writable UUID, Exif and jp2h boxes
|
|
1130
|
-
if ($uuid or $
|
|
1218
|
+
if ($uuid or $fakeID eq 'Exif' or ($fakeID eq 'xml ' and $$et{IsJXL}) or
|
|
1131
1219
|
($boxID eq 'jp2h' and $$et{EDIT_DIRS}{jp2h}))
|
|
1132
1220
|
{
|
|
1221
|
+
my $compress = $et->Options('Compress');
|
|
1222
|
+
$subdirInfo{Parent} = $fakeID;
|
|
1223
|
+
$subdirInfo{Compact} = 1 if $compress and $$et{IsJXL};
|
|
1133
1224
|
$newdir = $et->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
|
|
1134
1225
|
next if defined $newdir and not length $newdir; # next if deleting the box
|
|
1226
|
+
# compress JXL EXIF or XMP metadata if requested
|
|
1227
|
+
if (defined $newdir and $$et{IsJXL} and defined $compress and
|
|
1228
|
+
($fakeID eq 'Exif' or $fakeID eq 'xml '))
|
|
1229
|
+
{
|
|
1230
|
+
if ($compress and $boxID ne 'brob') {
|
|
1231
|
+
# rewrite as a Brotli-compressed 'brob' box
|
|
1232
|
+
if (eval { require IO::Compress::Brotli }) {
|
|
1233
|
+
my $pad = $boxID eq 'Exif' ? "\0\0\0\0" : '';
|
|
1234
|
+
my $compressed;
|
|
1235
|
+
eval { $compressed = IO::Compress::Brotli::bro($pad . $newdir) };
|
|
1236
|
+
if ($@ or not $compressed) {
|
|
1237
|
+
$et->Warn("Error encoding $boxID brob box");
|
|
1238
|
+
} else {
|
|
1239
|
+
$et->VPrint(0, " Writing Brotli-compressed $boxID\n");
|
|
1240
|
+
$newdir = $boxID . $compressed;
|
|
1241
|
+
$boxID = 'brob';
|
|
1242
|
+
$subdirStart = $valuePtr = 0;
|
|
1243
|
+
++$$et{CHANGED};
|
|
1244
|
+
}
|
|
1245
|
+
} else {
|
|
1246
|
+
$et->WarnOnce('Install IO::Compress::Brotli to write Brotli-compressed metadata');
|
|
1247
|
+
}
|
|
1248
|
+
} elsif (not $compress and $boxID eq 'brob') {
|
|
1249
|
+
# (in this case, ProcessBrotli has returned uncompressed data,
|
|
1250
|
+
# so change to the uncompressed 'xml ' or 'Exif' box type)
|
|
1251
|
+
$et->VPrint(0, " Writing uncompressed $fakeID\n");
|
|
1252
|
+
$boxID = $fakeID;
|
|
1253
|
+
$subdirStart = $valuePtr = 0;
|
|
1254
|
+
++$$et{CHANGED};
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1135
1257
|
} elsif (defined $uuid) {
|
|
1136
1258
|
$et->Warn("Not editing $$tagInfo{Name} box", 1);
|
|
1137
1259
|
}
|
|
1260
|
+
# remove this directory from our create list
|
|
1261
|
+
delete $$et{AddJp2Dirs}{$fakeID}; # (eg. 'Exif' or 'xml ')
|
|
1262
|
+
if ($boxID eq 'brob') {
|
|
1263
|
+
# (can't make tag Name 'XMP' or 'Exif' for Brotli-compressed tags because it
|
|
1264
|
+
# would break the logic in WriteDirectory(), so we do a lookup here instead)
|
|
1265
|
+
delete $$et{AddJp2Dirs}{{'xml '=>'XMP','Exif'=>'EXIF'}->{$fakeID}};
|
|
1266
|
+
} else {
|
|
1267
|
+
delete $$et{AddJp2Dirs}{$$tagInfo{Name}}; # (eg. 'EXIF' or 'XMP')
|
|
1268
|
+
}
|
|
1138
1269
|
# use old box data if not changed
|
|
1139
1270
|
defined $newdir or $newdir = substr($$dataPt, $subdirStart, $subdirLen);
|
|
1140
1271
|
my $prefixLen = $subdirStart - $valuePtr;
|
|
@@ -1210,19 +1341,108 @@ sub GetBits($$)
|
|
|
1210
1341
|
return $v;
|
|
1211
1342
|
}
|
|
1212
1343
|
|
|
1344
|
+
#------------------------------------------------------------------------------
|
|
1345
|
+
# Read/write Brotli-encoded metadata
|
|
1346
|
+
# Inputs: 0) ExifTool ref, 1) dirInfoRef, 2) tag table ref
|
|
1347
|
+
# Returns: 1 on success when reading, or new data when writing (undef if unchanged)
|
|
1348
|
+
# (ref https://libjxl.readthedocs.io/en/latest/api_decoder.html)
|
|
1349
|
+
sub ProcessBrotli($$$)
|
|
1350
|
+
{
|
|
1351
|
+
my ($et, $dirInfo, $tagTablePtr) = @_;
|
|
1352
|
+
my $dataPt = $$dirInfo{DataPt};
|
|
1353
|
+
|
|
1354
|
+
return 0 unless length($$dataPt) > 4;
|
|
1355
|
+
|
|
1356
|
+
my $isWriting = $$dirInfo{IsWriting};
|
|
1357
|
+
my $type = substr($$dataPt, 0, 4);
|
|
1358
|
+
$et->VerboseDir("Decrypted Brotli '${type}'") unless $isWriting;
|
|
1359
|
+
my %knownType = ( exif => 'Exif', 'xml ' => 'xml ', jumb => 'jumb' );
|
|
1360
|
+
my $stdType = $knownType{lc $type};
|
|
1361
|
+
unless ($stdType) {
|
|
1362
|
+
$et->Warn('Unknown Brotli box type', 1);
|
|
1363
|
+
return 1;
|
|
1364
|
+
}
|
|
1365
|
+
if ($type ne $stdType) {
|
|
1366
|
+
$et->Warn("Incorrect case for Brotli '${type}' data (should be '${stdType}')");
|
|
1367
|
+
$type = $stdType;
|
|
1368
|
+
}
|
|
1369
|
+
if (eval { require IO::Uncompress::Brotli }) {
|
|
1370
|
+
if ($isWriting and not eval { require IO::Compress::Brotli }) {
|
|
1371
|
+
$et->WarnOnce('Install IO::Compress::Brotli to write Brotli-compressed metadata');
|
|
1372
|
+
return undef;
|
|
1373
|
+
}
|
|
1374
|
+
my $compress = $et->Options('Compress');
|
|
1375
|
+
my $verbose = $isWriting ? 0 : $et->Options('Verbose');
|
|
1376
|
+
my $dat = substr($$dataPt, 4);
|
|
1377
|
+
eval { $dat = IO::Uncompress::Brotli::unbro($dat, 100000000) };
|
|
1378
|
+
$@ and $et->Warn("Error decoding $type brob box"), return 1;
|
|
1379
|
+
$verbose > 2 and $et->VerboseDump(\$dat, Prefix => $$et{INDENT} . ' ');
|
|
1380
|
+
my %dirInfo = ( DataPt => \$dat );
|
|
1381
|
+
if ($type eq 'xml ') {
|
|
1382
|
+
$dirInfo{DirName} = 'XMP'; # (necessary for block read/write)
|
|
1383
|
+
require Image::ExifTool::XMP;
|
|
1384
|
+
if ($isWriting) {
|
|
1385
|
+
$dirInfo{Compact} = 1 if $compress; # (no need to add padding if writing compressed)
|
|
1386
|
+
$dat = $et->WriteDirectory(\%dirInfo, $tagTablePtr);
|
|
1387
|
+
} else {
|
|
1388
|
+
Image::ExifTool::XMP::ProcessXMP($et, \%dirInfo, $tagTablePtr);
|
|
1389
|
+
}
|
|
1390
|
+
} elsif ($type eq 'Exif') {
|
|
1391
|
+
$dirInfo{DirName} = 'EXIF'; # (necessary for block read/write)
|
|
1392
|
+
$dirInfo{DirStart} = 4 + (length($dat) > 4 ? unpack("N", $dat) : 0);
|
|
1393
|
+
if ($dirInfo{DirStart} > length $dat) {
|
|
1394
|
+
$et->Warn("Corrupted Brotli '${type}' data");
|
|
1395
|
+
} elsif ($isWriting) {
|
|
1396
|
+
$dat = $et->WriteDirectory(\%dirInfo, $tagTablePtr, \&Image::ExifTool::WriteTIFF);
|
|
1397
|
+
# add back header word
|
|
1398
|
+
$dat = "\0\0\0\0" . $dat if defined $dat and length $dat;
|
|
1399
|
+
} else {
|
|
1400
|
+
$et->ProcessTIFF(\%dirInfo, $tagTablePtr);
|
|
1401
|
+
}
|
|
1402
|
+
} elsif ($type eq 'jumb') {
|
|
1403
|
+
return undef if $isWriting; # (can't yet write JUMBF)
|
|
1404
|
+
Image::ExifTool::ProcessJUMB($et, \%dirInfo, $tagTablePtr); # (untested)
|
|
1405
|
+
}
|
|
1406
|
+
if ($isWriting) {
|
|
1407
|
+
return undef unless defined $dat;
|
|
1408
|
+
# rewrite as uncompressed if Compress option is set to 0 (or '')
|
|
1409
|
+
return $dat if defined $compress and not $compress;
|
|
1410
|
+
eval { $dat = IO::Compress::Brotli::bro($dat) };
|
|
1411
|
+
$@ and $et->Warn("Error encoding $type brob box"), return undef;
|
|
1412
|
+
$et->VPrint(0, " Writing Brotli-compressed $type\n");
|
|
1413
|
+
return $type . $dat;
|
|
1414
|
+
}
|
|
1415
|
+
} else {
|
|
1416
|
+
$et->WarnOnce('Install IO::Uncompress::Brotli to decode Brotli-compressed metadata');
|
|
1417
|
+
return undef if $isWriting;
|
|
1418
|
+
}
|
|
1419
|
+
return 1;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1213
1422
|
#------------------------------------------------------------------------------
|
|
1214
1423
|
# Extract parameters from JPEG XL codestream [unverified!]
|
|
1215
1424
|
# Inputs: 0) ExifTool ref, 1) codestream ref
|
|
1216
|
-
# Returns: 1
|
|
1425
|
+
# Returns: 1 on success
|
|
1217
1426
|
sub ProcessJXLCodestream($$)
|
|
1218
1427
|
{
|
|
1219
1428
|
my ($et, $dataPt) = @_;
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1429
|
+
|
|
1430
|
+
return 0 unless $$dataPt =~ /^(\0\0\0\0)?\xff\x0a/; # validate codestream
|
|
1431
|
+
# ignore if already extracted (ie. subsequent jxlp boxes)
|
|
1432
|
+
return 0 if $$et{ProcessedJXLCodestream};
|
|
1433
|
+
$$et{ProcessedJXLCodestream} = 1;
|
|
1434
|
+
# work with first 64 bytes of codestream data
|
|
1435
|
+
# (and add padding if necessary to avoid unpacking past end of data)
|
|
1436
|
+
my $dat;
|
|
1437
|
+
if (length $$dataPt > 64) {
|
|
1438
|
+
$dat = substr($$dataPt, 0, 64);
|
|
1439
|
+
} elsif (length $$dataPt < 18) {
|
|
1440
|
+
$dat = $$dataPt . ("\0" x 18); # (so we'll have a minimum 14 bytes to work with)
|
|
1441
|
+
} else {
|
|
1442
|
+
$dat = $$dataPt;
|
|
1224
1443
|
}
|
|
1225
|
-
|
|
1444
|
+
$dat =~ s/^\0\0\0\0//; # remove jxlp header word
|
|
1445
|
+
my @a = unpack 'x2C12', $dat;
|
|
1226
1446
|
my ($x, $y);
|
|
1227
1447
|
my $small = GetBits(\@a, 1);
|
|
1228
1448
|
if ($small) {
|
|
@@ -1311,7 +1531,7 @@ sub ProcessJP2($$)
|
|
|
1311
1531
|
}
|
|
1312
1532
|
|
|
1313
1533
|
#------------------------------------------------------------------------------
|
|
1314
|
-
# Read meta information
|
|
1534
|
+
# Read/write meta information in a JPEG XL image
|
|
1315
1535
|
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
|
1316
1536
|
# Returns: 1 on success, 0 if this wasn't a valid JPEG XL file, -1 on write error
|
|
1317
1537
|
sub ProcessJXL($$)
|
|
@@ -1340,6 +1560,9 @@ sub ProcessJXL($$)
|
|
|
1340
1560
|
$$dirInfo{RAF} = new File::RandomAccess(\$buff);
|
|
1341
1561
|
} else {
|
|
1342
1562
|
$et->SetFileType('JXL Codestream','image/jxl', 'jxl');
|
|
1563
|
+
if ($$et{ImageDataHash} and $raf->Seek(0,0)) {
|
|
1564
|
+
$et->ImageDataHash($raf, undef, 'JXL');
|
|
1565
|
+
}
|
|
1343
1566
|
return ProcessJXLCodestream($et, \$hdr);
|
|
1344
1567
|
}
|
|
1345
1568
|
} else {
|