exiftool_vendored 12.60.0 → 12.62.0

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