exiftool_vendored 12.62.0 → 12.64.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 +50 -1
- data/bin/MANIFEST +4 -0
- data/bin/META.json +4 -1
- data/bin/META.yml +4 -1
- data/bin/Makefile.PL +7 -1
- data/bin/README +50 -46
- data/bin/config_files/guano.config +161 -0
- data/bin/exiftool +88 -62
- data/bin/lib/Image/ExifTool/7Z.pm +793 -0
- data/bin/lib/Image/ExifTool/Apple.pm +6 -3
- data/bin/lib/Image/ExifTool/Canon.pm +1 -0
- data/bin/lib/Image/ExifTool/CanonRaw.pm +4 -4
- data/bin/lib/Image/ExifTool/CanonVRD.pm +4 -1
- data/bin/lib/Image/ExifTool/Exif.pm +31 -14
- data/bin/lib/Image/ExifTool/FujiFilm.pm +3 -3
- data/bin/lib/Image/ExifTool/GPS.pm +5 -2
- data/bin/lib/Image/ExifTool/Geotag.pm +4 -1
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +226 -28
- data/bin/lib/Image/ExifTool/Lang/fr.pm +1467 -202
- data/bin/lib/Image/ExifTool/MPF.pm +2 -1
- data/bin/lib/Image/ExifTool/Matroska.pm +16 -1
- data/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
- data/bin/lib/Image/ExifTool/Nikon.pm +419 -5
- data/bin/lib/Image/ExifTool/NikonCustom.pm +13 -3
- data/bin/lib/Image/ExifTool/PDF.pm +9 -1
- data/bin/lib/Image/ExifTool/PLIST.pm +8 -1
- data/bin/lib/Image/ExifTool/PNG.pm +6 -6
- data/bin/lib/Image/ExifTool/PhaseOne.pm +5 -5
- data/bin/lib/Image/ExifTool/QuickTime.pm +74 -21
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +20 -19
- data/bin/lib/Image/ExifTool/README +2 -2
- data/bin/lib/Image/ExifTool/RIFF.pm +11 -9
- data/bin/lib/Image/ExifTool/Shortcuts.pm +2 -1
- data/bin/lib/Image/ExifTool/SigmaRaw.pm +4 -4
- data/bin/lib/Image/ExifTool/Sony.pm +103 -8
- data/bin/lib/Image/ExifTool/TagLookup.pm +4738 -4630
- data/bin/lib/Image/ExifTool/TagNames.pod +249 -5
- data/bin/lib/Image/ExifTool/Validate.pm +17 -1
- data/bin/lib/Image/ExifTool/WriteExif.pl +9 -7
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +21 -9
- data/bin/lib/Image/ExifTool/WriteXMP.pl +2 -2
- data/bin/lib/Image/ExifTool/Writer.pl +28 -10
- data/bin/lib/Image/ExifTool/XMP.pm +14 -2
- data/bin/lib/Image/ExifTool/XMP2.pl +32 -0
- data/bin/lib/Image/ExifTool/XMPStruct.pl +96 -28
- data/bin/lib/Image/ExifTool/ZIP.pm +5 -5
- data/bin/lib/Image/ExifTool.pm +67 -39
- data/bin/lib/Image/ExifTool.pod +83 -52
- data/bin/perl-Image-ExifTool.spec +44 -44
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +4 -2
@@ -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($$$);
|
@@ -130,7 +130,10 @@ my %j2cMarker = (
|
|
130
130
|
NOTES => q{
|
131
131
|
The tags below are found in JPEG 2000 images and the JUMBF metadata in JPEG
|
132
132
|
images, but not all of these are extracted. Note that ExifTool currently
|
133
|
-
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.
|
134
137
|
},
|
135
138
|
#
|
136
139
|
# NOTE: ONLY TAGS WITH "Format" DEFINED ARE EXTRACTED!
|
@@ -262,7 +265,7 @@ my %j2cMarker = (
|
|
262
265
|
uuid => [
|
263
266
|
{
|
264
267
|
Name => 'UUID-EXIF',
|
265
|
-
# (this is the EXIF that we create)
|
268
|
+
# (this is the EXIF that we create in JP2)
|
266
269
|
Condition => '$$valPt=~/^JpgTiffExif->JP2(?!Exif\0\0)/',
|
267
270
|
SubDirectory => {
|
268
271
|
TagTable => 'Image::ExifTool::Exif::Main',
|
@@ -298,7 +301,7 @@ my %j2cMarker = (
|
|
298
301
|
},
|
299
302
|
{
|
300
303
|
Name => 'UUID-IPTC',
|
301
|
-
# (this is the IPTC that we create)
|
304
|
+
# (this is the IPTC that we create in JP2)
|
302
305
|
Condition => '$$valPt=~/^\x33\xc7\xa4\xd2\xb8\x1d\x47\x23\xa0\xba\xf1\xa3\xe0\x97\xad\x38/',
|
303
306
|
SubDirectory => {
|
304
307
|
TagTable => 'Image::ExifTool::IPTC::Main',
|
@@ -431,7 +434,6 @@ my %j2cMarker = (
|
|
431
434
|
# stuff seen in JPEG XL images:
|
432
435
|
#
|
433
436
|
# jbrd - JPEG Bitstream Reconstruction Data (allows lossless conversion back to original JPG)
|
434
|
-
# jxlp - partial JXL codestream
|
435
437
|
jxlc => {
|
436
438
|
Name => 'JXLCodestream',
|
437
439
|
Format => 'undef',
|
@@ -441,6 +443,15 @@ my %j2cMarker = (
|
|
441
443
|
},
|
442
444
|
RawConv => 'Image::ExifTool::Jpeg2000::ProcessJXLCodestream($self,\$val); undef',
|
443
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
|
+
},
|
444
455
|
Exif => {
|
445
456
|
Name => 'EXIF',
|
446
457
|
SubDirectory => {
|
@@ -451,6 +462,38 @@ my %j2cMarker = (
|
|
451
462
|
Start => '$valuePtr + 4 + (length($$dataPt)-$valuePtr > 4 ? unpack("N", $$dataPt) : 0)',
|
452
463
|
},
|
453
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
|
+
}],
|
454
497
|
);
|
455
498
|
|
456
499
|
%Image::ExifTool::Jpeg2000::ImageHeader = (
|
@@ -840,12 +883,31 @@ sub CreateNewBoxes($$)
|
|
840
883
|
$tagTable = GetTagTable('Image::ExifTool::XMP::Main') if $dir eq 'XMP';
|
841
884
|
my %dirInfo = (
|
842
885
|
DirName => $dir,
|
843
|
-
Parent =>
|
886
|
+
Parent => $tag,
|
844
887
|
);
|
888
|
+
my $compress = $et->Options('Compress');
|
889
|
+
$dirInfo{Compact} = 1 if $$et{IsJXL} and $compress;
|
845
890
|
my $newdir = $et->WriteDirectory(\%dirInfo, $tagTable, $$subdir{WriteProc});
|
846
891
|
if (defined $newdir and length $newdir) {
|
847
892
|
# not sure why, but EXIF box is padded with leading 0's in my sample
|
848
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
|
+
}
|
849
911
|
my $boxhdr = pack('N', length($newdir) + length($pad) + 8) . $tag;
|
850
912
|
Write($outfile, $boxhdr, $pad, $newdir) or return 0;
|
851
913
|
next;
|
@@ -934,7 +996,7 @@ sub ProcessJpeg2000Box($$$)
|
|
934
996
|
my $raf = $$dirInfo{RAF};
|
935
997
|
my $outfile = $$dirInfo{OutFile};
|
936
998
|
my $dirEnd = $dirStart + $dirLen;
|
937
|
-
my ($err, $outBuff, $verbose, $doColour, $
|
999
|
+
my ($err, $outBuff, $verbose, $doColour, $hash);
|
938
1000
|
|
939
1001
|
if ($outfile) {
|
940
1002
|
unless ($raf) {
|
@@ -952,8 +1014,8 @@ sub ProcessJpeg2000Box($$$)
|
|
952
1014
|
# (must not set verbose flag when writing!)
|
953
1015
|
$verbose = $$et{OPTIONS}{Verbose};
|
954
1016
|
$et->VerboseDir($$dirInfo{DirName}) if $verbose;
|
955
|
-
# do
|
956
|
-
$
|
1017
|
+
# do hash if requested, but only for top-level image data
|
1018
|
+
$hash = $$et{ImageDataHash} if $raf;
|
957
1019
|
}
|
958
1020
|
# loop through all contained boxes
|
959
1021
|
my ($pos, $boxLen, $lastBox);
|
@@ -1023,8 +1085,8 @@ sub ProcessJpeg2000Box($$$)
|
|
1023
1085
|
my $msg = sprintf("offset 0x%.4x to end of file", $dataPos + $base + $pos);
|
1024
1086
|
$et->VPrint(0, "$$et{INDENT}- Tag '${boxID}' ($msg)\n");
|
1025
1087
|
}
|
1026
|
-
if ($
|
1027
|
-
$et->
|
1088
|
+
if ($hash and $isImageData{$boxID}) {
|
1089
|
+
$et->ImageDataHash($raf, undef, $boxID);
|
1028
1090
|
}
|
1029
1091
|
}
|
1030
1092
|
last; # (ignore the rest of the file when reading)
|
@@ -1042,8 +1104,8 @@ sub ProcessJpeg2000Box($$$)
|
|
1042
1104
|
Write($outfile, $$dataPt) or $err = 1;
|
1043
1105
|
$raf->Read($buff,$boxLen) == $boxLen or $err = '', last;
|
1044
1106
|
Write($outfile, $buff) or $err = 1;
|
1045
|
-
} elsif ($
|
1046
|
-
$et->
|
1107
|
+
} elsif ($hash and $isImageData{$boxID}) {
|
1108
|
+
$et->ImageDataHash($raf, $boxLen, $boxID);
|
1047
1109
|
} else {
|
1048
1110
|
$raf->Seek($boxLen, 1) or $err = 'Seek error', last;
|
1049
1111
|
}
|
@@ -1056,9 +1118,9 @@ sub ProcessJpeg2000Box($$$)
|
|
1056
1118
|
# read the box data
|
1057
1119
|
$dataPos = $raf->Tell() - $base;
|
1058
1120
|
$raf->Read($buff,$boxLen) == $boxLen or $err = '', last;
|
1059
|
-
if ($
|
1060
|
-
$
|
1061
|
-
$et->VPrint(0, "$$et{INDENT}(
|
1121
|
+
if ($hash and $isImageData{$boxID}) {
|
1122
|
+
$hash->add($buff);
|
1123
|
+
$et->VPrint(0, "$$et{INDENT}(ImageDataHash: $boxLen bytes of $boxID data)\n");
|
1062
1124
|
}
|
1063
1125
|
$valuePtr = 0;
|
1064
1126
|
$dataLen = $boxLen;
|
@@ -1144,19 +1206,66 @@ sub ProcessJpeg2000Box($$$)
|
|
1144
1206
|
$subdirInfo{DirName} =~ s/^UUID-//;
|
1145
1207
|
my $subTable = GetTagTable($$subdir{TagTable}) || $tagTablePtr;
|
1146
1208
|
if ($outfile) {
|
1147
|
-
#
|
1148
|
-
|
1149
|
-
|
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
|
+
}
|
1150
1216
|
my $newdir;
|
1151
1217
|
# only edit writable UUID, Exif and jp2h boxes
|
1152
|
-
if ($uuid or $
|
1218
|
+
if ($uuid or $fakeID eq 'Exif' or ($fakeID eq 'xml ' and $$et{IsJXL}) or
|
1153
1219
|
($boxID eq 'jp2h' and $$et{EDIT_DIRS}{jp2h}))
|
1154
1220
|
{
|
1221
|
+
my $compress = $et->Options('Compress');
|
1222
|
+
$subdirInfo{Parent} = $fakeID;
|
1223
|
+
$subdirInfo{Compact} = 1 if $compress and $$et{IsJXL};
|
1155
1224
|
$newdir = $et->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
|
1156
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
|
+
}
|
1157
1257
|
} elsif (defined $uuid) {
|
1158
1258
|
$et->Warn("Not editing $$tagInfo{Name} box", 1);
|
1159
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
|
+
}
|
1160
1269
|
# use old box data if not changed
|
1161
1270
|
defined $newdir or $newdir = substr($$dataPt, $subdirStart, $subdirLen);
|
1162
1271
|
my $prefixLen = $subdirStart - $valuePtr;
|
@@ -1232,19 +1341,108 @@ sub GetBits($$)
|
|
1232
1341
|
return $v;
|
1233
1342
|
}
|
1234
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
|
+
|
1235
1422
|
#------------------------------------------------------------------------------
|
1236
1423
|
# Extract parameters from JPEG XL codestream [unverified!]
|
1237
1424
|
# Inputs: 0) ExifTool ref, 1) codestream ref
|
1238
|
-
# Returns: 1
|
1425
|
+
# Returns: 1 on success
|
1239
1426
|
sub ProcessJXLCodestream($$)
|
1240
1427
|
{
|
1241
1428
|
my ($et, $dataPt) = @_;
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
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;
|
1246
1443
|
}
|
1247
|
-
|
1444
|
+
$dat =~ s/^\0\0\0\0//; # remove jxlp header word
|
1445
|
+
my @a = unpack 'x2C12', $dat;
|
1248
1446
|
my ($x, $y);
|
1249
1447
|
my $small = GetBits(\@a, 1);
|
1250
1448
|
if ($small) {
|
@@ -1362,8 +1560,8 @@ sub ProcessJXL($$)
|
|
1362
1560
|
$$dirInfo{RAF} = new File::RandomAccess(\$buff);
|
1363
1561
|
} else {
|
1364
1562
|
$et->SetFileType('JXL Codestream','image/jxl', 'jxl');
|
1365
|
-
if ($$et{
|
1366
|
-
$et->
|
1563
|
+
if ($$et{ImageDataHash} and $raf->Seek(0,0)) {
|
1564
|
+
$et->ImageDataHash($raf, undef, 'JXL');
|
1367
1565
|
}
|
1368
1566
|
return ProcessJXLCodestream($et, \$hdr);
|
1369
1567
|
}
|