exiftool_vendored 12.17.1 → 12.32.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +225 -1
  3. data/bin/MANIFEST +23 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +45 -43
  7. data/bin/arg_files/xmp2exif.args +2 -1
  8. data/bin/config_files/acdsee.config +193 -6
  9. data/bin/config_files/convert_regions.config +25 -14
  10. data/bin/config_files/cuepointlist.config +70 -0
  11. data/bin/config_files/example.config +2 -9
  12. data/bin/exiftool +142 -87
  13. data/bin/fmt_files/gpx.fmt +2 -2
  14. data/bin/fmt_files/gpx_wpt.fmt +2 -2
  15. data/bin/fmt_files/kml.fmt +1 -1
  16. data/bin/fmt_files/kml_track.fmt +1 -1
  17. data/bin/lib/Image/ExifTool/Apple.pm +3 -2
  18. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +30 -12
  19. data/bin/lib/Image/ExifTool/CBOR.pm +277 -0
  20. data/bin/lib/Image/ExifTool/Canon.pm +49 -18
  21. data/bin/lib/Image/ExifTool/DJI.pm +6 -6
  22. data/bin/lib/Image/ExifTool/DPX.pm +13 -2
  23. data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
  24. data/bin/lib/Image/ExifTool/Exif.pm +28 -11
  25. data/bin/lib/Image/ExifTool/FITS.pm +13 -2
  26. data/bin/lib/Image/ExifTool/FlashPix.pm +35 -10
  27. data/bin/lib/Image/ExifTool/FujiFilm.pm +19 -8
  28. data/bin/lib/Image/ExifTool/GPS.pm +22 -11
  29. data/bin/lib/Image/ExifTool/Geotag.pm +13 -2
  30. data/bin/lib/Image/ExifTool/GoPro.pm +16 -1
  31. data/bin/lib/Image/ExifTool/ICC_Profile.pm +2 -2
  32. data/bin/lib/Image/ExifTool/ID3.pm +15 -3
  33. data/bin/lib/Image/ExifTool/JPEG.pm +74 -4
  34. data/bin/lib/Image/ExifTool/JSON.pm +27 -4
  35. data/bin/lib/Image/ExifTool/Jpeg2000.pm +393 -16
  36. data/bin/lib/Image/ExifTool/LIF.pm +153 -0
  37. data/bin/lib/Image/ExifTool/Lang/nl.pm +60 -59
  38. data/bin/lib/Image/ExifTool/M2TS.pm +137 -5
  39. data/bin/lib/Image/ExifTool/MIE.pm +4 -3
  40. data/bin/lib/Image/ExifTool/MRC.pm +341 -0
  41. data/bin/lib/Image/ExifTool/MWG.pm +3 -3
  42. data/bin/lib/Image/ExifTool/MXF.pm +1 -1
  43. data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
  44. data/bin/lib/Image/ExifTool/Microsoft.pm +298 -82
  45. data/bin/lib/Image/ExifTool/Nikon.pm +19 -8
  46. data/bin/lib/Image/ExifTool/NikonSettings.pm +28 -11
  47. data/bin/lib/Image/ExifTool/Olympus.pm +6 -3
  48. data/bin/lib/Image/ExifTool/Other.pm +93 -0
  49. data/bin/lib/Image/ExifTool/PDF.pm +9 -12
  50. data/bin/lib/Image/ExifTool/PNG.pm +8 -7
  51. data/bin/lib/Image/ExifTool/Panasonic.pm +28 -3
  52. data/bin/lib/Image/ExifTool/Pentax.pm +28 -5
  53. data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
  54. data/bin/lib/Image/ExifTool/Photoshop.pm +6 -0
  55. data/bin/lib/Image/ExifTool/QuickTime.pm +247 -88
  56. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +283 -141
  57. data/bin/lib/Image/ExifTool/README +3 -0
  58. data/bin/lib/Image/ExifTool/RIFF.pm +89 -12
  59. data/bin/lib/Image/ExifTool/Samsung.pm +48 -10
  60. data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -0
  61. data/bin/lib/Image/ExifTool/Sony.pm +237 -78
  62. data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
  63. data/bin/lib/Image/ExifTool/TagLookup.pm +4125 -4028
  64. data/bin/lib/Image/ExifTool/TagNames.pod +644 -286
  65. data/bin/lib/Image/ExifTool/Torrent.pm +18 -11
  66. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
  67. data/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  68. data/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
  69. data/bin/lib/Image/ExifTool/WritePNG.pl +2 -0
  70. data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
  71. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +55 -21
  72. data/bin/lib/Image/ExifTool/WriteXMP.pl +7 -3
  73. data/bin/lib/Image/ExifTool/Writer.pl +47 -10
  74. data/bin/lib/Image/ExifTool/XMP.pm +39 -14
  75. data/bin/lib/Image/ExifTool/XMP2.pl +2 -1
  76. data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
  77. data/bin/lib/Image/ExifTool/ZISRAW.pm +121 -2
  78. data/bin/lib/Image/ExifTool.pm +223 -72
  79. data/bin/lib/Image/ExifTool.pod +114 -93
  80. data/bin/perl-Image-ExifTool.spec +43 -42
  81. data/lib/exiftool_vendored/version.rb +1 -1
  82. metadata +28 -13
