exiftool-vendored.exe 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.
Files changed (55) hide show
  1. package/LICENSE +254 -254
  2. package/bin/exiftool_files/Changes +110 -0
  3. package/bin/exiftool_files/LICENSE +674 -0
  4. package/bin/exiftool_files/Makefile.PL +7 -1
  5. package/bin/exiftool_files/README +50 -45
  6. package/bin/exiftool_files/config_files/guano.config +161 -0
  7. package/bin/exiftool_files/exiftool.pl +162 -104
  8. package/bin/exiftool_files/lib/Image/ExifTool/7Z.pm +793 -0
  9. package/bin/exiftool_files/lib/Image/ExifTool/Apple.pm +14 -7
  10. package/bin/exiftool_files/lib/Image/ExifTool/BMP.pm +0 -1
  11. package/bin/exiftool_files/lib/Image/ExifTool/BigTIFF.pm +8 -1
  12. package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +4 -4
  13. package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +4 -1
  14. package/bin/exiftool_files/lib/Image/ExifTool/CanonRaw.pm +4 -4
  15. package/bin/exiftool_files/lib/Image/ExifTool/CanonVRD.pm +4 -1
  16. package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +31 -14
  17. package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +9 -2
  18. package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +3 -3
  19. package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +5 -2
  20. package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +4 -1
  21. package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +243 -20
  22. package/bin/exiftool_files/lib/Image/ExifTool/Lang/fr.pm +1467 -202
  23. package/bin/exiftool_files/lib/Image/ExifTool/MPF.pm +2 -1
  24. package/bin/exiftool_files/lib/Image/ExifTool/Matroska.pm +16 -1
  25. package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
  26. package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +941 -33
  27. package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +874 -63
  28. package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +39 -12
  29. package/bin/exiftool_files/lib/Image/ExifTool/PLIST.pm +8 -1
  30. package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +6 -6
  31. package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +5 -5
  32. package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +96 -32
  33. package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +68 -37
  34. package/bin/exiftool_files/lib/Image/ExifTool/README +2 -2
  35. package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +11 -9
  36. package/bin/exiftool_files/lib/Image/ExifTool/Samsung.pm +227 -227
  37. package/bin/exiftool_files/lib/Image/ExifTool/Shortcuts.pm +2 -1
  38. package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +4 -4
  39. package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +237 -32
  40. package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +4762 -4629
  41. package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +737 -20
  42. package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +17 -1
  43. package/bin/exiftool_files/lib/Image/ExifTool/WPG.pm +296 -0
  44. package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +9 -7
  45. package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +7 -8
  46. package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +21 -9
  47. package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +2 -2
  48. package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +47 -16
  49. package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +30 -6
  50. package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +32 -0
  51. package/bin/exiftool_files/lib/Image/ExifTool/XMPStruct.pl +96 -28
  52. package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +159 -41
  53. package/bin/exiftool_files/lib/Image/ExifTool.pm +280 -164
  54. package/bin/exiftool_files/lib/Image/ExifTool.pod +117 -52
  55. package/package.json +3 -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.33';
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 => 'JP2',
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
- } elsif ($verbose) {
1011
- my $msg = sprintf("offset 0x%.4x to end of file", $dataPos + $base + $pos);
1012
- $et->VPrint(0, "$$et{INDENT}- Tag '${boxID}' ($msg)\n");
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
- # remove this directory from our create list
1126
- delete $$et{AddJp2Dirs}{$$tagInfo{Name}}; # (eg. 'EXIF' or 'XMP')
1127
- delete $$et{AddJp2Dirs}{$boxID}; # (eg. 'Exif' or 'xml ')
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 $boxID eq 'Exif' or ($boxID eq 'xml ' and $$et{IsJXL}) 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
- # add padding if necessary to avoid unpacking past end of data
1221
- if (length $$dataPt < 14) {
1222
- my $tmp = $$dataPt . ("\0" x 14);
1223
- $dataPt = \$tmp;
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
- my @a = unpack 'x2C12', $$dataPt;
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 from a JPEG XL image
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 {