exiftool_vendored 12.25.0 → 12.35.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +174 -7
  3. data/bin/MANIFEST +11 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +44 -43
  7. data/bin/arg_files/xmp2exif.args +2 -1
  8. data/bin/config_files/convert_regions.config +25 -14
  9. data/bin/config_files/example.config +1 -1
  10. data/bin/exiftool +118 -92
  11. data/bin/fmt_files/gpx.fmt +1 -1
  12. data/bin/fmt_files/gpx_wpt.fmt +1 -1
  13. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +16 -3
  14. data/bin/lib/Image/ExifTool/CBOR.pm +331 -0
  15. data/bin/lib/Image/ExifTool/Canon.pm +52 -20
  16. data/bin/lib/Image/ExifTool/Charset.pm +2 -0
  17. data/bin/lib/Image/ExifTool/DPX.pm +13 -2
  18. data/bin/lib/Image/ExifTool/Exif.pm +107 -8
  19. data/bin/lib/Image/ExifTool/FLIR.pm +33 -8
  20. data/bin/lib/Image/ExifTool/FlashPix.pm +35 -10
  21. data/bin/lib/Image/ExifTool/FujiFilm.pm +1 -0
  22. data/bin/lib/Image/ExifTool/Geotag.pm +13 -2
  23. data/bin/lib/Image/ExifTool/GoPro.pm +16 -1
  24. data/bin/lib/Image/ExifTool/ICC_Profile.pm +96 -4
  25. data/bin/lib/Image/ExifTool/ID3.pm +15 -3
  26. data/bin/lib/Image/ExifTool/JPEG.pm +68 -2
  27. data/bin/lib/Image/ExifTool/JSON.pm +7 -3
  28. data/bin/lib/Image/ExifTool/Jpeg2000.pm +164 -36
  29. data/bin/lib/Image/ExifTool/LIF.pm +153 -0
  30. data/bin/lib/Image/ExifTool/Lang/nl.pm +60 -59
  31. data/bin/lib/Image/ExifTool/M2TS.pm +103 -7
  32. data/bin/lib/Image/ExifTool/MIE.pm +2 -1
  33. data/bin/lib/Image/ExifTool/MRC.pm +1 -1
  34. data/bin/lib/Image/ExifTool/MacOS.pm +2 -2
  35. data/bin/lib/Image/ExifTool/Nikon.pm +50 -6
  36. data/bin/lib/Image/ExifTool/NikonSettings.pm +10 -2
  37. data/bin/lib/Image/ExifTool/Olympus.pm +9 -2
  38. data/bin/lib/Image/ExifTool/Other.pm +93 -0
  39. data/bin/lib/Image/ExifTool/PDF.pm +11 -12
  40. data/bin/lib/Image/ExifTool/PNG.pm +7 -6
  41. data/bin/lib/Image/ExifTool/Panasonic.pm +14 -2
  42. data/bin/lib/Image/ExifTool/Pentax.pm +28 -5
  43. data/bin/lib/Image/ExifTool/Photoshop.pm +6 -0
  44. data/bin/lib/Image/ExifTool/QuickTime.pm +123 -25
  45. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +203 -121
  46. data/bin/lib/Image/ExifTool/README +9 -2
  47. data/bin/lib/Image/ExifTool/RIFF.pm +7 -2
  48. data/bin/lib/Image/ExifTool/Samsung.pm +47 -10
  49. data/bin/lib/Image/ExifTool/Sony.pm +113 -42
  50. data/bin/lib/Image/ExifTool/TagLookup.pm +4599 -4451
  51. data/bin/lib/Image/ExifTool/TagNames.pod +276 -41
  52. data/bin/lib/Image/ExifTool/Torrent.pm +18 -11
  53. data/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  54. data/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
  55. data/bin/lib/Image/ExifTool/WritePNG.pl +2 -0
  56. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +21 -4
  57. data/bin/lib/Image/ExifTool/WriteXMP.pl +1 -1
  58. data/bin/lib/Image/ExifTool/Writer.pl +47 -2
  59. data/bin/lib/Image/ExifTool/XMP.pm +32 -12
  60. data/bin/lib/Image/ExifTool/XMP2.pl +5 -2
  61. data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
  62. data/bin/lib/Image/ExifTool/ZISRAW.pm +121 -2
  63. data/bin/lib/Image/ExifTool.pm +153 -52
  64. data/bin/lib/Image/ExifTool.pod +70 -60
  65. data/bin/perl-Image-ExifTool.spec +43 -42
  66. data/lib/exiftool_vendored/version.rb +1 -1
  67. metadata +6 -3
