exiftool_vendored 12.60.0 → 12.62.0

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