exiftool_vendored 12.60.0 → 12.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,7 @@ use strict;
16
16
  use vars qw($VERSION);
17
17
  use Image::ExifTool qw(:DataAccess :Utils);
18
18
 
19
- $VERSION = '1.33';
19
+ $VERSION = '1.34';
20
20
 
21
21
  sub ProcessJpeg2000Box($$$);
22
22
  sub ProcessJUMD($$$);
@@ -34,6 +34,9 @@ my %resolutionUnit = (
34
34
  6 => 'um',
35
35
  );
36
36
 
37
+ # top-level boxes containing image data
38
+ my %isImageData = ( jp2c=>1, jbrd=>1, jxlp=>1, jxlc=>1 );
39
+
37
40
  # map of where information is written in JPEG2000 image
38
41
  my %jp2Map = (
39
42
  IPTC => 'UUID-IPTC',
@@ -428,6 +431,7 @@ my %j2cMarker = (
428
431
  # stuff seen in JPEG XL images:
429
432
  #
430
433
  # jbrd - JPEG Bitstream Reconstruction Data (allows lossless conversion back to original JPG)
434
+ # jxlp - partial JXL codestream
431
435
  jxlc => {
432
436
  Name => 'JXLCodestream',
433
437
  Format => 'undef',
@@ -930,7 +934,7 @@ sub ProcessJpeg2000Box($$$)
930
934
  my $raf = $$dirInfo{RAF};
931
935
  my $outfile = $$dirInfo{OutFile};
932
936
  my $dirEnd = $dirStart + $dirLen;
933
- my ($err, $outBuff, $verbose, $doColour);
937
+ my ($err, $outBuff, $verbose, $doColour, $md5);
934
938
 
935
939
  if ($outfile) {
936
940
  unless ($raf) {
@@ -948,6 +952,8 @@ sub ProcessJpeg2000Box($$$)
948
952
  # (must not set verbose flag when writing!)
949
953
  $verbose = $$et{OPTIONS}{Verbose};
950
954
  $et->VerboseDir($$dirInfo{DirName}) if $verbose;
955
+ # do MD5 if requested, but only for top-level image data
956
+ $md5 = $$et{ImageDataMD5} if $raf;
951
957
  }
952
958
  # loop through all contained boxes
953
959
  my ($pos, $boxLen, $lastBox);
@@ -971,6 +977,11 @@ sub ProcessJpeg2000Box($$$)
971
977
  }
972
978
  $boxLen = unpack("x$pos N",$$dataPt); # (length includes header and data)
973
979
  $boxID = substr($$dataPt, $pos+4, 4);
980
+ # (ftbl box contains flst boxes with absolute file offsets, not currently handled)
981
+ if ($outfile and $boxID eq 'ftbl') {
982
+ $et->Error("Can't yet handle fragmented JPX files");
983
+ return -1;
984
+ }
974
985
  # remove old colr boxes if necessary
975
986
  if ($doColour and $boxID eq 'colr') {
976
987
  if ($doColour == 1) { # did we successfully write the new colr box?
@@ -1007,9 +1018,14 @@ sub ProcessJpeg2000Box($$$)
1007
1018
  while ($raf->Read($buff, 65536)) {
1008
1019
  Write($outfile, $buff) or $err = 1;
1009
1020
  }
1010
- } elsif ($verbose) {
1011
- my $msg = sprintf("offset 0x%.4x to end of file", $dataPos + $base + $pos);
1012
- $et->VPrint(0, "$$et{INDENT}- Tag '${boxID}' ($msg)\n");
1021
+ } else {
1022
+ if ($verbose) {
1023
+ my $msg = sprintf("offset 0x%.4x to end of file", $dataPos + $base + $pos);
1024
+ $et->VPrint(0, "$$et{INDENT}- Tag '${boxID}' ($msg)\n");
1025
+ }
1026
+ if ($md5 and $isImageData{$boxID}) {
1027
+ $et->ImageDataMD5($raf, undef, $boxID);
1028
+ }
1013
1029
  }
1014
1030
  last; # (ignore the rest of the file when reading)
1015
1031
  }
@@ -1026,6 +1042,8 @@ sub ProcessJpeg2000Box($$$)
1026
1042
  Write($outfile, $$dataPt) or $err = 1;
1027
1043
  $raf->Read($buff,$boxLen) == $boxLen or $err = '', last;
1028
1044
  Write($outfile, $buff) or $err = 1;
1045
+ } elsif ($md5 and $isImageData{$boxID}) {
1046
+ $et->ImageDataMD5($raf, $boxLen, $boxID);
1029
1047
  } else {
1030
1048
  $raf->Seek($boxLen, 1) or $err = 'Seek error', last;
1031
1049
  }
@@ -1038,6 +1056,10 @@ sub ProcessJpeg2000Box($$$)
1038
1056
  # read the box data
1039
1057
  $dataPos = $raf->Tell() - $base;
1040
1058
  $raf->Read($buff,$boxLen) == $boxLen or $err = '', last;
1059
+ if ($md5 and $isImageData{$boxID}) {
1060
+ $md5->add($buff);
1061
+ $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $boxLen bytes of $boxID data)\n");
1062
+ }
1041
1063
  $valuePtr = 0;
1042
1064
  $dataLen = $boxLen;
1043
1065
  } elsif ($pos + $boxLen > $dirEnd) {
@@ -1311,7 +1333,7 @@ sub ProcessJP2($$)
1311
1333
  }
