exiftool-vendored.pl 13.17.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 (50) hide show
  1. package/bin/Changes +102 -7
  2. package/bin/MANIFEST +14 -0
  3. package/bin/META.json +1 -1
  4. package/bin/META.yml +1 -1
  5. package/bin/README +46 -46
  6. package/bin/config_files/example.config +1 -1
  7. package/bin/exiftool +211 -121
  8. package/bin/lib/Image/ExifTool/Apple.pm +12 -2
  9. package/bin/lib/Image/ExifTool/BuildTagLookup.pm +2 -2
  10. package/bin/lib/Image/ExifTool/CanonRaw.pm +1 -1
  11. package/bin/lib/Image/ExifTool/DJI.pm +190 -29
  12. package/bin/lib/Image/ExifTool/DarwinCore.pm +22 -11
  13. package/bin/lib/Image/ExifTool/EXE.pm +2 -9
  14. package/bin/lib/Image/ExifTool/GM.pm +1 -1
  15. package/bin/lib/Image/ExifTool/GPS.pm +3 -3
  16. package/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  17. package/bin/lib/Image/ExifTool/GoPro.pm +86 -48
  18. package/bin/lib/Image/ExifTool/ICO.pm +2 -2
  19. package/bin/lib/Image/ExifTool/JPEG.pm +5 -1
  20. package/bin/lib/Image/ExifTool/JSON.pm +5 -1
  21. package/bin/lib/Image/ExifTool/Kodak.pm +3 -2
  22. package/bin/lib/Image/ExifTool/Nikon.pm +943 -1237
  23. package/bin/lib/Image/ExifTool/Olympus.pm +2 -1
  24. package/bin/lib/Image/ExifTool/PCAP.pm +462 -0
  25. package/bin/lib/Image/ExifTool/PDF.pm +10 -1
  26. package/bin/lib/Image/ExifTool/PLIST.pm +92 -29
  27. package/bin/lib/Image/ExifTool/PNG.pm +7 -1
  28. package/bin/lib/Image/ExifTool/Photoshop.pm +2 -2
  29. package/bin/lib/Image/ExifTool/Plot.pm +713 -0
  30. package/bin/lib/Image/ExifTool/Protobuf.pm +24 -11
  31. package/bin/lib/Image/ExifTool/Qualcomm.pm +78 -1
  32. package/bin/lib/Image/ExifTool/QuickTime.pm +347 -318
  33. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +75 -27
  34. package/bin/lib/Image/ExifTool/Sony.pm +33 -14
  35. package/bin/lib/Image/ExifTool/TagLookup.pm +5056 -4967
  36. package/bin/lib/Image/ExifTool/TagNames.pod +443 -334
  37. package/bin/lib/Image/ExifTool/Trailer.pm +3 -3
  38. package/bin/lib/Image/ExifTool/Validate.pm +4 -4
  39. package/bin/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
  40. package/bin/lib/Image/ExifTool/WriteExif.pl +9 -4
  41. package/bin/lib/Image/ExifTool/WritePDF.pl +1 -1
  42. package/bin/lib/Image/ExifTool/WriteQuickTime.pl +58 -5
  43. package/bin/lib/Image/ExifTool/Writer.pl +14 -13
  44. package/bin/lib/Image/ExifTool/XMP.pm +34 -6
  45. package/bin/lib/Image/ExifTool/XMP2.pl +2 -0
  46. package/bin/lib/Image/ExifTool.pm +63 -29
  47. package/bin/lib/Image/ExifTool.pod +83 -86
  48. package/bin/perl-Image-ExifTool.spec +45 -45
  49. package/bin/windows_exiftool.txt +95 -71
  50. package/package.json +3 -3
@@ -12,7 +12,7 @@ use strict;
12
12
  use vars qw($VERSION);
13
13
  use Image::ExifTool qw(:DataAccess :Utils);
14
14
 
15
- $VERSION = '1.00';
15
+ $VERSION = '1.01';
16
16
 
