exiftool_vendored 13.06.0 → 13.10.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +55 -4
  3. data/bin/MANIFEST +1 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +2 -2
  7. data/bin/exiftool +29 -15
  8. data/bin/lib/Image/ExifTool/AIFF.pm +1 -1
  9. data/bin/lib/Image/ExifTool/APE.pm +1 -1
  10. data/bin/lib/Image/ExifTool/ASF.pm +1 -1
  11. data/bin/lib/Image/ExifTool/Apple.pm +9 -7
  12. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +12 -3
  13. data/bin/lib/Image/ExifTool/Canon.pm +19 -1
  14. data/bin/lib/Image/ExifTool/DJI.pm +1 -1
  15. data/bin/lib/Image/ExifTool/Exif.pm +2 -2
  16. data/bin/lib/Image/ExifTool/FITS.pm +2 -2
  17. data/bin/lib/Image/ExifTool/FLIF.pm +2 -2
  18. data/bin/lib/Image/ExifTool/FlashPix.pm +11 -11
  19. data/bin/lib/Image/ExifTool/Font.pm +1 -1
  20. data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  21. data/bin/lib/Image/ExifTool/HP.pm +1 -1
  22. data/bin/lib/Image/ExifTool/ICC_Profile.pm +80 -1
  23. data/bin/lib/Image/ExifTool/ID3.pm +3 -3
  24. data/bin/lib/Image/ExifTool/IPTC.pm +2 -2
  25. data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
  26. data/bin/lib/Image/ExifTool/Jpeg2000.pm +8 -7
  27. data/bin/lib/Image/ExifTool/M2TS.pm +39 -9
  28. data/bin/lib/Image/ExifTool/MXF.pm +2 -2
  29. data/bin/lib/Image/ExifTool/Matroska.pm +1 -1
  30. data/bin/lib/Image/ExifTool/Microsoft.pm +1 -1
  31. data/bin/lib/Image/ExifTool/PDF.pm +15 -15
  32. data/bin/lib/Image/ExifTool/PLIST.pm +3 -3
  33. data/bin/lib/Image/ExifTool/PNG.pm +6 -5
  34. data/bin/lib/Image/ExifTool/Panasonic.pm +1 -1
  35. data/bin/lib/Image/ExifTool/PhaseOne.pm +3 -3
  36. data/bin/lib/Image/ExifTool/Photoshop.pm +64 -3
  37. data/bin/lib/Image/ExifTool/Protobuf.pm +4 -4
  38. data/bin/lib/Image/ExifTool/QuickTime.pm +72 -24
  39. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +336 -91
  40. data/bin/lib/Image/ExifTool/README +4 -1
  41. data/bin/lib/Image/ExifTool/RIFF.pm +3 -3
  42. data/bin/lib/Image/ExifTool/RTF.pm +1 -1
  43. data/bin/lib/Image/ExifTool/Ricoh.pm +3 -3
  44. data/bin/lib/Image/ExifTool/Sony.pm +2 -2
  45. data/bin/lib/Image/ExifTool/TagInfoXML.pm +4 -3
  46. data/bin/lib/Image/ExifTool/TagLookup.pm +6982 -6970
  47. data/bin/lib/Image/ExifTool/TagNames.pod +48 -5
  48. data/bin/lib/Image/ExifTool/VCard.pm +2 -2
  49. data/bin/lib/Image/ExifTool/Validate.pm +3 -3
  50. data/bin/lib/Image/ExifTool/WriteExif.pl +2 -2
  51. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +47 -13
  52. data/bin/lib/Image/ExifTool/WriteXMP.pl +2 -2
  53. data/bin/lib/Image/ExifTool/Writer.pl +32 -21
  54. data/bin/lib/Image/ExifTool/XMP.pm +9 -9
  55. data/bin/lib/Image/ExifTool/ZIP.pm +1 -1
  56. data/bin/lib/Image/ExifTool.pm +65 -61
  57. data/bin/lib/Image/ExifTool.pod +41 -35
  58. data/bin/perl-Image-ExifTool.spec +1 -1
  59. data/lib/exiftool_vendored/version.rb +1 -1
  60. metadata +2 -2
@@ -28,11 +28,12 @@ use strict;
28
28
  use vars qw($VERSION $AUTOLOAD $iptcDigestInfo %printFlags);