1312
1334
 
1313
1335
  #------------------------------------------------------------------------------
1314
- # Read meta information from a JPEG XL image
1336
+ # Read/write meta information in a JPEG XL image
1315
1337
  # Inputs: 0) ExifTool object reference, 1) dirInfo reference
1316
1338
  # Returns: 1 on success, 0 if this wasn't a valid JPEG XL file, -1 on write error
1317
1339
  sub ProcessJXL($$)
@@ -1340,6 +1362,9 @@ sub ProcessJXL($$)
1340
1362
  $$dirInfo{RAF} = new File::RandomAccess(\$buff);
1341
1363
  } else {
1342
1364
  $et->SetFileType('JXL Codestream','image/jxl', 'jxl');
1365
+ if ($$et{ImageDataMD5} and $raf->Seek(0,0)) {
1366
+ $et->ImageDataMD5($raf, undef, 'JXL');
1367
+ }
1343
1368
  return ProcessJXLCodestream($et, \$hdr);
1344
1369
  }
1345
1370
  } else {
@@ -17,7 +17,7 @@ use vars qw($VERSION);
17
17
  use Image::ExifTool qw(:DataAccess :Utils);
18
18
  use Image::ExifTool::Minolta;
19
19
 
20
- $VERSION = '1.17';
20
+ $VERSION = '1.18';
21
21
 
22
22
  sub ProcessMRW($$;$);
23
23
  sub WriteMRW($$;$);
@@ -489,7 +489,7 @@ sub ProcessMRW($$;$)
489
489
  $err and $et->Error("MRW format error", $$et{TIFF_TYPE} eq 'ARW');
490
490
  } else {
491
491
  $err and $et->Warn("MRW format error");
492
- $et->ImageDataMD5($raf, undef, 'raw');
492
+ $et->ImageDataMD5($raf, undef, 'raw') unless $$et{A100DataOffset};
493
493
  }
494
494
  return $rtnVal;
495
495
  }
@@ -64,7 +64,7 @@ use Image::ExifTool::Exif;
64
64
  use Image::ExifTool::GPS;
65
65
  use Image::ExifTool::XMP;
66
66
 
67
- $VERSION = '4.21';
67
+ $VERSION = '4.22';
68
68
 
69
69
  sub LensIDConv($$$);
70
70
  sub ProcessNikonAVI($$$);
@@ -2300,8 +2300,8 @@ my %base64coord = (
2300
2300
  },
2301
2301
  },
