exiftool_vendored 12.60.0 → 12.62.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.
@@ -11,6 +11,7 @@
11
11
  # 4) http://DataCompression.info/ArchiveFormats/RAR202.txt
12
12
  # 5) https://jira.atlassian.com/browse/CONF-21706
13
13
  # 6) http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/indesign/cs55-docs/IDML/idml-specification.pdf
14
+ # 7) https://www.rarlab.com/technote.htm
14
15
  #------------------------------------------------------------------------------
15
16
 
16
17
  package Image::ExifTool::ZIP;
@@ -19,7 +20,7 @@ use strict;
19
20
  use vars qw($VERSION $warnString);
20
21
  use Image::ExifTool qw(:DataAccess :Utils);
21
22
 
22
- $VERSION = '1.28';
23
+ $VERSION = '1.29';
23
24
 
24
25
  sub WarnProc($) { $warnString = $_[0]; }
25
26
 
@@ -191,7 +192,7 @@ my %iWorkType = (
191
192
  11 => 'Comment',
192
193
  );
193
194
 
194
- # RAR tags (ref 4)
195
+ # RAR v4 tags (ref 4)
195
196
  %Image::ExifTool::ZIP::RAR = (
196
197
  PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
197
198
  GROUPS => { 2 => 'Other' },
@@ -254,8 +255,45 @@ my %iWorkType = (
254
255
  },
255
256
  );
256
257
 
258
+ # RAR v5 tags (ref 7, github#203)
259
+ %Image::ExifTool::ZIP::RAR5 = (
260
+ GROUPS => { 2 => 'Other' },
261
+ VARS => { NO_ID => 1 },
262
+ NOTES => 'These tags are extracted from RAR v5 archive files.',
263
+ RARVersion => { },
264
+ CompressedSize => { },
265
+ ModifyDate => {
266
+ Groups => { 2 => 'Time' },
267
+ ValueConv => 'ConvertUnixTime($val,1)',
268
+ PrintConv => '$self->ConvertDateTime($val)',
269
+ },
270
+ UncompressedSize => { },
271
+ OperatingSystem => {
272
+ PrintConv => { 0 => 'Win32', 1 => 'Unix' },
273
+ },
274
+ ArchivedFileName => { },
275
+ );
276
+
277
+ #------------------------------------------------------------------------------
278
+ # Read unsigned LEB (Little Endian Base) from file
279
+ # Inputs: 0) RAF ref
280
+ # Returns: integer value
281
+ sub ReadULEB($)
282
+ {
283
+ my $raf = shift;
284
+ my ($i, $buff);
285
+ my $rtnVal = 0;
286
+ for ($i=0; ; ++$i) {
287
+ $raf->Read($buff, 1) or last;
288
+ my $num = ord($buff);
289
+ $rtnVal += ($num & 0x7f) << ($i * 7);
290
+ $num & 0x80 or last;
291
+ }
292
+ return $rtnVal;
293
+ }
294
+
257
295
  #------------------------------------------------------------------------------
258
- # Extract information from a RAR file (ref 4)
296
+ # Extract information from a RAR file
259
297
  # Inputs: 0) ExifTool object reference, 1) dirInfo reference
260
298
  # Returns: 1 on success, 0 if this wasn't a valid RAR file
261
299
  sub ProcessRAR($$)
@@ -263,51 +301,129 @@ sub ProcessRAR($$)
263
301
  my ($et, $dirInfo) = @_;
264
302
  my $raf = $$dirInfo{RAF};
265
303
  my ($flags, $buff);
304
+ my $docNum = 0;
266
305
 
267
- return 0 unless $raf->Read($buff, 7) and $buff eq "Rar!\x1a\x07\0";
306
+ return 0 unless $raf->Read($buff, 7) and $buff =~ "Rar!\x1a\x07[\0\x01]";
268
307
 