29
29
  use Image::ExifTool qw(:DataAccess :Utils);
30
30
 
31
- $VERSION = '1.71';
31
+ $VERSION = '1.72';
32
32
 
33
33
  sub ProcessPhotoshop($$$);
34
34
  sub WritePhotoshop($$$);
35
35
  sub ProcessLayers($$$);
36
+ sub ProcessChannelOptions($$$);
36
37
 
37
38
  # PrintFlags bit definitions (ref forum13785)
38
39
  %printFlags = (
@@ -322,7 +323,13 @@ my %unicodeString = (
322
323
  0x0432 => { Unknown => 1, Name => 'MeasurementScale' }, #7
323
324
  0x0433 => { Unknown => 1, Name => 'TimelineInfo' }, #7
324
325
  0x0434 => { Unknown => 1, Name => 'SheetDisclosure' }, #7
325
- 0x0435 => { Unknown => 1, Name => 'ChannelOptions' }, #7/forum16762
326
+ 0x0435 => {
327
+ Name => 'ChannelOptions', #7/forum16762
328
+ SubDirectory => {
329
+ TagTable => 'Image::ExifTool::Photoshop::ChannelOptions',
330
+ Start => 4,
331
+ },
332
+ },
326
333
  0x0436 => { Unknown => 1, Name => 'OnionSkins' }, #7
327
334
  0x0438 => { Unknown => 1, Name => 'CountInfo' }, #7
328
335
  0x043a => { Unknown => 1, Name => 'PrintInfo2' }, #7
@@ -350,6 +357,41 @@ my %unicodeString = (
350
357
  0x2710 => { Unknown => 1, Name => 'PrintFlagsInfo' },
351
358
  );
352
359
 
360
+ # Photoshop channel options (ref forum16762)
361
+ %Image::ExifTool::Photoshop::ChannelOptions = (
362
+ PROCESS_PROC => \&ProcessChannelOptions,
363
+ VARS => { IS_BINARY => 1 },
364
+ GROUPS => { 2 => 'Image' },
365
+ NOTES => 'These tags relate only to the appearance of a channel.',
366
+ 0 => {
367
+ Name => 'ChannelColorSpace',
368
+ Format => 'int16u',
369
+ PrintConv => {
370
+ 0 => 'RGB',
371
+ 1 => 'HSB',
372
+ 2 => 'CMYK',
373
+ 7 => 'Lab',
374
+ 8 => 'Grayscale',
375
+ },
376
+ },
377
+ 2 => {
378
+ Name => 'ChannelColorData',
379
+ Format => 'int16u[4]',
380
+ },
381
+ 11 => {
382
+ Name => 'ChannelOpacity',
383
+ PrintConv => '"$val%"',
384
+ },
385
+ 12 => {
386
+ Name => 'ChannelColorIndicates',
387
+ PrintConv => {
388
+ 0 => 'Selected Areas',
389
+ 1 => 'Masked Areas',
390
+ 2 => 'Spot Color',
391
+ },
392
+ },
393
+ );
394
+
353
395
  # Photoshop JPEG quality record (ref 2)
354
396
  %Image::ExifTool::Photoshop::JPEG_Quality = (
355
397
  PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
@@ -755,6 +797,25 @@ sub ProcessLayersAndMask($$$)
755
797
  return $raf->Seek($end, 0) ? 1 : 0;
756
798
  }
757
799
 
800
+ #------------------------------------------------------------------------------
801
+ # Process Photoshop channel options (ref forum16762)
802
+ # Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
803
+ # Returns: 1 on success
804
+ sub ProcessChannelOptions($$$)
805
+ {
806
+ my ($et, $dirInfo, $tagTablePtr) = @_;
807
+ my $end = $$dirInfo{DirStart} + $$dirInfo{DirLen};
808
+ $$dirInfo{DirLen} = 13;
809
+ my $i;
810
+ for ($i=0; $$dirInfo{DirStart} + 13 <= $end; ++$i) {
811
+ $$et{SET_GROUP1} = "Channel$i";
812
+ $et->ProcessBinaryData($dirInfo, $tagTablePtr);
813
+ $$dirInfo{DirStart} += 13;
814
+ }
815
+ delete $$et{SET_GROUP1};
816
+ return 1;
817
+ }
818
+
758
819
  #------------------------------------------------------------------------------
759
820
  # Process Photoshop layers (beginning with layer count)
760
821
  # Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
@@ -1050,7 +1111,7 @@ sub ProcessPhotoshop($$$)
1050
1111
  if ($$et{VALUE}{IPTCDigest} and $$et{VALUE}{CurrentIPTCDigest} and
1051
1112
  $$et{VALUE}{IPTCDigest} ne $$et{VALUE}{CurrentIPTCDigest})
1052
1113
  {
1053
- $et->WarnOnce('IPTCDigest is not current. XMP may be out of sync');
1114
+ $et->Warn('IPTCDigest is not current. XMP may be out of sync');
1054
1115
  }
1055
1116
  delete $$et{LOW_PRIORITY_DIR}{'*'};
1056
1117
  return $success;
@@ -127,7 +127,7 @@ sub ProcessProtobuf($$$;$)
127
127
  my $pos = $$dirInfo{Pos};
128
128
  last if $pos >= length $$dataPt;
129
129
  my ($buff, $id, $type) = ReadRecord($dirInfo);
130
- defined $buff or $et->WarnOnce('Protobuf format error'), last;
130
+ defined $buff or $et->Warn('Protobuf format error'), last;
131
131
  if ($type == 2 and $buff =~ /\.proto$/) {
132
132
  # save protocol name separately for directory type
133
133
  $$et{ProtocolName}{$dirName} = substr($buff, 0, -6);
@@ -145,7 +145,7 @@ sub ProcessProtobuf($$$;$)
145
145
  if ($type == 2 and $$tagInfo{Unknown}) {
146
146
  if ($$tagInfo{IsProtobuf}) {
147
147
  $$tagInfo{IsProtobuf} = 0 unless IsProtobuf(\$buff);
148
- } elsif (not defined $$tagInfo{IsProtobuf} and $buff =~ /[^\x20-\x7f]/ and
148
+ } elsif (not defined $$tagInfo{IsProtobuf} and $buff =~ /[^\x20-\x7e]/ and
149
149
  IsProtobuf(\$buff))
150
150
  {
151
151
  $$tagInfo{IsProtobuf} = 1;
@@ -175,7 +175,7 @@ sub ProcessProtobuf($$$;$)
175
175
  my %subdir = ( DataPt => \$buff, Base => $addr, DirName => $dirName );
176
176
  ProcessProtobuf($et, \%subdir, $tagTbl, "$prefix$id-");
177
177
  next;
178
- } elsif ($buff !~ /[^\x20-\x7f]/) {
178
+ } elsif ($buff !~ /[^\x20-\x7e]/) {
179
179
  $val = $buff; # assume this is an ASCII string
180
180
  } elsif (length($buff) % 4) {
181
181
  $val = '0x' . unpack('H*', $buff);
@@ -201,7 +201,7 @@ sub ProcessProtobuf($$$;$)
201
201
  );
202
202
  }
203
203
  # warn if we didn't finish exactly at the end of the buffer
204
- $et->WarnOnce('Truncated protobuf data') unless $prefix or $$dirInfo{Pos} == length $$dataPt;
204
+ $et->Warn('Truncated protobuf data') unless $prefix or $$dirInfo{Pos} == length $$dataPt;
205
205
  return 1;
206
206
  }
207
207
 
@@ -48,7 +48,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
48
48
  use Image::ExifTool::Exif;
49
49
  use Image::ExifTool::GPS;
50
50
 
51
- $VERSION = '3.05';
51
+ $VERSION = '3.07';
52
52
 
53
53
  sub ProcessMOV($$;$);
54
54
  sub ProcessKeys($$$);
@@ -69,8 +69,9 @@ sub Process_gps0($$$);
69
69
  sub Process_gsen($$$);
70
70
  sub Process_gdat($$$);
71
71
  sub Process_nbmt($$$);
72
+ sub ProcessLigoGPS($$$;$);
73
+ sub ProcessLigoJSON($$$);
72
74
  sub ProcessKenwood($$$);
73
- sub ProcessLIGO_JSON($$$);
74
75
  sub ProcessRIFFTrailer($$$);
75
76
  sub ProcessTTAD($$$);
76
77
  sub ProcessNMEA($$$);
@@ -255,7 +256,7 @@ my %timeInfo = (
255
256
  my $offset = (66 * 365 + 17) * 24 * 3600;
256
257
  return $val - $offset if $val >= $offset or $$self{OPTIONS}{QuickTimeUTC};
257
258
  if ($val and not $$self{IsWriting}) {
258
- $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
259
+ $self->Warn('Patched incorrect time zero for QuickTime date/time tag',1);
259
260
  }
260
261
  return $val;
261
262
  },
@@ -584,6 +585,14 @@ my %userDefined = (
584
585
  Condition => '$$valPt =~ /^\0[\0-\x04]..[a-zA-Z ]{4}/s',
585
586
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SkipInfo' },
586
587
  },
588
+ {
589
+ Name => 'LigoGPSInfo',
590
+ Condition => '$$valPt =~ /^LIGOGPSINFO\0/',
591
+ SubDirectory => {
592
+ TagTable => 'Image::ExifTool::QuickTime::Stream',
593
+ ProcessProc => \&ProcessLigoGPS,
594
+ },
595
+ },
587
596
  { Name => 'Skip', Unknown => 1, Binary => 1 },
588
597
  ],
589
598
  wide => { Unknown => 1, Binary => 1 },
@@ -677,7 +686,7 @@ my %userDefined = (
677
686
  Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/',
678
687
  Notes => 'raw 360Fly sensor data without ExtractEmbedded option',
679
688
  RawConv => q{
680
- $self->WarnOnce('Use the ExtractEmbedded option to decode timed SensorData',3);
689
+ $self->Warn('Use the ExtractEmbedded option to decode timed SensorData',3);
681
690
  return \$val;
682
691
  },
683
692
  },
@@ -762,11 +771,11 @@ my %userDefined = (
762
771
  ProcessProc => \&ProcessKenwood,
763
772
  },
764
773
  },{
765
- Name => 'LIGO_JSON',
774
+ Name => 'LigoJSON',
766
775
  Condition => '$$valPt =~ /^LIGOGPSINFO \{/',
767
776
  SubDirectory => {
768
777
  TagTable => 'Image::ExifTool::QuickTime::Stream',
769
- ProcessProc => \&ProcessLIGO_JSON,
778
+ ProcessProc => \&ProcessLigoJSON,
770
779
  },
771
780
  },{
772
781
  Name => 'FLIRData',
@@ -1303,7 +1312,7 @@ my %userDefined = (
1303
1312
  Condition => '$$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/',
1304
1313
  Notes => 'Garmin GPS sensor data',
1305
1314
  RawConv => q{
1306
- $self->WarnOnce('Use the ExtractEmbedded option to decode timed Garmin GPS',3);
1315
+ $self->Warn('Use the ExtractEmbedded option to decode timed Garmin GPS',3);
1307
1316
  return \$val;
1308
1317
  },
1309
1318
  },
@@ -1407,7 +1416,7 @@ my %userDefined = (
1407
1416
  if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1408
1417
  $val -= $offset;
1409
1418
  } elsif ($val and not $$self{IsWriting}) {
1410
- $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
1419
+ $self->Warn('Patched incorrect time zero for QuickTime date/time tag',1);
1411
1420
  }
1412
1421
  return $$self{CreateDate} = $val;
1413
1422
  },
