exiftool-vendored.exe 13.16.0 → 13.25.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 (49) hide show
  1. package/bin/README.txt +14 -14
  2. package/bin/exiftool.exe +0 -0
  3. package/bin/exiftool_files/exiftool.pl +118 -47
  4. package/bin/exiftool_files/lib/Image/ExifTool/Apple.pm +12 -2
  5. package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +16 -10
  6. package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +3 -2
  7. package/bin/exiftool_files/lib/Image/ExifTool/CanonRaw.pm +1 -1
  8. package/bin/exiftool_files/lib/Image/ExifTool/DJI.pm +190 -29
  9. package/bin/exiftool_files/lib/Image/ExifTool/DarwinCore.pm +22 -11
  10. package/bin/exiftool_files/lib/Image/ExifTool/EXE.pm +2 -9
  11. package/bin/exiftool_files/lib/Image/ExifTool/GM.pm +1 -1
  12. package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +3 -3
  13. package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.dat +0 -0
  14. package/bin/exiftool_files/lib/Image/ExifTool/GoPro.pm +86 -48
  15. package/bin/exiftool_files/lib/Image/ExifTool/ICO.pm +2 -2
  16. package/bin/exiftool_files/lib/Image/ExifTool/JPEG.pm +12 -2
  17. package/bin/exiftool_files/lib/Image/ExifTool/JSON.pm +5 -1
  18. package/bin/exiftool_files/lib/Image/ExifTool/Kodak.pm +3 -2
  19. package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +1003 -1399
  20. package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +4 -4
  21. package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +2 -1
  22. package/bin/exiftool_files/lib/Image/ExifTool/PCAP.pm +462 -0
  23. package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +10 -1
  24. package/bin/exiftool_files/lib/Image/ExifTool/PLIST.pm +92 -29
  25. package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +7 -1
  26. package/bin/exiftool_files/lib/Image/ExifTool/Photoshop.pm +2 -2
  27. package/bin/exiftool_files/lib/Image/ExifTool/Plot.pm +713 -0
  28. package/bin/exiftool_files/lib/Image/ExifTool/Protobuf.pm +24 -11
  29. package/bin/exiftool_files/lib/Image/ExifTool/Qualcomm.pm +78 -1
  30. package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +348 -318
  31. package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +75 -27
  32. package/bin/exiftool_files/lib/Image/ExifTool/Samsung.pm +4 -0
  33. package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +34 -15
  34. package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +5061 -4967
  35. package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +8302 -8160
  36. package/bin/exiftool_files/lib/Image/ExifTool/Trailer.pm +318 -0
  37. package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +4 -4
  38. package/bin/exiftool_files/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
  39. package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +9 -4
  40. package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +1 -1
  41. package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +62 -5
  42. package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +14 -13
  43. package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +34 -6
  44. package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +5 -2
  45. package/bin/exiftool_files/lib/Image/ExifTool.pm +193 -92
  46. package/bin/exiftool_files/lib/Image/ExifTool.pod +121 -124
  47. package/bin/exiftool_files/windows_exiftool.txt +95 -71
  48. package/package.json +4 -4
  49. package/bin/exiftool_files/lib/Image/ExifTool/Vivo.pm +0 -124