@@ -16,9 +16,10 @@ use strict;
16
16
  use vars qw($VERSION);
17
17
  use Image::ExifTool qw(:DataAccess :Utils);
18
18
 
19
- $VERSION = '1.27';
19
+ $VERSION = '1.31';
20
20
 
21
21
  sub ProcessJpeg2000Box($$$);
22
+ sub ProcessJUMD($$$);
22
23
 
23
24
  my %resolutionUnit = (
24
25
  -3 => 'km',
@@ -54,6 +55,22 @@ my %jp2Map = (
54
55
  MakerNotes => 'ExifIFD',
55
56
  );
56
57
 
58
+ # map of where information is written in a JXL image
59
+ my %jxlMap = (
60
+ IFD0 => 'Exif',
61
+ XMP => 'XML',
62
+ 'Exif' => 'JP2',
63
+ IFD1 => 'IFD0',
64
+ EXIF => 'IFD0', # to write EXIF as a block
65
+ ExifIFD => 'IFD0',
66
+ GPS => 'IFD0',
67
+ SubIFD => 'IFD0',
68
+ GlobParamIFD => 'IFD0',
69
+ PrintIM => 'IFD0',
70
+ InteropIFD => 'ExifIFD',
71
+ MakerNotes => 'ExifIFD',
72
+ );
73
+
57
74
  # UUID's for writable UUID directories (by tag name)
58
75
  my %uuid = (
59
76
  'UUID-EXIF' => 'JpgTiffExif->JP2',
@@ -107,9 +124,13 @@ my %j2cMarker = (
107
124
  WRITE_PROC => \&ProcessJpeg2000Box,
108
125
  PREFERRED => 1, # always add these tags when writing
109
126
  NOTES => q{
110
- The tags below are extracted from JPEG 2000 images, however ExifTool
111
- currently writes only EXIF, IPTC and XMP tags in these images.
127
+ The tags below are found in JPEG 2000 images and the JUMBF metadata in JPEG
128
+ images, but not all of these are extracted. Note that ExifTool currently
129
+ writes only EXIF, IPTC and XMP tags in Jpeg2000 images.
112
130
  },
131
+ #
132
+ # NOTE: ONLY TAGS WITH "Format" DEFINED ARE EXTRACTED!
133
+ #
113
134
  'jP ' => 'JP2Signature', # (ref 1)
114
135
  "jP\x1a\x1a" => 'JP2Signature', # (ref 2)
115
136
  prfl => 'Profile',
@@ -199,13 +220,22 @@ my %j2cMarker = (
199
220
  chck => 'DigitalSignature',
200
221
  mp7b => 'MPEG7Binary',
201
222
  free => 'Free',
202
- jp2c => 'ContiguousCodestream',
223
+ jp2c => [{
224
+ Name => 'ContiguousCodestream',
225
+ Condition => 'not $$self{jumd_level}',
226
+ },{
227
+ Name => 'PreviewImage',
228
+ Groups => { 2 => 'Preview' },
229
+ Format => 'undef',
230
+ Binary => 1,
231
+ }],
203
232
  jp2i => {
204
233
  Name => 'IntellectualProperty',
205
234
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
206
235
  },
207
- 'xml '=> {
236
+ 'xml '=> [{
208
237
  Name => 'XML',
238
+ Condition => 'not $$self{IsJXL}',
209
239
  Writable => 'undef',
210
240
  Flags => [ 'Binary', 'Protected', 'BlockExtract' ],
211
241
  List => 1,
@@ -213,12 +243,18 @@ my %j2cMarker = (
213
243
  by default, the XML data in this tag is parsed using the ExifTool XMP module
214
244
  to to allow individual tags to be accessed when reading, but it may also be
215
245
  extracted as a block via the "XML" tag, which is also how this tag is
216
- written and copied. This is a List-type tag because multiple XML blocks may
217
- exist
246
+ written and copied. It may also be extracted as a block by setting the API
247
+ BlockExtract option. This is a List-type tag because multiple XML blocks
248
+ may exist
218
249
  },
219
250
  # (note: extracting as a block was broken in 11.04, and finally fixed in 12.14)
220
251
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::XML' },
221
- },
252
+ },{
253
+ Name => 'XMP',
254
+ Notes => 'used for XMP in JPEG XL files',
255
+ # NOTE: the hacked code relies on this being at index 1 of the tagInfo list!
256
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
257
+ }],
222
258
  uuid => [
223
259
  {
224
260
  Name => 'UUID-EXIF',
@@ -302,6 +338,20 @@ my %j2cMarker = (
302
338
  Start => '$valuePtr + 16',
303
339
  },
304
340
  },
341
+ {
342
+ Name => 'UUID-Signature', # (seen in JUMB data of JPEG images)
343
+ Condition => '$$valPt=~/^casg\x00\x11\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71/',
344
+ Format => 'undef',
345
+ ValueConv => 'substr($val,16)',
346
+ },
347
+ {
348
+ Name => 'UUID-C2PAClaimSignature', # (seen in JUMB data of JPEG images)
349
+ Condition => '$$valPt=~/^c2cs\x00\x11\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71/',
350
+ SubDirectory => {
351
+ TagTable => 'Image::ExifTool::CBOR::Main',
352
+ Start => '$valuePtr + 16',
353
+ },
354
+ },
305
355
  {
306
356
  Name => 'UUID-Unknown',
307
357
  },
@@ -321,6 +371,73 @@ my %j2cMarker = (
321
371
  Name => 'URL',
322
372
  Format => 'string',
323
373
  },
374
+ # JUMBF boxes (ref https://github.com/thorfdbg/codestream-parser)
375
+ jumd => {
376
+ Name => 'JUMBFDescr',
377
+ SubDirectory => { TagTable => 'Image::ExifTool::Jpeg2000::JUMD' },
378
+ },
379
+ jumb => {
380
+ Name => 'JUMBFBox',
381
+ SubDirectory => {
382
+ TagTable => 'Image::ExifTool::Jpeg2000::Main',
383
+ ProcessProc => \&ProcessJUMB,
384
+ },
385
+ },
386
+ json => {
387
+ Name => 'JSONData',
388
+ Flags => [ 'Binary', 'Protected', 'BlockExtract' ],
389
+ Notes => q{
390
+ by default, data in this tag is parsed using the ExifTool JSON module to to
391
+ allow individual tags to be accessed when reading, but it may also be
392
+ extracted as a block via the "JSONData" tag or by setting the API
393
+ BlockExtract option
394
+ },
395
+ SubDirectory => { TagTable => 'Image::ExifTool::JSON::Main' },
396
+ },
397
+ cbor => {
398
+ Name => 'CBORData',
399
+ Flags => [ 'Binary', 'Protected' ],
400
+ SubDirectory => { TagTable => 'Image::ExifTool::CBOR::Main' },
401
+ },
402
+ bfdb => { # used in JUMBF (see # (used when tag is renamed according to JUMDLabel)
403
+ Name => 'BinaryDataType',
404
+ Notes => 'JUMBF, MIME type and optional file name',
405
+ Format => 'undef',
406
+ # (ignore "toggles" byte and just extract MIME type and file name)
407
+ ValueConv => '$_=substr($val,1); s/\0+$//; s/\0/, /; $_',
408
+ JUMBF_Suffix => 'Type', # (used when tag is renamed according to JUMDLabel)
409
+ },
410
+ bidb => { # used in JUMBF
411
+ Name => 'BinaryData',
412
+ Notes => 'JUMBF',
413
+ Groups => { 2 => 'Preview' },
414
+ Format => 'undef',
415
+ Binary => 1,
416
+ JUMBF_Suffix => 'Data', # (used when tag is renamed according to JUMDLabel)
417
+ },
418
+ #
419
+ # stuff seen in JPEG XL images:
420
+ #
421
+ # jbrd - JPEG Bitstream Reconstruction Data (allows lossless conversion back to original JPG)
422
+ jxlc => {
423
+ Name => 'JXLCodestream',
424
+ Format => 'undef',
425
+ Notes => q{
426
+ Codestream in JPEG XL image. Currently processed only to determine
427
+ ImageSize
428
+ },
429
+ RawConv => 'Image::ExifTool::Jpeg2000::ProcessJXLCodestream($self,\$val); undef',
430
+ },
431
+ Exif => {
432
+ Name => 'EXIF',
433
+ SubDirectory => {
434
+ TagTable => 'Image::ExifTool::Exif::Main',
435
+ ProcessProc => \&Image::ExifTool::ProcessTIFF,
436
+ WriteProc => \&Image::ExifTool::WriteTIFF,
437
+ DirName => 'EXIF',
438
+ Start => '$valuePtr + 4',
439
+ },
440
+ },
324
441
  );
325
442
 
326
443
  %Image::ExifTool::Jpeg2000::ImageHeader = (
@@ -375,6 +492,7 @@ my %j2cMarker = (
375
492
  'jp2 ' => 'JPEG 2000 Image (.JP2)', # image/jp2
376
493
  'jpm ' => 'JPEG 2000 Compound Image (.JPM)', # image/jpm
377
494
  'jpx ' => 'JPEG 2000 with extensions (.JPX)', # image/jpx
495
+ 'jxl ' => 'JPEG XL Image (.JXL)', # image/jxl
378
496
  },
379
497
  },
380
498
  1 => {
@@ -513,6 +631,114 @@ my %j2cMarker = (
513
631
  ],
514
632
  );
515
633
 
634
+ # JUMBF description box
635
+ %Image::ExifTool::Jpeg2000::JUMD = (
636
+ PROCESS_PROC => \&ProcessJUMD,
637
+ GROUPS => { 0 => 'JUMBF', 1 => 'JUMBF', 2 => 'Image' },
638
+ NOTES => 'Information extracted from the JUMBF description box.',
639
+ 'type' => {
640
+ Name => 'JUMDType',
641
+ ValueConv => 'unpack "H*", $val',
642
+ PrintConv => q{
643
+ my @a = $val =~ /^(\w{8})(\w{4})(\w{4})(\w{16})$/;
644
+ return $val unless @a;
645
+ my $ascii = pack 'H*', $a[0];
646
+ $a[0] = "($ascii)" if $ascii =~ /^[a-zA-Z0-9]{4}$/;
647
+ return join '-', @a;
648
+ },
649
+ # seen:
650
+ # cacb/cast/caas/cacl/casg/json-00110010800000aa00389b71
651
+ # 6579d6fbdba2446bb2ac1b82feeb89d1 - JPEG image
652
+ },
653
+ 'label' => { Name => 'JUMDLabel' },
654
+ 'toggles' => {
655
+ Name => 'JUMDToggles',
656
+ Unknown => 1,
657
+ PrintConv => { BITMASK => {
658
+ 0 => 'Requestable',
659
+ 1 => 'Label',
660
+ 2 => 'ID',
661
+ 3 => 'Signature',
662
+ }},
663
+ },
664
+ 'id' => { Name => 'JUMDID', Description => 'JUMD ID' },
665
+ 'sig' => { Name => 'JUMDSignature', PrintConv => 'unpack "H*", $val' },
666
+ );
667
+
668
+ #------------------------------------------------------------------------------
669
+ # Read JUMBF box to keep track of sub-document numbers
670
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
671
+ # Returns: 1 on success
672
+ sub ProcessJUMB($$$)
673
+ {
674
+ my ($et, $dirInfo, $tagTablePtr) = @_;
675
+ if ($$et{jumd_level}) {
676
+ ++$$et{jumd_level}[-1]; # increment current sub-document number
677
+ } else {
678
+ $$et{jumd_level} = [ ++$$et{DOC_COUNT} ]; # new top-level sub-document
679
+ $$et{SET_GROUP0} = 'JUMBF';
680
+ }
681
+ $$et{DOC_NUM} = join '-', @{$$et{jumd_level}};
682
+ push @{$$et{jumd_level}}, 0;
683
+ ProcessJpeg2000Box($et, $dirInfo, $tagTablePtr);
684
+ delete $$et{DOC_NUM};
685
+ delete $$et{JUMBFLabel};
686
+ pop @{$$et{jumd_level}};
687
+ if (@{$$et{jumd_level}} < 2) {
688
+ delete $$et{jumd_level};
689
+ delete $$et{SET_GROUP0};
690
+ }
691
+ return 1;
692
+ }
693
+
694
+ #------------------------------------------------------------------------------
695
+ # Read JUMBF description box (ref https://github.com/thorfdbg/codestream-parser)
696
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
697
+ # Returns: 1 on success
698
+ sub ProcessJUMD($$$)
699
+ {
700
+ my ($et, $dirInfo, $tagTablePtr) = @_;
701
+ my $dataPt = $$dirInfo{DataPt};
702
+ my $pos = $$dirInfo{DirStart};
703
+ my $end = $pos + $$dirInfo{DirLen};
704
+ $et->VerboseDir('JUMD', 0, $end-$pos);
705
+ delete $$et{JUMBFLabel};
706
+ $$dirInfo{DirLen} < 17 and $et->Warn('Truncated JUMD directory'), return 0;
707
+ my $type = substr($$dataPt, $pos, 4);
708
+ $et->HandleTag($tagTablePtr, 'type', substr($$dataPt, $pos, 16));
709
+ $pos += 16;
710
+ my $flags = Get8u($dataPt, $pos++);
711
+ $et->HandleTag($tagTablePtr, 'toggles', $flags);
712
+ if ($flags & 0x02) { # label exists?
713
+ pos($$dataPt) = $pos;
714
+ $$dataPt =~ /\0/g or $et->Warn('Missing JUMD label terminator'), return 0;
715
+ my $len = pos($$dataPt) - $pos;
716
+ my $name = substr($$dataPt, $pos, $len);
717
+ $et->HandleTag($tagTablePtr, 'label', $name);
718
+ $pos += $len;
719
+ if ($len) {
720
+ $name =~ s/[^-_a-zA-Z0-9]([a-z])/\U$1/g; # capitalize characters after illegal characters
721
+ $name =~ tr/-_a-zA-Z0-9//dc; # remove other illegal characters
722
+ $name =~ s/__/_/; # collapse double underlines
723
+ $name = ucfirst $name; # capitalize first letter
724
+ $name = "Tag$name" if length($name) < 2; # must at least 2 characters long
725
+ $$et{JUMBFLabel} = $name;
726
+ }
727
+ }
728
+ if ($flags & 0x04) { # ID exists?
729
+ $pos + 4 > $end and $et->Warn('Missing JUMD ID'), return 0;
730
+ $et->HandleTag($tagTablePtr, 'id', Get32u($dataPt, $pos));
731
+ $pos += 4;
732
+ }
733
+ if ($flags & 0x08) { # signature exists?
734
+ $pos + 32 > $end and $et->Warn('Missing JUMD signature'), return 0;
735
+ $et->HandleTag($tagTablePtr, 'sig', substr($$dataPt, $pos, 32));
736
+ $pos += 32;
737
+ }
738
+ $pos == $end or $et->Warn('Extra data in JUMD box'." $pos $end", 1);
739
+ return 1;
740
+ }
741
+
516
742
  #------------------------------------------------------------------------------
517
743
  # Create new JPEG 2000 boxes when writing
518
744
  # (Currently only supports adding top-level Writable JPEG2000 tags and certain UUID boxes)
@@ -542,8 +768,29 @@ sub CreateNewBoxes($$)
542
768
  $et->VerboseValue("+ Jpeg2000:$$tagInfo{Name}", $val);
543
769
  }
544
770
  }
545
- # add UUID boxes
771
+ # add UUID boxes (and/or JXL Exif/XML boxes)
546
772
  foreach $dirName (sort keys %$addDirs) {
773
+ # handle JPEG XL XMP and EXIF
774
+ if ($dirName eq 'XML' or $dirName eq 'Exif') {
775
+ my ($tag, $dir) = $dirName eq 'XML' ? ('xml ', 'XMP') : ('Exif', 'EXIF');
776
+ my $tagInfo = $Image::ExifTool::Jpeg2000::Main{$tag};
777
+ $tagInfo = $$tagInfo[1] if ref $tagInfo eq 'ARRAY'; # (hack for stupid JXL XMP)
778
+ my $subdir = $$tagInfo{SubDirectory};
779
+ my $tagTable = GetTagTable($$subdir{TagTable});
780
+ $tagTable = GetTagTable('Image::ExifTool::XMP::Main') if $dir eq 'XMP';
781
+ my %dirInfo = (
782
+ DirName => $dir,
783
+ Parent => 'JP2',
784
+ );
785
+ my $newdir = $et->WriteDirectory(\%dirInfo, $tagTable, $$subdir{WriteProc});
786
+ if (defined $newdir and length $newdir) {
787
+ # not sure why, but EXIF box is padded with leading 0's in my sample
788
+ my $pad = $dirName eq 'Exif' ? "\0\0\0\0" : '';
789
+ my $boxhdr = pack('N', length($newdir) + length($pad) + 8) . $tag;
790
+ Write($outfile, $boxhdr, $pad, $newdir) or return 0;
791
+ next;
792
+ }
793
+ }
547
794
  next unless $uuid{$dirName};
548
795
  my $tagInfo;
549
796
  foreach $tagInfo (@{$Image::ExifTool::Jpeg2000::Main{uuid}}) {
@@ -715,6 +962,14 @@ sub ProcessJpeg2000Box($$$)
715
962
  }
716
963
  }
717
964
  }
965
+ # create new tag for JUMBF data values with name corresponding to JUMBFLabel
966
+ if ($tagInfo and $$et{JUMBFLabel} and (not $$tagInfo{SubDirectory} or $$tagInfo{BlockExtract})) {
967
+ $tagInfo = { %$tagInfo, Name => $$et{JUMBFLabel} . ($$tagInfo{JUMBF_Suffix} || '') };
968
+ delete $$tagInfo{Description};
969
+ AddTagToTable($tagTablePtr, '_JUMBF_' . $$et{JUMBFLabel}, $tagInfo);
970
+ delete $$tagInfo{Protected}; # (must do this so -j -b returns JUMBF binary data)
971
+ $$tagInfo{TagID} = $boxID;
972
+ }
718
973
  if ($verbose) {
719
974
  $et->VerboseInfo($boxID, $tagInfo,
720
975
  Table => $tagTablePtr,
@@ -752,8 +1007,8 @@ sub ProcessJpeg2000Box($$$)
752
1007
  # remove this directory from our create list
753
1008
  delete $$et{AddJp2Dirs}{$$tagInfo{Name}};
754
1009
  my $newdir;
755
- # only edit writable UUID boxes
756
- if ($uuid) {
1010
+ # only edit writable UUID and Exif boxes
1011
+ if ($uuid or $boxID eq 'Exif' or ($boxID eq 'xml ' and $$et{IsJXL})) {
757
1012
  $newdir = $et->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
758
1013
  next if defined $newdir and not length $newdir; # next if deleting the box
759
1014
  } elsif (defined $uuid) {
@@ -803,6 +1058,68 @@ sub ProcessJpeg2000Box($$$)
803
1058
  return 1;
804
1059
  }
805
1060
 
1061
+ #------------------------------------------------------------------------------
1062
+ # Return bits from a bitstream object
1063
+ # Inputs: 0) array ref, 1) number of bits
1064
+ # Returns: specified number of bits as an integer, and shifts input bitstream
1065
+ sub GetBits($$)
1066
+ {
1067
+ my ($a, $n) = @_;
1068
+ my $v = 0;
1069
+ my $bit = 1;
1070
+ my $i;
1071
+ while ($n--) {
1072
+ for ($i=0; $i<@$a; ++$i) {
1073
+ # consume bits LSB first
1074
+ my $set = $$a[$i] & 1;
1075
+ $$a[$i] >>= 1;
1076
+ if ($i) {
1077
+ $$a[$i-1] |= 0x80 if $set;
1078
+ } else {
1079
+ $v |= $bit if $set;
1080
+ $bit <<= 1;
1081
+ }
1082
+ }
1083
+ }
1084
+ return $v;
1085
+ }
1086
+
1087
+ #------------------------------------------------------------------------------
1088
+ # Extract parameters from JPEG XL codestream [unverified!]
1089
+ # Inputs: 0) ExifTool ref, 1) codestream ref
1090
+ # Returns: 1
1091
+ sub ProcessJXLCodestream($$)
1092
+ {
1093
+ my ($et, $dataPt) = @_;
1094
+ # add padding if necessary to avoid unpacking past end of data
1095
+ if (length $$dataPt < 14) {
1096
+ my $tmp = $$dataPt . ("\0" x 14);
1097
+ $dataPt = \$tmp;
1098
+ }
1099
+ my @a = unpack 'x2C12', $$dataPt;
1100
+ my ($x, $y);
1101
+ my $small = GetBits(\@a, 1);
1102
+ if ($small) {
1103
+ $y = (GetBits(\@a, 5) + 1) * 8;
1104
+ } else {
1105
+ $y = GetBits(\@a, [9, 13, 18, 30]->[GetBits(\@a, 2)]) + 1;
1106
+ }
1107
+ my $ratio = GetBits(\@a, 3);
1108
+ if ($ratio == 0) {
1109
+ if ($small) {
1110
+ $x = (GetBits(\@a, 5) + 1) * 8;;
1111
+ } else {
1112
+ $x = GetBits(\@a, [9, 13, 18, 30]->[GetBits(\@a, 2)]) + 1;
1113
+ }
1114
+ } else {
1115
+ my $r = [[1,1],[12,10],[4,3],[3,2],[16,9],[5,4],[2,1]]->[$ratio-1];
1116
+ $x = int($y * $$r[0] / $$r[1]);
1117
+ }
1118
+ $et->FoundTag(ImageWidth => $x);
1119
+ $et->FoundTag(ImageHeight => $y);
1120
+ return 1;
1121
+ }
1122
+
806
1123
  #------------------------------------------------------------------------------
807
1124
  # Read/write meta information from a JPEG 2000 image
808
1125
  # Inputs: 0) ExifTool object reference, 1) dirInfo reference
@@ -817,8 +1134,9 @@ sub ProcessJP2($$)
817
1134
 
818
1135
  # check to be sure this is a valid JPG2000 file
819
1136
  return 0 unless $raf->Read($hdr,12) == 12;
820
- unless ($hdr eq "\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" or # (ref 1)
821
- $hdr eq "\x00\x00\x00\x0cjP\x1a\x1a\x0d\x0a\x87\x0a") # (ref 2)
1137
+ unless ($hdr eq "\0\0\0\x0cjP \x0d\x0a\x87\x0a" or # (ref 1)
1138
+ $hdr eq "\0\0\0\x0cjP\x1a\x1a\x0d\x0a\x87\x0a" or # (ref 2)
1139
+ $$et{IsJXL})
822
1140
  {
823
1141
  return 0 unless $hdr =~ /^\xff\x4f\xff\x51\0/; # check for JP2 codestream format
824
1142
  if ($outfile) {
@@ -835,17 +1153,23 @@ sub ProcessJP2($$)
835
1153
  }
836
1154
  if ($outfile) {
837
1155
  Write($outfile, $hdr) or return -1;
838
- $et->InitWriteDirs(\%jp2Map);
1156
+ if ($$et{IsJXL}) {
1157
+ $et->InitWriteDirs(\%jxlMap);
1158
+ $$et{AddJp2Tags} = { }; # (don't add JP2 tags in JXL files)
1159
+ } else {
1160
+ $et->InitWriteDirs(\%jp2Map);
1161
+ $$et{AddJp2Tags} = $et->GetNewTagInfoHash(\%Image::ExifTool::Jpeg2000::Main);
1162
+ }
839
1163
  # save list of directories to create
840
- my %addDirs = %{$$et{ADD_DIRS}};
1164
+ my %addDirs = %{$$et{ADD_DIRS}}; # (make a copy)
841
1165
  $$et{AddJp2Dirs} = \%addDirs;
842
- $$et{AddJp2Tags} = $et->GetNewTagInfoHash(\%Image::ExifTool::Jpeg2000::Main);
843
1166
  } else {
844
1167
  my ($buff, $fileType);
845
1168
  # recognize JPX and JPM as unique types of JP2
846
1169
  if ($raf->Read($buff, 12) == 12 and $buff =~ /^.{4}ftyp(.{4})/s) {
847
1170
  $fileType = 'JPX' if $1 eq 'jpx ';
848
1171
  $fileType = 'JPM' if $1 eq 'jpm ';
1172
+ $fileType = 'JXL' if $1 eq 'jxl ';
849
1173
  }
850
1174
  $raf->Seek(-length($buff), 1) if defined $buff;
851
1175
  $et->SetFileType($fileType);
@@ -860,6 +1184,59 @@ sub ProcessJP2($$)
860
1184
  return $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
861
1185
  }
862
1186
 
1187
+ #------------------------------------------------------------------------------
1188
+ # Read meta information from a JPEG XL image
1189
+ # Inputs: 0) ExifTool object reference, 1) dirInfo reference
1190
+ # Returns: 1 on success, 0 if this wasn't a valid JPEG XL file, -1 on write error
1191
+ sub ProcessJXL($$)
1192
+ {
1193
+ my ($et, $dirInfo) = @_;
1194
+ my $raf = $$dirInfo{RAF};
1195
+ my $outfile = $$dirInfo{OutFile};
1196
+ my ($hdr, $buff);
1197
+
1198
+ return 0 unless $raf->Read($hdr,12) == 12;
1199
+ if ($hdr eq "\0\0\0\x0cJXL \x0d\x0a\x87\x0a") {
1200
+ # JPEG XL in ISO BMFF container
1201
+ $$et{IsJXL} = 1;
1202
+ } elsif ($hdr =~ /^\xff\x0a/) {
1203
+ # JPEG XL codestream
1204
+ if ($outfile) {
1205
+ if ($$et{OPTIONS}{IgnoreMinorErrors}) {
1206
+ $et->Warn('Wrapped JXL codestream in ISO BMFF container');
1207
+ } else {
1208
+ $et->Error('Will wrap JXL codestream in ISO BMFF container for writing',1);
1209
+ return 0;
1210
+ }
1211
+ $$et{IsJXL} = 2;
1212
+ my $buff = "\0\0\0\x0cJXL \x0d\x0a\x87\x0a\0\0\0\x14ftypjxl \0\0\0\0jxl ";
1213
+ # add metadata to empty ISO BMFF container
1214
+ $$dirInfo{RAF} = new File::RandomAccess(\$buff);
1215
+ } else {
1216
+ $et->SetFileType('JXL Codestream','image/jxl', 'jxl');
1217
+ return ProcessJXLCodestream($et, \$hdr);
1218
+ }
1219
+ } else {
1220
+ return 0;
1221
+ }
1222
+ $raf->Seek(0,0) or $et->Error('Seek error'), return 0;
1223
+
1224
+ my $success = ProcessJP2($et, $dirInfo);
1225
+
1226
+ if ($outfile and $success > 0 and $$et{IsJXL} == 2) {
1227
+ # attach the JXL codestream box to the ISO BMFF file
1228
+ $raf->Seek(0,2) or return -1;
1229
+ my $size = $raf->Tell();
1230
+ $raf->Seek(0,0) or return -1;
1231
+ SetByteOrder('MM');
1232
+ Write($outfile, Set32u($size + 8), 'jxlc') or return -1;
1233
+ while ($raf->Read($buff, 65536)) {
1234
+ Write($outfile, $buff) or return -1;
1235
+ }
1236
+ }
1237
+ return $success;
1238
+ }
1239
+
863
1240
  1; # end
864
1241
 
865
1242
  __END__
@@ -0,0 +1,153 @@
1
+ #------------------------------------------------------------------------------
2
+ # File: LIF.pm
3
+ #
4
+ # Description: Read LIF (Leica Image File) files
5
+ #
6
+ # Revisions: 2021-06-21 - P. Harvey Created
7
+ #------------------------------------------------------------------------------
8
+
9
+ package Image::ExifTool::LIF;
10
+
11
+ use strict;
12
+ use vars qw($VERSION);
13
+ use Image::ExifTool qw(:DataAccess :Utils);
14
+ use Image::ExifTool::XMP;
15
+
16
+ $VERSION = '1.00';
17
+
18
+ %Image::ExifTool::LIF::Main = (
19
+ GROUPS => { 0 => 'XML', 1 => 'XML', 2 => 'Image' },
20
+ PROCESS_PROC => \&Image::ExifTool::XMP::ProcessXMP,
21
+ VARS => { NO_ID => 1 },
22
+ NOTES => q{
23
+ Tags extracted from Leica Image Format (LIF) imaging files. As well as the
24
+ tags listed below, all available information is extracted from the
25
+ XML-format metadata in the LIF header.
26
+ },
27
+ TimeStampList => {
28
+ Groups => { 2 => 'Time' },
29
+ ValueConv => q{
30
+ my $unixTimeZero = 134774 * 24 * 3600;
31
+ my @vals = split ' ', $val;
32
+ foreach (@vals) {
33
+ $_ = 1e-7 * hex($_);
34
+ # shift from Jan 1, 1601 to Jan 1, 1970
35
+ $_ = Image::ExifTool::ConvertUnixTime($_ - $unixTimeZero);
36
+ }
37
+ return \@vals;
38
+ },
39
+ },
40
+ );
41
+
42
+ #------------------------------------------------------------------------------
43
+ # Shorten obscenely long LIF tag names
44
+ # Inputs: Tag name
45
+ # Returns: Shortened tag name
46
+ sub ShortenTagNames($)
47
+ {
48
+ local $_;
49
+ $_ = shift;
50
+ s/DescriptionDimensionsDimensionDescription/Dimensions/;
51
+ s/DescriptionChannelsChannelDescription/Channel/;
52
+ s/ShutterListShutter/Shutter/;
53
+ s/SettingDefinition/Setting/;
54
+ s/AdditionalZPositionListAdditionalZPosition/AdditionalZPosition/;
55
+ s/LMSDataContainerHeader//g;
56
+ s/FilterWheelWheel/FilterWheel/;
57
+ s/FilterWheelFilter/FilterWheel/;
58
+ s/DetectorListDetector/Detector/;
59
+ s/OnlineDyeSeparationOnlineDyeSeparation/OnlineDyeSeparation/;
60
+ s/AotfListAotf/Aotf/;
61
+ s/SettingAotfLaserLineSetting/SettingAotfLaser/;
62
+ s/DataROISetROISet/DataROISet/;
63
+ s/AdditionalZPosition/AddZPos/;
64
+ s/FRAPplusBlock_FRAPBlock_FRAP_PrePost_Info/FRAP_/;
65
+ s/FRAPplusBlock_FRAPBlock_FRAP_(Master)?/FRAP_/;
66
+ s/LDM_Block_SequentialLDM_Block_Sequential_/LDM_/;
67
+ s/ATLConfocalSetting/ATLConfocal/;
68
+ s/LaserArrayLaser/Laser/;
69
+ s/LDM_Master/LDM_/;
70
+ s/(List)?ATLConfocal/ATL_/;
71
+ s/Separation/Sep/;
72
+ s/BleachPointsElement/BleachPoint/;
73
+ s/BeamPositionBeamPosition/BeamPosition/;
74
+ s/DataROISetPossible(ROI)?/DataROISet/;
75
+ s/RoiElementChildrenElementDataROISingle(Roi)?/Roi/;
76
+ s/InfoLaserLineSettingArrayLaserLineSetting/LastLineSetting/;
77
+ s/FilterWheelWheelNameFilterName/FilterWheelFilterName/;
78
+ s/LUT_ListLut/Lut/;
79
+ s/ROI_ListRoiRoidata/ROI_/;
80
+ s/LaserLineSettingArrayLaserLineSetting/LaserLineSetting/;
81
+ return $_;
82
+ }
83
+
84
+ #------------------------------------------------------------------------------
85
+ # Extract metadata from a LIF image
86
+ # Inputs: 0) ExifTool object reference, 1) dirInfo reference
87
+ # Returns: 1 on success, 0 if this wasn't a valid LIF file
88
+ sub ProcessLIF($$)
89
+ {
90
+ my ($et, $dirInfo) = @_;
91
+ my $raf = $$dirInfo{RAF};
92
+ my $buff;
93
+
94
+ # verify this is a valid LIF file
95
+ return 0 unless $raf->Read($buff, 15) == 15 and $buff =~ /^\x70\0{3}.{4}\x2a.{4}<\0/s;
96
+
97
+ $et->SetFileType();
98
+ SetByteOrder('II');
99
+
100
+ my $size = Get32u(\$buff, 4); # XML chunk size
101
+ my $len = Get32u(\$buff, 9) * 2; # XML data length
102
+
103
+ $size < $len and $et->Error('Corrupted LIF XML block'), return 1;
104
+ $size > 100000000 and $et->Error('LIF XML block too large'), return 1;
105
+
106
+ $raf->Seek(-2, 1) and $raf->Read($buff, $len) == $len or $et->Error('Truncated LIF XML block'), return 1;
107
+
108
+ my $tagTablePtr = GetTagTable('Image::ExifTool::LIF::Main');
109
+
110
+ # convert from UCS2 to UTF8
111
+ my $xml = Image::ExifTool::Decode($et, $buff, 'UCS2', 'II', 'UTF8');
112
+
113
+ my %dirInfo = ( DataPt => \$xml );
114
+
115
+ $$et{XmpIgnoreProps} = [ 'LMSDataContainerHeader', 'Element', 'Children', 'Data', 'Image', 'Attachment' ];
116
+ $$et{ShortenXmpTags} = \&ShortenTagNames;
117
+
118
+ $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
119
+
120
+ return 1;
121
+ }
122
+
123
+ 1; # end
124
+
125
+ __END__
126
+
127
+ =head1 NAME
128
+
129
+ Image::ExifTool::LIF - Read LIF meta information
130
+
131
+ =head1 SYNOPSIS
132
+
133
+ This module is used by Image::ExifTool
134
+
135
+ =head1 DESCRIPTION
136
+
137
+ This module contains definitions required by Image::ExifTool to read
138
+ metadata from Leica Image File (LIF) images.
139
+
140
+ =head1 AUTHOR
141
+
142
+ Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
143
+
144
+ This library is free software; you can redistribute it and/or modify it
145
+ under the same terms as Perl itself.
146
+
147
+ =head1 SEE ALSO
148
+
149
+ L<Image::ExifTool::TagNames/LIF Tags>,
150
+ L<Image::ExifTool(3pm)|Image::ExifTool>
151
+
152
+ =cut
153
+