@@ -11,7 +11,7 @@ use strict;
11
11
  use vars qw($VERSION);
12
12
  use Image::ExifTool qw(:DataAccess :Utils);
13
13
 
14
- $VERSION = '1.30';
14
+ $VERSION = '1.32';
15
15
 
16
16
  sub ProcessOcad($$$);
17
17
  sub ProcessJPEG_HDR($$$);
@@ -96,6 +96,10 @@ sub ProcessJPEG_HDR($$$);
96
96
  Name => 'Stim',
97
97
  Condition => '$$valPt =~ /^Stim\0/',
98
98
  SubDirectory => { TagTable => 'Image::ExifTool::Stim::Main' },
99
+ }, {
100
+ Name => 'JPS',
101
+ Condition => '$$valPt =~ /^_JPSJPS_/',
102
+ SubDirectory => { TagTable => 'Image::ExifTool::JPEG::JPS' },
99
103
  }, {
100
104
  Name => 'ThermalData', # (written by DJI FLIR models)
101
105
  Condition => '$$self{Make} eq "DJI"',
@@ -287,6 +291,68 @@ sub ProcessJPEG_HDR($$$);
287
291
  }],
288
292
  );
289
293
 
294
+ # JPS APP3 segment (ref http://paulbourke.net/stereographics/stereoimage/)
295
+ %Image::ExifTool::JPEG::JPS = (
296
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
297
+ GROUPS => { 0 => 'APP3', 1 => 'JPS', 2 => 'Image' },
298
+ NOTES => 'Tags found in JPEG Stereo (JPS) images.',
299
+ 0x0a => {
300
+ Name => 'JPSSeparation',
301
+ Format => 'int32u', # (just so we can look ahead to MediaType);
302
+ Notes => 'stereo only',
303
+ RawConv => q{
304
+ $$self{MediaType} = $val & 0xff;
305
+ return undef unless $$self{MediaType} == 1;
306
+ return(($val >> 24) & 0xff);
307
+ },
308
+ },
309
+ 0x08 => {
310
+ Name => 'HdrLength',
311
+ Format => 'int16u',
312
+ Hidden => 1,
313
+ RawConv => '$$self{HdrLength} = $val; undef',
314
+ },
315
+ 0x0b => {
316
+ Name => 'JPSFlags',
317
+ PrintConv => { BITMASK => {
318
+ 0 => 'Half height',
319
+ 1 => 'Half width',
320
+ 2 => 'Left field first',
321
+ }},
322
+ },
323
+ 0x0c => [{
324
+ Name => 'JPSLayout',
325
+ Condition => '$$self{MediaType} == 0',
326
+ Notes => 'mono',
327
+ PrintConv => {
328
+ 0 => 'Both Eyes',
329
+ 1 => 'Left Eye',
330
+ 2 => 'Right Eye',
331
+ },
332
+ },{
333
+ Name => 'JPSLayout',
334
+ Condition => '$$self{MediaType} == 1',
335
+ Notes => 'stereo',
336
+ PrintConv => {
337
+ 1 => 'Interleaved',
338
+ 2 => 'Side By Side',
339
+ 3 => 'Over Under',
340
+ 4 => 'Anaglyph',
341
+ },
342
+ }],
343
+ 0x0d => {
344
+ Name => 'JPSType',
345
+ Hook => '$varSize += $$self{HdrLength} - 4', # comment starts after header block
346
+ PrintConv => { 0 => 'Mono', 1 => 'Stereo' },
347
+ },
348
+ # 0x0e - in16u comment length (ignored -- assume the remainder is all comment)
349
+ # (this is offset if we had a 4-byte JPS header block)
350
+ 0x10 => {
351
+ Name => 'JPSComment',
352
+ Format => 'string',
353
+ },
354
+ );
355
+
290
356
  # EPPIM APP6 (Toshiba PrintIM) segment (ref PH, from PDR-M700 samples)