@@ -402,6 +402,7 @@ my %sLocationDetails = (
402
402
  STRUCT_NAME => 'LocationDetails',
403
403
  NAMESPACE => 'Iptc4xmpExt',
404
404
  GROUPS => { 2 => 'Location' },
405
+ NOTES => 'Note that the GPS elements of this structure are in the "exif" namespace.',
405
406
  Identifier => { List => 'Bag', Namespace => 'xmp' },
406
407
  City => { },
407
408
  CountryCode => { },
@@ -420,6 +421,7 @@ my %sLocationDetails = (
420
421
  PrintConvInv => '$val=~s/\s*m$//;$val',
421
422
  },
422
423
  GPSAltitudeRef => {
424
+ Namespace => 'exif',
423
425
  Writable => 'integer',
424
426
  PrintConv => {
425
427
  OTHER => sub {
@@ -1995,7 +1997,8 @@ my %sACDSeeRegionStruct = (
1995
1997
  Struct => {
1996
1998
  STRUCT_NAME => 'DeviceItem',
1997
1999
  NAMESPACE => { Item => 'http://ns.google.com/photos/dd/1.0/item/' },
1998
- Mime => { },
2000
+ # use this as a key to process Google trailer
2001
+ Mime => { RawConv => '$$self{ProcessGoogleTrailer} = $val' },
1999
2002
  Length => { Writable => 'integer' },
2000
2003
  Padding => { Writable => 'integer' },
2001
2004
  DataURI => { },
@@ -2149,7 +2152,7 @@ my %sACDSeeRegionStruct = (
2149
2152
  STRUCT_NAME => 'Item',
2150
2153
  # (use 'GItem' to avoid conflict with Google Device Container Item)
2151
2154
  NAMESPACE => { GItem => 'http://ns.google.com/photos/1.0/container/item/'},
2152
- Mime => { },
2155
+ Mime => { RawConv => '$$self{ProcessGoogleTrailer} = $val' },
2153
2156
  Semantic => { },
2154
2157
  Length => { Writable => 'integer' },
2155
2158
  Label => { },
@@ -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 $advFmtSelf);
31
31
 
32
- $VERSION = '13.16';
32
+ $VERSION = '13.25';
33
33
  $RELEASE = '';
34
34
  @ISA = qw(Exporter);
35
35
  %EXPORT_TAGS = (
@@ -155,9 +155,9 @@ sub ReadValue($$$;$$$);
155
155
  Real::Metafile Red RIFF AIFF ASF WTV DICOM FITS XISF MIE JSON HTML XMP::SVG
156
156
  Palm Palm::MOBI Palm::EXTH Torrent EXE EXE::PEVersion EXE::PEString
157
157
  EXE::DebugRSDS EXE::DebugNB10 EXE::Misc EXE::MachO EXE::PEF EXE::ELF EXE::AR
158
- EXE::CHM LNK Font VCard Text VCard::VCalendar VCard::VNote RSRC Rawzor ZIP
159
- ZIP::GZIP ZIP::RAR ZIP::RAR5 RTF OOXML iWork ISO FLIR::AFF FLIR::FPF MacOS
160
- MacOS::MDItem FlashPix::DocTable
158
+ EXE::CHM LNK PCAP Font VCard Text VCard::VCalendar VCard::VNote RSRC Rawzor
159
+ ZIP ZIP::GZIP ZIP::RAR ZIP::RAR5 RTF OOXML iWork ISO FLIR::AFF FLIR::FPF
160
+ MacOS MacOS::MDItem FlashPix::DocTable
161
161
  );
162
162
 
163
163
  # alphabetical list of current Lang modules
@@ -199,8 +199,8 @@ $defaultLang = 'en'; # default language
199
199
  LFP HTML VRD RTF FITS XISF XCF DSS QTIF FPX PICT ZIP GZIP PLIST
200
200
  RAR 7Z BZ2 CZI TAR EXE EXR HDR CHM LNK WMF AVC DEX DPX RAW Font
201
201
  JUMBF RSRC M2TS MacOS PHP PCX DCX DWF DWG DXF WTV Torrent VCard
202
- LRI R3D AA PDB PFM2 MRC LIF JXL MOI ISO ALIAS JSON MP3 DICOM PCD
203
- NKA ICO TXT AAC);
202
+ LRI R3D AA PDB PFM2 MRC LIF JXL MOI ISO ALIAS PCAP JSON MP3
203
+ DICOM PCD NKA ICO TXT AAC);
204
204
 
205
205
  # file types that we can write (edit)
206
206
  my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF RAF RAW PNG MIE PSD XMP PPM EPS
@@ -263,6 +263,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
263
263
  BPG => ['BPG', 'Better Portable Graphics'],
264
264
  BTF => ['BTF', 'Big Tagged Image File Format'], #(unofficial)
265
265
  BZ2 => ['BZ2', 'BZIP2 archive'],
266
+ CAP => 'PCAP',
266
267
  C2PA => ['JUMBF','Coalition for Content Provenance and Authenticity'],
267
268
  CHM => ['CHM', 'Microsoft Compiled HTML format'],
268
269
  CIFF => ['CRW', 'Camera Image File Format'],
@@ -454,6 +455,8 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
454
455
  PAC => ['RIFF', 'Lossless Predictive Audio Compression'],
455
456
  PAGES => ['ZIP', 'Apple Pages document'],
456
457
  PBM => ['PPM', 'Portable BitMap'],
458
+ PCAP => ['PCAP', 'Packet Capture'],
459
+ PCAPNG => ['PCAP', 'Packet Capture Next Generation'],
457
460
  PCD => ['PCD', 'Kodak Photo CD Image Pac'],
458
461
  PCT => 'PICT',
459
462
  PCX => ['PCX', 'PC Paintbrush'],
@@ -568,7 +571,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
568
571
  XLTX => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template'],
569
572
  XMP => ['XMP', 'Extensible Metadata Platform'],
570
573
  WOFF => ['Font', 'Web Open Font Format'],
571
- WOFF2=> ['Font', 'Web Open Font Format2'],
574
+ WOFF2=> ['Font', 'Web Open Font Format 2'],
572
575
  WPG => ['WPG', 'WordPerfect Graphics'],
573
576
  WTV => ['WTV', 'Windows recorded TV show'],
574
577
  ZIP => ['ZIP', 'ZIP archive'],
@@ -744,6 +747,7 @@ my %fileDescription = (
744
747
  OTF => 'application/font-otf',
745
748
  PAGES=> 'application/x-iwork-pages-sffpages',
746
749
  PBM => 'image/x-portable-bitmap',
750
+ PCAP => 'application/vnd.tcpdump.pcap',
747
751
  PCD => 'image/x-photo-cd',
748
752
  PCX => 'image/pcx',
749
753
  PDB => 'application/vnd.palm',
@@ -980,6 +984,7 @@ $testLen = 1024; # number of bytes to read when testing for magic number
980
984
  NKA => 'NIKONADJ',
981
985
  OGG => '(OggS|ID3)',
982
986
  ORF => '(II|MM)',
987
+ PCAP => '\xa1\xb2(\xc3\xd4|\x3c\x4d)\0.\0.|(\xd4\xc3|\x4d\x3c)\xb2\xa1.\0.\0|\x0a\x0d\x0d\x0a.{4}(\x1a\x2b\x3c\x4d|\x4d\x3c\x2b\x1a)|GMBU\0\x02',
983
988
  # PCD => signature is at byte 2048
984
989
  PCX => '\x0a[\0-\x05]\x01[\x01\x02\x04\x08].{64}[\0-\x02]',
985
990
  PDB => '.{60}(\.pdfADBE|TEXtREAd|BVokBDIC|DB99DBOS|PNRdPPrs|DataPPrs|vIMGView|PmDBPmDB|InfoINDB|ToGoToGo|SDocSilX|JbDbJBas|JfDbJFil|DATALSdb|Mdb1Mdb1|BOOKMOBI|DataPlkr|DataSprd|SM01SMem|TEXtTlDc|InfoTlIf|DataTlMl|DataTlPt|dataTDBP|TdatTide|ToRaTRPW|zTXTGPlm|BDOCWrdS)',
@@ -1135,6 +1140,7 @@ my @availableOptions = (
1135
1140
  [ 'IgnoreMinorErrors',undef, 'ignore minor errors when reading/writing' ],
1136
1141
  [ 'IgnoreTags', undef, 'list of tags to ignore when extracting' ],
1137
1142
  [ 'ImageHashType', 'MD5', 'image hash algorithm' ],
1143
+ [ 'KeepUTCTime', undef, 'do not convert times stored as UTC' ],
1138
1144
  [ 'Lang', $defaultLang, 'localized language for descriptions etc' ],
1139
1145
  [ 'LargeFileSupport', 1, 'flag indicating support of 64-bit file offsets' ],
1140
1146
  [ 'LimitLongValues', 60, 'length limit for long values' ],
@@ -1152,6 +1158,7 @@ my @availableOptions = (
1152
1158
  [ 'NoPDFList', undef, 'flag to avoid splitting PDF List-type tag values' ],
1153
1159
  [ 'NoWarning', undef, 'regular expression for warnings to suppress' ],
1154
1160
  [ 'Password', undef, 'password for password-protected PDF documents' ],
1161
+ [ 'Plot', undef, 'SVG plot settings' ],
1155
1162
  [ 'PrintCSV', undef, 'flag to print CSV directly (selected metadata types only)' ],
1156
1163
  [ 'PrintConv', 1, 'flag to enable print conversion' ],
1157
1164
  [ 'QuickTimeHandler', 1, 'flag to add mdir Handler to newly created Meta box' ],
@@ -2587,6 +2594,12 @@ sub Options($$;@)
2587
2594
  } else {
2588
2595
  warn("Can't set $param to undef\n");
2589
2596
  }
2597
+ } elsif ($param eq 'Plot') {
2598
+ # add to existing plot settings
2599
+ $newVal = "$oldVal,$newVal" if defined $oldVal and defined $newVal;
2600
+ $$options{$param} = $newVal;
2601
+ } elsif ($param eq 'KeepUTCTime') {
2602
+ $$options{$param} = $static_vars{$param} = $newVal;
2590
2603
  } elsif (lc $param eq 'geodir') {
2591
2604
  $Image::ExifTool::Geolocation::geoDir = $newVal;
2592
2605
  } else {
@@ -4245,7 +4258,7 @@ sub Init($)
4245
4258
  my $self = shift;
4246
4259
  # delete all DataMember variables (lower-case names)
4247
4260
  delete $$self{$_} foreach grep /[a-z]/, keys %$self;
4248
- undef %static_vars; # clear all static variables
4261
+ %static_vars = ( KeepUTCTime => $$self{OPTIONS}{KeepUTCTime} ); # reset static variables
4249
4262
  delete $$self{FOUND_TAGS}; # list of found tags
4250
4263
  delete $$self{EXIF_DATA}; # the EXIF data block
4251
4264
  delete $$self{EXIF_POS}; # EXIF position in file
@@ -6680,12 +6693,15 @@ sub ConvertUnixTime($;$$)
6680
6693
  $time = int($time + 1e-6) if $time != int($time); # avoid round-off errors
6681
6694
  $dec = '';
6682
6695
  }
6683
- if ($toLocal) {
6684
- @tm = localtime($time);
6685
- $tz = TimeZoneString(\@tm, $time);
6686
- } else {
6696
+ if (not $toLocal) {
6687
6697
  @tm = gmtime($time);
6688
6698
  $tz = '';
6699
+ } elsif ($static_vars{KeepUTCTime}) {
6700
+ @tm = gmtime($time);
6701
+ $tz = 'Z';
6702
+ } else {
6703
+ @tm = localtime($time);
6704
+ $tz = TimeZoneString(\@tm, $time);
6689
6705
  }
6690
6706
  my $str = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d$dec%s",
6691
6707
  $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $tz);
@@ -6868,10 +6884,10 @@ sub HDump($$$$;$$$)
6868
6884
  # Returns: Trailer info hash (with RAF and DirName set),
6869
6885
  # or undef if no recognized trailer was found
6870
6886
  # Notes: leaves file position unchanged
6871
- sub IdentifyTrailer($;$)
6887
+ sub IdentifyTrailer($$;$)
6872
6888
  {
6873
- my $raf = shift;
6874
- my $offset = shift || 0;
6889
+ my ($self, $raf, $offset) = @_;
6890
+ $offset or $offset = 0;
6875
6891
  my $pos = $raf->Tell();
6876
6892
  my ($buff, $type, $len);
6877
6893
  while ($raf->Seek(-$offset, 2) and ($len = $raf->Tell()) > 0) {
@@ -6898,6 +6914,11 @@ sub IdentifyTrailer($;$)
6898
6914
  $type = 'NikonApp';
6899
6915
  } elsif ($buff =~ /\xff{4}\x1b\*9HWfu\x84\x93\xa2\xb1$/) {
6900
6916
  $type = 'Vivo';
6917
+ } elsif ($buff =~ /jxrs...\0$/s) {
6918
+ $type = 'OnePlus';
6919
+ } elsif ($$self{ProcessGoogleTrailer}) {
6920
+ # check for Google trailer information if specific XMP tags exist
6921
+ $type = 'Google';
6901
6922
  }
6902
6923
  last;
6903
6924
  }
@@ -6929,18 +6950,24 @@ sub ProcessTrailers($$)
6929
6950
  my $success = 1;
6930
6951
  my $path = $$self{PATH};
6931
6952
 
6953
+ # get position of end of file
6954
+ $raf->Seek(0,2);
6955
+ $$self{FileEnd} = $raf->Tell();
6956
+
6932
6957
  for (;;) { # loop through all trailers
6958
+ $raf->Seek($pos);
6933
6959
  my ($proc, $outBuff);
6934
- if ($dirName eq 'Insta360') {
6935
- require 'Image/ExifTool/QuickTimeStream.pl';
6936
- $proc = 'Image::ExifTool::QuickTime::ProcessInsta360';
6937
- } elsif ($dirName eq 'NikonApp') {
6938
- require Image::ExifTool::Nikon;
6939
- $proc = 'Image::ExifTool::Nikon::ProcessNikonApp';
6940
- } else {
6941
- require "Image/ExifTool/$dirName.pm";
6942
- $proc = "Image::ExifTool::${dirName}::Process$dirName";
6943
- }
6960
+ # trailer-processing procs residing in modules of a different name
6961
+ my $module = {
6962
+ Insta360 => 'QuickTimeStream.pl',
6963
+ NikonApp => 'Nikon.pm',
6964
+ Vivo => 'Trailer.pm',
6965
+ OnePlus => 'Trailer.pm',
6966
+ Google => 'Trailer.pm',
6967
+ }->{$dirName} || "$dirName.pm";
6968
+ require "Image/ExifTool/$module";
6969
+ $module =~ s/(Stream)?\..*//; # remove extension and change QuickTimeStream to QuickTime
6970
+ $proc = "Image::ExifTool::${module}::Process$dirName";
6944
6971
  if ($outfile) {
6945
6972
  # write to local buffer so we can add trailer in proper order later
6946
6973
  $$outfile and $$dirInfo{OutFile} = \$outBuff, $outBuff = '';
@@ -6953,11 +6980,38 @@ sub ProcessTrailers($$)
6953
6980
  $$dirInfo{Trailer} = 1; # set Trailer flag in case proc cares
6954
6981
  # add trailer and DirName to SubDirectory PATH
6955
6982
  push @$path, 'Trailer', $dirName;
6956
-
6957
- # read or write this trailer
6958
- # (proc takes Offset as positive offset from end of trailer to end of file,
6959
- # and returns DataPos and DirLen, and Fixup if applicable, and updates
6960
- # OutFile when writing. Returns < 0 if we must scan for this trailer)
6983
+ #
6984
+ # Call proc to read or write this trailer
6985
+ #
6986
+ # Proc inputs:
6987
+ # 0) ExifTool ref, with FileEnd set, and TrailerStart possibly set (start of all trailers)
6988
+ # 1) DirInfo with the following elements:
6989
+ # DirName - name of this trailer
6990
+ # RAF - RAF reference
6991
+ # Offset - positive offset from end of this trailer to the end of file
6992
+ # OutFile - (write mode) scalar reference for output buffer consisting of an empty string
6993
+ # Trailer - flag set so proc knows we are processing a trailer (if it cares)
6994
+ # Fixup - optional fixup for pointers in trailer
6995
+ # ScanForTrailer - set if we should now scan for the trailer start. For JPEG
6996
+ # images the ExifTool TrailerStart member will also be set, but for TIFF
6997
+ # images TrailerStart will only be set when writing, so the proc should
6998
+ # scan from the current file position when reading in a TIFF image.
6999
+ # Proc returns in read mode (OutFile not set):
7000
+ # 1 = success
7001
+ # 0 = error processing trailer (no warning will be issued and remaining trailers will be ignored)
7002
+ # -1 = must scan from TrailerStart since length can not be determined
7003
+ # (in which case this routine will be called again later when TrailerStart is known)
7004
+ # Proc returns in write mode:
7005
+ # 1 = success (and proc updates OutFile with the trailer to write, or empty string to delete)
7006
+ # 0 = error processing trailer (will issue minor error)
7007
+ # -1 = caller to copy or delete the trailer as-is (from TrailerStart if DataPos isn't set)
7008
+ # - TrailerStart will always be set in write mode
7009
+ # - the write routine will not be called if all trailers are being deleted
7010
+ # Proc sets the following elements of $dirInfo in both read and write mode:
7011
+ # DataPos - file position for start of this trailer
7012
+ # DirLen - length of this trailer (subsequent trailers are not processed if this is not set)
7013
+ # Fixup - for any pointers in the trailer that need adjusting
7014
+ #
6961
7015
  no strict 'refs';
6962
7016
  my $result = &$proc($self, $dirInfo);
6963
7017
  use strict 'refs';
@@ -6965,8 +7019,27 @@ sub ProcessTrailers($$)
6965
7019
  # restore PATH (pop last 2 items)
6966
7020
  splice @$path, -2;
6967
7021
 
6968
- # check result
7022
+ my ($dataPos, $dirLen) = @$dirInfo{'DataPos','DirLen'};
6969
7023
  if ($outfile) {
7024
+ if ($result < 0) {
7025
+ # copy or delete the trailer ourself
7026
+ $result = 1;
7027
+ if ($$self{TrailerStart}) {
7028
+ $dataPos or $dataPos = $$self{TrailerStart};
7029
+ $dirLen or $dirLen = $$self{FileEnd} - $offset - $dataPos;
7030
+ }
7031
+ if ($$self{DEL_GROUP}{Trailer} or $$self{DEL_GROUP}{$dirName}) {
7032
+ my $bytes = $dirLen ? " ($dirLen bytes)" : '';
7033
+ $self->VPrint(0, "Deleting $dirName trailer$bytes\n");
7034
+ ++$$self{CHANGED};
7035
+ } elsif ($dataPos and $dirLen) {
7036
+ $self->VPrint(0, "Copying $dirName trailer ($dirLen bytes)\n");
7037
+ $result = 0 unless $raf->Seek($dataPos) and
7038
+ $raf->Read(${$$dirInfo{OutFile}}, $dirLen) == $dirLen;
7039
+ } else {
7040
+ $result = 0;
7041
+ }
7042
+ }
6970
7043
  if ($result > 0) {
6971
7044
  if ($outBuff) {
6972
7045
  # write trailers to OutFile in original order
@@ -6994,15 +7067,20 @@ sub ProcessTrailers($$)
6994
7067
  $success = 0;
6995
7068
  last;
6996
7069
  }
6997
- last unless $result > 0 and $$dirInfo{DirLen};
7070
+ last unless $result > 0 and $dirLen;
7071
+ $offset += $dirLen;
7072
+ last if $dataPos and $$self{TrailerStart} and $dataPos <= $$self{TrailerStart};
6998
7073
  # look for next trailer
6999
- $offset += $$dirInfo{DirLen};
7000
- my $nextTrail = IdentifyTrailer($raf, $offset) or last;
7074
+ my $nextTrail = $self->IdentifyTrailer($raf, $offset);
7075
+ # process Google trailer after all others if necessary and not done already
7076
+ unless ($nextTrail) {
7077
+ last unless $$self{ProcessGoogleTrailer};
7078
+ $nextTrail = { DirName => 'Google', RAF => $raf };
7079
+ }
7001
7080
  $dirName = $$dirInfo{DirName} = $$nextTrail{DirName};
7002
- $raf->Seek($pos, 0);
7003
7081
  }
7004
7082
  SetByteOrder($byteOrder); # restore original byte order
7005
- $raf->Seek($pos, 0); # restore original file position
7083
+ $raf->Seek($pos); # restore original file position
7006
7084
  $$dirInfo{OutFile} = $outfile; # restore original outfile
7007
7085
  $$dirInfo{Offset} = $offset; # return offset from EOF to start of first trailer
7008
7086
  $$dirInfo{Fixup} = $fixup; # return fixup information
@@ -7399,12 +7477,57 @@ sub ProcessJPEG($$;$)
7399
7477
  $foundSOS = 1;
7400
7478
  # all done with meta information unless we have a trailer
7401
7479
  $verbose and print $out "${indent}JPEG SOS\n";
7480
+ # process extended XMP now if it existed
7481
+ # (must do this before trailers because XMP is required to process Google trailer)
7482
+ if (%extendedXMP) {
7483
+ my $guid;
7484
+ # GUID indicated by the last main XMP segment
7485
+ my $goodGuid = $$self{VALUE}{HasExtendedXMP} || '';
7486
+ # GUID of the extended XMP that we will process ('2' for all)
7487
+ my $readGuid = $$options{ExtendedXMP} || 0;
7488
+ $readGuid = $goodGuid if $readGuid eq '1';
7489
+ foreach $guid (sort keys %extendedXMP) {
7490
+ next unless length $guid == 32; # ignore other (internal) keys
7491
+ my $extXMP = $extendedXMP{$guid};
7492
+ my ($off, @offsets, $warn);
7493
+ # make sure we have all chunks, and create a list of sorted offsets
7494
+ for ($off=0; $off<$$extXMP{Size}; ) {
7495
+ last unless defined $$extXMP{$off};
7496
+ push @offsets, $off;
7497
+ $off += length $$extXMP{$off};
7498
+ }
7499
+ unless ($off == $$extXMP{Size}) {
7500
+ $self->Warn("Incomplete extended XMP (GUID $guid)");
7501
+ next;
7502
+ }
7503
+ if ($guid eq $readGuid or $readGuid eq '2') {
7504
+ $warn = 'Reading non-' if $guid ne $goodGuid;
7505
+ my $buff = '';
7506
+ # assemble XMP all together
7507
+ $buff .= $$extXMP{$_} foreach @offsets;
7508
+ my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
7509
+ my %dirInfo = (
7510
+ DataPt => \$buff,
7511
+ Parent => 'APP1',
7512
+ IsExtended => 1,
7513
+ );
7514
+ $$path[$pn] = 'APP1';
7515
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7516
+ pop @$path;
7517
+ } else {
7518
+ $warn = 'Ignored ';
7519
+ $warn .= 'non-' if $guid ne $goodGuid;
7520
+ }
7521
+ $self->Warn("${warn}standard extended XMP (GUID $guid)") if $warn;
7522
+ delete $extendedXMP{$guid};
7523
+ }
7524
+ }
7402
7525
  unless ($fast) {
7403
- $trailInfo = IdentifyTrailer($raf);
7526
+ $trailInfo = $self->IdentifyTrailer($raf);
7404
7527
  # process trailer now unless we are doing verbose dump
7405
7528
  if ($trailInfo and $verbose < 3 and not $htmlDump) {
7406
7529
  # process trailers (keep trailInfo to finish processing later
7407
- # only if we can't finish without scanning from end of file)
7530
+ # only if we can't finish without scanning from JPEG EOF)
7408
7531
  $self->ProcessTrailers($trailInfo) and undef $trailInfo;
7409
7532
  }
7410
7533
  if ($wantTrailer and $$self{PreviewImageStart}) {
@@ -7577,7 +7700,7 @@ sub ProcessJPEG($$;$)
7577
7700
  my $n = length($1) + 1;
7578
7701
  $self->HDump($segPos+pos($$dataPt)-$n, $n, '[Vivo HiddenData]', undef, 0x08);
7579
7702
  }
7580
- my $tbl = GetTagTable('Image::ExifTool::Vivo::Main');
7703
+ my $tbl = GetTagTable('Image::ExifTool::Trailer::Vivo');
7581
7704
  $self->HandleTag($tbl, HiddenData => $1);
7582
7705
  }
7583
7706
  # avoid looking for preview unless necessary because it really slows
@@ -7855,6 +7978,12 @@ sub ProcessJPEG($$;$)
7855
7978
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7856
7979
  undef $scalado;
7857
7980
  }
7981
+ } elsif ($$segDataPt =~ /^Qualcomm Dual Camera Attributes/) {
7982
+ $dumpType = 'Qualcomm Dual Camera';
7983
+ my $tagTablePtr = GetTagTable('Image::ExifTool::Qualcomm::DualCamera');
7984
+ DirStart(\%dirInfo, 31);
7985
+ $dirInfo{DirName} = 'Qualcomm Dual Camera';
7986
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7858
7987
  } elsif ($$segDataPt =~ /^FPXR\0/) {
7859
7988
  next if $fast > 1; # skip processing for very fast
7860
7989
  $dumpType = 'FPXR';
@@ -8237,50 +8366,6 @@ sub ProcessJPEG($$;$)
8237
8366
  }
8238
8367
  undef $$segDataPt;
8239
8368
  }
8240
- # process extended XMP now if it existed
8241
- if (%extendedXMP) {
8242
- my $guid;
8243
- # GUID indicated by the last main XMP segment
8244
- my $goodGuid = $$self{VALUE}{HasExtendedXMP} || '';
8245
- # GUID of the extended XMP that we will process ('2' for all)
8246
- my $readGuid = $$options{ExtendedXMP} || 0;
8247
- $readGuid = $goodGuid if $readGuid eq '1';
8248
- foreach $guid (sort keys %extendedXMP) {
8249
- next unless length $guid == 32; # ignore other (internal) keys
8250
- my $extXMP = $extendedXMP{$guid};
8251
- my ($off, @offsets, $warn);
8252
- # make sure we have all chunks, and create a list of sorted offsets
8253
- for ($off=0; $off<$$extXMP{Size}; ) {
8254
- last unless defined $$extXMP{$off};
8255
- push @offsets, $off;
8256
- $off += length $$extXMP{$off};
8257
- }
8258
- unless ($off == $$extXMP{Size}) {
8259
- $self->Warn("Incomplete extended XMP (GUID $guid)");
8260
- next;
8261
- }
8262
- if ($guid eq $readGuid or $readGuid eq '2') {
8263
- $warn = 'Reading non-' if $guid ne $goodGuid;
8264
- my $buff = '';
8265
- # assemble XMP all together
8266
- $buff .= $$extXMP{$_} foreach @offsets;
8267
- my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
8268
- my %dirInfo = (
8269
- DataPt => \$buff,
8270
- Parent => 'APP1',
8271
- IsExtended => 1,
8272
- );
8273
- $$path[$pn] = 'APP1';
8274
- $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
8275
- pop @$path;
8276
- } else {
8277
- $warn = 'Ignored ';
8278
- $warn .= 'non-' if $guid ne $goodGuid;
8279
- }
8280
- $self->Warn("${warn}standard extended XMP (GUID $guid)") if $warn;
8281
- delete $extendedXMP{$guid};
8282
- }
8283
- }
8284
8369
  # print verbose hash message if necessary
8285
8370
  print $out "${indent}(ImageDataHash: $hashsize bytes of JPEG image data)\n" if $hashsize and $verbose;
8286
8371
  # calculate JPEGDigest if requested
@@ -8555,9 +8640,11 @@ sub DoProcessTIFF($$;$)
8555
8640
  }
8556
8641
  # process information in recognized trailers
8557
8642
  if ($raf) {
8558
- my $trailInfo = IdentifyTrailer($raf);
8643
+ my $trailInfo = $self->IdentifyTrailer($raf);
8559
8644
  if ($trailInfo) {
8560
- $$trailInfo{ScanForTrailer} = 1; # scan to find AFCP if necessary
8645
+ # scan to find AFCP if necessary (Note: we are scanning
8646
+ # from a random file position in the TIFF)
8647
+ $$trailInfo{ScanForTrailer} = 1;
8561
8648
  $self->ProcessTrailers($trailInfo);
8562
8649
  }
8563
8650
  # dump any other known trailer (eg. A100 RAW Data)
@@ -8658,11 +8745,12 @@ sub DoProcessTIFF($$;$)
8658
8745
  for (;;) {
8659
8746
  last unless $extra > 12;
8660
8747
  $raf->Seek($tiffEnd); # seek back to end of image
8661
- $trailInfo = IdentifyTrailer($raf);
8748
+ $trailInfo = $self->IdentifyTrailer($raf);
8662
8749
  last unless $trailInfo;
8663
8750
  my $tbuf = '';
8664
8751
  $$trailInfo{OutFile} = \$tbuf; # rewrite trailer(s)
8665
8752
  $$trailInfo{ScanForTrailer} = 1; # scan for AFCP if necessary
8753
+ $$self{TrailerStart} = $tiffEnd;
8666
8754
  # rewrite all trailers to buffer
8667
8755
  unless ($self->ProcessTrailers($trailInfo)) {
8668
8756
  undef $trailInfo;
@@ -8874,7 +8962,7 @@ sub ProcessDirectory($$$;$)
8874
8962
  ($$dirInfo{DirLen} or not defined $$dirInfo{DirLen}))
8875
8963
  {
8876
8964
  my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0) + $$self{BASE};
8877
- if ($$self{PROCESSED}{$addr}) {
8965
+ if ($$self{PROCESSED}{$addr} and not $$dirInfo{NotDup}) {
8878
8966
  $self->Warn("$dirName pointer references previous $$self{PROCESSED}{$addr} directory");
8879
8967
  # patch for bug in Windows phone 7.5 O/S that writes incorrect InteropIFD pointer
8880
8968
  return 0 unless $dirName eq 'GPS' and $$self{PROCESSED}{$addr} eq 'InteropIFD';
@@ -9085,10 +9173,11 @@ sub AddTagToTable($$;$$)
9085
9173
  # Handle simple extraction of new tag information
9086
9174
  # Inputs: 0) ExifTool object ref, 1) tag table reference, 2) tagID, 3) value,
9087
9175
  # 4-N) parameters hash: Index, DataPt, DataPos, Base, Start, Size, Parent,
9088
- # TagInfo, ProcessProc, RAF, Format, Count
9176
+ # TagInfo, ProcessProc, RAF, Format, Count, MakeTagInfo
9089
9177
  # Returns: tag key or undef if tag not found
9090
9178
  # Notes: if value is not defined, it is extracted from DataPt using TagInfo
9091
9179
  # Format and Count if provided
9180
+ # - set MakeTagInfo to add tag info for unknown tags with name made from tag ID
9092
9181
  sub HandleTag($$$$;%)
9093
9182
  {
9094
9183
  my ($self, $tagTablePtr, $tag, $val, %parms) = @_;
@@ -9100,6 +9189,15 @@ sub HandleTag($$$$;%)
9100
9189
 
9101
9190
  if ($tagInfo) {
9102
9191
  $subdir = $$tagInfo{SubDirectory};
9192
+ } elsif ($parms{MakeTagInfo}) {
9193
+ $self->VPrint(0, $$self{INDENT}, "[adding $tag]\n") if $verbose;
9194
+ my $name = $tag;
9195
+ $name =~ s/([A-Z]) ([A-Z][ A-Z])/${1}_$2/g; # underline between acronyms
9196
+ $name =~ s/([^A-Za-z])([a-z])/$1\u$2/g; # capitalize words
9197
+ $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
9198
+ $name = "Tag$name" if length($name) < 2 or $name =~ /^[-0-9]/;
9199
+ $tagInfo = { Name => ucfirst($name) };
9200
+ AddTagToTable($tagTablePtr, $tag, $tagInfo);
9103
9201
  } else {
9104
9202
  return undef unless $verbose;
9105
9203
  $tagInfo = { Name => "tag $tag" }; # create temporary tagInfo hash
@@ -9157,10 +9255,11 @@ sub HandleTag($$$$;%)
9157
9255
  }
9158
9256
  $self->Warn("RawConv $tag: " . CleanWarning()) if $evalWarning;
9159
9257
  return undef unless defined $val;
9160
- $val = $$val if ref $val eq 'SCALAR';
9161
- $dataPt = \$val;
9258
+ $dataPt = ref $val eq 'SCALAR' ? $val : \$val;
9162
9259
  $subdirStart = 0;
9163
- $subdirLen = length $val;
9260
+ $subdirLen = length $$dataPt;
9261
+ } elsif (not $dataPt) {
9262
+ $dataPt = ref $val eq 'SCALAR' ? $val : \$val;
9164
9263
  }
9165
9264
  if ($$subdir{Start}) {
9166
9265
  my $valuePtr = 0;
@@ -9169,7 +9268,6 @@ sub HandleTag($$$$;%)
9169
9268
  $subdirStart += $off;
9170
9269
  $subdirLen -= $off;
9171
9270
  }
9172
- $dataPt or $dataPt = \$val;
9173
9271
  # process subdirectory information
9174
9272
  my %dirInfo = (
9175
9273
  DirName => $$subdir{DirName} || $$tagInfo{Name},
@@ -9890,6 +9988,7 @@ sub ProcessBinaryData($$$)
9890
9988
  $subdirBase = eval($$subdir{Base}) + $base;
9891
9989
  }
9892
9990
  my $start = $$subdir{Start} || 0;
9991
+ my $notDup;
9893
9992
  if ($start =~ /\$/) {
9894
9993
  # ignore directories with a zero offset (ie. missing Nikon ShotInfo entries)
9895
9994
  next unless $val;
@@ -9900,6 +9999,7 @@ sub ProcessBinaryData($$$)
9900
9999
  $len = $dataLen - $start unless $len and $len <= $dataLen - $start;
9901
10000
  } else {
9902
10001
  $start += $dirStart + $entry;
10002
+ $notDup = 1,
9903
10003
  }
9904
10004
  my %subdirInfo = (
9905
10005
  DataPt => $dataPt,
@@ -9908,6 +10008,7 @@ sub ProcessBinaryData($$$)
9908
10008
  DirStart => $start,
9909
10009
  DirLen => $len,
9910
10010
  Base => $subdirBase,
10011
+ NotDup => $notDup,
9911
10012
  );
9912
10013
  delete $$self{NO_UNKNOWN};
9913
10014
  $self->ProcessDirectory(\%subdirInfo, $subTablePtr, $$subdir{ProcessProc});