2302
2302
  { # (Z6_2 firmware version 1.00 and Z7II firmware versions 1.00 & 1.01, ref 28)
2303
- # 0800=Z6/Z7 0801=Z50 0802=Z5 0803=Z6II/Z7II 0804=Zfc
2304
- Condition => '$$valPt =~ /^080[01234]/',
2303
+ # 0800=Z6/Z7 0801=Z50 0802=Z5 0803=Z6II/Z7II 0804=Zfc 0807=Z30
2304
+ Condition => '$$valPt =~ /^080[012347]/',
2305
2305
  Name => 'ShotInfoZ7II',
2306
2306
  SubDirectory => {
2307
2307
  TagTable => 'Image::ExifTool::Nikon::ShotInfoZ7II',
@@ -21,7 +21,7 @@ use vars qw($VERSION $AUTOLOAD $lastFetched);
21
21
  use Image::ExifTool qw(:DataAccess :Utils);
22
22
  require Exporter;
23
23
 
24
- $VERSION = '1.55';
24
+ $VERSION = '1.56';
25
25
 
26
26
  sub FetchObject($$$$);
27
27
  sub ExtractObject($$;$$);
@@ -41,7 +41,7 @@ my $cryptStream; # flag that streams are encrypted
41
41
  my $lastOffset; # last fetched object offset
42
42
  my %streamObjs; # hash of stream objects
43
43
  my %fetched; # dicts fetched in verbose mode (to avoid cyclical recursion)
44
- my $pdfVer; # version of PDF file being processed
44
+ my $pdfVer; # version of PDF file being processed (from header)
45
45
 
46
46
  # filters supported in DecodeStream()
47
47
  my %supportedFilter = (
@@ -115,6 +115,7 @@ my %supportedFilter = (
115
115
  CreationDate => {
116
116
  Name => 'CreateDate',
117
117
  Writable => 'date',
118
+ PDF2 => 1, # not deprecated in PDF 2.0
118
119
  Groups => { 2 => 'Time' },
119
120
  Shift => 'Time',
120
121
  PrintConv => '$self->ConvertDateTime($val)',
@@ -123,6 +124,7 @@ my %supportedFilter = (
123
124
  ModDate => {
124
125
  Name => 'ModifyDate',
125
126
  Writable => 'date',
127
+ PDF2 => 1, # not deprecated in PDF 2.0
126
128
  Groups => { 2 => 'Time' },
127
129
  Shift => 'Time',
128
130
  PrintConv => '$self->ConvertDateTime($val)',
@@ -168,7 +170,10 @@ my %supportedFilter = (
168
170
  Lang => 'Language',
169
171
  PageLayout => { },
170
172
  PageMode => { },
171
- Version => 'PDFVersion',
173
+ Version => {
174
+ Name => 'PDFVersion',
175
+ RawConv => '$$self{PDFVersion} = $val if $$self{PDFVersion} < $val; $val',
176
+ },
172
177
  );
173
178
 
174
179
  # tags extracted from the PDF Encrypt dictionary
@@ -1754,7 +1759,7 @@ sub ProcessDict($$$$;$$)
1754
1759
  my $unknown = $$tagTablePtr{EXTRACT_UNKNOWN};
1755
1760
  my $embedded = (defined $unknown and not $unknown and $et->Options('ExtractEmbedded'));
1756
1761
  my @tags = @{$$dict{_tags}};
1757
- my ($next, %join);
1762
+ my ($next, %join, $validInfo);
1758
1763
  my $index = 0;
1759
1764
 
1760
1765
  $nesting = ($nesting || 0) + 1;
@@ -1775,6 +1780,7 @@ sub ProcessDict($$$$;$$)
1775
1780
  last;
1776
1781
  }
1777
1782
  }
1783
+ $validInfo = ($et->Options('Validate') and $tagTablePtr eq \%Image::ExifTool::PDF::Info);
1778
1784
  #
1779
1785
  # extract information from all tags in the dictionary
1780
1786
  #
@@ -1810,6 +1816,10 @@ sub ProcessDict($$$$;$$)
1810
1816
  $isSubDoc = 1; # treat as a sub-document
1811
1817
  }
1812
1818
  }
1819
+ if ($validInfo and $$et{PDFVersion} >= 2.0 and (not $tagInfo or not $$tagInfo{PDF2})) {
1820
+ my $name = $tagInfo ? ":$$tagInfo{Name}" : " Info tag '${tag}'";
1821
+ $et->Warn("PDF$name is deprecated in PDF 2.0");
1822
+ }
1813
1823
  if ($verbose) {
1814
1824
  my ($val2, $extra);
1815
1825
  if (ref $val eq 'SCALAR') {
@@ -2118,9 +2128,8 @@ sub ReadPDF($$)
2118
2128
  $raf->Read($buff, 1024) >= 8 or return 0;
2119
2129
  $buff =~ /^(\s*)%PDF-(\d+\.\d+)/ or return 0;
2120
2130
  $$et{PDFBase} = length $1 and $et->Warn('PDF header is not at start of file',1);
2121
- $pdfVer = $2;
2131
+ $pdfVer = $$et{PDFVersion} = $2;
2122
2132
  $et->SetFileType(); # set the FileType tag
2123
- $et->Warn("The PDF $pdfVer specification is held hostage by the ISO") if $pdfVer >= 2.0;
2124
2133
  # store PDFVersion tag
2125
2134
  my $tagTablePtr = GetTagTable('Image::ExifTool::PDF::Root');
2126
2135
  $et->HandleTag($tagTablePtr, 'Version', $pdfVer);
@@ -2384,8 +2393,8 @@ This module is loaded automatically by Image::ExifTool when required.
2384
2393
  This code reads meta information from PDF (Adobe Portable Document Format)
2385
2394
  files. It supports object streams introduced in PDF-1.5 but only with a
2386
2395
  limited set of Filter and Predictor algorithms, however all standard
2387
- encryption methods through PDF-1.7 extension level 3 are supported,
2388
- including AESV2 (AES-128) and AESV3 (AES-256).
2396
+ encryption methods through PDF-2.0 are supported, including AESV2 (AES-128)
2397
+ and AESV3 (AES-256).
2389
2398
 
2390
2399
  =head1 AUTHOR
2391
2400
 
@@ -47,7 +47,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
47
47
  use Image::ExifTool::Exif;
48
48
  use Image::ExifTool::GPS;
49
49
 
50
- $VERSION = '2.84';
50
+ $VERSION = '2.85';
51
51
 
52
52
  sub ProcessMOV($$;$);
53
53
  sub ProcessKeys($$$);
@@ -470,6 +470,9 @@ my %eeBox2 = (
470
470
  vide => { avcC => 'stsd' }, # (parses H264 video stream)
471
471
  );
472
472
 
473
+ # image types in AVIF and HEIC files
474
+ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
475
+
473
476
  # QuickTime atoms
474
477
  %Image::ExifTool::QuickTime::Main = (
475
478
  PROCESS_PROC => \&ProcessMOV,
@@ -2884,7 +2887,7 @@ my %eeBox2 = (
2884
2887
  7 => 'SMPTE 240',
2885
2888
  8 => 'Generic film (color filters using illuminant C)',
2886
2889
  9 => 'BT.2020, BT.2100',
2887
- 10 => 'SMPTE 428 (CIE 1921 XYZ)',
2890
+ 10 => 'SMPTE 428 (CIE 1931 XYZ)', #forum14766
2888
2891
  11 => 'SMPTE RP 431-2',
2889
2892
  12 => 'SMPTE EG 432-1',
2890
2893
  22 => 'EBU Tech. 3213-E',
@@ -8784,15 +8787,15 @@ sub HandleItemInfo($)
8784
8787
  $et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
8785
8788
  }
8786
8789
  }
8787
- # do MD5 checksum of AVIF "av01" image data
8788
- if ($type eq 'av01' and $$et{ImageDataMD5}) {
8790
+ # do MD5 checksum of AVIF "av01" and HEIC image data
8791
+ if ($isImageData{$type} and $$et{ImageDataMD5}) {
8789
8792
  my $md5 = $$et{ImageDataMD5};
8790
8793
  my $tot = 0;
8791
8794
  foreach $extent (@{$$item{Extents}}) {
8792
- $raf->Seek($$extent[1] + $base, 0) or $et->Warn('Seek error in av01 image data'), last;
8793
- $tot += $et->ImageDataMD5($raf, $$extent[2], 'av01 image', 1);
8795
+ $raf->Seek($$extent[1] + $base, 0) or $et->Warn("Seek error in $type image data"), last;
8796
+ $tot += $et->ImageDataMD5($raf, $$extent[2], "$type image", 1);
8794
8797
  }
8795
- $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $tot bytes of av01 data)\n") if $tot;
8798
+ $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $tot bytes of $type data)\n") if $tot;
8796
8799
  }
8797
8800
  next unless $name;
8798
8801
  # assemble the data for this item
@@ -28,7 +28,7 @@ sub Process360Fly($$$);
28
28
  sub ProcessFMAS($$$);
29
29
  sub ProcessCAMM($$$);
30
30
 
31
- my $debug; # set to 1 for extra debugging messages
31
+ my $debug; # set to 'tEST' (all caps) for extra debugging messages
32
32
 
33
33
  # QuickTime data types that have ExifTool equivalents
34
34
  # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35)
@@ -89,6 +89,7 @@ my %insvDataLen = (
89
89
  0x600 => 8, # timestamps (ref 6)
90
90
  0x700 => 53, # GPS
91
91
  # 0x900 => 48, # ? (Insta360 X3)
92
+ # 0xa00 => 5?, # ? (Insta360 ONE RS)
92
93
  # 0xb00 => 10, # ? (Insta360 X3)
93
94
  );
94
95
 
@@ -1134,6 +1135,7 @@ sub ProcessSamples($)
1134
1135
  my $et = shift;
1135
1136
  my ($raf, $ee) = @$et{qw(RAF ee)};
1136
1137
  my ($i, $buff, $pos, $hdrLen, $hdrFmt, @time, @dur, $oldIndent, $md5);
1138
+ my ($mdatOffset, $mdatSize); # (for range-checking samples when MD5 is done)
1137
1139
 
1138
1140
  return unless $ee;
1139
1141
  delete $$et{ee}; # use only once
@@ -1230,6 +1232,10 @@ Sample: for ($i=0; ; ) {
1230
1232
  $oldIndent = $$et{INDENT};
1231
1233
  $$et{INDENT} = '';
1232
1234
  }
1235
+ if ($md5) {
1236
+ $mdatSize = $$et{MediaDataSize};
1237
+ $mdatOffset = $$et{MediaDataOffset} if defined $mdatSize;
1238
+ }
1233
1239
  # get required information from avcC box if parsing video data
1234
1240
  if ($type eq 'avcC') {
1235
1241
  $hdrLen = (Get8u(\$$ee{avcC}, 4) & 0x03) + 1;
@@ -1243,10 +1249,25 @@ Sample: for ($i=0; ; ) {
1243
1249
  delete $$et{FoundGPSLatitude};
1244
1250
  delete $$et{FoundGPSDateTime};
1245
1251
 
1246
- # read the sample data
1252
+ # range check the sample data for MD5 if necessary
1247
1253
  my $size = $$size[$i];
1248
- next unless $raf->Seek($$start[$i], 0) and $raf->Read($buff, $size) == $size;
1249
-
1254
+ if (defined $mdatOffset) {
1255
+ if ($$start[$i] < $mdatOffset) {
1256
+ $et->Warn("Sample $i for '${type}' data is before start of mdat");
1257
+ } elsif ($$start[$i] + $size > $mdatOffset + $mdatSize) {
1258
+ $et->Warn("Sample $i for '${type}' data runs off end of mdat");
1259
+ $size = $mdatOffset + $mdatSize - $$start[$i];
1260
+ $size = 0 if $size < 0;
1261
+ }
1262
+ }
1263
+ # read the sample data
1264
+ $raf->Seek($$start[$i], 0) or $et->WarnOnce("Seek error in $type data"), next;
1265
+ my $n = $raf->Read($buff, $size);
1266
+ unless ($n == $size) {
1267
+ $et->WarnOnce("Error reading $type data");
1268
+ next unless $n;
1269
+ $size = $n;
1270
+ }
1250
1271
  if ($md5) {
1251
1272
  $md5->add($buff);
1252
1273
  $md5size += length $buff;
@@ -1455,16 +1476,15 @@ sub ProcessFreeGPS($$$)
1455
1476
  $et->VerboseDump(\$buf2);
1456
1477
  }
1457
1478
  # (extract longitude as 9 digits, not 8, ref PH)
1458
- return 0 unless $buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})([NS])(\d{8})([EW])(\d{9})(\d{8})?/s;
1459
- ($yr,$mon,$day,$hr,$min,$sec,$lbl,$latRef,$lat,$lonRef,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8,$9/1e4,$10,$11/1e4,$12);
1460
- if (defined $spd) { # (Azdome)
1461
- $spd += 0; # remove leading 0's
1462
- } elsif ($buf2 =~ /^.{57}([-+]\d{4})(\d{3})/s) { # (EEEkit)
1463
- # $alt = $1 + 0; (doesn't look right for my sample, but the Ambarella A12 text has this)
1464
- $spd = $2 + 0;
1465
- }
1466
- $lbl =~ s/\0.*//s; $lbl =~ s/\s+$//; # truncate at null and remove trailing spaces
1467
- push @xtra, UserLabel => $lbl if length $lbl;
1479
+ if ($buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})([NS])(\d{8})([EW])(\d{9})(\d{8})?/s) {
1480
+ ($yr,$mon,$day,$hr,$min,$sec,$lbl,$latRef,$lat,$lonRef,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8,$9/1e4,$10,$11/1e4,$12);
1481
+ if (defined $spd) { # (Azdome)
1482
+ $spd += 0; # remove leading 0's
1483
+ } elsif ($buf2 =~ /^.{57}([-+]\d{4})(\d{3})/s) { # (EEEkit)
1484
+ # $alt = $1 + 0; (doesn't look right for my sample, but the Ambarella A12 text has this)
1485
+ $spd = $2 + 0;
1486
+ }
1487
+ }
1468
1488
  # extract accelerometer data (ref PH)
1469
1489
  if ($buf2 =~ /^.{65}(([-+]\d{3})([-+]\d{3})([-+]\d{3})([-+]\d{3})*)/s) {
1470
1490
  $_ = $1;
@@ -1472,7 +1492,15 @@ sub ProcessFreeGPS($$$)
1472
1492
  s/([-+])/ $1/g; s/^ //;
1473
1493
  push @xtra, AccelerometerData => $_;
1474
1494
  } elsif ($buf2 =~ /^.{173}([-+]\d{3})([-+]\d{3})([-+]\d{3})/s) { # (Azdome)
1495
+ # (Adzome may contain acc and date/time/label even if GPS doesn't exist)
1475
1496
  @acc = ($1/100, $2/100, $3/100);
1497
+ if (not defined $yr and $buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})/s) {
1498
+ ($yr,$mon,$day,$hr,$min,$sec,$lbl) = ($1,$2,$3,$4,$5,$6,$7);
1499
+ }
1500
+ }
1501
+ if (defined $lbl) {
1502
+ $lbl =~ s/\0.*//s; $lbl =~ s/\s+$//; # truncate at null and remove trailing spaces
1503
+ push @xtra, UserLabel => $lbl if length $lbl;
1476
1504
  }