@@ -2213,8 +2222,8 @@ my %userDefined = (
2213
2222
  _cx_ => { Name => 'CX', Format => 'rational64s', Unknown => 1 },
2214
2223
  _cy_ => { Name => 'CY', Format => 'rational64s', Unknown => 1 },
2215
2224
  rads => { Name => 'Rads', Format => 'rational64s', Unknown => 1 },
2216
- lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2217
- Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2225
+ lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess, Kodak proprietary)
2226
+ Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess, Kodak proprietary)
2218
2227
  pose => { Name => 'pose', SubDirectory => { TagTable => 'Image::ExifTool::Kodak::pose' } },
2219
2228
  # AMBA => Ambarella AVC atom (unknown data written by Kodak Playsport video cam)
2220
2229
  # tmlp - 1 byte: 0 (PixPro SP360/4KVR360)
@@ -2557,7 +2566,7 @@ my %userDefined = (
2557
2566
  TTID => { Name => 'TomTomID', ValueConv => 'unpack("x4H*",$val)' },
2558
2567
  TTVI => { Name => 'TomTomVI', Format => 'int32u', Unknown => 1 }, # seen: "0 1 61 508 508"
2559
2568
  # TTVD seen: "normal 720p 60fps 60fps 16/9 wide 1x"
2560
- TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7f]+/g); "@a"', List => 1 },
2569
+ TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7e]+/g); "@a"', List => 1 },
2561
2570
  );
