exiftool_vendored 12.99.0 → 13.03.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 +76 -3
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/arg_files/exif2xmp.args +4 -0
- data/bin/arg_files/xmp2exif.args +4 -0
- data/bin/exiftool +121 -50
- data/bin/lib/Image/ExifTool/Apple.pm +2 -2
- data/bin/lib/Image/ExifTool/CBOR.pm +4 -1
- data/bin/lib/Image/ExifTool/Canon.pm +35 -26
- data/bin/lib/Image/ExifTool/Exif.pm +15 -9
- data/bin/lib/Image/ExifTool/FlashPix.pm +5 -9
- data/bin/lib/Image/ExifTool/GIF.pm +143 -92
- data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
- data/bin/lib/Image/ExifTool/Geotag.pm +6 -5
- data/bin/lib/Image/ExifTool/GoPro.pm +2 -2
- data/bin/lib/Image/ExifTool/JPEG.pm +9 -1
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -2
- data/bin/lib/Image/ExifTool/LNK.pm +1 -1
- data/bin/lib/Image/ExifTool/M2TS.pm +2 -2
- data/bin/lib/Image/ExifTool/MIE.pm +9 -3
- data/bin/lib/Image/ExifTool/MacOS.pm +2 -1
- data/bin/lib/Image/ExifTool/Matroska.pm +10 -2
- data/bin/lib/Image/ExifTool/Nikon.pm +5 -2
- data/bin/lib/Image/ExifTool/PDF.pm +35 -4
- data/bin/lib/Image/ExifTool/PNG.pm +14 -3
- data/bin/lib/Image/ExifTool/PPM.pm +11 -2
- data/bin/lib/Image/ExifTool/PhaseOne.pm +2 -1
- data/bin/lib/Image/ExifTool/QuickTime.pm +6 -1
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +69 -7
- data/bin/lib/Image/ExifTool/RIFF.pm +7 -2
- data/bin/lib/Image/ExifTool/TagLookup.pm +5596 -5582
- data/bin/lib/Image/ExifTool/TagNames.pod +75 -21
- data/bin/lib/Image/ExifTool/Text.pm +3 -2
- data/bin/lib/Image/ExifTool/Validate.pm +2 -2
- data/bin/lib/Image/ExifTool/WriteRIFF.pl +13 -4
- data/bin/lib/Image/ExifTool/Writer.pl +42 -66
- data/bin/lib/Image/ExifTool/XMP.pm +19 -4
- data/bin/lib/Image/ExifTool/XMP2.pl +60 -0
- data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -2
- data/bin/lib/Image/ExifTool.pm +204 -86
- data/bin/lib/Image/ExifTool.pod +58 -31
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +2 -2
@@ -88,7 +88,7 @@ sub ProcessCTMD($$$);
|
|
88
88
|
sub ProcessExifInfo($$$);
|
89
89
|
sub SwapWords($);
|
90
90
|
|
91
|
-
$VERSION = '4.
|
91
|
+
$VERSION = '4.84';
|
92
92
|
|
93
93
|
# Note: Removed 'USM' from 'L' lenses since it is redundant - PH
|
94
94
|
# (or is it? Ref 32 shows 5 non-USM L-type lenses)
|
@@ -9830,35 +9830,39 @@ sub LensWithTC($$)
|
|
9830
9830
|
|
9831
9831
|
#------------------------------------------------------------------------------
|
9832
9832
|
# Attempt to calculate sensor size for Canon cameras
|
9833
|
-
# Inputs: 0
|
9833
|
+
# Inputs: 0) ExifTool ref
|
9834
9834
|
# Returns: Sensor diagonal size in mm, or undef
|
9835
9835
|
# Notes: This algorithm is fairly reliable, but has been found to give incorrect
|
9836
9836
|
# values for some firmware versions of the EOS 20D, A310, SD40 and IXUS 65
|
9837
9837
|
# (ref http://wyw.dcweb.cn/download.asp?path=&file=jhead-2.96-ccdwidth_hack.zip)
|
9838
|
-
sub CalcSensorDiag(
|
9838
|
+
sub CalcSensorDiag($)
|
9839
9839
|
{
|
9840
|
-
my
|
9841
|
-
#
|
9842
|
-
|
9843
|
-
|
9844
|
-
|
9845
|
-
|
9846
|
-
|
9847
|
-
|
9848
|
-
|
9849
|
-
|
9850
|
-
|
9851
|
-
|
9852
|
-
|
9853
|
-
|
9854
|
-
|
9855
|
-
|
9856
|
-
|
9857
|
-
|
9858
|
-
|
9859
|
-
|
9860
|
-
|
9861
|
-
|
9840
|
+
my $et = shift;
|
9841
|
+
# calculation is based on the rational value of FocalPlaneX/YResolution
|
9842
|
+
# (most Canon cameras store the sensor size in the denominator)
|
9843
|
+
return undef unless $$et{TAG_EXTRA}{FocalPlaneXResolution} and
|
9844
|
+
$$et{TAG_EXTRA}{FocalPlaneYResolution};
|
9845
|
+
my $xres = $$et{TAG_EXTRA}{FocalPlaneXResolution}{Rational};
|
9846
|
+
my $yres = $$et{TAG_EXTRA}{FocalPlaneYResolution}{Rational};
|
9847
|
+
return undef unless $xres and $yres;
|
9848
|
+
# assumptions: 1) numerators are image width/height * 1000
|
9849
|
+
# 2) denominators are sensor width/height in inches * 1000
|
9850
|
+
my @xres = split /[ \/]/, $xres;
|
9851
|
+
my @yres = split /[ \/]/, $yres;
|
9852
|
+
# verify assumptions as best we can:
|
9853
|
+
# numerators are always divisible by 1000
|
9854
|
+
if ($xres[0] % 1000 == 0 and $yres[0] % 1000 == 0 and
|
9855
|
+
# at least 640x480 pixels (DC models - PH)
|
9856
|
+
$xres[0] >= 640000 and $yres[0] >= 480000 and
|
9857
|
+
# ... but not too big!
|
9858
|
+
$xres[0] < 10000000 and $yres[0] < 10000000 and
|
9859
|
+
# minimum sensor size is 0.061 inches (DC models - PH)
|
9860
|
+
$xres[1] >= 61 and $xres[1] < 1500 and
|
9861
|
+
$yres[1] >= 61 and $yres[1] < 1000 and
|
9862
|
+
# sensor isn't square (may happen if rationals have been reduced)
|
9863
|
+
$xres[1] != $yres[1])
|
9864
|
+
{
|
9865
|
+
return sqrt($xres[1]*$xres[1] + $yres[1]*$yres[1]) * 0.0254;
|
9862
9866
|
}
|
9863
9867
|
return undef;
|
9864
9868
|
}
|
@@ -10251,7 +10255,11 @@ sub ProcessSerialData($$$)
|
|
10251
10255
|
$et->ProcessDirectory(\%dirInfo, $subTablePtr);
|
10252
10256
|
} elsif (not $$tagInfo{Unknown} or $unknown) {
|
10253
10257
|
# don't extract zero-length information
|
10254
|
-
$et->FoundTag($tagInfo, $val) if $count;
|
10258
|
+
my $key = $et->FoundTag($tagInfo, $val) if $count;
|
10259
|
+
if ($key) {
|
10260
|
+
$$et{TAG_EXTRA}{$key}{G6} = $format if $$et{OPTIONS}{SaveFormat};
|
10261
|
+
$$et{TAG_EXTRA}{$key}{BinVal} = substr($$dataPt, $pos+$offset, $len) if $$et{OPTIONS}{SaveBin};
|
10262
|
+
}
|
10255
10263
|
}
|
10256
10264
|
$pos += $len;
|
10257
10265
|
}
|
@@ -10448,6 +10456,7 @@ sub ProcessCTMD($$$)
|
|
10448
10456
|
Start => $pos + 6,
|
10449
10457
|
Addr => $$dirInfo{Base} + $pos + 6,
|
10450
10458
|
Prefix => $$et{INDENT},
|
10459
|
+
Out => $et->Options('TextOut'),
|
10451
10460
|
) if $verbose > 2;
|
10452
10461
|
if ($$tagTablePtr{$type}) {
|
10453
10462
|
$et->HandleTag($tagTablePtr, $type, undef,
|
@@ -57,7 +57,7 @@ use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber %intFormat
|
|
57
57
|
use Image::ExifTool qw(:DataAccess :Utils);
|
58
58
|
use Image::ExifTool::MakerNotes;
|
59
59
|
|
60
|
-
$VERSION = '4.
|
60
|
+
$VERSION = '4.54';
|
61
61
|
|
62
62
|
sub ProcessExif($$$);
|
63
63
|
sub WriteExif($$$);
|
@@ -2931,7 +2931,7 @@ my %opcodeInfo = (
|
|
2931
2931
|
0xa433 => { Name => 'LensMake', Writable => 'string' }, #24
|
2932
2932
|
0xa434 => { Name => 'LensModel', Writable => 'string' }, #24
|
2933
2933
|
0xa435 => { Name => 'LensSerialNumber', Writable => 'string' }, #24
|
2934
|
-
0xa436 => { Name => '
|
2934
|
+
0xa436 => { Name => 'ImageTitle', Writable => 'string' }, #33
|
2935
2935
|
0xa437 => { Name => 'Photographer', Writable => 'string' }, #33
|
2936
2936
|
0xa438 => { Name => 'ImageEditor', Writable => 'string' }, #33
|
2937
2937
|
0xa439 => { Name => 'CameraFirmware', Writable => 'string' }, #33
|
@@ -4421,6 +4421,13 @@ my %opcodeInfo = (
|
|
4421
4421
|
Writable => 'int32u',
|
4422
4422
|
WriteGroup => 'IFD0',
|
4423
4423
|
},
|
4424
|
+
0xcea1 => {
|
4425
|
+
Name => 'SEAL', # (writable directory!)
|
4426
|
+
Writable => 'string',
|
4427
|
+
WriteGroup => 'IFD0',
|
4428
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::SEAL' },
|
4429
|
+
WriteCheck => 'return "Can only delete"', # (don't allow writing)
|
4430
|
+
},
|
4424
4431
|
0xea1c => { #13
|
4425
4432
|
Name => 'Padding',
|
4426
4433
|
Binary => 1,
|
@@ -5346,10 +5353,7 @@ sub CalcScaleFactor35efl
|
|
5346
5353
|
# calculate Canon sensor size using a dedicated algorithm
|
5347
5354
|
if ($$et{Make} eq 'Canon') {
|
5348
5355
|
require Image::ExifTool::Canon;
|
5349
|
-
my $canonDiag = Image::ExifTool::Canon::CalcSensorDiag(
|
5350
|
-
$$et{RATIONAL}{FocalPlaneXResolution},
|
5351
|
-
$$et{RATIONAL}{FocalPlaneYResolution},
|
5352
|
-
);
|
5356
|
+
my $canonDiag = Image::ExifTool::Canon::CalcSensorDiag($et);
|
5353
5357
|
$diag = $canonDiag if $canonDiag;
|
5354
5358
|
}
|
5355
5359
|
unless ($diag and Image::ExifTool::IsFloat($diag)) {
|
@@ -6171,7 +6175,7 @@ sub ProcessExif($$$)
|
|
6171
6175
|
my $base = $$dirInfo{Base} || 0;
|
6172
6176
|
my $firstBase = $base;
|
6173
6177
|
my $raf = $$dirInfo{RAF};
|
6174
|
-
my ($verbose,$validate,$saveFormat) = @{$$et{OPTIONS}}{qw(Verbose Validate SaveFormat)};
|
6178
|
+
my ($verbose,$validate,$saveFormat,$saveBin) = @{$$et{OPTIONS}}{qw(Verbose Validate SaveFormat SaveBin)};
|
6175
6179
|
my $htmlDump = $$et{HTML_DUMP};
|
6176
6180
|
my $success = 1;
|
6177
6181
|
my ($tagKey, $dirSize, $makerAddr, $strEnc, %offsetInfo, $offName, $nextOffName, $doHash);
|
@@ -6361,7 +6365,7 @@ sub ProcessExif($$$)
|
|
6361
6365
|
my $valueDataLen = $dataLen;
|
6362
6366
|
my $valuePtr = $entry + 8; # pointer to value within $$dataPt
|
6363
6367
|
my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID);
|
6364
|
-
my ($origFormStr, $bad, $rational, $subOffName);
|
6368
|
+
my ($origFormStr, $bad, $rational, $binVal, $subOffName);
|
6365
6369
|
# save the EXIF format codes if requested
|
6366
6370
|
$$et{SaveFormat}{$saveFormat = $formatStr} = 1 if $saveFormat;
|
6367
6371
|
# hack to patch incorrect count in Kodak SubIFD3 tags
|
@@ -6658,6 +6662,7 @@ sub ProcessExif($$$)
|
|
6658
6662
|
} else {
|
6659
6663
|
# convert according to specified format
|
6660
6664
|
$val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize,\$rational);
|
6665
|
+
$binVal = substr($$valueDataPt,$valuePtr,$readSize) if $saveBin;
|
6661
6666
|
# re-code if necessary
|
6662
6667
|
if (defined $val) {
|
6663
6668
|
if ($formatStr eq 'utf8') {
|
@@ -7055,7 +7060,8 @@ sub ProcessExif($$$)
|
|
7055
7060
|
# set the group 1 name for tags in specified tables
|
7056
7061
|
$et->SetGroup($tagKey, $dirName) if $$tagTablePtr{SET_GROUP1};
|
7057
7062
|
# save original components of rational numbers (used when copying)
|
7058
|
-
$$et{
|
7063
|
+
$$et{TAG_EXTRA}{$tagKey}{Rational} = $rational if defined $rational;
|
7064
|
+
$$et{TAG_EXTRA}{$tagKey}{BinVal} = $binVal if defined $binVal;
|
7059
7065
|
$$et{TAG_EXTRA}{$tagKey}{G6} = $saveFormat if $saveFormat;
|
7060
7066
|
if ($$et{MAKER_NOTE_FIXUP}) {
|
7061
7067
|
$$et{TAG_EXTRA}{$tagKey}{Fixup} = $$et{MAKER_NOTE_FIXUP};
|
@@ -22,7 +22,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
|
|
22
22
|
use Image::ExifTool::Exif;
|
23
23
|
use Image::ExifTool::ASF; # for GetGUID()
|
24
24
|
|
25
|
-
$VERSION = '1.
|
25
|
+
$VERSION = '1.49';
|
26
26
|
|
27
27
|
sub ProcessFPX($$);
|
28
28
|
sub ProcessFPXR($$$);
|
@@ -1717,16 +1717,14 @@ sub ProcessDocumentTable($)
|
|
1717
1717
|
my $offsets = $$value{$key};
|
1718
1718
|
last unless defined $offsets;
|
1719
1719
|
my $doc;
|
1720
|
-
$doc = $$extra{$key}{G3}
|
1721
|
-
$doc = '' unless $doc;
|
1720
|
+
$doc = $$extra{$key}{G3} || '';
|
1722
1721
|
# get DocFlags for this sub-document
|
1723
1722
|
my ($docFlags, $docTable);
|
1724
1723
|
for ($j=0; ; ++$j) {
|
1725
1724
|
my $key = 'DocFlags' . ($j ? " ($j)" : '');
|
1726
1725
|
last unless defined $$value{$key};
|
1727
1726
|
my $tmp;
|
1728
|
-
$tmp = $$extra{$key}{G3}
|
1729
|
-
$tmp = '' unless $tmp;
|
1727
|
+
$tmp = $$extra{$key}{G3} || '';
|
1730
1728
|
if ($tmp eq $doc) {
|
1731
1729
|
$docFlags = $$value{$key};
|
1732
1730
|
last;
|
@@ -1739,8 +1737,7 @@ sub ProcessDocumentTable($)
|
|
1739
1737
|
my $key = $tag . ($j ? " ($j)" : '');
|
1740
1738
|
last unless defined $$value{$key};
|
1741
1739
|
my $tmp;
|
1742
|
-
$tmp = $$extra{$key}{G3}
|
1743
|
-
$tmp = '' unless $tmp;
|
1740
|
+
$tmp = $$extra{$key}{G3} || '';
|
1744
1741
|
if ($tmp eq $doc) {
|
1745
1742
|
$docTable = \$$value{$key};
|
1746
1743
|
last;
|
@@ -2505,8 +2502,7 @@ sub ProcessFPX($$)
|
|
2505
2502
|
for ($copy=1; ;++$copy) {
|
2506
2503
|
my $key = "$tag ($copy)";
|
2507
2504
|
last unless defined $$et{VALUE}{$key};
|
2508
|
-
|
2509
|
-
next if $extra and $$extra{G3}; # not Main if family 3 group is set
|
2505
|
+
next if $$et{TAG_EXTRA}{$key}{G3}; # not Main if family 3 group is set
|
2510
2506
|
foreach $member ('PRIORITY','VALUE','FILE_ORDER','TAG_INFO','TAG_EXTRA') {
|
2511
2507
|
my $pHash = $$et{$member};
|
2512
2508
|
my $t = $$pHash{$tag};
|
@@ -20,7 +20,7 @@ use strict;
|
|
20
20
|
use vars qw($VERSION);
|
21
21
|
use Image::ExifTool qw(:DataAccess :Utils);
|
22
22
|
|
23
|
-
$VERSION = '1.
|
23
|
+
$VERSION = '1.21';
|
24
24
|
|
25
25
|
# road map of directory locations in GIF images
|
26
26
|
my %gifMap = (
|
@@ -28,6 +28,9 @@ my %gifMap = (
|
|
28
28
|
ICC_Profile => 'GIF',
|
29
29
|
);
|
30
30
|
|
31
|
+
# application extensions that we can write, and the order they are written
|
32
|
+
my @appExtensions = ( 'XMP Data/XMP', 'ICCRGBG1/012' );
|
33
|
+
|
31
34
|
%Image::ExifTool::GIF::Main = (
|
32
35
|
GROUPS => { 2 => 'Image' },
|
33
36
|
VARS => { NO_ID => 1 },
|
@@ -61,19 +64,26 @@ my %gifMap = (
|
|
61
64
|
%Image::ExifTool::GIF::Extensions = (
|
62
65
|
GROUPS => { 2 => 'Image' },
|
63
66
|
NOTES => 'Tags extracted from GIF89a application extensions.',
|
67
|
+
WRITE_PROC => sub { return 1 }, # (dummy proc to facilitate writable directories)
|
64
68
|
'NETSCAPE/2.0' => { #3
|
65
69
|
Name => 'Animation',
|
66
70
|
SubDirectory => { TagTable => 'Image::ExifTool::GIF::Animation' },
|
67
71
|
},
|
68
72
|
'XMP Data/XMP' => { #2
|
69
73
|
Name => 'XMP',
|
70
|
-
IncludeLengthBytes
|
71
|
-
|
74
|
+
# IncludeLengthBytes indicates the length bytes are part of the data value...
|
75
|
+
# undef = data may contain nulls and is split into 255-byte blocks
|
76
|
+
# 1 = data may not contain nulls and is not split; NULL padding is added as necessary
|
77
|
+
# 2 = data is not split and may be edited in place; 257-byte landing zone is added
|
78
|
+
# (Terminator may be specified for a value of 1 above, but must be specified for 2)
|
79
|
+
IncludeLengthBytes => 2,
|
80
|
+
Terminator => q(<\\?xpacket end=['"][wr]['"]\\?>), # (regex to match end of valid data)
|
81
|
+
Writable => 2, # (writable directory!)
|
72
82
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
|
73
83
|
},
|
74
84
|
'ICCRGBG1/012' => { #4
|
75
85
|
Name => 'ICC_Profile',
|
76
|
-
Writable => 2,
|
86
|
+
Writable => 2, # (writable directory!)
|
77
87
|
SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
|
78
88
|
},
|
79
89
|
'MIDICTRL/Jon' => { #5
|
@@ -162,7 +172,7 @@ my %gifMap = (
|
|
162
172
|
);
|
163
173
|
|
164
174
|
#------------------------------------------------------------------------------
|
165
|
-
#
|
175
|
+
# Read/write meta information in GIF image
|
166
176
|
# Inputs: 0) ExifTool object reference, 1) Directory information ref
|
167
177
|
# Returns: 1 on success, 0 if this wasn't a valid GIF file, or -1 if
|
168
178
|
# an output file was specified and a write error occurred
|
@@ -174,7 +184,7 @@ sub ProcessGIF($$)
|
|
174
184
|
my $verbose = $et->Options('Verbose');
|
175
185
|
my $out = $et->Options('TextOut');
|
176
186
|
my ($a, $s, $ch, $length, $buff);
|
177
|
-
my ($err, $newComment, $setComment, $nvComment);
|
187
|
+
my ($err, $newComment, $setComment, $nvComment, $newExt);
|
178
188
|
my ($addDirs, %doneDir);
|
179
189
|
my ($frameCount, $delayTime) = (0, 0);
|
180
190
|
|
@@ -186,9 +196,19 @@ sub ProcessGIF($$)
|
|
186
196
|
my $ver = $1;
|
187
197
|
my $rtnVal = 0;
|
188
198
|
my $tagTablePtr = GetTagTable('Image::ExifTool::GIF::Main');
|
199
|
+
my $extTable = GetTagTable('Image::ExifTool::GIF::Extensions');
|
189
200
|
SetByteOrder('II');
|
190
201
|
|
191
202
|
if ($outfile) {
|
203
|
+
# add any user-defined writable app extensions to the list
|
204
|
+
my $ext;
|
205
|
+
foreach $ext (sort keys %$extTable) {
|
206
|
+
next unless ref $$extTable{$ext} eq 'HASH';
|
207
|
+
my $extInfo = $$extTable{$ext};
|
208
|
+
next unless $$extInfo{SubDirectory} and $$extInfo{Writable} and not $gifMap{$$extInfo{Name}};
|
209
|
+
$gifMap{$$extInfo{Name}} = 'GIF';
|
210
|
+
push @appExtensions, $ext;
|
211
|
+
}
|
192
212
|
$et->InitWriteDirs(\%gifMap, 'XMP'); # make XMP the preferred group for GIF
|
193
213
|
$addDirs = $$et{ADD_DIRS};
|
194
214
|
# determine if we are editing the File:Comment tag
|
@@ -196,8 +216,9 @@ sub ProcessGIF($$)
|
|
196
216
|
$newComment = $et->GetNewValue('Comment', \$nvComment);
|
197
217
|
$setComment = 1 if $nvComment or $$delGroup{File};
|
198
218
|
# change to GIF 89a if adding comment, XMP or ICC_Profile
|
199
|
-
$buff = 'GIF89a' if
|
219
|
+
$buff = 'GIF89a' if %$addDirs or defined $newComment;
|
200
220
|
Write($outfile, $buff, $s) or $err = 1;
|
221
|
+
$newExt = $et->GetNewTagInfoHash($extTable);
|
201
222
|
} else {
|
202
223
|
$et->SetFileType(); # set file type
|
203
224
|
$et->HandleTag($tagTablePtr, 'GIFVersion', $ver);
|
@@ -238,45 +259,50 @@ Block:
|
|
238
259
|
undef $nvComment; # delete any other extraneous comments
|
239
260
|
++$$et{CHANGED}; # increment file changed flag
|
240
261
|
}
|
241
|
-
# add application
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
my $
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
262
|
+
# add application extensions if necessary
|
263
|
+
my $ext;
|
264
|
+
my @new = sort keys %$newExt;
|
265
|
+
foreach $ext (@appExtensions, @new) {
|
266
|
+
my $extInfo = $$extTable{$ext};
|
267
|
+
my $name = $$extInfo{Name};
|
268
|
+
if ($$newExt{$ext}) {
|
269
|
+
delete $$newExt{$ext};
|
270
|
+
$doneDir{$name} = 1; # (we wrote this as a block instead)
|
271
|
+
$buff = $et->GetNewValue($extInfo);
|
272
|
+
$et->VerboseValue("+ GIF:$name", $buff);
|
273
|
+
} elsif (exists $$addDirs{$name} and not defined $doneDir{$name}) {
|
274
|
+
$doneDir{$name} = 1;
|
275
|
+
my $tbl = GetTagTable($$extInfo{SubDirectory}{TagTable});
|
276
|
+
my %dirInfo = ( Parent => 'GIF' );
|
277
|
+
$verbose and print $out "Creating $name application extension block:\n";
|
278
|
+
$buff = $et->WriteDirectory(\%dirInfo, $tbl);
|
254
279
|
} else {
|
255
|
-
|
280
|
+
next;
|
256
281
|
}
|
257
|
-
}
|
258
|
-
# add application extension containing ICC_Profile if necessary
|
259
|
-
if (exists $$addDirs{ICC_Profile} and not defined $doneDir{ICC_Profile}) {
|
260
|
-
$doneDir{ICC_Profile} = 1;
|
261
|
-
# write new ICC_Profile
|
262
|
-
my $iccTable = GetTagTable('Image::ExifTool::ICC_Profile::Main');
|
263
|
-
my %dirInfo = ( Parent => 'GIF' );
|
264
|
-
$verbose and print $out "Creating ICC_Profile application extension block:\n";
|
265
|
-
$buff = $et->WriteDirectory(\%dirInfo, $iccTable);
|
266
282
|
if (defined $buff and length $buff) {
|
283
|
+
++$$et{CHANGED};
|
284
|
+
Write($outfile, "\x21\xff\x0b", substr($ext,0,8), substr($ext,9,3)) or $err = 1;
|
267
285
|
my $pos = 0;
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
286
|
+
if (not $$extTable{$ext}{IncludeLengthBytes}) {
|
287
|
+
my $len = length $buff;
|
288
|
+
while ($pos < length $buff) {
|
289
|
+
my $n = length($buff) - $pos;
|
290
|
+
$n = 255 if $n > 255;
|
291
|
+
Write($outfile, chr($n), substr($buff, $pos, $n)) or $err = 1;
|
292
|
+
$pos += $n;
|
293
|
+
}
|
294
|
+
Write($outfile, "\0") or $err = 1; # write null terminator
|
295
|
+
} elsif ($$extTable{$ext}{IncludeLengthBytes} < 2) {
|
296
|
+
$pos += ord(substr($buff,$pos,1)) + 1 while $pos < length $buff;
|
297
|
+
# write data, null padding and terminator
|
298
|
+
Write($outfile, $buff, "\0" x ($pos - length($buff) + 1)) or $err = 1;
|
299
|
+
} else {
|
300
|
+
# write data, landing zone and null terminator
|
301
|
+
Write($outfile, $buff, pack('C*',1,reverse(0..255),0)) or $err = 1;
|
275
302
|
}
|
276
|
-
|
277
|
-
++$doneDir{ICC_Profile}; # set to 2 to indicate we added a new profile
|
303
|
+
++$doneDir{$name}; # set to 2 to indicate we added it
|
278
304
|
} else {
|
279
|
-
$verbose and print $out " -> no
|
305
|
+
$verbose and print $out " -> no $name to add\n";
|
280
306
|
}
|
281
307
|
}
|
282
308
|
}
|
@@ -286,7 +312,7 @@ Block:
|
|
286
312
|
# image descriptor
|
287
313
|
last unless $raf->Read($buff, 8) == 8 and $raf->Read($ch, 1);
|
288
314
|
Write($outfile, $buff, $ch) or $err = 1 if $outfile;
|
289
|
-
if ($verbose) {
|
315
|
+
if ($verbose and not $outfile) {
|
290
316
|
my ($left, $top, $w, $h) = unpack('v*', $buff);
|
291
317
|
print $out "Image: left=$left top=$top width=$w height=$h\n";
|
292
318
|
}
|
@@ -352,9 +378,9 @@ Block:
|
|
352
378
|
}
|
353
379
|
if ($isOverwriting) {
|
354
380
|
++$$et{CHANGED}; # increment file changed flag
|
355
|
-
$et->VerboseValue('- Comment', $comment);
|
381
|
+
$et->VerboseValue('- GIF:Comment', $comment);
|
356
382
|
$comment = $newComment;
|
357
|
-
$et->VerboseValue('+ Comment', $comment) if defined $comment;
|
383
|
+
$et->VerboseValue('+ GIF:Comment', $comment) if defined $comment;
|
358
384
|
undef $nvComment; # just delete remaining comments
|
359
385
|
} else {
|
360
386
|
undef $setComment; # leave remaining comments alone
|
@@ -393,14 +419,19 @@ Block:
|
|
393
419
|
$tag =~ tr/\0-\x1f//d; # remove nulls and control characters
|
394
420
|
$verbose and print $out "Application Extension: $tag\n";
|
395
421
|
|
396
|
-
my $extTable = GetTagTable('Image::ExifTool::GIF::Extensions');
|
397
422
|
my $extInfo = $$extTable{$tag};
|
398
|
-
my ($subdir, $inclLen, $justCopy);
|
423
|
+
my ($subdir, $inclLen, $justCopy, $name);
|
399
424
|
if ($extInfo) {
|
400
|
-
$
|
425
|
+
if ($outfile and $$newExt{$$extInfo{TagID}}) {
|
426
|
+
delete $$newExt{$$extInfo{TagID}}; # don't create again
|
427
|
+
# (write as a block -- don't define $subdir)
|
428
|
+
} else {
|
429
|
+
$subdir = $$extInfo{SubDirectory};
|
430
|
+
}
|
401
431
|
$inclLen = $$extInfo{IncludeLengthBytes};
|
402
|
-
|
403
|
-
|
432
|
+
$name = $$extInfo{Name};
|
433
|
+
# rewrite as-is unless this is a writable
|
434
|
+
$justCopy = 1 if $outfile and not $$extInfo{Writable};
|
404
435
|
} else {
|
405
436
|
$justCopy = 1 if $outfile;
|
406
437
|
}
|
@@ -415,62 +446,82 @@ Block:
|
|
415
446
|
Write($outfile, $ch, $buff) or $err = 1 if $justCopy;
|
416
447
|
$dat .= $inclLen ? $ch . $buff : $buff;
|
417
448
|
}
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
if ($
|
424
|
-
|
425
|
-
|
426
|
-
$
|
449
|
+
if ($justCopy) {
|
450
|
+
Write($outfile, "\0") or $err = 1;
|
451
|
+
next;
|
452
|
+
} elsif ($inclLen) {
|
453
|
+
# remove landing zone or padding
|
454
|
+
if ($$extInfo{Terminator} and $dat =~ /$$extInfo{Terminator}/g) {
|
455
|
+
$dat = substr($dat, 0, pos($dat));
|
456
|
+
} elsif ($dat =~ /\0/g) {
|
457
|
+
$dat = substr($dat, 0, pos($dat) - 1);
|
427
458
|
}
|
459
|
+
}
|
460
|
+
if ($subdir) {
|
428
461
|
my %dirInfo = (
|
429
462
|
DataPt => \$dat,
|
430
463
|
DataLen => length $dat,
|
431
|
-
DirLen => $
|
464
|
+
DirLen => length $dat,
|
432
465
|
DirName => $name,
|
433
466
|
Parent => 'GIF',
|
434
467
|
);
|
435
468
|
my $subTable = GetTagTable($$subdir{TagTable});
|
436
|
-
|
469
|
+
unless ($outfile) {
|
437
470
|
$et->ProcessDirectory(\%dirInfo, $subTable);
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
$
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
my $pos = 0;
|
461
|
-
my $len = length $dat;
|
462
|
-
while ($pos < $len) {
|
463
|
-
my $n = $len - $pos;
|
464
|
-
$n = 255 if $n > 255;
|
465
|
-
Write($outfile, chr($n), substr($dat, $pos, $n)) or $err = 1;
|
466
|
-
$pos += $n;
|
467
|
-
}
|
468
|
-
}
|
469
|
-
Write($outfile, "\0") or $err = 1; # write null terminator
|
471
|
+
next;
|
472
|
+
}
|
473
|
+
next if $justCopy;
|
474
|
+
if ($doneDir{$name} and $doneDir{$name} > 1) {
|
475
|
+
$et->Warn("Duplicate $name block created");
|
476
|
+
}
|
477
|
+
$buff = $et->WriteDirectory(\%dirInfo, $subTable);
|
478
|
+
if (defined $buff) {
|
479
|
+
next unless length $buff; # delete this extension if length is zero
|
480
|
+
$dat = $buff;
|
481
|
+
}
|
482
|
+
$doneDir{$name} = 1;
|
483
|
+
} elsif ($outfile and not $justCopy) {
|
484
|
+
my $nvHash = $et->GetNewValueHash($extInfo);
|
485
|
+
if ($nvHash and $et->IsOverwriting($nvHash, $dat)) {
|
486
|
+
++$$et{CHANGED};
|
487
|
+
my $val = $et->GetNewValue($extInfo);
|
488
|
+
$et->VerboseValue("- GIF:$name", $dat);
|
489
|
+
next unless defined $val and length $val;
|
490
|
+
$dat = $val;
|
491
|
+
$et->VerboseValue("+ GIF:$name", $dat);
|
492
|
+
$doneDir{$name} = 1; # (possibly wrote dir as a block)
|
470
493
|
}
|
471
494
|
} elsif (not $outfile) {
|
472
495
|
$et->HandleTag($extTable, $tag, $dat);
|
496
|
+
next;
|
497
|
+
}
|
498
|
+
Write($outfile, $hdr) or $err = 1; # write extension header
|
499
|
+
if ($inclLen) {
|
500
|
+
# check for null just to be safe
|
501
|
+
$et->Error("$name contained NULL character") if $inclLen and $dat =~ /\0/;
|
502
|
+
if ($inclLen > 1) {
|
503
|
+
# add landing zone (without terminator, which will be added later)
|
504
|
+
$dat .= pack('C*',1,reverse(0..255)) if $inclLen;
|
505
|
+
} else {
|
506
|
+
# pad with nulls as required
|
507
|
+
my $pos = 0;
|
508
|
+
$pos += ord(substr($dat,$pos,1)) + 1 while $pos < length $dat;
|
509
|
+
$dat .= "\0" x ($pos - length($dat));
|
510
|
+
}
|
511
|
+
# write data and landing zone
|
512
|
+
Write($outfile, $dat) or $err = 1;
|
513
|
+
} else {
|
514
|
+
# write as sub-blocks
|
515
|
+
my $pos = 0;
|
516
|
+
my $len = length $dat;
|
517
|
+
while ($pos < $len) {
|
518
|
+
my $n = $len - $pos;
|
519
|
+
$n = 255 if $n > 255;
|
520
|
+
Write($outfile, chr($n), substr($dat, $pos, $n)) or $err = 1;
|
521
|
+
$pos += $n;
|
522
|
+
}
|
473
523
|
}
|
524
|
+
Write($outfile, "\0") or $err = 1; # write null terminator
|
474
525
|
next;
|
475
526
|
|
476
527
|
} elsif ($a == 0xf9 and $length == 4) { # graphic control extension
|
@@ -489,7 +540,7 @@ Block:
|
|
489
540
|
|
490
541
|
last unless $raf->Read($buff, $length) == $length;
|
491
542
|
Write($outfile, $ch, $s, $buff) or $err = 1 if $outfile;
|
492
|
-
if ($verbose) {
|
543
|
+
if ($verbose and not $outfile) {
|
493
544
|
my ($left, $top, $w, $h) = unpack('v4', $buff);
|
494
545
|
print $out "Text: left=$left top=$top width=$w height=$h\n";
|
495
546
|
}
|
Binary file
|
@@ -31,7 +31,7 @@ use vars qw($VERSION);
|
|
31
31
|
use Image::ExifTool qw(:Public);
|
32
32
|
use Image::ExifTool::GPS;
|
33
33
|
|
34
|
-
$VERSION = '1.
|
34
|
+
$VERSION = '1.80';
|
35
35
|
|
36
36
|
sub JITTER() { return 2 } # maximum time jitter
|
37
37
|
|
@@ -562,8 +562,8 @@ DoneFix: $isDate = 1;
|
|
562
562
|
next;
|
563
563
|
} elsif ($format eq 'JSON') {
|
564
564
|
# Google Takeout JSON format
|
565
|
-
if (/"(latitudeE7|longitudeE7|latE7|lngE7|timestamp|startTime|point|durationMinutesOffsetFromStartTime)"\s*:\s*"?(.*?)"?,?\s*[\x0d\x0a]/) {
|
566
|
-
if ($1 eq 'timestamp') {
|
565
|
+
if (/"(latitudeE7|longitudeE7|latE7|lngE7|timestamp|startTime|point|durationMinutesOffsetFromStartTime|time)"\s*:\s*"?(.*?)"?,?\s*[\x0d\x0a]/) {
|
566
|
+
if ($1 eq 'timestamp' or $1 eq 'time') {
|
567
567
|
$time = GetTime($2);
|
568
568
|
goto DoneFix if $time and $$fix{lat} and $$fix{lon};
|
569
569
|
} elsif ($1 eq 'startTime') { # (new format)
|
@@ -1127,8 +1127,9 @@ sub SetGeoValues($$;$)
|
|
1127
1127
|
$iExt = $i1;
|
1128
1128
|
}
|
1129
1129
|
if (abs($time - $tn) > $geoMaxExtSecs) {
|
1130
|
-
$err or $err = 'Time is too far from nearest GPS fix'
|
1131
|
-
$et->VPrint(2, ' Nearest fix: ', PrintFixTime($tn),
|
1130
|
+
$err or $err = 'Time is too far from nearest GPS fix';
|
1131
|
+
$et->VPrint(2, ' Nearest fix: ', PrintFixTime($tn), ' (',
|
1132
|
+
int(abs $time-$tn), " sec away)\n") if $verbose > 2;
|
1132
1133
|
$fix = { } if $$geotag{DateTimeOnly};
|
1133
1134
|
} else {
|
1134
1135
|
$fix = $$points{$tn};
|
@@ -16,7 +16,7 @@ use vars qw($VERSION);
|
|
16
16
|
use Image::ExifTool qw(:DataAccess :Utils);
|
17
17
|
use Image::ExifTool::QuickTime;
|
18
18
|
|
19
|
-
$VERSION = '1.
|
19
|
+
$VERSION = '1.09';
|
20
20
|
|
21
21
|
sub ProcessGoPro($$$);
|
22
22
|
sub ProcessString($$$);
|
@@ -604,7 +604,7 @@ sub ScaleValues($$)
|
|
604
604
|
sub AddUnits($$$)
|
605
605
|
{
|
606
606
|
my ($et, $val, $tag) = @_;
|
607
|
-
if ($et and $$et{TAG_EXTRA}{$tag}
|
607
|
+
if ($et and $$et{TAG_EXTRA}{$tag}{Units}) {
|
608
608
|
my $u = $$et{TAG_EXTRA}{$tag}{Units};
|
609
609
|
$u = [ $u ] unless ref $u eq 'ARRAY';
|
610
610
|
my @a = split ' ', $val;
|