1477
1505
 
1478
1506
  } elsif ($$dataPt =~ /^.{52}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/s) {
@@ -1741,8 +1769,6 @@ sub ProcessFreeGPS($$$)
1741
1769
  # save tag values extracted by above code
1742
1770
  #
1743
1771
  FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
1744
- # lat/long are in DDDMM.MMMM format
1745
- ConvertLatLon($lat, $lon) unless $ddd;
1746
1772
  $sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
1747
1773
  if (defined $yr) {
1748
1774
  my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$yr,$mon,$day,$hr,$min,$sec);
@@ -1751,8 +1777,12 @@ sub ProcessFreeGPS($$$)
1751
1777
  my $time = sprintf('%.2d:%.2d:%sZ',$hr,$min,$sec);
1752
1778
  $et->HandleTag($tagTbl, GPSTimeStamp => $time);
1753
1779
  }
1754
- $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
1755
- $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
1780
+ if (defined $lat) {
1781
+ # lat/long are in DDDMM.MMMM format unless $ddd is set
1782
+ ConvertLatLon($lat, $lon) unless $ddd;
1783
+ $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
1784
+ $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
1785
+ }
1756
1786
  $et->HandleTag($tagTbl, GPSAltitude => $alt) if defined $alt;
1757
1787
  $et->HandleTag($tagTbl, GPSSpeed => $spd) if defined $spd;