291
357
  %Image::ExifTool::JPEG::EPPIM = (
292
358
  GROUPS => { 0 => 'APP6', 1 => 'EPPIM', 2 => 'Image' },
@@ -545,7 +611,7 @@ sub ProcessJPEG_HDR($$$);
545
611
  },
546
612
  2 => {
547
613
  Name => 'ImageFormat',
548
- ValueConv => 'chr($val)',
614
+ ValueConv => 'chr($val & 0xff)',
549
615
  PrintConv => { B => 'IMode B' },
550
616
  },
551
617
  3 => {
@@ -14,7 +14,7 @@ use vars qw($VERSION);
14
14
  use Image::ExifTool qw(:DataAccess :Utils);
15
15
  use Image::ExifTool::Import;
16
16
 
17
- $VERSION = '1.03';
17
+ $VERSION = '1.05';
18
18
 
19
19
  sub ProcessJSON($$);
20
20
  sub ProcessTag($$$$%);
@@ -83,8 +83,12 @@ sub ProcessTag($$$$%)
83
83
  FoundTag($et, $tagTablePtr, $tag, $val, %flags, Struct => 1);
84
84
  return unless $et->Options('Struct') > 1;
85
85
  }
86
- foreach (sort keys %$val) {
87
- ProcessTag($et, $tagTablePtr, $tag . ucfirst, $$val{$_}, %flags, Flat => 1);
86
+ # support hashes with ordered keys
87
+ my @keys = $$val{_ordered_keys_} ? @{$$val{_ordered_keys_}} : sort keys %$val;
88
+ foreach (@keys) {
89
+ my $tg = $tag . ((/^\d/ and $tag =~ /\d$/) ? '_' : '') . ucfirst;
90
+ $tg =~ s/([^a-zA-Z])([a-z])/$1\U$2/g;
91
+ ProcessTag($et, $tagTablePtr, $tg, $$val{$_}, %flags, Flat => 1);
88
92
  }
89
93
  } elsif (ref $val eq 'ARRAY') {
90
94
  foreach (@$val) {
@@ -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.29';
19
+ $VERSION = '1.32';
20
20
 
21
21
  sub ProcessJpeg2000Box($$$);
22
22
  sub ProcessJUMD($$$);
@@ -42,8 +42,9 @@ my %jp2Map = (
42
42
  'UUID-IPTC' => 'JP2',
43
43
  'UUID-EXIF' => 'JP2',
44
44
  'UUID-XMP' => 'JP2',
45
- # jp2h => 'JP2', (not yet functional)
46
- # ICC_Profile => 'jp2h', (not yet functional)
45
+ jp2h => 'JP2',
46
+ colr => 'jp2h',
47
+ ICC_Profile => 'colr',
47
48
  IFD1 => 'IFD0',
48
49
  EXIF => 'IFD0', # to write EXIF as a block
49
50
  ExifIFD => 'IFD0',
@@ -116,9 +117,6 @@ my %j2cMarker = (
116
117
  0x76 => 'NLT', # non-linearity point transformation
117
118
  );
118
119
 
119
- my %jumbfTypes = (
120
- );
121
-
122
120
  # JPEG 2000 "box" (ie. atom) names
123
121
  # Note: only tags with a defined "Format" are extracted
124
122
  %Image::ExifTool::Jpeg2000::Main = (
@@ -127,10 +125,13 @@ my %jumbfTypes = (
127
125
  WRITE_PROC => \&ProcessJpeg2000Box,
128
126
  PREFERRED => 1, # always add these tags when writing
129
127
  NOTES => q{
130
- The tags below are extracted from JPEG 2000 images and the JUMBF metadata in
131
- JPEG images. Note that ExifTool currently writes only EXIF, IPTC and XMP
132
- tags in Jpeg2000 images.
128
+ The tags below are found in JPEG 2000 images and the JUMBF metadata in JPEG
129
+ images, but not all of these are extracted. Note that ExifTool currently
130
+ writes only EXIF, IPTC and XMP tags in Jpeg2000 images.
133
131
  },
132
+ #
133
+ # NOTE: ONLY TAGS WITH "Format" DEFINED ARE EXTRACTED!
134
+ #
134
135
  'jP ' => 'JP2Signature', # (ref 1)
135
136
  "jP\x1a\x1a" => 'JP2Signature', # (ref 2)
136
137
  prfl => 'Profile',
@@ -340,10 +341,20 @@ my %jumbfTypes = (
340
341
  },
341
342
  {
342
343
  Name => 'UUID-Signature', # (seen in JUMB data of JPEG images)
344
+ # (may be able to remove this when JUMBF specification is finalized)
343
345
  Condition => '$$valPt=~/^casg\x00\x11\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71/',
344
346
  Format => 'undef',
345
347
  ValueConv => 'substr($val,16)',
346
348
  },
349
+ {
350
+ Name => 'UUID-C2PAClaimSignature', # (seen in incorrectly-formatted JUMB data of JPEG images)
351
+ # (may be able to remove this when JUMBF specification is finalized)
352
+ Condition => '$$valPt=~/^c2cs\x00\x11\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71/',
353
+ SubDirectory => {
354
+ TagTable => 'Image::ExifTool::CBOR::Main',
355
+ Start => '$valuePtr + 16',
356
+ },
357
+ },
347
358
  {
348
359
  Name => 'UUID-Unknown',
349
360
  },
@@ -386,6 +397,27 @@ my %jumbfTypes = (
386
397
  },
387
398
  SubDirectory => { TagTable => 'Image::ExifTool::JSON::Main' },
388
399
  },
400
+ cbor => {
401
+ Name => 'CBORData',
402
+ Flags => [ 'Binary', 'Protected' ],
403
+ SubDirectory => { TagTable => 'Image::ExifTool::CBOR::Main' },
404
+ },
405
+ bfdb => { # used in JUMBF (see # (used when tag is renamed according to JUMDLabel)
406
+ Name => 'BinaryDataType',
407
+ Notes => 'JUMBF, MIME type and optional file name',
408
+ Format => 'undef',
409
+ # (ignore "toggles" byte and just extract MIME type and file name)
410
+ ValueConv => '$_=substr($val,1); s/\0+$//; s/\0/, /; $_',
411
+ JUMBF_Suffix => 'Type', # (used when tag is renamed according to JUMDLabel)
412
+ },
413
+ bidb => { # used in JUMBF
414
+ Name => 'BinaryData',
415
+ Notes => 'JUMBF',
416
+ Groups => { 2 => 'Preview' },
417
+ Format => 'undef',
418
+ Binary => 1,
419
+ JUMBF_Suffix => 'Data', # (used when tag is renamed according to JUMDLabel)
420
+ },
389
421
  #
390
422
  # stuff seen in JPEG XL images:
391
423
  #
@@ -529,11 +561,31 @@ my %jumbfTypes = (
529
561
 
530
562
  %Image::ExifTool::Jpeg2000::ColorSpec = (
531
563
  PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
564
+ WRITE_PROC => \&Image::ExifTool::WriteBinaryData, # (we don't actually call this)
532
565
  GROUPS => { 2 => 'Image' },
533
566
  FORMAT => 'int8s',
567
+ WRITABLE => 1,
568
+ WRITE_GROUP => 'colr',
569
+ DATAMEMBER => [ 0 ],
570
+ IS_SUBDIR => [ 3 ],
571
+ NOTES => q{
572
+ The table below contains tags in the color specification (colr) box. This
573
+ box may be rewritten by writing either ICC_Profile, ColorSpace or
574
+ ColorSpecData. When writing, any existing colr boxes are replaced with the
575
+ newly created colr box.
576
+
577
+ B<NOTE>: Care must be taken when writing this color specification because
578
+ writing a specification that is incompatible with the image data may make
579
+ the image undisplayable.
580
+ },
534
581
  0 => {
535
582
  Name => 'ColorSpecMethod',
536
583
  RawConv => '$$self{ColorSpecMethod} = $val',
584
+ Protected => 1,
585
+ Notes => q{
586
+ default for writing is 2 when writing ICC_Profile, 1 when writing
587
+ ColorSpace, or 4 when writing ColorSpecData
588
+ },
537
589
  PrintConv => {
538
590
  1 => 'Enumerated',
539
591
  2 => 'Restricted ICC',
@@ -541,9 +593,15 @@ my %jumbfTypes = (
541
593
  4 => 'Vendor Color',
542
594
  },
543
595
  },
544
- 1 => 'ColorSpecPrecedence',
596
+ 1 => {
597
+ Name => 'ColorSpecPrecedence',
598
+ Notes => 'default for writing is 0',
599
+ Protected => 1,
600
+ },
545
601
  2 => {
546
602
  Name => 'ColorSpecApproximation',
603
+ Notes => 'default for writing is 0',
604
+ Protected => 1,
547
605
  PrintConv => {
548
606
  0 => 'Not Specified',
549
607
  1 => 'Accurate',
@@ -568,6 +626,7 @@ my %jumbfTypes = (
568
626
  Name => 'ColorSpace',
569
627
  Condition => '$$self{ColorSpecMethod} == 1',
570
628
  Format => 'int32u',
629
+ Protected => 1,
571
630
  PrintConv => { # ref 15444-2 2002-05-15
572
631
  0 => 'Bi-level',
573
632
  1 => 'YCbCr(1)',
@@ -597,6 +656,8 @@ my %jumbfTypes = (
597
656
  {
598
657
  Name => 'ColorSpecData',
599
658
  Format => 'undef[$size-3]',
659
+ Writable => 'undef',
660
+ Protected => 1,
600
661
  Binary => 1,
601
662
  },
602
663
  ],
@@ -607,23 +668,24 @@ my %jumbfTypes = (
607
668
  PROCESS_PROC => \&ProcessJUMD,
608
669
  GROUPS => { 0 => 'JUMBF', 1 => 'JUMBF', 2 => 'Image' },
609
670
  NOTES => 'Information extracted from the JUMBF description box.',
610
- 'jumd-type' => {
671
+ 'type' => {
611
672
  Name => 'JUMDType',
612
673
  ValueConv => 'unpack "H*", $val',
613
674
  PrintConv => q{
614
675
  my @a = $val =~ /^(\w{8})(\w{4})(\w{4})(\w{16})$/;
615
676
  return $val unless @a;
616
677
  my $ascii = pack 'H*', $a[0];
617
- $a[0] = $ascii if $ascii =~ /^[a-zA-Z0-9]{4}$/;
678
+ $a[0] = "($ascii)" if $ascii =~ /^[a-zA-Z0-9]{4}$/;
618
679
  return join '-', @a;
619
680
  },
620
681
  # seen:
621
682
  # cacb/cast/caas/cacl/casg/json-00110010800000aa00389b71
622
683
  # 6579d6fbdba2446bb2ac1b82feeb89d1 - JPEG image
623
684
  },
624
- 'jumd-label' => { Name => 'JUMDLabel' },
625
- 'jumd-flags' => {
626
- Name => 'JUMDFlags',
685
+ 'label' => { Name => 'JUMDLabel' },
686
+ 'toggles' => {
687
+ Name => 'JUMDToggles',
688
+ Unknown => 1,
627
689
  PrintConv => { BITMASK => {
628
690
  0 => 'Requestable',
629
691
  1 => 'Label',
@@ -631,8 +693,8 @@ my %jumbfTypes = (
631
693
  3 => 'Signature',
632
694
  }},
633
695
  },
634
- 'jumd-id' => { Name => 'JUMDID', Description => 'JUMD ID' },
635
- 'jumd-sig' => { Name => 'JUMDSignature', PrintConv => 'unpack "H*", $val' },
696
+ 'id' => { Name => 'JUMDID', Description => 'JUMD ID' },
697
+ 'sig' => { Name => 'JUMDSignature', PrintConv => 'unpack "H*", $val' },
636
698
  );
637
699
 
638
700
  #------------------------------------------------------------------------------
@@ -675,20 +737,21 @@ sub ProcessJUMD($$$)
675
737
  delete $$et{JUMBFLabel};
676
738
  $$dirInfo{DirLen} < 17 and $et->Warn('Truncated JUMD directory'), return 0;
677
739
  my $type = substr($$dataPt, $pos, 4);
678
- $et->HandleTag($tagTablePtr, 'jumd-type', substr($$dataPt, $pos, 16));
740
+ $et->HandleTag($tagTablePtr, 'type', substr($$dataPt, $pos, 16));
679
741
  $pos += 16;
680
742
  my $flags = Get8u($dataPt, $pos++);
681
- $et->HandleTag($tagTablePtr, 'jumd-flags', $flags);
743
+ $et->HandleTag($tagTablePtr, 'toggles', $flags);
682
744
  if ($flags & 0x02) { # label exists?
683
745
  pos($$dataPt) = $pos;
684
746
  $$dataPt =~ /\0/g or $et->Warn('Missing JUMD label terminator'), return 0;
685
747
  my $len = pos($$dataPt) - $pos;
686
748
  my $name = substr($$dataPt, $pos, $len);
687
- $et->HandleTag($tagTablePtr, 'jumd-label', $name);
749
+ $et->HandleTag($tagTablePtr, 'label', $name);
688
750
  $pos += $len;
689
751
  if ($len) {
690
752
  $name =~ s/[^-_a-zA-Z0-9]([a-z])/\U$1/g; # capitalize characters after illegal characters
691
753
  $name =~ tr/-_a-zA-Z0-9//dc; # remove other illegal characters
754
+ $name =~ s/__/_/; # collapse double underlines
692
755
  $name = ucfirst $name; # capitalize first letter
693
756
  $name = "Tag$name" if length($name) < 2; # must at least 2 characters long
694
757
  $$et{JUMBFLabel} = $name;
@@ -696,12 +759,12 @@ sub ProcessJUMD($$$)
696
759
  }
697
760
  if ($flags & 0x04) { # ID exists?
698
761
  $pos + 4 > $end and $et->Warn('Missing JUMD ID'), return 0;
699
- $et->HandleTag($tagTablePtr, 'jumd-id', Get32u($dataPt, $pos));
762
+ $et->HandleTag($tagTablePtr, 'id', Get32u($dataPt, $pos));
700
763
  $pos += 4;
701
764
  }
702
765
  if ($flags & 0x08) { # signature exists?
703
766
  $pos + 32 > $end and $et->Warn('Missing JUMD signature'), return 0;
704
- $et->HandleTag($tagTablePtr, 'jumd-sig', substr($$dataPt, $pos, 32));
767
+ $et->HandleTag($tagTablePtr, 'sig', substr($$dataPt, $pos, 32));
705
768
  $pos += 32;
706
769
  }
707
770
  $pos == $end or $et->Warn('Extra data in JUMD box'." $pos $end", 1);
@@ -784,6 +847,48 @@ sub CreateNewBoxes($$)
784
847
  return 1;
785
848
  }
786
849
 
850
+ #------------------------------------------------------------------------------
851
+ # Create Color Specification Box
852
+ # Inputs: 0) ExifTool object ref, 1) Output file or scalar ref
853
+ # Returns: 1 on success
854
+ sub CreateColorSpec($$)
855
+ {
856
+ my ($et, $outfile) = @_;
857
+ my $meth = $et->GetNewValue('Jpeg2000:ColorSpecMethod');
858
+ my $prec = $et->GetNewValue('Jpeg2000:ColorSpecPrecedence') || 0;
859
+ my $approx = $et->GetNewValue('Jpeg2000:ColorSpecApproximation') || 0;
860
+ my $icc = $et->GetNewValue('ICC_Profile');
861
+ my $space = $et->GetNewValue('Jpeg2000:ColorSpace');
862
+ my $cdata = $et->GetNewValue('Jpeg2000:ColorSpecData');
863
+ unless ($meth) {
864
+ if ($icc) {
865
+ $meth = 2;
866
+ } elsif (defined $space) {
867
+ $meth = 1;
868
+ } elsif (defined $cdata) {
869
+ $meth = 4;
870
+ } else {
871
+ $et->Warn('Color space not defined'), return 0;
872
+ }
873
+ }
874
+ if ($meth eq '1') {
875
+ defined $space or $et->Warn('Must specify ColorSpace'), return 0;
876
+ $cdata = pack('N', $space);
877
+ } elsif ($meth eq '2' or $meth eq '3') {
878
+ defined $icc or $et->Warn('Must specify ICC_Profile'), return 0;
879
+ $cdata = $icc;
880
+ } elsif ($meth eq '4') {
881
+ defined $cdata or $et->Warn('Must specify ColorSpecData'), return 0;
882
+ } else {
883
+ $et->Warn('Unknown ColorSpecMethod'), return 0;
884
+ }
885
+ my $boxhdr = pack('N', length($cdata) + 11) . 'colr';
886
+ Write($outfile, $boxhdr, pack('CCC',$meth,$prec,$approx), $cdata) or return 0;
887
+ ++$$et{CHANGED};
888
+ $et->VPrint(1, " + Jpeg2000:ColorSpec\n");
889
+ return 1;
890
+ }
891
+
787
892
  #------------------------------------------------------------------------------
788
893
  # Process JPEG 2000 box
789
894
  # Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) Pointer to tag table
@@ -801,7 +906,7 @@ sub ProcessJpeg2000Box($$$)
801
906
  my $raf = $$dirInfo{RAF};
802
907
  my $outfile = $$dirInfo{OutFile};
803
908
  my $dirEnd = $dirStart + $dirLen;
804
- my ($err, $outBuff, $verbose);
909
+ my ($err, $outBuff, $verbose, $doColour);
805
910
 
806
911
  if ($outfile) {
807
912
  unless ($raf) {
@@ -809,13 +914,19 @@ sub ProcessJpeg2000Box($$$)
809
914
  $outBuff = '';
810
915
  $outfile = \$outBuff;
811
916
  }
917
+ # determine if we will be writing colr box
918
+ if ($$dirInfo{DirName} and $$dirInfo{DirName} eq 'JP2Header') {
919
+ $doColour = 2 if defined $et->GetNewValue('ColorSpecMethod') or $et->GetNewValue('ICC_Profile') or
920
+ defined $et->GetNewValue('ColorSpecPrecedence') or defined $et->GetNewValue('ColorSpace') or
921
+ defined $et->GetNewValue('ColorSpecApproximation') or defined $et->GetNewValue('ColorSpecData');
922
+ }
812
923
  } else {
813
924
  # (must not set verbose flag when writing!)
814
925
  $verbose = $$et{OPTIONS}{Verbose};
815
926
  $et->VerboseDir($$dirInfo{DirName}) if $verbose;
816
927
  }
817
928
  # loop through all contained boxes
818
- my ($pos, $boxLen);
929
+ my ($pos, $boxLen, $lastBox);
819
930
  for ($pos=$dirStart; ; $pos+=$boxLen) {
820
931
  my ($boxID, $buff, $valuePtr);
821
932
  my $hdrLen = 8; # the box header length
@@ -824,9 +935,7 @@ sub ProcessJpeg2000Box($$$)
824
935
  my $n = $raf->Read($buff,$hdrLen);
825
936
  unless ($n == $hdrLen) {
826
937
  $n and $err = '', last;
827
- if ($outfile) {
828
- CreateNewBoxes($et, $outfile) or $err = 1;
829
- }
938
+ CreateNewBoxes($et, $outfile) or $err = 1 if $outfile;
830
939
  last;
831
940
  }
832
941
  $dataPt = \$buff;
@@ -838,6 +947,17 @@ sub ProcessJpeg2000Box($$$)
838
947
  }
839
948
  $boxLen = unpack("x$pos N",$$dataPt); # (length includes header and data)
840
949
  $boxID = substr($$dataPt, $pos+4, 4);
950
+ # remove old colr boxes if necessary
951
+ if ($doColour and $boxID eq 'colr') {
952
+ if ($doColour == 1) { # did we successfully write the new colr box?
953
+ $et->VPrint(1," - Jpeg2000:ColorSpec\n");
954
+ ++$$et{CHANGED};
955
+ next;
956
+ }
957
+ $et->Warn('Out-of-order colr box encountered');
958
+ undef $doColour;
959
+ }
960
+ $lastBox = $boxID;
841
961
  $pos += $hdrLen; # move to end of box header
842
962
  if ($boxLen == 1) {
843
963
  # box header contains an additional 8-byte integer for length
@@ -931,6 +1051,14 @@ sub ProcessJpeg2000Box($$$)
931
1051
  }
932
1052
  }
933
1053
  }
1054
+ # create new tag for JUMBF data values with name corresponding to JUMBFLabel
1055
+ if ($tagInfo and $$et{JUMBFLabel} and (not $$tagInfo{SubDirectory} or $$tagInfo{BlockExtract})) {
1056
+ $tagInfo = { %$tagInfo, Name => $$et{JUMBFLabel} . ($$tagInfo{JUMBF_Suffix} || '') };
1057
+ delete $$tagInfo{Description};
1058
+ AddTagToTable($tagTablePtr, '_JUMBF_' . $$et{JUMBFLabel}, $tagInfo);
1059
+ delete $$tagInfo{Protected}; # (must do this so -j -b returns JUMBF binary data)
1060
+ $$tagInfo{TagID} = $boxID;
1061
+ }
934
1062
  if ($verbose) {
935
1063
  $et->VerboseInfo($boxID, $tagInfo,
936
1064
  Table => $tagTablePtr,
@@ -941,13 +1069,6 @@ sub ProcessJpeg2000Box($$$)
941
1069
  );
942
1070
  next unless $tagInfo;
943
1071
  }
944
- # create new tag for JUMBF data values with name corresponding to JUMBFLabel
945
- if ($$et{JUMBFLabel} and (not $$tagInfo{SubDirectory} or $$tagInfo{BlockExtract})) {
946
- $tagInfo = { %$tagInfo, Name => $$et{JUMBFLabel} };
947
- AddTagToTable($tagTablePtr, '_JUMBF_' . $$et{JUMBFLabel}, $tagInfo);
948
- delete $$tagInfo{Protected}; # (must do this so -j -b returns JUMBF binary data)
949
- $$tagInfo{TagID} = $boxID;
950
- }
951
1072
  if ($$tagInfo{SubDirectory}) {
952
1073
  my $subdir = $$tagInfo{SubDirectory};
953
1074
  my $subdirStart = $valuePtr;
@@ -975,8 +1096,10 @@ sub ProcessJpeg2000Box($$$)
975
1096
  # remove this directory from our create list
976
1097
  delete $$et{AddJp2Dirs}{$$tagInfo{Name}};
977
1098
  my $newdir;
978
- # only edit writable UUID and Exif boxes
979
- if ($uuid or $boxID eq 'Exif' or ($boxID eq 'xml ' and $$et{IsJXL})) {
1099
+ # only edit writable UUID, Exif and jp2h boxes
1100
+ if ($uuid or $boxID eq 'Exif' or ($boxID eq 'xml ' and $$et{IsJXL}) or
1101
+ ($boxID eq 'jp2h' and $$et{EDIT_DIRS}{jp2h}))
1102
+ {
980
1103
  $newdir = $et->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
981
1104
  next if defined $newdir and not length $newdir; # next if deleting the box
982
1105
  } elsif (defined $uuid) {
@@ -988,6 +1111,11 @@ sub ProcessJpeg2000Box($$$)
988
1111
  my $boxhdr = pack('N', length($newdir) + 8 + $prefixLen) . $boxID;
989
1112
  $boxhdr .= substr($$dataPt, $valuePtr, $prefixLen) if $prefixLen;
990
1113
  Write($outfile, $boxhdr, $newdir) or $err = 1;
1114
+ # write new colr box immediately after ihdr
1115
+ if ($doColour and $boxID eq 'ihdr') {
1116
+ # (shouldn't be multiple ihdr boxes, but just in case, write only 1)
1117
+ $doColour = $doColour==2 ? CreateColorSpec($et, $outfile) : 0;
1118
+ }
991
1119
  } else {
992
1120
  # extract as a block if specified
993
1121
  $subdirInfo{BlockInfo} = $tagInfo if $$tagInfo{BlockExtract};
@@ -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
+