17
17
  %Image::ExifTool::Trailer::Vivo = (
18
18
  GROUPS => { 0 => 'Trailer', 1 => 'Vivo', 2 => 'Image' },
@@ -214,7 +214,7 @@ sub ProcessGoogle($$)
214
214
 
215
215
  delete $$et{ProcessGoogleTrailer}; # reset flag to process the Google trailer
216
216
 
217
- return -1 if $$dirInfo{OutFile};
217
+ return -1 if $$dirInfo{OutFile}; # let caller handle the writing
218
218
 
219
219
  # sometimes DirectoryItemLength is missing the Primary entry
220
220
  $len = [ $len ] unless ref $len eq 'ARRAY';
@@ -271,7 +271,7 @@ sub ProcessGoogle($$)
271
271
  DataPos => $start + $pos,
272
272
  DirLen => $$len[$i],
273
273
  });
274
- $et->HandleTag($tagTable, $$tag[$i], \$buff, { DataPos => $start + $pos, DataPt => \$buff });
274
+ $et->HandleTag($tagTable, $$tag[$i], \$buff, DataPos => $start + $pos, DataPt => \$buff);
275
275
  # (haven't seen non-zero padding, but I assume this is how it works
276
276
  $pos += $$len[$i] + (($pad and $$pad[$i]) ? $$pad[$i] : 0);
277
277
  }