1758
1788
  $et->HandleTag($tagTbl, GPSTrack => $trk) if defined $trk;
@@ -2835,7 +2865,7 @@ sub ProcessInsta360($;$)
2835
2865
  $raf->Read($buff, $len) == $len or last;
2836
2866
  $et->VerboseDump(\$buff) if $verbose > 2;
2837
2867
  if ($dlen) {
2838
- if ($len % $dlen) {
2868
+ if ($len % $dlen and $id != 0x700) { # (have seen one 0x700 record which was expected format but not multiple of 53 bytes)
2839
2869
  $et->Warn(sprintf('Unexpected Insta360 record 0x%x length',$id));
2840
2870
  } elsif ($id == 0x200) {
2841
2871
  $et->FoundTag(PreviewImage => $buff);
@@ -2865,10 +2895,9 @@ sub ProcessInsta360($;$)
2865
2895
  $et->HandleTag($tagTbl, VideoTimeStamp => sprintf('%.3f', Get64u(\$buff, $p) / 1000));
2866
2896
  }
2867
2897
  } elsif ($id == 0x700) {
2868
- for ($p=0; $p<$len; $p+=$dlen) {
2898
+ for ($p=0; $p+$dlen<=$len; $p+=$dlen) {
2869
2899
  my $tmp = substr($buff, $p, $dlen);
2870
2900
  my @a = unpack('VVvaa8aa8aa8a8a8', $tmp);
2871
- next unless $a[3] eq 'A'; # (ignore void fixes)
2872
2901
  unless (($a[5] eq 'N' or $a[5] eq 'S') and # (quick validation)
2873
2902
  ($a[7] eq 'E' or $a[7] eq 'W' or
2874
2903
  # (odd, but I've seen "O" instead of "W". Perhaps
@@ -2878,6 +2907,7 @@ sub ProcessInsta360($;$)
2878
2907
  $et->Warn('Unrecognized INSV GPS format');
2879
2908
  last;
2880
2909
  }
2910
+ next unless $a[3] eq 'A'; # (ignore void fixes)
2881
2911
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2882
2912
  $a[$_] = GetDouble(\$a[$_], 0) foreach 4,6,8,9,10;
2883
2913
  $a[4] = -abs($a[4]) if $a[5] eq 'S'; # (abs just in case it was already signed)
@@ -34,7 +34,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
34
34
  use Image::ExifTool::Exif;
35
35
  use Image::ExifTool::Minolta;
36
36
 
37
- $VERSION = '3.59';
37
+ $VERSION = '3.60';
38
38
 
39
39
  sub ProcessSRF($$$);
40
40
  sub ProcessSR2($$$);
@@ -161,6 +161,7 @@ sub PrintInvLensSpec($;$$);
161
161
  32876 => 'Sony E 11mm F1.8', #JR
162
162
  32877 => 'Sony E 15mm F1.4 G', #JR
163
163
  32878 => 'Sony FE 20-70mm F4 G', #JR
164
+ 32879 => 'Sony FE 50mm F1.4 GM', #JR
164
165
 
165
166
  # (comment this out so LensID will report the LensModel, which is more useful)
166
167
  # 32952 => 'Metabones Canon EF Speed Booster Ultra', #JR (corresponds to 184, but 'Advanced' mode, LensMount reported as E-mount)
@@ -259,7 +260,10 @@ sub PrintInvLensSpec($;$$);
259
260
  50534 => 'Sigma 20mm F1.4 DG DN | A', #JR (022)
260
261
  50535 => 'Sigma 24mm F1.4 DG DN | A', #JR (022)
261
262
  50536 => 'Sigma 60-600mm F4.5-6.3 DG DN OS | S', #JR (023)
263
+ 50537 => 'Sigma 50mm F2 DG DN | C', #JR (023)
264
+ 50538 => 'Sigma 17mm F4 DG DN | C', #JR (023)
262
265
  50539 => 'Sigma 50mm F1.4 DG DN | A', #JR (023)
266
+ 50544 => 'Sigma 23mm F1.4 DC DN | C', #JR (023)
263
267
 
264
268
  50992 => 'Voigtlander SUPER WIDE-HELIAR 15mm F4.5 III', #JR
265
269
  50993 => 'Voigtlander HELIAR-HYPER WIDE 10mm F5.6', #IB
@@ -1165,7 +1169,7 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 );
1165
1169
  },
1166
1170
  },{
1167
1171
  Name => 'AFAreaModeSetting',
1168
- Condition => '$$self{Model} =~ /^(NEX-|ILCE-|ILME-|DSC-(RX10M4|RX100M6|RX100M7|RX100M5A|HX99|RX0M2))/',
1172
+ Condition => '$$self{Model} =~ /^(NEX-|ILCE-|ILME-|ZV-E|DSC-(RX10M4|RX100M6|RX100M7|RX100M5A|HX99|RX0M2))/',
1169
1173
  Notes => 'NEX, ILCE and some DSC models',
1170
1174
  RawConv => '$$self{AFAreaILCE} = $val',
1171
1175
  DataMember => 'AFAreaILCE',
@@ -1201,7 +1205,7 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 );
1201
1205
  # observed values in range (0 0) to (640 480), with center (320 240) often seen
1202
1206
  # for NEX-5R/6, positions appear to be in an 11x9 grid
1203
1207
  Name => 'FlexibleSpotPosition',
1204
- Condition => '$$self{Model} =~ /^(NEX-|ILCE-|ILME-|DSC-(RX10M4|RX100M6|RX100M7|RX100M5A|HX99|RX0M2))/',
1208
+ Condition => '$$self{Model} =~ /^(NEX-|ILCE-|ILME-|ZV-E|DSC-(RX10M4|RX100M6|RX100M7|RX100M5A|HX99|RX0M2))/',
1205
1209
  Writable => 'int16u',
1206
1210
  Count => 2,
1207
1211
  Notes => q{
@@ -1669,6 +1673,7 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 );
1669
1673
  # 0x28 (e) for ILCE-7RM4/9M2, DSC-RX100M7, ZV-1/E10
1670
1674
  # 0x31 (e) for ILCE-1/7M4/7SM3, ILME-FX3
1671
1675
  # 0x32 (e) for ILCE-7RM5, ILME-FX30
1676
+ # 0x33 (e) for ZV-E1
1672
1677
  # first byte decoded: 40, 204, 202, 27, 58, 62, 48, 215, 28, 106, 89 respectively
1673
1678
  {
1674
1679
  Name => 'Tag9400a',
@@ -1683,7 +1688,7 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 );
1683
1688
  SubDirectory => { TagTable => 'Image::ExifTool::Sony::Tag9400b' },
1684
1689
  },{
1685
1690
  Name => 'Tag9400c',
1686
- Condition => '$$valPt =~ /^[\x23\x24\x26\x28\x31\x32]/',
1691
+ Condition => '$$valPt =~ /^[\x23\x24\x26\x28\x31\x32\x33]/',
1687
1692
  SubDirectory => { TagTable => 'Image::ExifTool::Sony::Tag9400c' },
1688
1693
  },{
1689
1694
  Name => 'Sony_0x9400',
@@ -1896,7 +1901,7 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 );
1896
1901
  },
