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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +76 -3
  3. data/bin/META.json +1 -1
  4. data/bin/META.yml +1 -1
  5. data/bin/README +2 -2
  6. data/bin/arg_files/exif2xmp.args +4 -0
  7. data/bin/arg_files/xmp2exif.args +4 -0
  8. data/bin/exiftool +121 -50
  9. data/bin/lib/Image/ExifTool/Apple.pm +2 -2
  10. data/bin/lib/Image/ExifTool/CBOR.pm +4 -1
  11. data/bin/lib/Image/ExifTool/Canon.pm +35 -26
  12. data/bin/lib/Image/ExifTool/Exif.pm +15 -9
  13. data/bin/lib/Image/ExifTool/FlashPix.pm +5 -9
  14. data/bin/lib/Image/ExifTool/GIF.pm +143 -92
  15. data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  16. data/bin/lib/Image/ExifTool/Geotag.pm +6 -5
  17. data/bin/lib/Image/ExifTool/GoPro.pm +2 -2
  18. data/bin/lib/Image/ExifTool/JPEG.pm +9 -1
  19. data/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -2
  20. data/bin/lib/Image/ExifTool/LNK.pm +1 -1
  21. data/bin/lib/Image/ExifTool/M2TS.pm +2 -2
  22. data/bin/lib/Image/ExifTool/MIE.pm +9 -3
  23. data/bin/lib/Image/ExifTool/MacOS.pm +2 -1
  24. data/bin/lib/Image/ExifTool/Matroska.pm +10 -2
  25. data/bin/lib/Image/ExifTool/Nikon.pm +5 -2
  26. data/bin/lib/Image/ExifTool/PDF.pm +35 -4
  27. data/bin/lib/Image/ExifTool/PNG.pm +14 -3
  28. data/bin/lib/Image/ExifTool/PPM.pm +11 -2
  29. data/bin/lib/Image/ExifTool/PhaseOne.pm +2 -1
  30. data/bin/lib/Image/ExifTool/QuickTime.pm +6 -1
  31. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +69 -7
  32. data/bin/lib/Image/ExifTool/RIFF.pm +7 -2
  33. data/bin/lib/Image/ExifTool/TagLookup.pm +5596 -5582
  34. data/bin/lib/Image/ExifTool/TagNames.pod +75 -21
  35. data/bin/lib/Image/ExifTool/Text.pm +3 -2
  36. data/bin/lib/Image/ExifTool/Validate.pm +2 -2
  37. data/bin/lib/Image/ExifTool/WriteRIFF.pl +13 -4
  38. data/bin/lib/Image/ExifTool/Writer.pl +42 -66
  39. data/bin/lib/Image/ExifTool/XMP.pm +19 -4
  40. data/bin/lib/Image/ExifTool/XMP2.pl +60 -0
  41. data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -2
  42. data/bin/lib/Image/ExifTool.pm +204 -86
  43. data/bin/lib/Image/ExifTool.pod +58 -31
  44. data/bin/perl-Image-ExifTool.spec +1 -1
  45. data/lib/exiftool_vendored/version.rb +1 -1
  46. metadata +2 -2
@@ -88,7 +88,7 @@ sub ProcessCTMD($$$);
88
88
  sub ProcessExifInfo($$$);
89
89
  sub SwapWords($);
90
90
 
