exiftool_vendored 12.59.0 → 12.61.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.
- 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/) {
|