2562
2571
 
2563
2572
  # User-specific media data atoms (ref 11)
@@ -2813,7 +2822,7 @@ my %userDefined = (
2813
2822
  },
2814
2823
  iinf => [{
2815
2824
  Name => 'ItemInformation',
2816
- Condition => '$$valPt =~ /^\0/', # (check for version 0)
2825
+ Condition => '$$self{LastItemID} = -1; $$valPt =~ /^\0/', # (check for version 0)
2817
2826
  SubDirectory => {
2818
2827
  TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2819
2828
  Start => 6, # (4-byte version/flags + 2-byte count)
@@ -2885,6 +2894,17 @@ my %userDefined = (
2885
2894
  %unknownInfo,
2886
2895
  },
2887
2896
  ],
2897
+ grpl => {
2898
+ Name => 'Unknown_grpl',
2899
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::grpl' },
2900
+ },
2901
+ );
2902
+
2903
+ # unknown grpl container
2904
+ %Image::ExifTool::QuickTime::grpl = (
2905
+ PROCESS_PROC => \&ProcessMOV,
2906
+ GROUPS => { 2 => 'Video' },
2907
+ # altr - seen "00 00 00 00 00 00 00 41 00 00 00 02 00 00 00 42 00 00 00 2e"
2888
2908
  );
2889
2909
 
2890
2910
  # additional metadata container (ref ISO14496-12:2015)
@@ -3029,6 +3049,7 @@ my %userDefined = (
3029
3049
  );
3030
3050
 
3031
3051
  # ref https://aomediacodec.github.io/av1-spec/av1-spec.pdf
3052
+ # (NOTE: conversions are the same as Image::ExifTool::ICC_Profile::ColorRep tags)
3032
3053
  %Image::ExifTool::QuickTime::ColorRep = (
3033
3054
  PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3034
3055
  GROUPS => { 2 => 'Video' },
@@ -3312,7 +3333,13 @@ my %userDefined = (
3312
3333
  the associations between items in the file. This information is used by
3313
3334
  ExifTool, but these entries are not extracted as tags.
3314
3335
  },
3315
- dimg => { Name => 'DerivedImageRef', RawConv => 'undef' },
3336
+ dimg => {
3337
+ Name => 'DerivedImageRef',
3338
+ # also parse these for the ID of the primary 'tmap' item
3339
+ # (tone-mapped image in HDRGainMap HEIC by iPhone 15 and 16)
3340
+ RawConv => \&ParseContentDescribes,
3341
+ WriteHook => \&ParseContentDescribes,
3342
+ },
3316
3343
  thmb => { Name => 'ThumbnailRef', RawConv => 'undef' },
3317
3344
  auxl => { Name => 'AuxiliaryImageRef', RawConv => 'undef' },
3318
3345
  cdsc => {
@@ -3330,7 +3357,8 @@ my %userDefined = (
3330
3357
  # hvc1 - HEVC image
3331
3358
  # lhv1 - L-HEVC image
3332
3359
  # infe - ItemInformationEntry
3333
- # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,hvt1(tile image)
3360
+ # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,tmap,hvt1(tile image)
3361
+ # ('tmap' has something to do with the new gainmap written by iPhone 15 and 16)
3334
3362
  infe => {
3335
3363
  Name => 'ItemInfoEntry',
3336
3364
  RawConv => \&ParseItemInfoEntry,
@@ -6560,7 +6588,7 @@ my %userDefined = (
6560
6588
  PROCESS_PROC => \&ProcessKeys,
6561
6589
  WRITE_PROC => \&WriteKeys,
6562
6590
  CHECK_PROC => \&CheckQTValue,
6563
- VARS => { LONG_TAGS => 8 },
6591
+ VARS => { LONG_TAGS => 9 },
6564
6592
  WRITABLE => 1,
6565
6593
  # (not PREFERRED when writing)
6566
6594
  GROUPS => { 1 => 'Keys' },
@@ -6758,6 +6786,7 @@ my %userDefined = (
6758
6786
  ValueConv => 'unpack("N", $val)',
6759
6787
  Writable => 0, # (don't make this writable because it is found in timed metadata)
6760
6788
  },
6789
+ 'full-frame-rate-playback-intent' => 'FullFrameRatePlaybackIntent', #forum16824
6761
6790
  #
6762
6791
  # seen in Apple ProRes RAW file
6763
6792
  #
@@ -8778,6 +8807,7 @@ sub PrintableTagID($;$)
8778
8807
  # ContentType - mime type of item
8779
8808
  # ContentEncoding - item encoding
8780
8809
  # URI - URI of a 'uri '-type item
8810
+ # infe - raw data for 'infe' box (when writing only) [retracted]
8781
8811
  # ipma:
8782
8812
  # Association - list of associated properties in the ipco container
8783
8813
  # Essential - list of "essential" flags for the associated properties
@@ -8928,10 +8958,18 @@ sub ParseItemInfoEntry($$)
8928
8958
  $$items{$id}{URI} = GetString(\$val, $pos);
8929
8959
  }
8930
8960
  }
8961
+ #[retracted] # save raw infe box when writing in case we need to sort items later
8962
+ #[retracted] $$items{$id}{infe} = pack('N', length($val)+8) . 'infe' . $val if $$et{IsWriting};
8931
8963
  $et->VPrint(1, "$$et{INDENT} Item $id: Type=", $$items{$id}{Type} || '',
8932
8964
  ' Name=', $$items{$id}{Name} || '',
8933
8965
  ' ContentType=', $$items{$id}{ContentType} || '',
8966
+ ($$et{PrimaryItem} and $$et{PrimaryItem} == $id) ? ' (PrimaryItem)' : '',
8934
8967
  "\n") if $verbose > 1;
8968
+ unless ($id > $$et{LastItemID}) {
8969
+ $et->Warn('Item info entries are out of order'); #[retracted] unless $$et{IsWriting};
8970
+ #[retracted] $$et{ItemsNotSorted} = 1; # set flag indicating the items weren't sorted
8971
+ }
8972
+ $$et{LastItemID} = $id;
8935
8973
  return undef;
8936
8974
  }
8937
8975
 
@@ -8952,6 +8990,7 @@ sub ParseItemPropAssoc($$)
8952
8990
  my $flg = Get32u(\$val, 0);
8953
8991
  my $num = Get32u(\$val, 4);
8954
8992
  my $pos = 8;
8993
+ my $lastID = -1;
8955
8994
  for ($i=0; $i<$num; ++$i) {
8956
8995
  if ($ver == 0) {
8957
8996
  return undef if $pos + 3 > $len;
@@ -8984,6 +9023,9 @@ sub ParseItemPropAssoc($$)
8984
9023
  $$items{$id}{Association} = \@association;
8985
9024
  $$items{$id}{Essential} = \@essential;
8986
9025
  $et->VPrint(1, "$$et{INDENT} Item $id properties: @association\n") if $verbose > 1;
9026
+ # (according to ISO/IEC 23008-12, these entries must be sorted by item ID)
9027
+ $et->Warn('Item property association entries are out of order') unless $id > $lastID;
9028
+ $lastID = $id;
8987
9029
  }
8988
9030
  return undef;
8989
9031
  }
@@ -9030,18 +9072,21 @@ sub HandleItemInfo($)
9030
9072
  }
9031
9073
  }
9032
9074
  $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
9033
- $warn = "Can't currently extract $type with construction method $$item{ConstructionMethod}" if $$item{ConstructionMethod};
9034
- $et->WarnOnce($warn) if $warn and $name;
9075
+ # Note: In HEIC's, these seem to indicate data in 'idat' instead of 'mdat'
9076
+ my $constMeth = $$item{ConstructionMethod} || 0;
9077
+ $warn = "Can't currently extract $type with construction method $constMeth" if $constMeth > 1;
9078
+ $warn = "No 'idat' for $type object with construction method 1" if $constMeth == 1 and not $$et{MediaDataInfo};
9079
+ $et->Warn($warn) if $warn and $name;
9035
9080
  $warn = 'Not this file' if $$item{DataReferenceIndex}; # (can only extract from "this file")
9036
9081
  unless (($$item{Extents} and @{$$item{Extents}}) or $warn) {
9037
9082
  $warn = "No Extents for $type item";
9038
- $et->WarnOnce($warn) if $name;
9083
+ $et->Warn($warn) if $name;
9039
9084
  }
9040
9085
  if ($warn) {
9041
9086
  $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9042
9087
  next;
9043
9088
  }
9044
- my $base = $$item{BaseOffset} || 0;
9089
+ my $base = ($$item{BaseOffset} || 0) + ($constMeth ? $$et{MediaDataInfo}[0] : 0);
9045
9090
  if ($verbose > 2) {
9046
9091
  # do verbose hex dump
9047
9092
  my $len = 0;
@@ -9099,7 +9144,7 @@ sub HandleItemInfo($)
9099
9144
  $et->VerboseDump(\$buff);
9100
9145
  } else {
9101
9146
  $warn = "Error inflating $name metadata";
9102
- $et->WarnOnce($warn);
9147
+ $et->Warn($warn);
9103
9148
  $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9104
9149
  next;
9105
9150
  }
@@ -9180,6 +9225,7 @@ sub HandleItemInfo($)
9180
9225
  delete $$et{DOC_NUM};
9181
9226
  }
