exiftool_vendored 12.34.0 → 12.38.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1290,9 +1290,19 @@ my %convPFn = ( PrintConv => \&ConvertPfn, PrintConvInv => \&ConvertPfnInv );
1290
1290
  },
1291
1291
  },
1292
1292
  0x0106 => [{
1293
+ Name => 'AEBShotCount',
1294
+ Condition => '$$self{Model} =~ /\b90D\b/',
1295
+ Notes => 'EOS 90D', # (and who knows what others?)
1296
+ PrintConv => {
1297
+ 2 => '2 shots',
1298
+ 3 => '3 shots',
1299
+ 5 => '5 shots',
1300
+ 7 => '7 shots',
1301
+ },
1302
+ },{
1293
1303
  Name => 'AEBShotCount',
1294
1304
  Condition => '$count == 1',
1295
- Notes => 'one value for some models...',
1305
+ Notes => 'other models storing a single value',
1296
1306
  PrintConv => {
1297
1307
  0 => '3 shots',
1298
1308
  1 => '2 shots',
@@ -1302,7 +1312,7 @@ my %convPFn = ( PrintConv => \&ConvertPfn, PrintConvInv => \&ConvertPfnInv );
1302
1312
  },{
1303
1313
  Name => 'AEBShotCount',
1304
1314
  Count => 2,
1305
- Notes => 'two values for others',
1315
+ Notes => 'models storing two values',
1306
1316
  PrintConv => {
1307
1317
  '3 0' => '3 shots',
1308
1318
  '2 1' => '2 shots',
@@ -15,7 +15,7 @@ use strict;
15
15
  use vars qw($VERSION);
16
16
  use Image::ExifTool::XMP;
17
17
 
18
- $VERSION = '1.05';
18
+ $VERSION = '1.06';
19
19
 
20
20
  my %dateTimeInfo = (
21
21
  # NOTE: Do NOT put "Groups" here because Groups hash must not be common!
@@ -38,7 +38,7 @@ my %event = (
38
38
  earliestDate => { %dateTimeInfo, Groups => { 2 => 'Time' } },
39
39
  endDayOfYear => { Writable => 'integer', Groups => { 2 => 'Time' } },
40
40
  eventDate => { %dateTimeInfo, Groups => { 2 => 'Time' } },
41
- eventID => { },
41
+ eventID => { Avoid => 1, Notes => 'avoided in favor of XMP-iptcExt:EventID' },
42
42
  eventRemarks => { Writable => 'lang-alt' },
43
43
  eventTime => {
44
44
  Groups => { 2 => 'Time' },
@@ -56,7 +56,7 @@ use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber %intFormat
56
56
  use Image::ExifTool qw(:DataAccess :Utils);
57
57
  use Image::ExifTool::MakerNotes;
58
58
 
59
- $VERSION = '4.37';
59
+ $VERSION = '4.38';
60
60
 
61
61
  sub ProcessExif($$$);
62
62
  sub WriteExif($$$);
@@ -4816,11 +4816,23 @@ my %subSecConv = (
4816
4816
  },
4817
4817
  GPSPosition => {
4818
4818
  Groups => { 2 => 'Location' },
4819
+ Writable => 1,
4820
+ WriteAlso => {
4821
+ GPSLatitude => '$val =~ /(.*?)( ?[NS])?,/ ? $1 : undef',
4822
+ GPSLatitudeRef => '$val =~ /(-?)(.*?) ?([NS]?),/ ? ($3 || ($1 ? "S" : "N")) : undef',
4823
+ GPSLongitude => '$val =~ /, ?(.*?)( ?[EW]?)$/ ? $1 : undef',
4824
+ GPSLongitudeRef => '$val =~ /, ?(-?)(.*?) ?([EW]?)$/ ? ($3 || ($1 ? "W" : "E")) : undef',
4825
+ },
4819
4826
  Require => {
4820
4827
  0 => 'GPSLatitude',
4821
4828
  1 => 'GPSLongitude',
4822
4829
  },
4823
4830
  Priority => 0,
4831
+ Notes => q{
4832
+ when written, writes GPSLatitude, GPSLatitudeRef, GPSLongitude and
4833
+ GPSLongitudeRef. This tag may be written using the same coordinate
4834
+ format as provided by Google Maps when right-clicking on a location
4835
+ },
4824
4836
  ValueConv => '(length($val[0]) or length($val[1])) ? "$val[0] $val[1]" : undef',
4825
4837
  PrintConv => '"$prt[0], $prt[1]"',
4826
4838
  },
@@ -24,7 +24,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
24
24
  use Image::ExifTool::Exif;
25
25
  use Image::ExifTool::GPS;
26
26
 
27
- $VERSION = '1.18';
27
+ $VERSION = '1.19';
28
28
 
29
29
  sub ProcessFLIR($$;$);
30
30
  sub ProcessFLIRText($$$);
@@ -99,7 +99,7 @@ my %float8g = ( Format => 'float', PrintConv => 'sprintf("%.8g",$val)' );
99
99
  NOTES => q{
100
100
  Information extracted from FLIR FFF images and the APP1 FLIR segment of JPEG
101
101
  images. These tags may also be extracted from the first frame of an FLIR
102
- SEQ file.
102
+ SEQ file, or all frames if the ExtractEmbedded option is used.
103
103
  },
104
104
  "_header" => {
105
105
  Name => 'FFFHeader',
@@ -1457,6 +1457,7 @@ sub ProcessFLIR($$;$)
1457
1457
  my $raf = $$dirInfo{RAF} || new File::RandomAccess($$dirInfo{DataPt});
1458
1458
  my $verbose = $et->Options('Verbose');
1459
1459
  my $out = $et->Options('TextOut');
1460
+ my $base = $raf->Tell();
1460
1461
  my ($i, $hdr, $buff, $rec);
1461
1462
 
1462
1463
  # read and verify FFF header
@@ -1485,15 +1486,18 @@ sub ProcessFLIR($$;$)
1485
1486
  my $ver = Get32u(\$hdr, 0x14);
1486
1487
  last if $ver >= 100 and $ver < 200; # (have seen 100 and 101 - PH)
1487
1488
  ToggleByteOrder();
1488
- $i and $et->Warn("Unsupported FLIR $type version"), return 1;
1489
+ next unless $i;
1490
+ return 0 if $$et{DOC_NUM};
1491
+ $et->Warn("Unsupported FLIR $type version");
1492
+ return 1;
1489
1493
  }
1490
1494
 
1491
1495
  # read the FLIR record directory
1492
1496
  my $pos = Get32u(\$hdr, 0x18);
1493
1497
  my $num = Get32u(\$hdr, 0x1c);
1494
- unless ($raf->Seek($pos) and $raf->Read($buff, $num * 0x20) == $num * 0x20) {
1498
+ unless ($raf->Seek($base+$pos) and $raf->Read($buff, $num * 0x20) == $num * 0x20) {
1495
1499
  $et->Warn('Truncated FLIR FFF directory');
1496
- return 1;
1500
+ return $$et{DOC_NUM} ? 0 : 1;
1497
1501
  }
1498
1502
 
1499
1503
  unless ($tagTablePtr) {
@@ -1504,6 +1508,7 @@ sub ProcessFLIR($$;$)
1504
1508
  # process the header data
1505
1509
  $et->HandleTag($tagTablePtr, '_header', $hdr);
1506
1510
 
1511
+ my $success = 1;
1507
1512
  my $oldIndent = $$et{INDENT};
1508
1513
  $$et{INDENT} .= '| ';
1509
1514
  $et->VerboseDir($type, $num);
@@ -1533,12 +1538,22 @@ sub ProcessFLIR($$;$)
1533
1538
  $verbose and printf $out "%s%d) FLIR Record 0x%.2x, offset 0x%.4x, length 0x%.4x\n",
1534
1539
  $$et{INDENT}, $i, $recType, $recPos, $recLen;
1535
1540
 
1536
- unless ($raf->Seek($recPos) and $raf->Read($rec, $recLen) == $recLen) {
1537
- $et->Warn('Invalid FLIR record');
1541
+ # skip RawData records for embedded documents
1542
+ if ($recType == 1 and $$et{DOC_NUM}) {
1543
+ $raf->Seek($base+$recPos+$recLen) or $success = 0, last;
1544
+ next;
1545
+ }
1546
+ unless ($raf->Seek($base+$recPos) and $raf->Read($rec, $recLen) == $recLen) {
1547
+ if ($$et{DOC_NUM}) {
1548
+ $success = 0; # abort processing more documents
1549
+ } else {
1550
+ $et->Warn('Invalid FLIR record');
1551
+ }
1538
1552
  last;
1539
1553
  }
1540
1554
  if ($$tagTablePtr{$recType}) {
1541
1555
  $et->HandleTag($tagTablePtr, $recType, undef,
1556
+ Base => $base,
1542
1557
  DataPt => \$rec,
1543
1558
  DataPos => $recPos,
1544
1559
  Start => 0,
@@ -1550,7 +1565,17 @@ sub ProcessFLIR($$;$)
1550
1565
  }
1551
1566
  delete $$et{SET_GROUP0};
1552
1567
  $$et{INDENT} = $oldIndent;
1553
- return 1;
1568
+
1569
+ # extract information from subsequent frames in SEQ file if ExtractEmbedded is used
1570
+ if ($$dirInfo{RAF} and $et->Options('ExtractEmbedded') and not $$et{DOC_NUM}) {
1571
+ for (;;) {
1572
+ $$et{DOC_NUM} = $$et{DOC_COUNT} + 1;
1573
+ last unless ProcessFLIR($et, $dirInfo, $tagTablePtr);
1574
+ # (DOC_COUNT will be incremented automatically if we extracted any tags)
1575
+ }
1576
+ delete $$et{DOC_NUM};
1577
+ }
1578
+ return $success;
1554
1579
  }
1555
1580
 
1556
1581
  #------------------------------------------------------------------------------
@@ -20,7 +20,7 @@ use strict;
20
20
  use vars qw($VERSION);
21
21
  use Image::ExifTool qw(:DataAccess :Utils);
22
22
 
23
- $VERSION = '1.18';
23
+ $VERSION = '1.19';
24
24
 
25
25
  # road map of directory locations in GIF images
26
26
  my %gifMap = (
@@ -54,6 +54,7 @@ my %gifMap = (
54
54
  Extensions => { # (for documentation only)
55
55
  SubDirectory => { TagTable => 'Image::ExifTool::GIF::Extensions' },
56
56
  },
57
+ TransparentColor => { },
57
58
  );
58
59
 
59
60
  # GIF89a application extensions:
@@ -475,6 +476,9 @@ Block:
475
476
  my $delay = Get16u(\$buff, 1);
476
477
  $delayTime += $delay;
477
478
  $verbose and printf $out "Graphic Control: delay=%.2f\n", $delay / 100;
479
+ # get transparent colour
480
+ my $bits = Get8u(\$buff, 0);
481
+ $et->HandleTag($tagTablePtr, 'TransparentColor', Get8u(\$buff,3)) if $bits & 0x01;
478
482
  $raf->Seek(-$length, 1) or last;
479
483
 
480
484
  } elsif ($a == 0x01 and $length == 12) { # plain text extension
@@ -137,22 +137,26 @@ my %coordConv = (
137
137
  my ($v, $et) = @_;
138
138
  $v = $et->TimeNow() if lc($v) eq 'now';
139
139
  my @tz;
140
- if ($v =~ s/([-+])(.*)//s) { # remove timezone
140
+ if ($v =~ s/([-+])(\d{1,2}):?(\d{2})\s*(DST)?$//i) { # remove timezone
141
141
  my $s = $1 eq '-' ? 1 : -1; # opposite sign to convert back to UTC
142
142
  my $t = $2;
143
- @tz = ($s*$1, $s*$2) if $t =~ /^(\d{2}):?(\d{2})\s*$/;
143
+ @tz = ($s*$2, $s*$3);
144
144
  }
145
- my @a = ($v =~ /((?=\d|\.\d)\d*(?:\.\d*)?)/g);
146
- push @a, '00' while @a < 3;
145
+ # (note: we must allow '.' as a time separator, eg. '10.30.00', with is tricky due to decimal seconds)
146
+ # YYYYmmddHHMMSS[.ss] format
147
+ my @a = ($v =~ /^[^\d]*\d{4}[^\d]*\d{1,2}[^\d]*\d{1,2}[^\d]*(\d{1,2})[^\d]*(\d{2})[^\d]*(\d{2}(?:\.\d+)?)[^\d]*$/);
148
+ # HHMMSS[.ss] format
149
+ @a or @a = ($v =~ /^[^\d]*(\d{1,2})[^\d]*(\d{2})[^\d]*(\d{2}(?:\.\d+)?)[^\d]*$/);
150
+ @a or warn('Invalid time (use HH:MM:SS[.ss][+/-HH:MM|Z])'), return undef;
147
151
  if (@tz) {
148
152
  # adjust to UTC
149
- $a[-2] += $tz[1];
150
- $a[-3] += $tz[0];
151
- while ($a[-2] >= 60) { $a[-2] -= 60; ++$a[-3] }
152
- while ($a[-2] < 0) { $a[-2] += 60; --$a[-3] }
153
- $a[-3] = ($a[-3] + 24) % 24;
153
+ $a[1] += $tz[1];
154
+ $a[0] += $tz[0];
155
+ while ($a[1] >= 60) { $a[1] -= 60; ++$a[0] }
156
+ while ($a[1] < 0) { $a[1] += 60; --$a[0] }
157
+ $a[0] = ($a[0] + 24) % 24;
154
158
  }
155
- return "$a[-3]:$a[-2]:$a[-1]";
159
+ return join(':', @a);
156
160
  },
157
161
  },
158
162
  0x0008 => {
@@ -25,7 +25,7 @@ use strict;
25
25
  use vars qw($VERSION);
26
26
  use Image::ExifTool qw(:DataAccess :Utils);
27
27
 
28
- $VERSION = '1.37';
28
+ $VERSION = '1.38';
29
29
 
30
30
  sub ProcessICC($$);
31
31
  sub ProcessICC_Profile($$$);
@@ -549,7 +549,7 @@ my %manuSig = ( #6
549
549
  A2B3 => 'AToB3',
550
550
  A2M0 => 'AToM0',
551
551
  B2A3 => 'BToA3',
552
- bcp0 => 'BRDFColorimetricParameter3Tag',
552
+ bcp0 => 'BRDFColorimetricParam0',
553
553
  bcp1 => 'BRDFColorimetricParam1',
554
554
  bcp2 => 'BRDFColorimetricParam2',
555
555
  bcp3 => 'BRDFColorimetricParam3',
@@ -602,7 +602,7 @@ my %manuSig = ( #6
602
602
  gdb2 => 'GamutBoundaryDescription2',
603
603
  gdb3 => 'GamutBoundaryDescription3',
604
604
  'mdv '=> 'MultiplexDefaultValues',
605
- mcta => 'MultiplixTypeArray',
605
+ mcta => 'MultiplexTypeArray',
606
606
  minf => 'MeasurementInfo',
607
607
  miin => 'MeasurementInputInfo',
608
608
  M2A0 => 'MToA0',
@@ -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.31';
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',
@@ -560,11 +561,34 @@ my %j2cMarker = (
560
561
 
561
562
  %Image::ExifTool::Jpeg2000::ColorSpec = (
562
563
  PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
564
+ WRITE_PROC => \&Image::ExifTool::WriteBinaryData, # (we don't actually call this)
563
565
  GROUPS => { 2 => 'Image' },
564
566
  FORMAT => 'int8s',
567
+ WRITABLE => 1,
568
+ # (Note: 'colr' is not a real group, but is used as a hack to write the
569
+ # necessary colr box. This hack necessitated another hack in TagInfoXML.pm
570
+ # to avoid reporting this fake group in the XML output)
571
+ WRITE_GROUP => 'colr',
572
+ DATAMEMBER => [ 0 ],
573
+ IS_SUBDIR => [ 3 ],
574
+ NOTES => q{
575
+ The table below contains tags in the color specification (colr) box. This
576
+ box may be rewritten by writing either ICC_Profile, ColorSpace or
577
+ ColorSpecData. When writing, any existing colr boxes are replaced with the
578
+ newly created colr box.
579
+
580
+ B<NOTE>: Care must be taken when writing this color specification because
581
+ writing a specification that is incompatible with the image data may make
582
+ the image undisplayable.
583
+ },
565
584
  0 => {
566
585
  Name => 'ColorSpecMethod',
567
586
  RawConv => '$$self{ColorSpecMethod} = $val',
587
+ Protected => 1,
588
+ Notes => q{
589
+ default for writing is 2 when writing ICC_Profile, 1 when writing
590
+ ColorSpace, or 4 when writing ColorSpecData
591
+ },
568
592
  PrintConv => {
569
593
  1 => 'Enumerated',
570
594
  2 => 'Restricted ICC',
@@ -572,9 +596,15 @@ my %j2cMarker = (
572
596
  4 => 'Vendor Color',
573
597
  },
574
598
  },
575
- 1 => 'ColorSpecPrecedence',
599
+ 1 => {
600
+ Name => 'ColorSpecPrecedence',
601
+ Notes => 'default for writing is 0',
602
+ Protected => 1,
603
+ },
576
604
  2 => {
577
605
  Name => 'ColorSpecApproximation',
606
+ Notes => 'default for writing is 0',
607
+ Protected => 1,
578
608
  PrintConv => {
579
609
  0 => 'Not Specified',
580
610
  1 => 'Accurate',
@@ -599,6 +629,7 @@ my %j2cMarker = (
599
629
  Name => 'ColorSpace',
600
630
  Condition => '$$self{ColorSpecMethod} == 1',
601
631
  Format => 'int32u',
632
+ Protected => 1,
602
633
  PrintConv => { # ref 15444-2 2002-05-15
603
634
  0 => 'Bi-level',
604
635
  1 => 'YCbCr(1)',
@@ -628,6 +659,8 @@ my %j2cMarker = (
628
659
  {
629
660
  Name => 'ColorSpecData',
630
661
  Format => 'undef[$size-3]',
662
+ Writable => 'undef',
663
+ Protected => 1,
631
664
  Binary => 1,
632
665
  },
633
666
  ],
@@ -817,6 +850,48 @@ sub CreateNewBoxes($$)
817
850
  return 1;
818
851
  }
819
852
 
853
+ #------------------------------------------------------------------------------
854
+ # Create Color Specification Box
855
+ # Inputs: 0) ExifTool object ref, 1) Output file or scalar ref
856
+ # Returns: 1 on success
857
+ sub CreateColorSpec($$)
858
+ {
859
+ my ($et, $outfile) = @_;
860
+ my $meth = $et->GetNewValue('Jpeg2000:ColorSpecMethod');
861
+ my $prec = $et->GetNewValue('Jpeg2000:ColorSpecPrecedence') || 0;
862
+ my $approx = $et->GetNewValue('Jpeg2000:ColorSpecApproximation') || 0;
863
+ my $icc = $et->GetNewValue('ICC_Profile');
864
+ my $space = $et->GetNewValue('Jpeg2000:ColorSpace');
865
+ my $cdata = $et->GetNewValue('Jpeg2000:ColorSpecData');
866
+ unless ($meth) {
867
+ if ($icc) {
868
+ $meth = 2;
869
+ } elsif (defined $space) {
870
+ $meth = 1;
871
+ } elsif (defined $cdata) {
872
+ $meth = 4;
873
+ } else {
874
+ $et->Warn('Color space not defined'), return 0;
875
+ }
876
+ }
877
+ if ($meth eq '1') {
878
+ defined $space or $et->Warn('Must specify ColorSpace'), return 0;
879
+ $cdata = pack('N', $space);
880
+ } elsif ($meth eq '2' or $meth eq '3') {
881
+ defined $icc or $et->Warn('Must specify ICC_Profile'), return 0;
882
+ $cdata = $icc;
883
+ } elsif ($meth eq '4') {
884
+ defined $cdata or $et->Warn('Must specify ColorSpecData'), return 0;
885
+ } else {
886
+ $et->Warn('Unknown ColorSpecMethod'), return 0;
887
+ }
888
+ my $boxhdr = pack('N', length($cdata) + 11) . 'colr';
889
+ Write($outfile, $boxhdr, pack('CCC',$meth,$prec,$approx), $cdata) or return 0;
890
+ ++$$et{CHANGED};
891
+ $et->VPrint(1, " + Jpeg2000:ColorSpec\n");
892
+ return 1;
893
+ }
894
+
820
895
  #------------------------------------------------------------------------------
821
896
  # Process JPEG 2000 box
822
897
  # Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) Pointer to tag table
@@ -834,7 +909,7 @@ sub ProcessJpeg2000Box($$$)
834
909
  my $raf = $$dirInfo{RAF};
835
910
  my $outfile = $$dirInfo{OutFile};
836
911
  my $dirEnd = $dirStart + $dirLen;
837
- my ($err, $outBuff, $verbose);
912
+ my ($err, $outBuff, $verbose, $doColour);
838
913
 
839
914
  if ($outfile) {
840
915
  unless ($raf) {
@@ -842,13 +917,19 @@ sub ProcessJpeg2000Box($$$)
842
917
  $outBuff = '';
843
918
  $outfile = \$outBuff;
844
919
  }
920
+ # determine if we will be writing colr box
921
+ if ($$dirInfo{DirName} and $$dirInfo{DirName} eq 'JP2Header') {
922
+ $doColour = 2 if defined $et->GetNewValue('ColorSpecMethod') or $et->GetNewValue('ICC_Profile') or
923
+ defined $et->GetNewValue('ColorSpecPrecedence') or defined $et->GetNewValue('ColorSpace') or
924
+ defined $et->GetNewValue('ColorSpecApproximation') or defined $et->GetNewValue('ColorSpecData');
925
+ }
845
926
  } else {
846
927
  # (must not set verbose flag when writing!)
847
928
  $verbose = $$et{OPTIONS}{Verbose};
848
929
  $et->VerboseDir($$dirInfo{DirName}) if $verbose;
849
930
  }
850
931
  # loop through all contained boxes
851
- my ($pos, $boxLen);
932
+ my ($pos, $boxLen, $lastBox);
852
933
  for ($pos=$dirStart; ; $pos+=$boxLen) {
853
934
  my ($boxID, $buff, $valuePtr);
854
935
  my $hdrLen = 8; # the box header length
@@ -857,9 +938,7 @@ sub ProcessJpeg2000Box($$$)
857
938
  my $n = $raf->Read($buff,$hdrLen);
858
939
  unless ($n == $hdrLen) {
859
940
  $n and $err = '', last;
860
- if ($outfile) {
861
- CreateNewBoxes($et, $outfile) or $err = 1;
862
- }
941
+ CreateNewBoxes($et, $outfile) or $err = 1 if $outfile;
863
942
  last;
864
943
  }
865
944
  $dataPt = \$buff;
@@ -871,6 +950,17 @@ sub ProcessJpeg2000Box($$$)
871
950
  }
872
951
  $boxLen = unpack("x$pos N",$$dataPt); # (length includes header and data)
873
952
  $boxID = substr($$dataPt, $pos+4, 4);
953
+ # remove old colr boxes if necessary
954
+ if ($doColour and $boxID eq 'colr') {
955
+ if ($doColour == 1) { # did we successfully write the new colr box?
956
+ $et->VPrint(1," - Jpeg2000:ColorSpec\n");
957
+ ++$$et{CHANGED};
958
+ next;
959
+ }
960
+ $et->Warn('Out-of-order colr box encountered');
961
+ undef $doColour;
962
+ }
963
+ $lastBox = $boxID;
874
964
  $pos += $hdrLen; # move to end of box header
875
965
  if ($boxLen == 1) {
876
966
  # box header contains an additional 8-byte integer for length
@@ -1009,8 +1099,10 @@ sub ProcessJpeg2000Box($$$)
1009
1099
  # remove this directory from our create list
1010
1100
  delete $$et{AddJp2Dirs}{$$tagInfo{Name}};
1011
1101
  my $newdir;
1012
- # only edit writable UUID and Exif boxes
1013
- if ($uuid or $boxID eq 'Exif' or ($boxID eq 'xml ' and $$et{IsJXL})) {
1102
+ # only edit writable UUID, Exif and jp2h boxes
1103
+ if ($uuid or $boxID eq 'Exif' or ($boxID eq 'xml ' and $$et{IsJXL}) or
1104
+ ($boxID eq 'jp2h' and $$et{EDIT_DIRS}{jp2h}))
1105
+ {
1014
1106
  $newdir = $et->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
1015
1107
  next if defined $newdir and not length $newdir; # next if deleting the box
1016
1108
  } elsif (defined $uuid) {
@@ -1022,6 +1114,11 @@ sub ProcessJpeg2000Box($$$)
1022
1114
  my $boxhdr = pack('N', length($newdir) + 8 + $prefixLen) . $boxID;
1023
1115
  $boxhdr .= substr($$dataPt, $valuePtr, $prefixLen) if $prefixLen;
1024
1116
  Write($outfile, $boxhdr, $newdir) or $err = 1;
1117
+ # write new colr box immediately after ihdr
1118
+ if ($doColour and $boxID eq 'ihdr') {
1119
+ # (shouldn't be multiple ihdr boxes, but just in case, write only 1)
1120
+ $doColour = $doColour==2 ? CreateColorSpec($et, $outfile) : 0;
1121
+ }
1025
1122
  } else {
1026
1123
  # extract as a block if specified
1027
1124
  $subdirInfo{BlockInfo} = $tagInfo if $$tagInfo{BlockExtract};