exiftool_vendored 12.59.0 → 12.61.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/Changes +46 -6
- data/bin/MANIFEST +1 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/exiftool +83 -46
- data/bin/lib/Image/ExifTool/CanonRaw.pm +5 -1
- data/bin/lib/Image/ExifTool/Exif.pm +53 -14
- data/bin/lib/Image/ExifTool/FujiFilm.pm +6 -3
- data/bin/lib/Image/ExifTool/Geotag.pm +30 -7
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +31 -6
- data/bin/lib/Image/ExifTool/MakerNotes.pm +2 -1
- data/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -9
- data/bin/lib/Image/ExifTool/Nikon.pm +3 -3
- data/bin/lib/Image/ExifTool/PDF.pm +15 -6
- data/bin/lib/Image/ExifTool/PNG.pm +1 -6
- data/bin/lib/Image/ExifTool/QuickTime.pm +10 -0
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +51 -21
- data/bin/lib/Image/ExifTool/RIFF.pm +2 -9
- data/bin/lib/Image/ExifTool/Ricoh.pm +2 -1
- data/bin/lib/Image/ExifTool/SigmaRaw.pm +9 -3
- data/bin/lib/Image/ExifTool/Sony.pm +17 -10
- data/bin/lib/Image/ExifTool/TagLookup.pm +3 -2
- data/bin/lib/Image/ExifTool/TagNames.pod +5 -1
- data/bin/lib/Image/ExifTool/WriteExif.pl +2 -9
- data/bin/lib/Image/ExifTool/WritePDF.pl +7 -8
- data/bin/lib/Image/ExifTool/Writer.pl +40 -6
- data/bin/lib/Image/ExifTool/XMP.pm +13 -4
- data/bin/lib/Image/ExifTool.pm +102 -45
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +2 -2
@@ -29,7 +29,7 @@ use vars qw($VERSION);
|
|
29
29
|
use Image::ExifTool qw(:Public);
|
30
30
|
use Image::ExifTool::GPS;
|
31
31
|
|
32
|
-
$VERSION = '1.
|
32
|
+
$VERSION = '1.71';
|
33
33
|
|
34
34
|
sub JITTER() { return 2 } # maximum time jitter
|
35
35
|
|
@@ -92,7 +92,7 @@ my %isOrient = ( dir => 1, pitch => 1, roll => 1 ); # test for orientation key
|
|
92
92
|
# tags which may exist separately in some formats (eg. CSV)
|
93
93
|
my %sepTags = ( dir => 1, pitch => 1, roll => 1, track => 1, speed => 1 );
|
94
94
|
|
95
|
-
# conversion factors for GPSSpeed
|
95
|
+
# conversion factors for GPSSpeed (standard EXIF units only)
|
96
96
|
my %speedConv = (
|
97
97
|
'K' => 1.852, # km/h per knot
|
98
98
|
'M' => 1.150779448, # mph per knot
|
@@ -102,7 +102,14 @@ my %speedConv = (
|
|
102
102
|
'mph' => 'M',
|
103
103
|
);
|
104
104
|
|
105
|
-
|
105
|
+
# all recognized speed conversion factors (non-EXIF included)
|
106
|
+
my %otherConv = (
|
107
|
+
'km/h' => 1.852,
|
108
|
+
'mph' => 1.150779448,
|
109
|
+
'm/s' => 0.514444,
|
110
|
+
);
|
111
|
+
|
112
|
+
my $secPerDay = 24 * 3600; # a useful constant
|
106
113
|
|
107
114
|
#------------------------------------------------------------------------------
|
108
115
|
# Load GPS track log file
|
@@ -140,6 +147,7 @@ sub LoadTrackLog($$;$)
|
|
140
147
|
my ($raf, $from, $time, $isDate, $noDate, $noDateChanged, $lastDate, $dateFlarm);
|
141
148
|
my ($nmeaStart, $fixSecs, @fixTimes, $lastFix, %nmea, @csvHeadings, $sortFixes);
|
142
149
|
my ($canCut, $cutPDOP, $cutHDOP, $cutSats, $e0, $e1, @tmp, $trackFile, $trackTime);
|
150
|
+
my $scaleSpeed;
|
143
151
|
|
144
152
|
unless (eval { require Time::Local }) {
|
145
153
|
return 'Geotag feature requires Time::Local installed';
|
@@ -246,7 +254,9 @@ sub LoadTrackLog($$;$)
|
|
246
254
|
$format = 'CSV';
|
247
255
|
# convert recognized headings to our parameter names
|
248
256
|
foreach (@csvHeadings) {
|
257
|
+
my $head = $_;
|
249
258
|
my $param;
|
259
|
+
my $xtra = '';
|
250
260
|
s/^GPS ?//; # remove leading "GPS" to simplify regex patterns
|
251
261
|
if (/^Time ?\(seconds\)$/i) { # DJI
|
252
262
|
# DJI CSV log files have a column "Time(seconds)" which is seconds since
|
@@ -274,7 +284,16 @@ sub LoadTrackLog($$;$)
|
|
274
284
|
/ref$/i and $param .= 'ref';
|
275
285
|
} elsif (/^(Pos)?Alt/i) {
|
276
286
|
$param = 'alt';
|
277
|
-
} elsif (/^
|
287
|
+
} elsif (/^Speed/i) {
|
288
|
+
$param = 'speed';
|
289
|
+
# (recognize units in brackets)
|
290
|
+
if (m{\((mph|km/h|m/s)\)}) {
|
291
|
+
$scaleSpeed = $otherConv{$1};
|
292
|
+
$xtra = " in $1";
|
293
|
+
} else {
|
294
|
+
$xtra = ' in knots';
|
295
|
+
}
|
296
|
+
} elsif (/^(Angle)?(Heading|Track|Bearing)/i) {
|
278
297
|
$param = 'track';
|
279
298
|
} elsif (/^(Angle)?Pitch/i or /^Camera ?Elevation ?Angle/i) {
|
280
299
|
$param = 'pitch';
|
@@ -284,10 +303,10 @@ sub LoadTrackLog($$;$)
|
|
284
303
|
$param = 'dir';
|
285
304
|
}
|
286
305
|
if ($param) {
|
287
|
-
$et->VPrint(2, "CSV column '${
|
306
|
+
$et->VPrint(2, "CSV column '${head}' is $param$xtra\n");
|
288
307
|
$_ = $param;
|
289
308
|
} else {
|
290
|
-
$et->VPrint(2, "CSV column '${
|
309
|
+
$et->VPrint(2, "CSV column '${head}' ignored\n");
|
291
310
|
$_ = ''; # ignore this column
|
292
311
|
}
|
293
312
|
}
|
@@ -479,9 +498,11 @@ DoneFix: $isDate = 1;
|
|
479
498
|
my ($param, $date, $secs, %neg);
|
480
499
|
foreach $param (@csvHeadings) {
|
481
500
|
my $val = shift @vals;
|
482
|
-
last unless defined $val;
|
501
|
+
last unless defined $val and length($val);
|
483
502
|
next unless $param;
|
484
503
|
if ($param eq 'datetime') {
|
504
|
+
# (fix formats like "24.07.2016 13:47:30")
|
505
|
+
$val =~ s/^(\d{2})[^\d](\d{2})[^\d](\d{4}) /$3:$2:$1 /;
|
485
506
|
local $SIG{'__WARN__'} = sub { };
|
486
507
|
my $dateTime = $et->InverseDateTime($val);
|
487
508
|
if ($dateTime) {
|
@@ -510,6 +531,7 @@ DoneFix: $isDate = 1;
|
|
510
531
|
$date = $trackTime;
|
511
532
|
$secs = $val;
|
512
533
|
} else {
|
534
|
+
$val /= $scaleSpeed if $scaleSpeed and $param eq 'speed';
|
513
535
|
$$fix{$param} = $val;
|
514
536
|
$$has{$param} = 1 if $sepTags{$param};
|
515
537
|
}
|
@@ -1204,6 +1226,7 @@ Category: foreach $category (qw{pos track alt orient atemp}) {
|
|
1204
1226
|
@r = $et->SetNewValue(GPSTrackRef => (defined $$tFix{track} ? 'T' : undef), %opts);
|
1205
1227
|
my ($spd, $ref);
|
1206
1228
|
if (defined($spd = $$tFix{speed})) {
|
1229
|
+
# convert to specified units if necessary
|
1207
1230
|
$ref = $$et{OPTIONS}{GeoSpeedRef};
|
1208
1231
|
if ($ref and defined $speedConv{$ref}) {
|
1209
1232
|
$ref = $speedConv{$ref} if $speedConv{$speedConv{$ref}};
|
@@ -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.
|
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
|
-
}
|
1011
|
-
|
1012
|
-
|
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
|
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 {
|
@@ -21,7 +21,7 @@ sub ProcessKodakPatch($$$);
|
|
21
21
|
sub WriteUnknownOrPreview($$$);
|
22
22
|
sub FixLeicaBase($$;$);
|
23
23
|
|
24
|
-
$VERSION = '2.
|
24
|
+
$VERSION = '2.14';
|
25
25
|
|
26
26
|
my $debug; # set to 1 to enable debugging code
|
27
27
|
|
@@ -92,6 +92,7 @@ my $debug; # set to 1 to enable debugging code
|
|
92
92
|
{
|
93
93
|
Name => 'MakerNoteDJIInfo',
|
94
94
|
Condition => '$$valPt =~ /^\[ae_dbg_info:/',
|
95
|
+
NotIFD => 1,
|
95
96
|
SubDirectory => { TagTable => 'Image::ExifTool::DJI::Info' },
|
96
97
|
},
|
97
98
|
{
|
@@ -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.
|
20
|
+
$VERSION = '1.18';
|
21
21
|
|
22
22
|
sub ProcessMRW($$;$);
|
23
23
|
sub WriteMRW($$;$);
|
@@ -489,14 +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
|
-
|
493
|
-
my ($num, $md5) = (0, $$et{ImageDataMD5});
|
494
|
-
while ($raf->Read($data, 65536)) {
|
495
|
-
$md5->add($data);
|
496
|
-
$num += length $data;
|
497
|
-
}
|
498
|
-
$et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $num bytes of raw data)\n");
|
499
|
-
}
|
492
|
+
$et->ImageDataMD5($raf, undef, 'raw') unless $$et{A100DataOffset};
|
500
493
|
}
|
501
494
|
return $rtnVal;
|
502
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.
|
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[
|
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.
|
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 =>
|
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);
|
@@ -1542,12 +1542,7 @@ sub ProcessPNG($$)
|
|
1542
1542
|
# skip over data chunks if possible/necessary
|
1543
1543
|
} elsif (not $validate or $len > $chunkSizeLimit) {
|
1544
1544
|
if ($md5) {
|
1545
|
-
|
1546
|
-
my $n = $len > 65536 ? 65536 : $len;
|
1547
|
-
$raf->Read($dbuf,$n) == $n or last;
|
1548
|
-
$md5->add($dbuf);
|
1549
|
-
$len -= $n;
|
1550
|
-
}
|
1545
|
+
$et->ImageDataMD5($raf, $len);
|
1551
1546
|
$raf->Read($cbuf, 4) == 4 or $et->Warn('Truncated data'), last;
|
1552
1547
|
} else {
|
1553
1548
|
$raf->Seek($len + 4, 1) or $et->Warn('Seek error'), last;
|
@@ -8784,6 +8784,16 @@ sub HandleItemInfo($)
|
|
8784
8784
|
$et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
|
8785
8785
|
}
|
8786
8786
|
}
|
8787
|
+
# do MD5 checksum of AVIF "av01" image data
|
8788
|
+
if ($type eq 'av01' and $$et{ImageDataMD5}) {
|
8789
|
+
my $md5 = $$et{ImageDataMD5};
|
8790
|
+
my $tot = 0;
|
8791
|
+
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);
|
8794
|
+
}
|
8795
|
+
$et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $tot bytes of av01 data)\n") if $tot;
|
8796
|
+
}
|
8787
8797
|
next unless $name;
|
8788
8798
|
# assemble the data for this item
|
8789
8799
|
undef $buff;
|
@@ -28,7 +28,7 @@ sub Process360Fly($$$);
|
|
28
28
|
sub ProcessFMAS($$$);
|
29
29
|
sub ProcessCAMM($$$);
|
30
30
|
|
31
|
-
my $debug; # set to
|
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
|
-
#
|
1252
|
+
# range check the sample data for MD5 if necessary
|
1247
1253
|
my $size = $$size[$i];
|
1248
|
-
|
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
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
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
|
-
|
1755
|
-
|
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
|
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)
|
@@ -30,7 +30,7 @@ use strict;
|
|
30
30
|
use vars qw($VERSION $AUTOLOAD);
|
31
31
|
use Image::ExifTool qw(:DataAccess :Utils);
|
32
32
|
|
33
|
-
$VERSION = '1.
|
33
|
+
$VERSION = '1.64';
|
34
34
|
|
35
35
|
sub ConvertTimecode($);
|
36
36
|
sub ProcessSGLT($$$);
|
@@ -2102,14 +2102,7 @@ sub ProcessRIFF($$)
|
|
2102
2102
|
# do MD5 if required
|
2103
2103
|
if ($md5 and $isImageData{$tag}) {
|
2104
2104
|
$rewind = $raf->Tell();
|
2105
|
-
|
2106
|
-
while ($more) {
|
2107
|
-
my $n = $more > 65536 ? 65536 : $more;
|
2108
|
-
$raf->Read($buff, $n) == $n or $err = 1, last;
|
2109
|
-
$md5->add($buff);
|
2110
|
-
$more -= $n;
|
2111
|
-
}
|
2112
|
-
$et->VPrint(0, "$$et{INDENT}(ImageDataMD5: '${tag}' chunk, $len2 bytes)\n");
|
2105
|
+
$et->ImageDataMD5($raf, $len2, "'${tag}' chunk");
|
2113
2106
|
}
|
2114
2107
|
if ($tag eq 'LIST_movi' and $ee) {
|
2115
2108
|
$raf->Seek($rewind, 0) or $err = 1, last if $rewind;
|
@@ -19,7 +19,7 @@ use vars qw($VERSION);
|
|
19
19
|
use Image::ExifTool qw(:DataAccess :Utils);
|
20
20
|
use Image::ExifTool::Exif;
|
21
21
|
|
22
|
-
$VERSION = '1.
|
22
|
+
$VERSION = '1.36';
|
23
23
|
|
24
24
|
sub ProcessRicohText($$$);
|
25
25
|
sub ProcessRicohRMETA($$$);
|
@@ -949,6 +949,7 @@ sub ProcessRicohText($$$)
|
|
949
949
|
|
950
950
|
my $data = substr($$dataPt, $dirStart, $dirLen);
|
951
951
|
return 1 if $data =~ /^\0/; # blank Ricoh maker notes
|
952
|
+
$et->VerboseDir('RicohText', undef, $dirLen);
|
952
953
|
# validate text maker notes
|
953
954
|
unless ($data =~ /^(Rev|Rv)/) {
|
954
955
|
$et->Warn('Bad Ricoh maker notes');
|
@@ -16,7 +16,7 @@ use vars qw($VERSION);
|
|
16
16
|
use Image::ExifTool qw(:DataAccess :Utils);
|
17
17
|
use Image::ExifTool::Sigma;
|
18
18
|
|
19
|
-
$VERSION = '1.
|
19
|
+
$VERSION = '1.30';
|
20
20
|
|
21
21
|
sub ProcessX3FHeader($$$);
|
22
22
|
sub ProcessX3FDirectory($$$);
|
@@ -545,10 +545,16 @@ sub ProcessX3FDirectory($$$)
|
|
545
545
|
if ($$tagInfo{Name} eq 'PreviewImage') {
|
546
546
|
# check image header to see if this is a JPEG preview image
|
547
547
|
$raf->Read($buff, 28) == 28 or return 'Error reading PreviewImage header';
|
548
|
-
# ignore all image data but JPEG compressed (version 2.0, type 2, format 18)
|
549
|
-
next unless $buff =~ /^SECi\0\0\x02\0\x02\0\0\0\x12\0\0\0/;
|
550
548
|
$offset += 28;
|
551
549
|
$len -= 28;
|
550
|
+
# ignore all image data but JPEG compressed (version 2.0, type 2, format 18)
|
551
|
+
unless ($buff =~ /^SECi\0\0\x02\0\x02\0\0\0\x12\0\0\0/) {
|
552
|
+
# do MD5 on non-preview data if requested
|
553
|
+
if ($$et{ImageDataMD5} and substr($buff,8,1) ne "\x02") {
|
554
|
+
$et->ImageDataMD5($raf, $len, 'SigmaRaw IMAG');
|
555
|
+
}
|
556
|
+
next;
|
557
|
+
}
|
552
558
|
$raf->Read($buff, $len) == $len or return "Error reading PreviewImage data";
|
553
559
|
# check fore EXIF segment, and extract this image as the JpgFromRaw
|
554
560
|
if ($buff =~ /^\xff\xd8\xff\xe1/) {
|