exiftool_vendored 12.62.0 → 12.64.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.
- 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
|
}
|