@@ -188,10 +188,10 @@ my %validValue = (
188
188
  0x115 => undef, # SamplesPerPixel
189
189
  0x116 => undef, # RowsPerStrip
190
190
  0x117 => undef, # StripByteCounts
191
- 0x11a => 'defined $val', # XResolution
192
- 0x11b => 'defined $val', # YResolution
191
+ # (optional as of 3.0) 0x11a => 'defined $val', # XResolution
192
+ # (optional as of 3.0) 0x11b => 'defined $val', # YResolution
193
193
  0x11c => undef, # PlanarConfiguration
194
- 0x128 => '$val =~ /^[123]$/', # ResolutionUnit
194
+ # (optional as of 3.0) 0x128 => '$val =~ /^[123]$/', # ResolutionUnit
195
195
  0x201 => undef, # JPEGInterchangeFormat
196
196
  0x202 => undef, # JPEGInterchangeFormatLength
197
197
  0x212 => undef, # YCbCrSubSampling
@@ -218,7 +218,7 @@ my %validValue = (
218
218
  ExifIFD => {
219
219
  0x9000 => 'defined $val and $val =~ /^\d{4}$/', # ExifVersion
220
220
  0x9101 => 'defined $val', # ComponentsConfiguration
221
- 0xa000 => 'defined $val', # FlashpixVersion
221
+ # (optional as of 3.0) 0xa000 => 'defined $val', # FlashpixVersion
222
222
  0xa001 => '$val == 1 or $val == 0xffff', # ColorSpace
223
223
  0xa002 => 'defined $val', # PixelXDimension
224
224
  0xa003 => 'defined $val', # PixelYDimension
@@ -566,7 +566,7 @@ sub WriteCRW($$)
566
566
  my $trailPt;
567
567
  while ($success) {
568
568
  # check to see if trailer(s) exist(s)
569
- my $trailInfo = Image::ExifTool::IdentifyTrailer($raf) or last;
569
+ my $trailInfo = $et->IdentifyTrailer($raf) or last;
570
570
  # rewrite the trailer(s)
571
571
  $buff = '';
572
572
  $$trailInfo{OutFile} = \$buff;
@@ -25,9 +25,9 @@ my %crossDelete = (
25
25
  # mandatory tag default values
26
26
  my %mandatory = (
27
27
  IFD0 => {
28
- 0x011a => 72, # XResolution
29
- 0x011b => 72, # YResolution
30
- 0x0128 => 2, # ResolutionUnit (inches)
28
+ # (optional as of 3.0) 0x011a => 72, # XResolution
29
+ # (optional as of 3.0) 0x011b => 72, # YResolution
30
+ # (optional as of 3.0) 0x0128 => 2, # ResolutionUnit (inches)
31
31
  0x0213 => 1, # YCbCrPositioning (centered)
32
32
  # 0x8769 => ????, # ExifOffset
33
33
  },
@@ -40,7 +40,7 @@ my %mandatory = (
40
40
  ExifIFD => {
41
41
  0x9000 => '0232', # ExifVersion
42
42
  0x9101 => "1 2 3 0",# ComponentsConfiguration
43
- 0xa000 => '0100', # FlashpixVersion
43
+ # (optional as of 3.0) 0xa000 => '0100', # FlashpixVersion
44
44
  0xa001 => 0xffff, # ColorSpace (uncalibrated)
45
45
  # 0xa002 => ????, # ExifImageWidth
46
46
  # 0xa003 => ????, # ExifImageHeight
@@ -2269,6 +2269,11 @@ NoOverwrite: next if $isNew > 0;
2269
2269
  # build list of offsets to process
2270
2270
  my @offsetList;
2271
2271
  if ($ifd >= 0) {
2272
+ $dirName = $$dirInfo{DirName} || 'unknown';
2273
+ if ($ifd) {
2274
+ $dirName =~ s/\d+$//;
2275
+ $dirName .= $ifd;
2276
+ }
2272
2277
  my $offsetInfo = $offsetInfo[$ifd] or next;
2273
2278
  if ($$offsetInfo{0x111} and $$offsetInfo{0x144}) {
2274
2279
  # SubIFD may contain double-referenced data as both strips and tiles
@@ -546,7 +546,7 @@ sub WritePDF($$)
546
546
  }
547
547
  if ($metaChanged) {
548
548
  if ($newXMP) {
549
- unless ($metaRef) {
549
+ unless (ref $metaRef) {
550
550
  # allocate new PDF object
551
551
  $metaRef = \ "$nextObject 0 R";
552
552
  ++$nextObject;
@@ -891,7 +891,7 @@ sub WriteQuickTime($$$)
891
891
  $et or return 1; # allow dummy access to autoload this package
892
892
  my ($mdat, @mdat, @mdatEdit, $edit, $track, $outBuff, $co, $term, $delCount);
893
893
  my (%langTags, $canCreate, $delGrp, %boxPos, %didDir, $writeLast, $err, $atomCount);
894
- my ($tag, $lastTag, $lastPos, $errStr, $trailer, $buf2, $keysGrp, $keysPath);
894
+ my ($tag, $lastTag, $lastPos, $errStr, $trailer, $buf2, $keysGrp, $keysPath, $itemIndex);
895
895
  my $outfile = $$dirInfo{OutFile} || return 0;
896
896
  my $raf = $$dirInfo{RAF}; # (will be null for lower-level atoms)
897
897
  my $dataPt = $$dirInfo{DataPt}; # (will be null for top-level atoms)
@@ -982,8 +982,25 @@ sub WriteQuickTime($$$)
982
982
  $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT} if $$tagTablePtr{VARS};
983
983
 
984
984
  $tag = $lastTag = '';
985
-
985
+ $itemIndex = 0 if $dirName eq 'ItemPropertyContainer';
986
+
987
+ # read ahead to parse item property associations if this is 'iprp' ItemProperties
988
+ # (necessary to determine which properties belong to primary item in HEIC file)
989
+ if ($dirName eq 'ItemProperties') {
990
+ my $pos = $raf->Tell();
991
+ for (;;) {
992
+ $raf->Read($buf2, 8) == 8 or last;
993
+ my $size = Get32u(\$buf2, 0) - 8; # (atom size without 8-byte header)
994
+ $tag = substr($buf2, 4, 4);
995
+ last if $size < 0;
996
+ $tag eq 'ipma' or $raf->Seek($size, 1), next;
997
+ ParseItemPropAssoc($buf2,$et) if $raf->Read($buf2,$size) == $size;
998
+ last;
999
+ }
1000
+ $raf->Seek($pos);
1001
+ }
986
1002
  for (;;) { # loop through all atoms at this level
1003
+ ++$itemIndex if defined $itemIndex;
987
1004
  $lastPos = $raf->Tell();
988
1005
  # stop processing if we reached a known trailer
989
1006
  if ($trailer and $lastPos >= $$trailer[1]) {
@@ -1186,6 +1203,36 @@ sub WriteQuickTime($$$)
1186
1203
  }
1187
1204
  undef $tagInfo if $tagInfo and $$tagInfo{AddedUnknown};
1188
1205
 
1206
+ # don't write this tag unless associated with the primary item
1207
+ # (Note: This relies on iinf and dimg coming before iprp)
1208
+ if (defined $itemIndex and $$et{ItemInfo}) {
1209
+ my $items = $$et{ItemInfo};
1210
+ my ($id, $prop, $isPrimary);
1211
+ my $primary = $$et{PrimaryItem};
1212
+ unless (defined $primary) {
1213
+ ($primary) = sort { $a <=> $b } keys %{$$et{ItemInfo}} if $$et{ItemInfo};
1214
+ $primary = 0 unless defined $primary;
1215
+ }
1216
+ my $pitem = $$items{$primary} || { };
1217
+ $$pitem{RefersTo} or $$pitem{RefersTo} = { };
1218
+ ItemID2: foreach $id (reverse sort { $a <=> $b } keys %$items) {
1219
+ next unless $$items{$id}{Association};
1220
+ my $item = $$items{$id};
1221
+ foreach $prop (@{$$item{Association}}) {
1222
+ next unless $prop == $itemIndex;
1223
+ my $dont = $dontInherit{$tag} || 0;
1224
+ last unless $id == $primary or (not $dont and
1225
+ ($$item{RefersTo} and $$item{RefersTo}{$primary})) or
1226
+ # special case to assume this property belongs to the primary
1227
+ # image if it belongs to an item referred to by the primary
1228
+ ($dont != 1 and $$pitem{RefersTo}{$id});
1229
+ $isPrimary = 1;
1230
+ last ItemID2;
1231
+ }
1232
+ }
1233
+ undef $tagInfo unless $isPrimary;
1234
+ }
1235
+
1189
1236
  if ($tagInfo and (not defined $$tagInfo{Writable} or $$tagInfo{Writable})) {
1190
1237
  my $subdir = $$tagInfo{SubDirectory};
1191
1238
  my ($newData, @chunkOffset);
@@ -1225,6 +1272,7 @@ sub WriteQuickTime($$$)
1225
1272
  OutFile => $outfile,
1226
1273
  NoRefTest=> 1, # don't check directory references
1227
1274
  WriteGroup => $$tagInfo{WriteGroup},
1275
+ Permanent => $$tagInfo{Permanent},
1228
1276
  # initialize array to hold details about chunk offset table
1229
1277
  # (each entry has 3-5 items: 0=atom type, 1=table offset, 2=table size,
1230
1278
  # 3=optional base offset, 4=optional item ID)
@@ -1468,7 +1516,10 @@ sub WriteQuickTime($$$)
1468
1516
  ++$$et{CHANGED};
1469
1517
  my $grp = $et->GetGroup($langInfo, 1);
1470
1518
  $et->VerboseValue("- $grp:$$langInfo{Name}", $val);
1471
- next unless defined $newData and not $$didTag{$nvHash};
1519
+ unless (defined $newData and not $$didTag{$nvHash}) {
1520
+ # must not delete items from iprp because it will mess up the ordering
1521
+ next unless defined $itemIndex;
1522
+ }
1472
1523
  $et->VerboseValue("+ $grp:$$langInfo{Name}", $newData);
1473
1524
  # add back necessary header and encode as necessary
1474
1525
  if (defined $lang) {
@@ -1497,7 +1548,7 @@ sub WriteQuickTime($$$)
1497
1548
  $newData = $et->Encode($newData, 'UTF8');
1498
1549
  } elsif ($format and not $$tagInfo{Binary}) {
1499
1550
  # format new value for writing
1500
- $newData = WriteValue($newData, $format);
1551
+ $newData = WriteValue($newData, $format, $$tagInfo{Count});
1501
1552
  }
1502
1553
  }
1503
1554
  $$didTag{$nvHash} = 1; # set flag so we don't add this tag again
@@ -1564,7 +1615,9 @@ sub WriteQuickTime($$$)
1564
1615
  }
1565
1616
  if ($msg) {
1566
1617
  # (allow empty sample description for non-audio/video handler types, eg. 'url ', 'meta')
1567
- if ($$et{MediaType}) {
1618
+ # (also, incorrectly written 'mett' SampleEntry by Google phones,
1619
+ # see https://exiftool.org/forum/index.php?msg=91158)
1620
+ if ($avType{$$et{MediaType}}) {
1568
1621
  my $grp = $$et{CUR_WRITE_GROUP} || $parent;
1569
1622
  $et->Error("$msg for $grp");
1570
1623
  return $rtnErr;
@@ -1296,15 +1296,15 @@ sub SetNewValuesFromFile($$;@)
1296
1296
  # ! option to decide how it is handled here. !
1297
1297
  # +------------------------------------------+
1298
1298
  foreach (qw(ByteUnit Charset CharsetEXIF CharsetFileName CharsetID3 CharsetIPTC
1299
- CharsetPhotoshop Composite DateFormat Debug EncodeHangs Escape ExtendedXMP
1300
- ExtractEmbedded FastScan Filter FixBase Geolocation GeolocAltNames
1301
- GeolocFeature GeolocMinPop GeolocMaxDist GlobalTimeShift GPSQuadrant
1302
- HexTagIDs IgnoreGroups IgnoreMinorErrors IgnoreTags ImageHashType Lang
1303
- LargeFileSupport LigoGPSScale ListItem ListSep MDItemTags
1304
- MissingTagValue NoPDFList NoWarning Password PrintConv QuickTimeUTC
1305
- RequestTags SaveFormat SavePath ScanForXMP StructFormat SystemTags
1306
- TimeZone Unknown UserParam Validate WindowsLongPath WindowsWideFile
1307
- XAttrTags XMPAutoConv))
1299
+ CharsetPhotoshop Composite DateFormat Debug EncodeHangs Escape
1300
+ ExtendedXMP ExtractEmbedded FastScan Filter FixBase Geolocation
1301
+ GeolocAltNames GeolocFeature GeolocMinPop GeolocMaxDist
1302
+ GlobalTimeShift GPSQuadrant HexTagIDs IgnoreGroups IgnoreMinorErrors
1303
+ IgnoreTags ImageHashType KeepUTCTime Lang LargeFileSupport
1304
+ LigoGPSScale ListItem ListSep MDItemTags MissingTagValue NoPDFList
1305
+ NoWarning Password PrintConv QuickTimeUTC RequestTags SaveFormat
1306
+ SavePath ScanForXMP StructFormat SystemTags TimeZone Unknown UserParam
1307
+ Validate WindowsLongPath WindowsWideFile XAttrTags XMPAutoConv))
1308
1308
  {
1309
1309
  $srcExifTool->Options($_ => $$options{$_});
1310
1310
  }
@@ -4262,9 +4262,11 @@ sub WriteDirectory($$$;$)
4262
4262
  if ($permanentDir{$grp0} and not ($$dirInfo{TagInfo} and $$dirInfo{TagInfo}{Deletable})) {
4263
4263
  undef $delFlag;
4264
4264
  }
4265
- # (never delete an entire QuickTime group)
4266
4265
  if ($delFlag) {
4267
- if (($grp0 =~ /^(MakerNotes)$/ or $grp1 =~ /^(IFD0|ExifIFD|MakerNotes)$/) and
4266
+ if ($$dirInfo{Permanent}) {
4267
+ $self->Warn("Not deleting permanent $dirName directory");
4268
+ undef $grp1;
4269
+ } elsif (($grp0 =~ /^(MakerNotes)$/ or $grp1 =~ /^(IFD0|ExifIFD|MakerNotes)$/) and
4268
4270
  $self->IsRawType() and
4269
4271
  # allow non-permanent MakerNote directories to be deleted (ie. NikonCapture)
4270
4272
  (not $$dirInfo{TagInfo} or not defined $$dirInfo{TagInfo}{Permanent} or
@@ -5044,7 +5046,6 @@ TryLib: for ($lib=$strptimeLib; ; $lib='') {
5044
5046
  last;
5045
5047
  }
5046
5048
  if (not $lib) {
5047
- last unless $$self{OPTIONS}{StrictDate};
5048
5049
  warn $wrn || "Install POSIX::strptime or Time::Piece for inverse date/time conversions\n";
5049
5050
  return undef;
5050
5051
  } elsif ($lib eq 'POSIX::strptime') {
@@ -6027,7 +6028,7 @@ sub WriteJPEG($$)
6027
6028
  Write($outfile, $hdr, $s, $segData) or $err = 1;
6028
6029
  my ($buff, $endPos, $trailInfo);
6029
6030
  my $delPreview = $$self{DEL_PREVIEW};
6030
- $trailInfo = IdentifyTrailer($raf) unless $$delGroup{Trailer};
6031
+ $trailInfo = $self->IdentifyTrailer($raf) unless $$delGroup{Trailer};
6031
6032
  my $nvTrail = $self->GetNewValueHash($Image::ExifTool::Extra{Trailer});
6032
6033
  unless ($oldOutfile or $delPreview or $trailInfo or $$delGroup{Trailer} or $nvTrail or
6033
6034
  $$self{HiddenData})
@@ -50,7 +50,7 @@ use Image::ExifTool::Exif;
50
50
  use Image::ExifTool::GPS;
51
51
  require Exporter;
52
52
 
53
- $VERSION = '3.70';
53
+ $VERSION = '3.72';
54
54
  @ISA = qw(Exporter);
55
55
  @EXPORT_OK = qw(EscapeXML UnescapeXML);
56
56
 
@@ -165,7 +165,7 @@ my %xmpNS = (
165
165
  #
166
166
  plus => 'http://ns.useplus.org/ldf/xmp/1.0/',
167
167
  # (prism recommendations from http://www.prismstandard.org/specifications/3.0/Image_Guide_3.0.htm)
168
- prism => 'http://prismstandard.org/namespaces/basic/2.0/',
168
+ prism => 'http://prismstandard.org/namespaces/basic/2.0/', # (maybe left at 2.0 to avoid compatibility issues -- think hard before changing this)
169
169
  prl => 'http://prismstandard.org/namespaces/prl/2.1/',
170
170
  pur => 'http://prismstandard.org/namespaces/prismusagerights/2.1/',
171
171
  pmi => 'http://prismstandard.org/namespaces/pmi/2.2/',
@@ -1247,8 +1247,22 @@ my %sPantryItem = (
1247
1247
  NAMESPACE => 'pdfx',
1248
1248
  NOTES => q{
1249
1249
  PDF extension tags. This namespace is used to store application-defined PDF
1250
- information, so there are no pre-defined tags. User-defined tags must be
1251
- created to enable writing of XMP-pdfx information.
1250
+ information, so there are few pre-defined tags. User-defined tags must be
1251
+ created to enable writing of other XMP-pdfx information.
1252
+ },
1253
+ SourceModified => {
1254
+ Name => 'SourceModified',
1255
+ Groups => { 2 => 'Time' },
1256
+ Shift => 'Time',
1257
+ ValueConv => 'require Image::ExifTool::PDF; $val = Image::ExifTool::PDF::ConvertPDFDate($val)',
1258
+ ValueConvInv => q{
1259
+ require Image::ExifTool::PDF;
1260
+ $val = Image::ExifTool::PDF::WritePDFValue($self,$val,"date");
1261
+ $val =~ tr/()//d;
1262
+ return $val;
1263
+ },
1264
+ PrintConv => '$self->ConvertDateTime($val)',
1265
+ PrintConvInv => '$self->InverseDateTime($val)',
1252
1266
  },
1253
1267
  );
1254
1268
 
@@ -3804,8 +3818,22 @@ sub ParseXMPElement($$$;$$$$)
3804
3818
  my ($parseResource, %attrs, @attrs);
3805
3819
  # this hangs Perl (v5.18.4) for a specific capture string [patched in ExifTool 12.98]
3806
3820
  # while ($attrs =~ m/(\S+?)\s*=\s*(['"])(.*?)\2/sg) {
3807
- while ($attrs =~ /(\S+?)\s*=\s*(['"])/g) {
3808
- my ($attr, $quote) = ($1, $2);
3821
+ # this may hang Perl v5.26.3 (but not v5.18.4) if there is lots of garbage in the XMP [patched in 13.23]
3822
+ # while ($attrs =~ /(\S+?)\s*=\s*(['"])/g) {
3823
+ for (;;) {
3824
+ my ($attr, $quote);
3825
+ if (length($attrs) < 2000) { # (do it the easy way if attributes aren't stupid long)
3826
+ last unless $attrs =~ /(\S+)\s*=\s*(['"])/g;
3827
+ ($attr, $quote) = ($1, $2);
3828
+ } else {
3829
+ # 13.23 patch to avoid capturing tons of garbage if XMP is corrupted
3830
+ last unless $attrs =~ /=\s*(['"])/g;
3831
+ $quote = $1;
3832
+ my $p = pos($attrs) > 1000 ? pos($attrs) - 1000 : 0;
3833
+ my $tmp = substr($attrs, $p, pos($attrs)-$p);
3834
+ last unless $tmp =~ /(\S+)\s*=\s*$quote$/;
3835
+ $attr = $1;
3836
+ }
3809
3837
  my $p0 = pos($attrs);
3810
3838
  last unless $attrs =~ /$quote/g;
3811
3839
  my $val = substr($attrs, $p0, pos($attrs)-$p0-1);
@@ -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 {
@@ -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.17';
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) {
@@ -6900,6 +6916,9 @@ sub IdentifyTrailer($;$)
6900
6916
  $type = 'Vivo';
6901
6917
  } elsif ($buff =~ /jxrs...\0$/s) {
6902
6918
  $type = 'OnePlus';
6919
+ } elsif ($$self{ProcessGoogleTrailer}) {
6920
+ # check for Google trailer information if specific XMP tags exist
6921
+ $type = 'Google';
6903
6922
  }
6904
6923
  last;
6905
6924
  }
@@ -7052,7 +7071,7 @@ sub ProcessTrailers($$)
7052
7071
  $offset += $dirLen;
7053
7072
  last if $dataPos and $$self{TrailerStart} and $dataPos <= $$self{TrailerStart};
7054
7073
  # look for next trailer
7055
- my $nextTrail = IdentifyTrailer($raf, $offset);
7074
+ my $nextTrail = $self->IdentifyTrailer($raf, $offset);
7056
7075
  # process Google trailer after all others if necessary and not done already
7057
7076
  unless ($nextTrail) {
7058
7077
  last unless $$self{ProcessGoogleTrailer};
@@ -7504,11 +7523,7 @@ sub ProcessJPEG($$;$)
7504
7523
  }
7505
7524
  }
7506
7525
  unless ($fast) {
7507
- $trailInfo = IdentifyTrailer($raf);
7508
- # check for Google trailer information if specific XMP tags exist
7509
- if (not $trailInfo and $$self{ProcessGoogleTrailer}) {
7510
- $trailInfo = { DirName => 'Google', RAF => $raf };
7511
- }
7526
+ $trailInfo = $self->IdentifyTrailer($raf);
7512
7527
  # process trailer now unless we are doing verbose dump
7513
7528
  if ($trailInfo and $verbose < 3 and not $htmlDump) {
7514
7529
  # process trailers (keep trailInfo to finish processing later
@@ -7963,6 +7978,12 @@ sub ProcessJPEG($$;$)
7963
7978
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7964
7979
  undef $scalado;
7965
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);
7966
7987
  } elsif ($$segDataPt =~ /^FPXR\0/) {
7967
7988
  next if $fast > 1; # skip processing for very fast
7968
7989
  $dumpType = 'FPXR';
@@ -8619,7 +8640,7 @@ sub DoProcessTIFF($$;$)
8619
8640
  }
8620
8641
  # process information in recognized trailers
8621
8642
  if ($raf) {
8622
- my $trailInfo = IdentifyTrailer($raf);
8643
+ my $trailInfo = $self->IdentifyTrailer($raf);
8623
8644
  if ($trailInfo) {
8624
8645
  # scan to find AFCP if necessary (Note: we are scanning
8625
8646
  # from a random file position in the TIFF)
@@ -8724,7 +8745,7 @@ sub DoProcessTIFF($$;$)
8724
8745
  for (;;) {
8725
8746
  last unless $extra > 12;
8726
8747
  $raf->Seek($tiffEnd); # seek back to end of image
8727
- $trailInfo = IdentifyTrailer($raf);
8748
+ $trailInfo = $self->IdentifyTrailer($raf);
8728
8749
  last unless $trailInfo;
8729
8750
  my $tbuf = '';
8730
8751
  $$trailInfo{OutFile} = \$tbuf; # rewrite trailer(s)
@@ -8941,7 +8962,7 @@ sub ProcessDirectory($$$;$)
8941
8962
  ($$dirInfo{DirLen} or not defined $$dirInfo{DirLen}))
8942
8963
  {
8943
8964
  my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0) + $$self{BASE};
8944
- if ($$self{PROCESSED}{$addr}) {
8965
+ if ($$self{PROCESSED}{$addr} and not $$dirInfo{NotDup}) {
8945
8966
  $self->Warn("$dirName pointer references previous $$self{PROCESSED}{$addr} directory");
8946
8967
  # patch for bug in Windows phone 7.5 O/S that writes incorrect InteropIFD pointer
8947
8968
  return 0 unless $dirName eq 'GPS' and $$self{PROCESSED}{$addr} eq 'InteropIFD';
@@ -9152,10 +9173,11 @@ sub AddTagToTable($$;$$)
9152
9173
  # Handle simple extraction of new tag information
9153
9174
  # Inputs: 0) ExifTool object ref, 1) tag table reference, 2) tagID, 3) value,
9154
9175
  # 4-N) parameters hash: Index, DataPt, DataPos, Base, Start, Size, Parent,
9155
- # TagInfo, ProcessProc, RAF, Format, Count
9176
+ # TagInfo, ProcessProc, RAF, Format, Count, MakeTagInfo
9156
9177
  # Returns: tag key or undef if tag not found
9157
9178
  # Notes: if value is not defined, it is extracted from DataPt using TagInfo
9158
9179
  # Format and Count if provided
9180
+ # - set MakeTagInfo to add tag info for unknown tags with name made from tag ID
9159
9181
  sub HandleTag($$$$;%)
9160
9182
  {
9161
9183
  my ($self, $tagTablePtr, $tag, $val, %parms) = @_;
@@ -9167,6 +9189,15 @@ sub HandleTag($$$$;%)
9167
9189
 
9168
9190
  if ($tagInfo) {
9169
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);
9170
9201
  } else {
9171
9202
  return undef unless $verbose;
9172
9203
  $tagInfo = { Name => "tag $tag" }; # create temporary tagInfo hash
@@ -9224,10 +9255,11 @@ sub HandleTag($$$$;%)
9224
9255
  }
9225
9256
  $self->Warn("RawConv $tag: " . CleanWarning()) if $evalWarning;
9226
9257
  return undef unless defined $val;
9227
- $val = $$val if ref $val eq 'SCALAR';
9228
- $dataPt = \$val;
9258
+ $dataPt = ref $val eq 'SCALAR' ? $val : \$val;
9229
9259
  $subdirStart = 0;
9230
- $subdirLen = length $val;
9260
+ $subdirLen = length $$dataPt;
9261
+ } elsif (not $dataPt) {
9262
+ $dataPt = ref $val eq 'SCALAR' ? $val : \$val;
9231
9263
  }
9232
9264
  if ($$subdir{Start}) {
9233
9265
  my $valuePtr = 0;
@@ -9236,7 +9268,6 @@ sub HandleTag($$$$;%)
9236
9268
  $subdirStart += $off;
9237
9269
  $subdirLen -= $off;
9238
9270
  }
9239
- $dataPt or $dataPt = \$val;
9240
9271
  # process subdirectory information
9241
9272
  my %dirInfo = (
9242
9273
  DirName => $$subdir{DirName} || $$tagInfo{Name},
@@ -9957,6 +9988,7 @@ sub ProcessBinaryData($$$)
9957
9988
  $subdirBase = eval($$subdir{Base}) + $base;
9958
9989
  }
9959
9990
  my $start = $$subdir{Start} || 0;
9991
+ my $notDup;
9960
9992
  if ($start =~ /\$/) {
9961
9993
  # ignore directories with a zero offset (ie. missing Nikon ShotInfo entries)
9962
9994
  next unless $val;
@@ -9967,6 +9999,7 @@ sub ProcessBinaryData($$$)
9967
9999
  $len = $dataLen - $start unless $len and $len <= $dataLen - $start;
9968
10000
  } else {
9969
10001
  $start += $dirStart + $entry;
10002
+ $notDup = 1,
9970
10003
  }
9971
10004
  my %subdirInfo = (
9972
10005
  DataPt => $dataPt,
@@ -9975,6 +10008,7 @@ sub ProcessBinaryData($$$)
9975
10008
  DirStart => $start,
9976
10009
  DirLen => $len,
9977
10010
  Base => $subdirBase,
10011
+ NotDup => $notDup,
9978
10012
  );
9979
10013
  delete $$self{NO_UNKNOWN};
9980
10014
  $self->ProcessDirectory(\%subdirInfo, $subTablePtr, $$subdir{ProcessProc});