269
- $et->SetFileType();
270
- SetByteOrder('II');
271
- my $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::RAR');
272
- my $docNum = 0;
308
+ if ($buff eq "Rar!\x1a\x07\0") { # RARv4 (ref 4)
273
309
 
274
- for (;;) {
275
- # read block header
276
- $raf->Read($buff, 7) == 7 or last;
277
- my ($type, $flags, $size) = unpack('xxCvv', $buff);
278
- $size -= 7;
279
- if ($flags & 0x8000) {
280
- $raf->Read($buff, 4) == 4 or last;
281
- $size += unpack('V',$buff) - 4;
282
- }
283
- last if $size < 0;
284
- next unless $size; # ignore blocks with no data
285
- # don't try to read very large blocks unless LargeFileSupport is enabled
286
- if ($size >= 0x80000000 and not $et->Options('LargeFileSupport')) {
287
- $et->Warn('Large block encountered. Aborting.');
288
- last;
310
+ $et->SetFileType();
311
+ SetByteOrder('II');
312
+ my $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::RAR5');
313
+ $et->HandleTag($tagTablePtr, 'RARVersion', 4);
314
+ $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::RAR');
315
+
316
+ for (;;) {
317
+ # read block header
318
+ $raf->Read($buff, 7) == 7 or last;
319
+ my ($type, $flags, $size) = unpack('xxCvv', $buff);
320
+ $size -= 7;
321
+ if ($flags & 0x8000) {
322
+ $raf->Read($buff, 4) == 4 or last;
323
+ $size += unpack('V',$buff) - 4;
324
+ }
325
+ last if $size < 0;
326
+ next unless $size; # ignore blocks with no data
327
+ # don't try to read very large blocks unless LargeFileSupport is enabled
328
+ if ($size >= 0x80000000 and not $et->Options('LargeFileSupport')) {
329
+ $et->Warn('Large block encountered. Aborting.');
330
+ last;
331
+ }
332
+ # process the block
333
+ if ($type == 0x74) { # file block
334
+ # read maximum 4 KB from a file block
335
+ my $n = $size > 4096 ? 4096 : $size;
336
+ $raf->Read($buff, $n) == $n or last;
337
+ # add compressed size to start of data so we can extract it with the other tags
338
+ $buff = pack('V',$size) . $buff;
339
+ $$et{DOC_NUM} = ++$docNum;
340
+ $et->ProcessDirectory({ DataPt => \$buff }, $tagTablePtr);
341
+ $size -= $n;
342
+ } elsif ($type == 0x75 and $size > 6) { # comment block
343
+ $raf->Read($buff, $size) == $size or last;
344
+ # save comment, only if "Stored" (this is untested)
345
+ if (Get8u(\$buff, 3) == 0x30) {
346
+ $et->FoundTag('Comment', substr($buff, 6));
347
+ }
348
+ next;
349
+ }
350
+ # seek to the start of the next block
351
+ $raf->Seek($size, 1) or last if $size;
289
352
  }
290
- # process the block
291
- if ($type == 0x74) { # file block
292
- # read maximum 4 KB from a file block
293
- my $n = $size > 4096 ? 4096 : $size;
294
- $raf->Read($buff, $n) == $n or last;
295
- # add compressed size to start of data so we can extract it with the other tags
296
- $buff = pack('V',$size) . $buff;
297
- $$et{DOC_NUM} = ++$docNum;
298
- $et->ProcessDirectory({ DataPt => \$buff }, $tagTablePtr);
299
- $size -= $n;
300
- } elsif ($type == 0x75 and $size > 6) { # comment block
301
- $raf->Read($buff, $size) == $size or last;
302
- # save comment, only if "Stored" (this is untested)
303
- if (Get8u(\$buff, 3) == 0x30) {
304
- $et->FoundTag('Comment', substr($buff, 6));
353
+
354
+ } else { # RARv5 (ref 7, github#203)
355
+
356
+ return 0 unless $raf->Read($buff, 1) and $buff eq "\0";
357
+ $et->SetFileType();
358
+ my $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::RAR5');
359
+ $et->HandleTag($tagTablePtr, 'RARVersion', 5);
360
+ $$et{INDENT} .= '| ';
361
+
362
+ # loop through header blocks
363
+ for (;;) {
364
+ $raf->Seek(4, 1); # skip header CRC
365
+ my $headSize = ReadULEB($raf);
366
+ last if $headSize == 0;
367
+ # read the header and create new RAF object for reading it
368
+ my $header;
369
+ $raf->Read($header, $headSize) == $headSize or last;
370
+ my $rafHdr = new File::RandomAccess(\$header);
371
+ my $headType = ReadULEB($rafHdr); # get header type
372
+
373
+ if ($headType == 4) { # encryption block
374
+ $et->Warn("File is encrypted.", 0);
375
+ last;
305
376
  }
306
- next;
377
+ # skip over all headers except file or service header
378
+ next unless $headType == 2 or $headType == 3;
379
+ $et->VerboseDir('RAR5 file', undef, $headSize) if $headType == 2;
380
+
381
+ my $headFlag = ReadULEB($rafHdr);
382
+ ReadULEB($rafHdr); # skip extraSize
383
+ my $dataSize;
384
+ if ($headFlag & 0x0002) {
385
+ $dataSize = ReadULEB($rafHdr); # compressed data size
386
+ if ($headType == 2) {
387
+ $et->HandleTag($tagTablePtr, 'CompressedSize', $dataSize);
388
+ } else {
389
+ $raf->Seek($dataSize, 1); # skip service data section
390
+ next;
391
+ }
392
+ } else {
393
+ next if $headType == 3; # all done with service header
394
+ $dataSize = 0;
395
+ }
396
+ my $fileFlag = ReadULEB($rafHdr);
397
+ my $uncompressedSize = ReadULEB($rafHdr);
398
+ $et->HandleTag($tagTablePtr, 'UncompressedSize', $uncompressedSize) unless $fileFlag & 0x0008;
399
+ ReadULEB($rafHdr); # skip file attributes
400
+ if ($fileFlag & 0x0002) {
401
+ $rafHdr->Read($buff, 4) == 4 or last;
402
+ # (untested)
403
+ $et->HandleTag($tagTablePtr, 'ModifyDate', unpack('V', $buff));
404
+ }
405
+ $rafHdr->Seek(4, 1) if $fileFlag & 0x0004; # skip CRC if present
406
+
407
+ ReadULEB($rafHdr); # skip compressionInfo
408
+
409
+ # get operating system
410
+ my $os = ReadULEB($rafHdr);
411
+ $et->HandleTag($tagTablePtr, 'OperatingSystem', $os);
412
+
413
+ # get filename
414
+ $rafHdr->Read($buff, 1) == 1 or last;
415
+ my $nameLen = ord($buff);
416
+ $rafHdr->Read($buff, $nameLen) == $nameLen or last;
417
+ $buff =~ s/\0+$//; # remove trailing nulls (if any)
418
+ $et->HandleTag($tagTablePtr, 'ArchivedFileName', $buff);
419
+
420
+ $$et{DOC_NUM} = ++$docNum;
421
+
422
+ $raf->Seek($dataSize, 1); # skip data section
307
423
  }
308
- # seek to the start of the next block
309
- $raf->Seek($size, 1) or last if $size;
424
+ $$et{INDENT} = substr($$et{INDENT}, 0, -2);
310
425
  }
426
+
311
427
  $$et{DOC_NUM} = 0;
312
428
  if ($docNum > 1 and not $et->Options('Duplicates')) {
313
429
  $et->Warn("Use the Duplicates option to extract tags for all $docNum files", 1);
@@ -735,6 +851,8 @@ under the same terms as Perl itself.
735
851
 
736
852
  =item L<http://DataCompression.info/ArchiveFormats/RAR202.txt>
737
853
 
854
+ =item L<https://www.rarlab.com/technote.htm>
855
+
738
856
  =back
739
857
 
740
858
  =head1 SEE ALSO
@@ -29,7 +29,7 @@ use vars qw($VERSION $RELEASE @ISA @EXPORT_OK %EXPORT_TAGS $AUTOLOAD @fileTypes
29
29
  %jpegMarker %specialTags %fileTypeLookup $testLen $exeDir
30
30
  %static_vars);
31
31
 
32
- $VERSION = '12.60';
32
+ $VERSION = '12.62';
33
33
  $RELEASE = '';
34
34
  @ISA = qw(Exporter);
35
35
  %EXPORT_TAGS = (
@@ -142,8 +142,8 @@ sub ReadValue($$$;$$$);
142
142
  @loadAllTables = qw(
143
143
  PhotoMechanic Exif GeoTiff CanonRaw KyoceraRaw Lytro MinoltaRaw PanasonicRaw
144
144
  SigmaRaw JPEG GIMP Jpeg2000 GIF BMP BMP::OS2 BMP::Extra BPG BPG::Extensions
145
- ICO PICT PNG MNG FLIF DjVu DPX OpenEXR ZISRAW MRC LIF MRC::FEI12 MIFF PCX
146
- PGF PSP PhotoCD Radiance Other::PFM PDF PostScript Photoshop::Header
145
+ WPG ICO PICT PNG MNG FLIF DjVu DPX OpenEXR ZISRAW MRC LIF MRC::FEI12 MIFF
146
+ PCX PGF PSP PhotoCD Radiance Other::PFM PDF PostScript Photoshop::Header
147
147
  Photoshop::Layers Photoshop::ImageData FujiFilm::RAF FujiFilm::IFD
148
148
  Samsung::Trailer Sony::SRF2 Sony::SR2SubIFD Sony::PMP ITC ID3 ID3::Lyrics3
149
149
  FLAC Ogg Vorbis APE APE::NewHeader APE::OldHeader Audible MPC MPEG::Audio
@@ -152,8 +152,9 @@ sub ReadValue($$$;$$$);
152
152
  Real::Media Real::Audio Real::Metafile Red RIFF AIFF ASF WTV DICOM FITS MIE
153
153
  JSON HTML XMP::SVG Palm Palm::MOBI Palm::EXTH Torrent EXE EXE::PEVersion
154
154
  EXE::PEString EXE::MachO EXE::PEF EXE::ELF EXE::AR EXE::CHM LNK Font VCard
155
- Text VCard::VCalendar VCard::VNote RSRC Rawzor ZIP ZIP::GZIP ZIP::RAR RTF
156
- OOXML iWork ISO FLIR::AFF FLIR::FPF MacOS MacOS::MDItem FlashPix::DocTable
155
+ Text VCard::VCalendar VCard::VNote RSRC Rawzor ZIP ZIP::GZIP ZIP::RAR
156
+ ZIP::RAR5 RTF OOXML iWork ISO FLIR::AFF FLIR::FPF MacOS MacOS::MDItem
157
+ FlashPix::DocTable
157
158
  );
158
159
 
159
160
  # alphabetical list of current Lang modules
@@ -190,12 +191,12 @@ $defaultLang = 'en'; # default language
190
191
  # 3) PLIST must be in this list for the binary PLIST format, although it may
191
192
  # cause a file to be checked twice for XML
192
193
  @fileTypes = qw(JPEG EXV CRW DR4 TIFF GIF MRW RAF X3F JP2 PNG MIE MIFF PS PDF
193
- PSD XMP BMP BPG PPM RIFF AIFF ASF MOV MPEG Real SWF PSP FLV OGG
194
- FLAC APE MPC MKV MXF DV PMP IND PGF ICC ITC FLIR FLIF FPF LFP
195
- HTML VRD RTF FITS XCF DSS QTIF FPX PICT ZIP GZIP PLIST RAR BZ2
196
- CZI TAR EXE EXR HDR CHM LNK WMF AVC DEX DPX RAW Font RSRC M2TS
197
- MacOS PHP PCX DCX DWF DWG DXF WTV Torrent VCard LRI R3D AA PDB
198
- PFM2 MRC LIF JXL MOI ISO ALIAS JSON MP3 DICOM PCD ICO TXT);
194
+ PSD XMP BMP WPG BPG PPM RIFF AIFF ASF MOV MPEG Real SWF PSP FLV
195
+ OGG FLAC APE MPC MKV MXF DV PMP IND PGF ICC ITC FLIR FLIF FPF
196
+ LFP HTML VRD RTF FITS XCF DSS QTIF FPX PICT ZIP GZIP PLIST RAR
197
+ BZ2 CZI TAR EXE EXR HDR CHM LNK WMF AVC DEX DPX RAW Font RSRC
198
+ M2TS MacOS PHP PCX DCX DWF DWG DXF WTV Torrent VCard LRI R3D AA
199
+ PDB PFM2 MRC LIF JXL MOI ISO ALIAS JSON MP3 DICOM PCD ICO TXT);
199
200
 
200
201
  # file types that we can write (edit)
201
202
  my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF RAF RAW PNG MIE PSD XMP PPM EPS
@@ -554,6 +555,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
554
555
  XMP => ['XMP', 'Extensible Metadata Platform'],
555
556
  WOFF => ['Font', 'Web Open Font Format'],
556
557
  WOFF2=> ['Font', 'Web Open Font Format2'],
558
+ WPG => ['WPG', 'WordPerfect Graphics'],
557
559
  WTV => ['WTV', 'Windows recorded TV show'],
558
560
  ZIP => ['ZIP', 'ZIP archive'],
559
561
  );
@@ -789,6 +791,7 @@ my %fileDescription = (
789
791
  WMA => 'audio/x-ms-wma',
790
792
  WMF => 'application/x-wmf',
791
793
  WMV => 'video/x-ms-wmv',
794
+ WPG => 'image/x-wpg',
792
795
  WTV => 'video/x-ms-wtv',
793
796
  X3F => 'image/x-sigma-x3f',
794
797
  XCF => 'image/x-xcf',
@@ -970,7 +973,7 @@ $testLen = 1024; # number of bytes to read when testing for magic number
970
973
  QTIF => '.{4}(idsc|idat|iicc)',
971
974
  R3D => '\0\0..RED(1|2)',
972
975
  RAF => 'FUJIFILM',
973
- RAR => 'Rar!\x1a\x07\0',
976
+ RAR => 'Rar!\x1a\x07\x01?\0',
974
977
  RAW => '(.{25}ARECOYK|II|MM)',
975
978
  Real => '(\.RMF|\.ra\xfd|pnm://|rtsp://|http://)',
976
979
  RIFF => '(RIFF|LA0[234]|OFR |LPAC|wvpk|RF64)', # RIFF plus other variants
@@ -984,6 +987,7 @@ $testLen = 1024; # number of bytes to read when testing for magic number
984
987
  VCard=> '(?i)BEGIN:(VCARD|VCALENDAR|VNOTE)\r\n',
985
988
  VRD => 'CANON OPTIONAL DATA\0',
986
989
  WMF => '(\xd7\xcd\xc6\x9a\0\0|\x01\0\x09\0\0\x03)',
990
+ WPG => '\xff\x57\x50\x43',
987
991
  WTV => '\xb7\xd8\x00\x20\x37\x49\xda\x11\xa6\x4e\x00\x07\xe9\x5e\xad\x8d',
988
992
  X3F => 'FOVb',
989
993
  XCF => 'gimp xcf ',
@@ -1825,13 +1829,13 @@ my %systemTagsNotes = (
1825
1829
  },
1826
1830
  ImageDataMD5 => {
1827
1831
  Notes => q{
1828
- MD5 of image data. Generated only if specifically requested for JPEG and
1829
- TIFF-based images, PNG, CRW, CR3, MRW, RAF, X3F and AVIF images, MOV/MP4
1830
- videos, and some RIFF-based files. The MD5 includes the main image data,
1831
- plus JpgFromRaw/OtherImage for some formats, but does not include
1832
- ThumbnailImage or PreviewImage. Includes video and audio data for MOV/MP4.
1833
- The L<XMP-et:OriginalImageMD5 tag|XMP.html#ExifTool> provides a place to
1834
- store these values in the file.
1832
+ MD5 of image data. Generated only if specifically requested for JPEG, TIFF,
1833
+ PNG, CRW, CR3, MRW, RAF, X3F, IIQ, JP2, JXL, HEIC and AVIF images, MOV/MP4
1834
+ videos, and some RIFF-based files such as AVI, WAV and WEBP. The MD5
1835
+ includes the main image data, plus JpgFromRaw/OtherImage for some formats,
1836
+ but does not include ThumbnailImage or PreviewImage. Includes video and
1837
+ audio data for MOV/MP4. The L<XMP-et:OriginalImageMD5 tag|XMP.html#ExifTool>
1838
+ provides a place to store these values in the file.
1835
1839
  },
1836
1840
  },
1837
1841
  );
@@ -2125,8 +2129,10 @@ sub Options($$;@)
2125
2129
 
2126
2130
  while (@_) {
2127
2131
  my $param = shift;
2132
+ my $plus;
2128
2133
  # fix parameter case if necessary
2129
2134
  unless (exists $$options{$param}) {
2135
+ $plus = $param =~ s/\+$//;
2130
2136
  my ($fixed) = grep /^$param$/i, keys %$options;
2131
2137
  if ($fixed) {
2132
2138
  $param = $fixed;
@@ -2291,6 +2297,23 @@ sub Options($$;@)
2291
2297
  $compact{$p} = $val; # preserve most recent setting
2292
2298
  }
2293
2299
  $$options{Compact} = $$options{XMPShorthand} = \%compact;
2300
+ } elsif ($param eq 'NoWarning') {
2301
+ # validate regular expression
2302
+ undef $evalWarning;
2303
+ if (defined $newVal) {
2304
+ local $SIG{'__WARN__'} = \&SetWarning;
2305
+ eval { $param =~ /$newVal/ };
2306
+ $@ and $evalWarning = $@;
2307
+ }
2308
+ if ($evalWarning) {
2309
+ warn 'NoWarning: ' . CleanWarning() . "\n";
2310
+ next;
2311
+ }
2312
+ # add to existing expression if specified
2313
+ if ($plus and defined $oldVal) {
2314
+ $newVal = defined $newVal ? "$oldVal|$newVal" : $oldVal;
2315
+ }
2316
+ $$options{$param} = $newVal;
2294
2317
  } else {
2295
2318
  if ($param eq 'Escape') {
2296
2319
  # set ESCAPE_PROC
@@ -2385,6 +2408,7 @@ sub ClearOptions($)
2385
2408
  MissingTagValue =>undef,# value for missing tags when expanded in expressions
2386
2409
  NoMultiExif => undef, # raise error when writing multi-segment EXIF
2387
2410
  NoPDFList => undef, # flag to avoid splitting PDF List-type tag values
2411
+ NoWarning => undef, # regular expression for warnings to suppress
2388
2412
  Password => undef, # password for password-protected PDF documents
2389
2413
  PrintConv => 1, # flag to enable print conversion
2390
2414
  QuickTimeHandler => 1, # flag to add mdir Handler to newly created Meta box
@@ -2503,23 +2527,6 @@ sub ExtractInfo($;@)
2503
2527
  }
2504
2528
  }
2505
2529
  ++$$self{FILE_SEQUENCE}; # count files read
2506
- # extract information from alternate files if necessary
2507
- my ($g8, $altExifTool);
2508
- foreach $g8 (keys %{$$self{ALT_EXIFTOOL}}) {
2509
- $altExifTool = $$self{ALT_EXIFTOOL}{$g8};
2510
- next if $$altExifTool{DID_EXTRACT}; # avoid extracting twice
2511
- $$altExifTool{OPTIONS} = $$self{OPTIONS};
2512
- $$altExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
2513
- $$altExifTool{REQ_TAG_LOOKUP} = $$self{REQ_TAG_LOOKUP};
2514
- $altExifTool->ExtractInfo($$altExifTool{ALT_FILE});
2515
- # set family 8 group name for all tags
2516
- foreach (keys %{$$altExifTool{VALUE}}) {
2517
- my $ex = $$altExifTool{TAG_EXTRA}{$_};
2518
- $ex or $ex = $$altExifTool{TAG_EXTRA}{$_} = { };
2519
- $$ex{G8} = $g8;
2520
- }
2521
- $$altExifTool{DID_EXTRACT} = 1;
2522
- }
2523
2530
  }
2524
2531
 
2525
2532
  my $filename = $$self{FILENAME}; # image file name ('' if already open)
@@ -2661,7 +2668,7 @@ sub ExtractInfo($;@)
2661
2668
  if ($isDir or (defined $stat[2] and ($stat[2] & 0170000) == 0040000)) {
2662
2669
  $self->FoundTag('FileType', 'DIR');
2663
2670
  $self->FoundTag('FileTypeExtension', '');
2664
- $self->BuildCompositeTags() if $$options{Composite};
2671
+ $self->ExtractAltInfo();
2665
2672
  $raf->Close() if $raf;
2666
2673
  return 1;
2667
2674
  }
@@ -2679,7 +2686,7 @@ sub ExtractInfo($;@)
2679
2686
  } else {
2680
2687
  $self->Error('Unknown file type');
2681
2688
  }
2682
- $self->BuildCompositeTags() if $fast == 4 and $$options{Composite};
2689
+ $self->ExtractAltInfo();
2683
2690
  last; # don't read the file
2684
2691
  }
2685
2692
  if (@fileTypeList) {
@@ -2845,8 +2852,7 @@ sub ExtractInfo($;@)
2845
2852
  }
2846
2853
  unless ($reEntry) {
2847
2854
  $$self{PATH} = [ ]; # reset PATH
2848
- # calculate Composite tags
2849
- $self->BuildCompositeTags() if $$options{Composite};
2855
+ $self->ExtractAltInfo();
2850
2856
  # do our HTML dump if requested
2851
2857
  if ($$self{HTML_DUMP}) {
2852
2858
  $raf->Seek(0, 2); # seek to end of file
@@ -3648,14 +3654,15 @@ sub SetNewGroups($;@)
3648
3654
 
3649
3655
  #------------------------------------------------------------------------------
3650
3656
  # Build Composite tags from Require'd/Desire'd tags
3651
- # Inputs: 0) ExifTool object reference
3657
+ # Inputs: 0) ExifTool object reference, 1) flag to build only tags that require
3658
+ # tags from alternate files (without this, these tags are ignored)
3652
3659
  # Note: Tag values are calculated in alphabetical order unless a tag Require's
3653
3660
  # or Desire's another Composite tag, in which case the calculation is
3654
3661
  # deferred until after the other tag is calculated.
3655
3662
  sub BuildCompositeTags($)
3656
3663
  {
3657
3664
  local $_;
3658
- my $self = shift;
3665
+ my ($self, $altOnly) = @_;
3659
3666
 
3660
3667
  $$self{BuildingComposite} = 1;
3661
3668
 
@@ -3684,7 +3691,7 @@ COMPOSITE_TAG:
3684
3691
  # loop through sub-documents if necessary
3685
3692
  my $docNum = 0;
3686
3693
  for (;;) {
3687
- my (%tagKey, $found, $index);
3694
+ my (%tagKey, $found, $index, $requireAlt);
3688
3695
  # save Require'd and Desire'd tag values in list
3689
3696
  for ($index=0; ; ++$index) {
3690
3697
  my $reqTag = $$require{$index} || $$desire{$index} || $$inhibit{$index};
@@ -3739,6 +3746,8 @@ COMPOSITE_TAG:
3739
3746
  if ($reqTag =~ /\b(File\d+):/i and $$self{ALT_EXIFTOOL}{$1}) {
3740
3747
  $et = $$self{ALT_EXIFTOOL}{$1};
3741
3748
  $altFile = $1;
3749
+ # set flags indicating we require tags from alternate files
3750
+ $$self{DoAltComposite} = $requireAlt = 1;
3742
3751
  }
3743
3752
  # (CAREFUL! keys may not be sequential if one was deleted)
3744
3753
  for ($key=$name, $i=$$et{DUPL_TAG}{$name} || 0; ; --$i) {
@@ -3770,6 +3779,8 @@ COMPOSITE_TAG:
3770
3779
  }
3771
3780
  $tagKey{$index} = $reqTag;
3772
3781
  }
3782
+ # stop now if this requires alternate tags and we aren't building them
3783
+ last if $requireAlt xor $altOnly;
3773
3784
  if ($docNum) {
3774
3785
  if ($found) {
3775
3786
  $$self{DOC_NUM} = $docNum;
@@ -4067,6 +4078,49 @@ sub CombineInfo($;@)
4067
4078
  return \%combinedInfo;
4068
4079
  }
4069
4080
 
4081
+ #------------------------------------------------------------------------------
4082
+ # Read metadata from alternate files and build composite tags
4083
+ # Inputs: 0) ExifTool ref
4084
+ # Notes: This is called after reading the main file so the tags are available
4085
+ # for being used in the file name, but before building Composite tags
4086
+ # so tags from the alternate files may be used in the Composite tags
4087
+ sub ExtractAltInfo($)
4088
+ {
4089
+ my $self = shift;
4090
+ # extract information from alternate files if necessary
4091
+ my ($g8, $altExifTool);
4092
+ my $opts = $$self{OPTIONS};
4093
+ if ($$opts{Composite} and (not $$opts{FastScan} or $$opts{FastScan} < 5)) {
4094
+ # build all composite tags except those requiring tags from alternate files
4095
+ $self->BuildCompositeTags();
4096
+ }
4097
+ foreach $g8 (sort keys %{$$self{ALT_EXIFTOOL}}) {
4098
+ $altExifTool = $$self{ALT_EXIFTOOL}{$g8};
4099
+ next if $$altExifTool{DID_EXTRACT}; # avoid extracting twice
4100
+ $$altExifTool{OPTIONS} = $$self{OPTIONS};
4101
+ $$altExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
4102
+ $$altExifTool{REQ_TAG_LOOKUP} = $$self{REQ_TAG_LOOKUP};
4103
+ my $fileName = $$altExifTool{ALT_FILE};
4104
+ # allow tags from the main file to be used in the alternate file names
4105
+ # (eg. -file1 '$originalfilename')
4106
+ if ($fileName =~ /\$/) {
4107
+ my @tags = reverse sort keys %{$$self{VALUE}};
4108
+ $fileName = $self->InsertTagValues(\@tags, $fileName, 'Warn');
4109
+ next unless defined $fileName;
4110
+ }
4111
+ $altExifTool->ExtractInfo($fileName);
4112
+ # set family 8 group name for all tags
4113
+ foreach (keys %{$$altExifTool{VALUE}}) {
4114
+ my $ex = $$altExifTool{TAG_EXTRA}{$_};
4115
+ $ex or $ex = $$altExifTool{TAG_EXTRA}{$_} = { };
4116
+ $$ex{G8} = $g8;
4117
+ }
4118
+ $$altExifTool{DID_EXTRACT} = 1;
4119
+ }
4120
+ # if necessary, build composite tags that rely on tags from alternate files
4121
+ $self->BuildCompositeTags(1) if $$self{DoAltComposite};
4122
+ }
4123
+
4070
4124
  #------------------------------------------------------------------------------
4071
4125
  # Get tag table name
4072
4126
  # Inputs: 0) ExifTool object reference, 1) tag key
@@ -4917,12 +4971,14 @@ sub AUTOLOAD
4917
4971
  sub Warn($$;$)
4918
4972
  {
4919
4973
  my ($self, $str, $ignorable) = @_;
4974
+ my $noWarn = $self->Options('NoWarning');
4920
4975
  if ($ignorable) {
4921
4976
  return 0 if $$self{OPTIONS}{IgnoreMinorErrors};
4922
4977
  return 0 if $ignorable eq '3' and $$self{OPTIONS}{Validate};
4978
+ return 1 if defined $noWarn and eval { $str =~ /$noWarn/ };
4923
4979
  $str = $ignorable eq '2' ? "[Minor] $str" : "[minor] $str";
4924
4980
  }
4925
- $self->FoundTag('Warning', $str);
4981
+ $self->FoundTag('Warning', $str) unless defined $noWarn and eval { $str =~ /$noWarn/ };
4926
4982
  return 1;
4927
4983
  }
4928
4984
 
@@ -6432,12 +6488,12 @@ sub ProcessJPEG($$)
6432
6488
  my $htmlDump = $$self{HTML_DUMP};
6433
6489
  my %dumpParms = ( Out => $out );
6434
6490
  my ($ch, $s, $length, $md5, $md5size);
6435
- my ($success, $wantTrailer, $trailInfo, $foundSOS, %jumbfChunk);
6491
+ my ($success, $wantTrailer, $trailInfo, $foundSOS, $gotSize, %jumbfChunk);
6436
6492
  my (@iccChunk, $iccChunkCount, $iccChunksTotal, @flirChunk, $flirCount, $flirTotal);
6437
6493
  my ($preview, $scalado, @dqt, $subSampling, $dumpEnd, %extendedXMP);
6438
6494
 
6439
- # get pointer to MD5 object if it exists and we are the top-level JPEG
6440
- if ($$self{FILE_TYPE} eq 'JPEG' and not $$self{DOC_NUM}) {
6495
+ # get pointer to MD5 object if it exists and we are the top-level JPEG or JP2
6496
+ if ($$self{FILE_TYPE} =~ /^(JPEG|JP2)$/ and not $$self{DOC_NUM}) {
6441
6497
  $md5 = $$self{ImageDataMD5};
6442
6498
  $md5size = 0;
6443
6499
  }
@@ -6561,7 +6617,8 @@ sub ProcessJPEG($$)
6561
6617
  $self->HDump($segPos-4, $length+4, "[JPEG $markerName]", undef, 0x08);
6562
6618
  $dumpEnd = $segPos + $length;
6563
6619
  }
6564
- next unless $length >= 6;
6620
+ next if $length < 6 or $gotSize;
6621
+ $gotSize = 1; # (ignore subsequent SOF segments in probably corrupted JPEG)
6565
6622
  # extract some useful information
6566
6623
  my ($p, $h, $w, $n) = unpack('Cn2C', $$segDataPt);
6567
6624
  my $sof = GetTagTable('Image::ExifTool::JPEG::SOF');
@@ -6751,6 +6808,11 @@ sub ProcessJPEG($$)
6751
6808
  pop @$path;
6752
6809
  $verbose and print $out "JPEG SOD\n";
6753
6810
  $success = 1;
6811
+ if ($md5 and $$self{FILE_TYPE} eq 'JP2') {
6812
+ my $pos = $raf->Tell();
6813
+ $self->ImageDataMD5($raf, undef, 'SOD');
6814
+ $raf->Seek($pos, 0);
6815
+ }
6754
6816
  next if $verbose > 2 or $htmlDump;
6755
6817
  last; # all done parsing file
6756
6818
  } elsif (defined $markerLenBytes{$marker}) {
@@ -7483,8 +7545,11 @@ sub ProcessJPEG($$)
7483
7545
  }
7484
7546
  } elsif ($marker == 0x51) { # SIZ (J2C)
7485
7547
  my ($w, $h) = unpack('x2N2', $$segDataPt);
7486
- $self->FoundTag('ImageWidth', $w);
7487
- $self->FoundTag('ImageHeight', $h);
7548
+ unless ($gotSize) {
7549
+ $gotSize = 1;
7550
+ $self->FoundTag('ImageWidth', $w);
7551
+ $self->FoundTag('ImageHeight', $h);
7552
+ }
7488
7553
  } elsif (($marker & 0xf0) != 0xe0) {
7489
7554
  $dumpType = "$markerName segment";
7490
7555
  $desc = "[JPEG $markerName]"; # (other known JPEG segments)
@@ -7824,6 +7889,9 @@ sub DoProcessTIFF($$;$)
7824
7889
  if ($$self{TIFF_TYPE} eq 'TIFF') {
7825
7890
  $self->FoundTag(PageCount => $$self{PageCount}) if $$self{MultiPage};
7826
7891
  }
7892
+ if ($$self{ImageDataMD5} and $$self{A100DataOffset} and $raf->Seek($$self{A100DataOffset},0)) {
7893
+ $self->ImageDataMD5($raf, undef, 'A100');
7894
+ }
7827
7895
  return 1;
7828
7896
  }
7829
7897
  #