1897
1902
  0x940c => [{
1898
1903
  Name => 'Tag940c',
1899
- Condition => '$$self{Model} =~ /^(NEX-|ILCE-|ILME-|Lunar|ZV-E10)\b/',
1904
+ Condition => '$$self{Model} =~ /^(NEX-|ILCE-|ILME-|Lunar|ZV-E10|ZV-E1)\b/',
1900
1905
  SubDirectory => { TagTable => 'Image::ExifTool::Sony::Tag940c' },
1901
1906
  },{
1902
1907
  Name => 'Sony_0x940c',
@@ -2064,6 +2069,7 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 );
2064
2069
  389 => 'ZV-1F', #IB
2065
2070
  390 => 'ILCE-7RM5', #IB
2066
2071
  391 => 'ILME-FX30', #JR
2072
+ 393 => 'ZV-E1', #JR
2067
2073
  },
2068
2074
  },
2069
2075
  0xb020 => { #2
@@ -8238,7 +8244,7 @@ my %isoSetting2010 = (
8238
8244
  },
8239
8245
  0x002a => [{
8240
8246
  Name => 'Quality2',
8241
- Condition => '$$self{Model} !~ /^(ILCE-(1|7M4|7RM5|7SM3)|ILME-FX3)\b/',
8247
+ Condition => '$$self{Model} !~ /^(ILCE-(1|7M4|7RM5|7SM3)|ILME-(FX3|FX30)|ZV-E1)\b/',
8242
8248
  PrintConv => {
8243
8249
  0 => 'JPEG',
8244
8250
  1 => 'RAW',
@@ -8247,7 +8253,6 @@ my %isoSetting2010 = (
8247
8253
  },
8248
8254
  },{
8249
8255
  Name => 'Quality2',
8250
- Condition => '$$self{Model} =~ /^(ILCE-(1|7M4|7RM5|7SM3)|ILME-FX3)\b/',
8251
8256
  PrintConv => {
8252
8257
  1 => 'JPEG',
8253
8258
  2 => 'RAW',
@@ -8258,13 +8263,13 @@ my %isoSetting2010 = (
8258
8263
  }],
8259
8264
  0x0047 => {
8260
8265
  Name => 'SonyImageHeight',
8261
- Condition => '$$self{Model} !~ /^(ILCE-(1|7M4|7RM5|7SM3)|ILME-FX3)\b/',
8266
+ Condition => '$$self{Model} !~ /^(ILCE-(1|7M4|7RM5|7SM3)|ILME-(FX3|FX30)|ZV-E1)\b/',
8262
8267
  Format => 'int16u',
8263
8268
  PrintConv => '$val > 0 ? 8*$val : "n.a."',
8264
8269
  },
8265
8270
  0x0053 => {
8266
8271
  Name => 'ModelReleaseYear',
8267
- Condition => '$$self{Model} !~ /^(ILCE-(1|7M4|7RM5|7SM3)|ILME-FX3)\b/',
8272
+ Condition => '$$self{Model} !~ /^(ILCE-(1|7M4|7RM5|7SM3)|ILME-(FX3|FX30)|ZV-E1)\b/',
8268
8273
  Format => 'int8u',
8269
8274
  PrintConv => 'sprintf("20%.2d", $val)',
8270
8275
  },
@@ -8279,9 +8284,10 @@ my %isoSetting2010 = (
8279
8284
  FIRST_ENTRY => 0,
8280
8285
  GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
8281
8286
  DATAMEMBER => [ 0 ],
8282
- IS_SUBDIR => [ 0x044e, 0x0498, 0x049d, 0x04a1, 0x04a2, 0x059d, 0x0634, 0x0636, 0x064c, 0x0653, 0x0678, 0x06b8, 0x06de, 0x06e7 ],
8287
+ IS_SUBDIR => [ 0x03e2, 0x044e, 0x0498, 0x049d, 0x04a1, 0x04a2, 0x059d, 0x0634, 0x0636, 0x064c, 0x0653, 0x0678, 0x06b8, 0x06de, 0x06e7 ],
8283
8288
  0x0000 => { Name => 'Ver9401', Hidden => 1, RawConv => '$$self{Ver9401} = $val; $$self{OPTIONS}{Unknown}<2 ? undef : $val' },
8284
8289
 
8290
+ 0x03e2 => { Name => 'ISOInfo', Condition => '$$self{Ver9401} == 181', Format => 'int8u[5]', SubDirectory => { TagTable => 'Image::ExifTool::Sony::ISOInfo' } },
8285
8291
  0x044e => { Name => 'ISOInfo', Condition => '$$self{Ver9401} == 178', Format => 'int8u[5]', SubDirectory => { TagTable => 'Image::ExifTool::Sony::ISOInfo' } },
8286
8292
  0x0498 => { Name => 'ISOInfo', Condition => '$$self{Ver9401} == 148', Format => 'int8u[5]', SubDirectory => { TagTable => 'Image::ExifTool::Sony::ISOInfo' } },
8287
8293
  0x049d => { Name => 'ISOInfo', Condition => '$$self{Ver9401} == 167', Format => 'int8u[5]', SubDirectory => { TagTable => 'Image::ExifTool::Sony::ISOInfo' } },
@@ -2498,6 +2498,9 @@ my %tagLookup = (
2498
2498
  'energysavingmode' => { 238 => 0x648, 239 => 0x678 },
2499
2499
  'engineer' => { 521 => 'engineer' },
2500
2500
  'enhancedarktones' => { 280 => 0x8 },
2501
+ 'enhancedenoisealreadyapplied' => { 491 => 'EnhanceDenoiseAlreadyApplied' },
2502
+ 'enhancedenoiselumaamount' => { 491 => 'EnhanceDenoiseLumaAmount' },
2503
+ 'enhancedenoiseversion' => { 491 => 'EnhanceDenoiseVersion' },
2501
2504
  'enhancedetailsalreadyapplied' => { 491 => 'EnhanceDetailsAlreadyApplied' },
2502
2505
  'enhancedetailsversion' => { 491 => 'EnhanceDetailsVersion' },
2503
2506
  'enhancement' => { 112 => 0x16, 113 => 0x3016 },
@@ -9489,6 +9492,7 @@ my %tagExists = (
9489
9492
  'imagefullheight' => 1,
9490
9493
  'imagefullwidth' => 1,
9491
9494
  'imageheader' => 1,
9495
+ 'imageheightinches' => 1,
9492
9496
  'imagehorizonpixelpack' => 1,
9493
9497
  'imageid' => 1,
9494
9498
  'imageinfo' => 1,
@@ -9522,6 +9526,7 @@ my %tagExists = (
9522
9526
  'imagetoolbar' => 1,
9523
9527
  'imageuidlist' => 1,
9524
9528
  'imageversion' => 1,
9529
+ 'imagewidthinches' => 1,
9525
9530
  'imageworkstationmake' => 1,
9526
9531
  'imagingdata' => 1,
9527
9532
  'imdb' => 1,
@@ -11103,6 +11108,7 @@ my %tagExists = (
11103
11108
  'rafdata' => 1,
11104
11109
  'rafversion' => 1,
11105
11110
  'rangeimagelocalset' => 1,
11111
+ 'rarversion' => 1,
11106
11112
  'rasterpadding' => 1,
11107
11113
  'rate' => 1,
11108
11114
  'rated' => 1,
@@ -11192,6 +11198,8 @@ my %tagExists = (
11192
11198
  'recordingtimeyear' => 1,
11193
11199
  'recordingtimeyearmonth' => 1,
11194
11200
  'recordingtimeyearmonthday' => 1,
11201
+ 'records' => 1,
11202
+ 'recordsv2' => 1,
11195
11203
  'rect' => 1,
11196
11204
  'rectangleofinterest' => 1,
11197
11205
  'red1header' => 1,
@@ -12468,6 +12476,7 @@ my %tagExists = (
12468
12476
  'worldtime' => 1,
12469
12477
  'worldtocamera' => 1,
12470
12478
  'worldtondc' => 1,
12479
+ 'wpgversion' => 1,
12471
12480
  'wrapmodes' => 1,
12472
12481
  'writername' => 1,
12473
12482
  'writers' => 1,