91
- $VERSION = '4.82';
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/1) rational values for FocalPlaneX/YResolution
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 ($xres, $yres) = @_;
9841
- # most Canon cameras store the sensor size in the denominator
9842
- if ($xres and $yres) {
9843
- # assumptions: 1) numerators are image width/height * 1000
9844
- # 2) denominators are sensor width/height in inches * 1000
9845
- my @xres = split /[ \/]/, $xres;
9846
- my @yres = split /[ \/]/, $yres;
9847
- # verify assumptions as best we can:
9848
- # numerators are always divisible by 1000
9849
- if ($xres[0] % 1000 == 0 and $yres[0] % 1000 == 0 and
9850
- # at least 640x480 pixels (DC models - PH)
9851
- $xres[0] >= 640000 and $yres[0] >= 480000 and
9852
- # ... but not too big!
9853
- $xres[0] < 10000000 and $yres[0] < 10000000 and
9854
- # minimum sensor size is 0.061 inches (DC models - PH)
9855
- $xres[1] >= 61 and $xres[1] < 1500 and
9856
- $yres[1] >= 61 and $yres[1] < 1000 and
9857
- # sensor isn't square (may happen if rationals have been reduced)
9858
- $xres[1] != $yres[1])
9859
- {
9860
- return sqrt($xres[1]*$xres[1] + $yres[1]*$yres[1]) * 0.0254;
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.52';
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 => 'Title', Writable => 'string', Avoid => 1 }, #33
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{RATIONAL}{$tagKey} = $rational if defined $rational;
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.48';
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} if $$extra{$key};
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} if $$extra{$key};
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} if $$extra{$key};
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
- my $extra = $$et{TAG_EXTRA}{$key};
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.20';
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 => 1, # length bytes are included in the data
71
- Writable => 2,
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
- # Process meta information in GIF image
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 $$addDirs{XMP} or $$addDirs{ICC_Profile} or defined $newComment;
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 extension containing XMP block if necessary
242
- # (this will place XMP before the first non-extension block)
243
- if (exists $$addDirs{XMP} and not defined $doneDir{XMP}) {
244
- $doneDir{XMP} = 1;
245
- # write new XMP data
246
- my $xmpTable = GetTagTable('Image::ExifTool::XMP::Main');
247
- my %dirInfo = ( Parent => 'GIF' );
248
- $verbose and print $out "Creating XMP application extension block:\n";
249
- $buff = $et->WriteDirectory(\%dirInfo, $xmpTable);
250
- if (defined $buff and length $buff) {
251
- my $lz = pack('C*',1,reverse(0..255),0);
252
- Write($outfile, "\x21\xff\x0bXMP DataXMP", $buff, $lz) or $err = 1;
253
- ++$doneDir{XMP}; # set to 2 to indicate we added XMP
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
- $verbose and print $out " -> no XMP to add\n";
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
- Write($outfile, "\x21\xff\x0bICCRGBG1012") or $err = 1;
269
- my $len = length $buff;
270
- while ($pos < $len) {
271
- my $n = $len - $pos;
272
- $n = 255 if $n > 255;
273
- Write($outfile, chr($n), substr($buff, $pos, $n)) or $err = 1;
274
- $pos += $n;
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
- Write($outfile, "\0") or $err = 1; # write null terminator
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 ICC_Profile to add\n";
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
- $subdir = $$extInfo{SubDirectory};
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
- # rewrite as-is unless this is a writable subdirectory
403
- $justCopy = 1 if $outfile and (not $subdir or not $$extInfo{Writable});
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
- Write($outfile, "\0") if $justCopy;
419
-
420
- if ($subdir) {
421
- my $dirLen = length $dat;
422
- my $name = $$extInfo{Name};
423
- if ($name eq 'XMP') {
424
- # get length of XMP without landing zone data
425
- # (note that LZ data may not be exactly the same as what we use)
426
- $dirLen = pos($dat) if $dat =~ /<\?xpacket end=['"][wr]['"]\?>/g;
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 => $dirLen,
464
+ DirLen => length $dat,
432
465
  DirName => $name,
433
466
  Parent => 'GIF',
434
467
  );
435
468
  my $subTable = GetTagTable($$subdir{TagTable});
436
- if (not $outfile) {
469
+ unless ($outfile) {
437
470
  $et->ProcessDirectory(\%dirInfo, $subTable);
438
- } elsif ($$extInfo{Writable}) {
439
- if ($doneDir{$name} and $doneDir{$name} > 1) {
440
- $et->Warn("Duplicate $name block created");
441
- }
442
- $buff = $et->WriteDirectory(\%dirInfo, $subTable);
443
- if (defined $buff) {
444
- next unless length $buff; # delete this extension if length is zero
445
- # check for null just to be safe
446
- $et->Error("$name contained NULL character") if $buff =~ /\0/;
447
- $dat = $buff;
448
- # add landing zone (without terminator, which will be added later)
449
- $dat .= pack('C*',1,reverse(0..255)) if $$extInfo{IncludeLengthBytes};
450
- } # (else rewrite original data)
451
-
452
- $doneDir{$name} = 1;
453
-
454
- if ($$extInfo{IncludeLengthBytes}) {
455
- # write data and landing zone
456
- Write($outfile, $hdr, $dat) or $err = 1;
457
- } else {
458
- # write as sub-blocks
459
- Write($outfile, $hdr) or $err = 1;
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.78';
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'.' '.abs($time-$tn).' > '.$geoMaxExtSecs;
1131
- $et->VPrint(2, ' Nearest fix: ', PrintFixTime($tn), "\n") if $verbose > 2;
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.08';
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} and $$et{TAG_EXTRA}{$tag}{Units}) {
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;