exiftool_vendored 13.06.0 → 13.10.0

Sign up to get free protection for your applications and to get access to all the features.
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