9182
9227
  delete $$et{ItemInfo};
9228
+ delete $$et{MediaDataInfo};
9183
9229
  }
9184
9230
 
9185
9231
  #------------------------------------------------------------------------------
@@ -9188,7 +9234,7 @@ sub HandleItemInfo($)
9188
9234
  sub EEWarn($)
9189
9235
  {
9190
9236
  my $et = shift;
9191
- $et->WarnOnce('The ExtractEmbedded option may find more tags in the media data',3);
9237
+ $et->Warn('The ExtractEmbedded option may find more tags in the media data',3);
9192
9238
  }
9193
9239
 
9194
9240
  #------------------------------------------------------------------------------
@@ -9675,7 +9721,7 @@ sub ProcessMOV($$;$)
9675
9721
  $warnStr = 'End of processing at large atom (LargeFileSupport not enabled)';
9676
9722
  last;
9677
9723
  } elsif ($et->Options('LargeFileSupport') eq '2') {
9678
- $et->WarnOnce('Processing large atom (LargeFileSupport is 2)');
9724
+ $et->Warn('Processing large atom (LargeFileSupport is 2)');
9679
9725
  }
9680
9726
  }
9681
9727
  $size = $hi * 4294967296 + $lo - 16;
@@ -9690,7 +9736,7 @@ sub ProcessMOV($$;$)
9690
9736
  if ($$et{ValidatePath}{$path} and not $dupTagOK{$tag} and not $dupDirOK{$dirID}) {
9691
9737
  my $i = Get32u(\$tag,0);
9692
9738
  my $str = $i < 255 ? "index $i" : "tag '" . PrintableTagID($tag,2) . "'";
9693
- $et->WarnOnce("Duplicate $str at " . join('-', @{$$et{PATH}}));
9739
+ $et->Warn("Duplicate $str at " . join('-', @{$$et{PATH}}));
9694
9740
  $$et{ValidatePath} = { } if $path eq 'MOV-moov'; # avoid warnings for all contained dups
9695
9741
  }
9696
9742
  $$et{ValidatePath}{$path} = 1;
@@ -9762,8 +9808,10 @@ sub ProcessMOV($$;$)
9762
9808
  # save required tag sizes
9763
9809
  if ($$tagTablePtr{"$tag-size"}) {
9764
9810
  $et->HandleTag($tagTablePtr, "$tag-size", $size);
9765
- $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()) if $$tagTablePtr{"$tag-offset"};
9811
+ $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()+$dirBase) if $$tagTablePtr{"$tag-offset"};
9766
9812
  }
9813
+ # save position/size of 'idat'
9814
+ $$et{MediaDataInfo} = [ $raf->Tell() + $dirBase, $size ] if $tag eq 'idat';
9767
9815
  # stop processing at mdat/idat if -fast2 is used
9768
9816
  last if $fast > 1 and ($tag eq 'mdat' or ($tag eq 'idat' and $$et{FileType} ne 'HEIC'));
9769
9817
  # load values only if associated with a tag (or verbose) and not too big