exiftool-vendored.pl 12.97.0 → 13.0.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.
Files changed (37) hide show
  1. package/bin/Changes +48 -2
  2. package/bin/META.json +1 -1
  3. package/bin/META.yml +1 -1
  4. package/bin/README +2 -2
  5. package/bin/arg_files/exif2xmp.args +4 -0
  6. package/bin/arg_files/xmp2exif.args +4 -0
  7. package/bin/config_files/example.config +2 -1
  8. package/bin/exiftool +255 -54
  9. package/bin/lib/File/RandomAccess.pm +5 -2
  10. package/bin/lib/Image/ExifTool/APP12.pm +3 -2
  11. package/bin/lib/Image/ExifTool/Canon.pm +2 -1
  12. package/bin/lib/Image/ExifTool/Exif.pm +1 -1
  13. package/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  14. package/bin/lib/Image/ExifTool/Geotag.pm +4 -3
  15. package/bin/lib/Image/ExifTool/Import.pm +7 -3
  16. package/bin/lib/Image/ExifTool/InDesign.pm +4 -3
  17. package/bin/lib/Image/ExifTool/JSON.pm +3 -4
  18. package/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -1
  19. package/bin/lib/Image/ExifTool/Lytro.pm +2 -2
  20. package/bin/lib/Image/ExifTool/M2TS.pm +10 -2
  21. package/bin/lib/Image/ExifTool/PhaseOne.pm +2 -1
  22. package/bin/lib/Image/ExifTool/QuickTime.pm +17 -6
  23. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +88 -9
  24. package/bin/lib/Image/ExifTool/Sony.pm +6 -1
  25. package/bin/lib/Image/ExifTool/TagLookup.pm +14 -9
  26. package/bin/lib/Image/ExifTool/TagNames.pod +40 -23
  27. package/bin/lib/Image/ExifTool/WritePDF.pl +47 -21
  28. package/bin/lib/Image/ExifTool/WriteXMP.pl +16 -4
  29. package/bin/lib/Image/ExifTool/Writer.pl +16 -6
  30. package/bin/lib/Image/ExifTool/XMP.pm +19 -4
  31. package/bin/lib/Image/ExifTool/XMPStruct.pl +15 -7
  32. package/bin/lib/Image/ExifTool.pm +37 -12
  33. package/bin/lib/Image/ExifTool.pod +31 -8
  34. package/bin/perl-Image-ExifTool.spec +1 -1
  35. package/bin/pp_build_exe.args +4 -4
  36. package/bin/windows_exiftool.txt +32 -10
  37. package/package.json +3 -3
package/bin/Changes CHANGED
@@ -4,9 +4,55 @@ ExifTool Version History
4
4
 
5
5
  RSS feed: https://exiftool.org/rss.xml
6
6
 
7
- Note: The most recent production release is Version 12.76. (Other versions are
7
+ Note: The most recent production release is Version 13.00. (Other versions are
8
8
  considered development releases, and are not uploaded to MetaCPAN.)
9
9
 
10
+ Oct. 29, 2024 - Version 13.00 (production release)
11
+
12
+ - Added support for reading GPS from Autel Evo II drone videos
13
+ - Added a new PhaseOne RawFormat value
14
+ - Updated XMP-exifEX tags, exif2xmp.args and xmp2exif.args according to the
15
+ 2024 EXIF for XMP specification
16
+ - Updated Geolocation databases from current geonames.org files
17
+ - Updated the MIME types for a couple of font formats
18
+ - Enhanced -diff option to work with -ec (C-style escape of special chars)
19
+ - Changed name of EXIF Title tag to ImageTitle to correspond with specifiation
20
+ - Reverted a geotag warning message to remove debugging information that was
21
+ added in version 12.67 and moved this information into a -v3 verbose message
22
+ - Patched Perl bug which gave "Can't spawn" warning in Windows if control-C
23
+ was pressed while displaying the exiftool application help
24
+ - Fixed API StructFormat JSONQ setting to properly quote values in structures
25
+ - Fixed incorrect AudioStreamType for M2TS videos containing a PGS stream
26
+ - Fixed -diff option so "same tags" messages are only shown when -v is added
27
+ (as per the documentation)
28
+
29
+ Oct. 18, 2024 - Version 12.99
30
+
31
+ - Added -diff option to compare the metadata in two files
32
+ - Added a new Canon lens (thanks Norbert Wasser)
33
+ - Decode GPS from 70mai A810 dashcam videos
34
+ - Decode a new QuickTime tag
35
+ - Patched to recognize C2PA APP11 JUMBF header with incorrect byte order
36
+ written by buggy Microsoft software
37
+ - Patched to maintain order of entries in a JSON object when reading
38
+ - Patched to maintain order of CSV columns when setting tags from a CSV file
39
+ - Patched to maintain order of XMP lang-alt entries when writing/copying
40
+ - Fixed typo in an APP12 tag name
41
+ - API Changes:
42
+ - Structured values returned as HASH references with the Struct option may
43
+ contain a new "_ordered_keys_" entry used to preserve the order of the
44
+ entries
45
+ - Added the OrderedKeys method to return the ordered or sorted keys from a
46
+ returned structure value
47
+
48
+ Oct. 8, 2024 - Version 12.98
49
+
50
+ - Added write support for PDF files with huge offsets
51
+ - Added a number of new Sony LensType values (thanks Jos Roost)
52
+ - Require -v4 or higher to output "JPG RST" lines
53
+ - Patched problem with -fast option when reading HEIC file via a pipe
54
+ - Patched to avoid hang when reading some corrupted XMP
55
+
10
56
  Sept. 25, 2024 - Version 12.97
11
57
 
12
58
  - Added ability to ignore up to 4095 bytes of garbage at the end of an INDD
@@ -143,7 +189,7 @@ June 7, 2024 - Version 12.86
143
189
  - Patched some Olympus WB_RBLevels tags to allow 4 values to be written as per
144
190
  some newer models
145
191
  - Fixed issue when writing IPTC date tags with a date/time value containing
146
- subseconds with 4 or more digits
192
+ subseconds with 4 or more digits
147
193
 
148
194
  May 21, 2024 - Version 12.85
149
195
 
package/bin/META.json CHANGED
@@ -50,5 +50,5 @@
50
50
  }
51
51
  },
52
52
  "release_status" : "stable",
53
- "version" : "12.97"
53
+ "version" : "13.00"
54
54
  }
package/bin/META.yml CHANGED
@@ -31,4 +31,4 @@ recommends:
31
31
  Time::HiRes: '0'
32
32
  requires:
33
33
  perl: '5.004'
34
- version: '12.97'
34
+ version: '13.00'
package/bin/README CHANGED
@@ -109,8 +109,8 @@ your home directory, then you would type the following commands in a
109
109
  terminal window to extract and run ExifTool:
110
110
 
111
111
  cd ~/Desktop
112
- gzip -dc Image-ExifTool-12.97.tar.gz | tar -xf -
113
- cd Image-ExifTool-12.97
112
+ gzip -dc Image-ExifTool-13.00.tar.gz | tar -xf -
113
+ cd Image-ExifTool-13.00
114
114
  ./exiftool t/images/ExifTool.jpg
115
115
 
116
116
  Note: These commands extract meta information from one of the test images.
@@ -13,6 +13,7 @@
13
13
  # 2015/01/12 - PH Avoid copying to non-standard namespaces
14
14
  # 2018/05/07 - PH Added support for GPSDestXxxRef tags
15
15
  # 2022/03/31 - PH IPTC Photometadata Mapping Guidelines 2202.1 update
16
+ # 2024/10/24 - PH Additions for the 2024 EXIF for XMP spec
16
17
  #
17
18
  # References: http://www.metadataworkinggroup.org/specs/
18
19
  # https://iptc.org/std/photometadata/documentation/mappingguidelines/
@@ -50,4 +51,7 @@
50
51
  -XMP:GPSDestLatitude < Composite:GPSDestLatitude
51
52
  -XMP:GPSDestLongitude < Composite:GPSDestLongitude
52
53
  -XMP:GPSDateTime < Composite:GPSDateTime
54
+ # new for the 2024 EXIF for XMP specification
55
+ -XMP-exifEX:PhotographicSensitivity < EXIF:ISO
56
+ -XMP-exif:DateTimeDigitized < EXIF:CreateDate
53
57
  # end
@@ -17,6 +17,7 @@
17
17
  # 2022/03/31 - PH IPTC Photometadata Mapping Guidelines 2202.1 update
18
18
  # 2023/01/30 - PH Also write Composite:SubSecDateTimeOriginal from
19
19
  # XMP-exif:DateTimeOriginal
20
+ # 2024/10/24 - PH Additions for the 2024 EXIF for XMP spec
20
21
  #
21
22
  # References: http://www.metadataworkinggroup.org/specs/
22
23
  # https://iptc.org/std/photometadata/documentation/mappingguidelines/
@@ -36,6 +37,9 @@
36
37
  -EXIF:all < XMP-tiff:all
37
38
  -EXIF:ImageDescription < XMP-dc:Description
38
39
  -EXIF:DateTimeOriginal < XMP-photoshop:DateCreated
40
+ # new for the 2024 EXIF for XMP specification
41
+ -EXIF:ISO < XMP-exifEX:PhotographicSensitivity
42
+ -EXIF:CreateDate < XMP-exif:DateTimeDigitized
39
43
  # the following SubSec tags also write/delete the corresponding EXIF
40
44
  # SubSecTime and OffsetTime tags as appropriate
41
45
  -Composite:SubSecDateTimeOriginal < XMP-exif:DateTimeOriginal
@@ -82,7 +82,8 @@
82
82
  },
83
83
  # add more user-defined EXIF tags here...
84
84
  },
85
- # the Geotag feature writes these additional GPS tags if available:
85
+ # the Geotag feature writes GPSPitch and GPSRoll tags, but these
86
+ # aren't standard EXIF so we define custom tags here:
86
87
  'Image::ExifTool::GPS::Main' => {
87
88
  # Example 2. GPS:GPSPitch
88
89
  0xd000 => {
package/bin/exiftool CHANGED
@@ -11,7 +11,7 @@ use strict;
11
11
  use warnings;
12
12
  require 5.004;
13
13
 
14
- my $version = '12.97';
14
+ my $version = '13.00';
15
15
 
16
16
  # add our 'lib' directory to the include list BEFORE 'use Image::ExifTool'
17
17
  my $exePath;
@@ -56,6 +56,8 @@ sub PrintCSV();
56
56
  sub AddGroups($$$$);
57
57
  sub ConvertBinary($);
58
58
  sub IsEqual($$);
59
+ sub Printable($);
60
+ sub LengthUTF8($);
59
61
  sub Infile($;$);
60
62
  sub AddSetTagsFile($;$);
61
63
  sub Warning($$);
@@ -150,6 +152,7 @@ my $csvAdd; # flag to add CSV information to existing lists
150
152
  my $csvDelim; # delimiter for CSV files
151
153
  my $csvSaveCount; # save counter for last CSV file loaded
152
154
  my $deleteOrig; # 0=restore original files, 1=delete originals, 2=delete w/o asking
155
+ my $diff; # file name for comparing differences
153
156
  my $disableOutput; # flag to disable normal output
154
157
  my $doSetFileName; # flag set if FileName may be written
155
158
  my $doUnzip; # flag to extract info from .gz and .bz2 files
@@ -256,11 +259,13 @@ my %optArgs = (
256
259
  '-csvdelim' => 1,
257
260
  '-d' => 1, '-dateformat' => 1,
258
261
  '-D' => 0, # necessary to avoid matching lower-case equivalent
262
+ '-diff' => 1,
259
263
  '-echo' => 1, '-echo#' => 1,
260
264
  '-efile' => 1, '-efile#' => 1, '-efile!' => 1, '-efile#!' => 1,
261
265
  '-ext' => 1, '--ext' => 1, '-ext+' => 1, '--ext+' => 1,
262
266
  '-extension' => 1, '--extension' => 1, '-extension+' => 1, '--extension+' => 1,
263
267
  '-fileorder' => 1, '-fileorder#' => 1,
268
+ '-file#' => 1,
264
269
  '-geotag' => 1,
265
270
  '-globaltimeshift' => 1,
266
271
  '-i' => 1, '-ignore' => 1,
@@ -332,7 +337,7 @@ sub Exit {
332
337
  exit shift;
333
338
  }
334
339
  # my warning and error routines (NEVER say "die"!)
335
- sub Warn {
340
+ sub Warn {
336
341
  if ($quiet < 2 or $_[0] =~ /^Error/) {
337
342
  my $oldWarn = $SIG{'__WARN__'};
338
343
  delete $SIG{'__WARN__'};
@@ -472,6 +477,7 @@ undef $comma;
472
477
  undef $csv;
473
478
  undef $csvAdd;
474
479
  undef $deleteOrig;
480
+ undef $diff;
475
481
  undef $disableOutput;
476
482
  undef $doSetFileName;
477
483
  undef $doUnzip;
@@ -943,6 +949,11 @@ for (;;) {
943
949
  next;
944
950
  }
945
951
  (/^D$/ or $a eq 'decimal') and $showTagID = 'D', next;
952
+ if (/^diff$/i) {
953
+ $diff = shift;
954
+ defined $diff or Error("Expecting file name for -$_ option\n"), $badCmd=1;
955
+ next;
956
+ }
946
957
  /^delete_original(!?)$/i and $deleteOrig = ($1 ? 2 : 1), next;
947
958
  /^list_dir$/i and $listDir = 1, next;
948
959
  (/^e$/ or $a eq '-composite') and $mt->Options(Composite => 0), next;
@@ -1501,8 +1512,11 @@ if ($overwriteOrig > 1 and $outOpt) {
1501
1512
  next;
1502
1513
  }
1503
1514
 
1504
- if ($tagOut and ($csv or %printFmt or $tabFormat or $xml or ($verbose and $html))) {
1505
- Warn "Sorry, -W may not be combined with -csv, -htmlDump, -j, -p, -t or -X\n";
1515
+ if (($tagOut or defined $diff) and ($csv or $json or %printFmt or $tabFormat or $xml or
1516
+ ($verbose and $html)))
1517
+ {
1518
+ my $opt = $tagOut ? '-W' : '-diff';
1519
+ Warn "Sorry, $opt may not be combined with -csv, -htmlDump, -j, -p, -t or -X\n";
1506
1520
  $rtnVal = 1;
1507
1521
  next;
1508
1522
  }
@@ -1757,9 +1771,14 @@ if (@newValues) {
1757
1771
  $rtnVal = 1;
1758
1772
  next;
1759
1773
  }
1760
- if ($isWriting and @tags and not $outOpt) {
1761
- my ($tg, $s) = @tags > 1 ? ("$tags[0] ...", 's') : ($tags[0], '');
1762
- Warn "Ignored superfluous tag name$s or invalid option$s: -$tg\n";
1774
+ if ($isWriting) {
1775
+ if (defined $diff) {
1776
+ Error "Can't use -diff option when writing tags\n";
1777
+ next;
1778
+ } elsif (@tags and not $outOpt) {
1779
+ my ($tg, $s) = @tags > 1 ? ("$tags[0] ...", 's') : ($tags[0], '');
1780
+ Warn "Ignored superfluous tag name$s or invalid option$s: -$tg\n";
1781
+ }
1763
1782
  }
1764
1783
  # save current state of new values if setting values from target file
1765
1784
  # or if we may be translating to a different format
@@ -1804,8 +1823,7 @@ if ($outOpt) {
1804
1823
  $type = Image::ExifTool::GetFileExtension($outOpt);
1805
1824
  $type = uc($outOpt) unless defined $type;
1806
1825
  }
1807
- Warn "Can't write $type files\n";
1808
- $rtnVal = 1;
1826
+ Error "Can't write $type files\n";
1809
1827
  next;
1810
1828
  }
1811
1829
  $scanWritable = $type unless CanCreate($type);
@@ -1903,10 +1921,7 @@ if (@dbKeys) {
1903
1921
  # process all specified files
1904
1922
  ProcessFiles($mt);
1905
1923
 
1906
- if ($filtered and not $validFile) {
1907
- Warn "No file with specified extension\n";
1908
- $rtnVal = 1;
1909
- }
1924
+ Error "No file with specified extension\n" if $filtered and not $validFile;
1910
1925
 
1911
1926
  # print CSV information if necessary
1912
1927
  PrintCSV() if $csv and not $isWriting;
@@ -2014,7 +2029,7 @@ Exit $rtnValApp; # all done
2014
2029
  sub GetImageInfo($$)
2015
2030
  {
2016
2031
  my ($et, $orig) = @_;
2017
- my (@foundTags, $info, $file, $ind, $g8);
2032
+ my (@foundTags, @found2, $info, $info2, $et2, $file, $file2, $ind, $g8);
2018
2033
 
2019
2034
  # set window title for this file if necessary
2020
2035
  if (defined $windowTitle) {
@@ -2151,7 +2166,7 @@ sub GetImageInfo($$)
2151
2166
  }
2152
2167
  # can't make use of $info if verbose because we must reprocess
2153
2168
  # the file anyway to generate the verbose output
2154
- undef $info if $verbose or defined $fastCondition;
2169
+ undef $info if $verbose or defined $fastCondition or defined $diff;
2155
2170
  } elsif ($file =~ s/^(\@JSON:)(.*)/$1/) {
2156
2171
  # read JSON file from command line
2157
2172
  my $dat = $2;
@@ -2183,7 +2198,7 @@ sub GetImageInfo($$)
2183
2198
 
2184
2199
  my $lineCount = 0;
2185
2200
  my ($fp, $outfile, $append);
2186
- if ($textOut and ($verbose or $et->Options('PrintCSV')) and not $tagOut) {
2201
+ if ($textOut and ($verbose or $et->Options('PrintCSV')) and not ($tagOut or defined $diff)) {
2187
2202
  ($fp, $outfile, $append) = OpenOutputFile($orig);
2188
2203
  $fp or EFile($file), ++$countBad, return;
2189
2204
  # delete file if we exit prematurely (unless appending)
@@ -2228,7 +2243,7 @@ sub GetImageInfo($$)
2228
2243
  require Image::ExifTool::HTML;
2229
2244
  my $f = Image::ExifTool::HTML::EscapeHTML($file);
2230
2245
  print "<!-- $f -->\n";
2231
- } elsif (not ($json or $xml)) {
2246
+ } elsif (not ($json or $xml or defined $diff)) {
2232
2247
  $o = \*STDOUT if ($multiFile and not $quiet) or $progress;
2233
2248
  }
2234
2249
  }
@@ -2257,10 +2272,37 @@ sub GetImageInfo($$)
2257
2272
  } else {
2258
2273
  @foundTags = @tags;
2259
2274
  }
2275
+ if (defined $diff) {
2276
+ $file2 = FilenameSPrintf($diff, $orig);
2277
+ if ($file eq $file2) {
2278
+ Warn "Error: Diffing file with itself - $file2\n";
2279
+ EFile($file);
2280
+ ++$countBad;
2281
+ return;
2282
+ }
2283
+ if ($et->Exists($file2)) {
2284
+ $showGroup = 1 unless defined $showGroup;
2285
+ $allGroup = 1 unless defined $allGroup;
2286
+ $et->Options(Duplicates => 1, Sort => "Group$showGroup", Verbose => 0);
2287
+ $et2 = Image::ExifTool->new;
2288
+ $et2->Options(%{$$et{OPTIONS}});
2289
+ @found2 = @foundTags;
2290
+ $info2 = $et2->ImageInfo($file2, \@found2);
2291
+ } else {
2292
+ $info2 = { Error => "Diff file not found" };
2293
+ }
2294
+ if ($$info2{Error}) {
2295
+ Warn "Error: $$info2{Error} - $file2\n";
2296
+ EFile($file);
2297
+ ++$countBad;
2298
+ return;
2299
+ }
2300
+ }
2260
2301
  # extract the information
2261
2302
  $info = $et->ImageInfo(Infile($pipe), \@foundTags);
2262
2303
  $et->Options(Duplicates => $oldDups);
2263
2304
  }
2305
+
2264
2306
  # all done now if we already wrote output text file (eg. verbose option)
2265
2307
  if ($fp) {
2266
2308
  if (defined $outfile) {
@@ -2274,7 +2316,7 @@ sub GetImageInfo($$)
2274
2316
  }
2275
2317
  }
2276
2318
  if ($info->{Error}) {
2277
- Warn "Error: $info->{Error} - $file\n";
2319
+ Warn "Error: $$info{Error} - $file\n";
2278
2320
  EFile($file);
2279
2321
  ++$countBad;
2280
2322
  return;
@@ -2298,6 +2340,105 @@ sub GetImageInfo($$)
2298
2340
  $tmpText = $outfile unless $append;
2299
2341
  }
2300
2342
 
2343
+ # print differences if requested
2344
+ if (defined $diff) {
2345
+ my (%done2, $wasDiff, @diffs, @groupTags2);
2346
+ my $v = $verbose || 0;
2347
+ print $fp "======== diff < $file > $file2\n";
2348
+ my ($g2, $same) = (0, 0); # start with $g2 false, but not equal to '' to avoid infinite loop
2349
+ for (;;) {
2350
+ my $tag = shift @foundTags;
2351
+ my ($g, $tag2);
2352
+ if (defined $tag) {
2353
+ $g = $et->GetGroup($tag, $showGroup);
2354
+ } else {
2355
+ for (;;) {
2356
+ $tag2 = shift @found2;
2357
+ defined $tag2 or $g = '', last;
2358
+ $done2{$tag2} or $g = $et2->GetGroup($tag2, $showGroup), last;
2359
+ }
2360
+ }
2361
+ if ($g ne $g2) {
2362
+ my $t2;
2363
+ # add any outstanding tags from diff file not yet handled in previous group ($g2)
2364
+ foreach $t2 (@groupTags2) {
2365
+ next if $done2{$t2};
2366
+ my $val2 = $et2->GetValue($t2);
2367
+ next unless defined $val2;
2368
+ my $name = $outFormat < 1 ? $et2->GetDescription($t2) : GetTagName($t2);
2369
+ my $len = LengthUTF8($name);
2370
+ my $pad = $outFormat < 2 ? ' ' x ($len < 32 ? 32 - $len : 0) : '';
2371
+ if ($allGroup) {
2372
+ my $grp = "[$g2]";
2373
+ $grp .= ' ' x (15 - length($grp)) if length($grp) < 15 and $outFormat < 2;
2374
+ push @diffs, sprintf "> %s %s%s: %s\n", $grp, $name, $pad, Printable($val2);
2375
+ } else {
2376
+ push @diffs, sprintf "> %s%s: %s\n", $name, $pad, Printable($val2);
2377
+ }
2378
+ $done2{$t2} = 1;
2379
+ }
2380
+ my $str = '';
2381
+ $v and ($same or $v > 1) and $str = " ($same same tag" . ($same==1 ? '' : 's') . ')';
2382
+ if (not $allGroup) {
2383
+ print $fp "---- $g2 ----$str\n" if $g2 and ($str or @diffs);
2384
+ } elsif ($str and $g2) {
2385
+ printf $fp " %-13s%s\n", $g2, $str;
2386
+ }
2387
+ # print all differences for this group
2388
+ @diffs and print($fp @diffs), $wasDiff = 1, @diffs = ();
2389
+ last unless $g;
2390
+ ($g2, $same) = ($g, 0);
2391
+ # build list of all tags in the new group of the diff file
2392
+ @groupTags2 = ();
2393
+ foreach $t2 (@found2) {
2394
+ $done2{$t2} or $g ne $et2->GetGroup($t2, $showGroup) or push @groupTags2, $t2;
2395
+ }
2396
+ }
2397
+ next unless defined $tag;
2398
+ my $val = $et->GetValue($tag);
2399
+ next unless defined $val; # (just in case)
2400
+ my $name = GetTagName($tag);
2401
+ # get matching tag key from diff file
2402
+ my @tags2 = grep /^$name( |$)/, @groupTags2;
2403
+ $name = $et->GetDescription($tag) if $outFormat < 1;
2404
+ my ($val2, $t2);
2405
+ foreach $t2 (@tags2) {
2406
+ next if $done2{$t2};
2407
+ $tag2 = $t2;
2408
+ $val2 = $et2->GetValue($t2);
2409
+ last;
2410
+ }
2411
+ if (defined $val2 and IsEqual($val, $val2)) {
2412
+ ++$same;
2413
+ } else {
2414
+ my $len = LengthUTF8($name);
2415
+ my $pad = $outFormat < 2 ? ' ' x ($len < 32 ? 32 - $len : 0) : '';
2416
+ if ($allGroup) {
2417
+ my $grp = "[$g]";
2418
+ $grp .= ' ' x (15 - length($grp)) if length($grp) < 15 and $outFormat < 2;
2419
+ push @diffs, sprintf "< %s %s%s: %s\n", $grp, $name, $pad, Printable($val);
2420
+ if (defined $val2) {
2421
+ $grp = ' ' x length($grp), $name = ' ' x $len if $v < 3;
2422
+ push @diffs, sprintf "> %s %s%s: %s\n", $grp, $name, $pad, Printable($val2);
2423
+ }
2424
+ } else {
2425
+ push @diffs, sprintf "< %s%s: %s\n", $name, $pad, Printable($val);
2426
+ $name = ' ' x $len if $v < 3;
2427
+ push @diffs, sprintf "> %s%s %s\n", $name, $pad, Printable($val2) if defined $val2;
2428
+ }
2429
+ }
2430
+ $done2{$tag2} = 1 if defined $tag2;
2431
+ }
2432
+ print $fp "(no metadata differences)\n" unless $wasDiff;
2433
+ undef $tmpText;
2434
+ if (defined $outfile) {
2435
+ ++$created{$outfile};
2436
+ close($fp);
2437
+ undef $tmpText;
2438
+ }
2439
+ ++$count;
2440
+ return;
2441
+ }
2301
2442
  # restore state of comma flag for this file if appending
2302
2443
  $comma = $outComma{$outfile} if $append and ($textOverwrite & 0x02);
2303
2444
 
@@ -2766,21 +2907,7 @@ TAG: foreach $tag (@foundTags) {
2766
2907
  # pad description to a constant length
2767
2908
  # (get actual character length when using alternate languages
2768
2909
  # because these descriptions may contain UTF8-encoded characters)
2769
- my $padLen = $wid;
2770
- if (not $fixLen) {
2771
- $padLen -= length $desc;
2772
- } elsif ($fixLen == 1) {
2773
- $padLen -= length Encode::decode_utf8($desc);
2774
- } else {
2775
- my $gcstr = eval { Unicode::GCString->new(Encode::decode_utf8($desc)) };
2776
- if ($gcstr) {
2777
- $padLen -= $gcstr->columns;
2778
- } else {
2779
- $padLen -= length Encode::decode_utf8($desc);
2780
- Warning($et, 'Unicode::GCString problem. Columns may be misaligned');
2781
- $fixLen = 1;
2782
- }
2783
- }
2910
+ my $padLen = $wid - LengthUTF8($desc);
2784
2911
  $padLen = 0 if $padLen < 0;
2785
2912
  $buff .= $desc . (' ' x $padLen) . ": $val\n";
2786
2913
  } elsif ($outFormat == 2) {
@@ -3011,7 +3138,7 @@ sub SetImageInfo($$$)
3011
3138
  }
3012
3139
  $found = 1;
3013
3140
  $verbose and print $vout "Setting new values from $csv database\n";
3014
- foreach $tag (sort keys %$csvInfo) {
3141
+ foreach $tag (OrderedKeys($csvInfo)) {
3015
3142
  next if $tag =~ /\b(SourceFile|Directory|FileName)$/i; # don't write these
3016
3143
  my ($rtn, $wrn) = $et->SetNewValue($tag, $$csvInfo{$tag},
3017
3144
  Protected => 1, AddValue => $csvAdd,
@@ -3427,8 +3554,7 @@ sub FormatXML($$$)
3427
3554
  } elsif (ref $val eq 'HASH') {
3428
3555
  $gt = " rdf:parseType='Resource'>";
3429
3556
  my $val2 = '';
3430
- my @keys = $$val{_ordered_keys_} ? @{$$val{_ordered_keys_}} : sort keys %$val;
3431
- foreach (@keys) {
3557
+ foreach (OrderedKeys($val)) {
3432
3558
  # (some variable-namespace XML structure fields may have a different group)
3433
3559
  my ($ns, $tg) = ($grp, $_);
3434
3560
  if (/^(.*?):(.*)/) {
@@ -3512,7 +3638,7 @@ sub FormatJSON($$$;$)
3512
3638
  print $fp $bra;
3513
3639
  foreach (@$val) {
3514
3640
  print $fp ',' if $comma;
3515
- FormatJSON($fp, $_, $ind);
3641
+ FormatJSON($fp, $_, $ind, $quote);
3516
3642
  $comma = 1,
3517
3643
  }
3518
3644
  print $fp $ket,
@@ -3520,8 +3646,7 @@ sub FormatJSON($$$;$)
3520
3646
  } elsif (ref $val eq 'HASH') {
3521
3647
  my ($bra, $ket, $sep) = $json == 1 ? ('{','}',':') : ('Array(',')',' =>');
3522
3648
  print $fp $bra;
3523
- my @keys = $$val{_ordered_keys_} ? @{$$val{_ordered_keys_}} : sort keys %$val;
3524
- foreach (@keys) {
3649
+ foreach (OrderedKeys($val)) {
3525
3650
  print $fp ',' if $comma;
3526
3651
  my $key = EscapeJSON($_, 1);
3527
3652
  print $fp qq(\n$ind $key$sep );
@@ -3529,7 +3654,7 @@ sub FormatJSON($$$;$)
3529
3654
  if ($showTagID and $_ eq 'id' and $showTagID eq 'H' and $$val{$_} =~ /^\d+\.\d+$/) {
3530
3655
  print $fp qq{"$$val{$_}"};
3531
3656
  } else {
3532
- FormatJSON($fp, $$val{$_}, "$ind ");
3657
+ FormatJSON($fp, $$val{$_}, "$ind ", $quote);
3533
3658
  }
3534
3659
  $comma = 1,
3535
3660
  }
@@ -3678,6 +3803,59 @@ sub IsEqual($$)
3678
3803
  return 1;
3679
3804
  }
3680
3805
 
3806
+ #------------------------------------------------------------------------------
3807
+ # Get the printable rendition of a value
3808
+ # Inputs: 0) value (may be a reference)
3809
+ # Returns: de-referenced value
3810
+ sub Printable($)
3811
+ {
3812
+ my $val = shift;
3813
+ if (ref $val) {
3814
+ if ($structOpt) {
3815
+ require Image::ExifTool::XMP;
3816
+ $val = Image::ExifTool::XMP::SerializeStruct($mt, $val);
3817
+ } elsif (ref $val eq 'ARRAY') {
3818
+ $val = join($listSep, @$val);
3819
+ } elsif (ref $val eq 'SCALAR') {
3820
+ $val = '(Binary data '.length($$val).' bytes)';
3821
+ }
3822
+ }
3823
+ if ($escapeC) {
3824
+ $val =~ s/([\0-\x1f\\\x7f])/$escC{$1} || sprintf('\x%.2x', ord $1)/eg;
3825
+ } else {
3826
+ # translate unprintable chars in value and remove trailing spaces
3827
+ $val =~ tr/\x01-\x1f\x7f/./;
3828
+ $val =~ s/\x00//g;
3829
+ $val =~ s/\s+$//;
3830
+ }
3831
+ return $val;
3832
+ }
3833
+
3834
+ #------------------------------------------------------------------------------
3835
+ # Get character length of a UTF-8 string
3836
+ # Inputs: 0) string
3837
+ # Returns: number of characters (not bytes) in the UTF-8 string
3838
+ sub LengthUTF8($)
3839
+ {
3840
+ my $str = shift;
3841
+ my $len;
3842
+ if (not $fixLen) {
3843
+ $len = length $str;
3844
+ } elsif ($fixLen == 1) {
3845
+ $len = length Encode::decode_utf8($str);
3846
+ } else {
3847
+ my $gcstr = eval { Unicode::GCString->new(Encode::decode_utf8($str)) };
3848
+ if ($gcstr) {
3849
+ $len = $gcstr->columns;
3850
+ } else {
3851
+ $len = length Encode::decode_utf8($str);
3852
+ Warning($mt, 'Unicode::GCString problem. Columns may be misaligned');
3853
+ $fixLen = 1;
3854
+ }
3855
+ }
3856
+ return $len;
3857
+ }
3858
+
3681
3859
  #------------------------------------------------------------------------------
3682
3860
  # Add tag list for copying tags from specified file
3683
3861
  # Inputs: 0) set tags file name (or FMT), 1) options for SetNewValuesFromFile()
@@ -4867,6 +5045,7 @@ L<Other options|/Other options>
4867
5045
 
4868
5046
  L<Special features|/Special features>
4869
5047
 
5048
+ -diff FILE2 Compare metadata with another file
4870
5049
  -geotag TRKFILE Geotag images from specified GPS log
4871
5050
  -globalTimeShift SHIFT Shift all formatted date/time values
4872
5051
  -use MODULE Add features from plug-in module
@@ -4958,13 +5137,13 @@ may be used to to indicate any XMP namespace (eg. C<--xmp-all:dabs>).
4958
5137
  Write a new value for the specified tag (eg. C<-comment=wow>), or delete the
4959
5138
  tag if no I<VALUE> is given (eg. C<-comment=>). C<+=> and C<-=> are used to
4960
5139
  add or remove existing entries from a list, or to shift date/time values
4961
- (see L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl> and note 6 below
4962
- for more details). C<+=> may also be used to increment numerical values (or
4963
- decrement if I<VALUE> is negative), and C<-=> may be used to conditionally
4964
- delete or replace a tag (see L</WRITING EXAMPLES> for examples). C<^=> is
4965
- used to write an empty string instead of deleting the tag when no I<VALUE>
4966
- is given, but otherwise it is equivalent to C<=>. (Note that the caret must
4967
- be quoted on the Windows command line.)
5140
+ (see L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl> and notes 6 and
5141
+ 7 below for more details). C<+=> may also be used to increment numerical
5142
+ values (or decrement if I<VALUE> is negative), and C<-=> may be used to
5143
+ conditionally delete or replace a tag (see L</WRITING EXAMPLES> for
5144
+ examples). C<^=> is used to write an empty string instead of deleting the
5145
+ tag when no I<VALUE> is given, but otherwise it is equivalent to C<=>.
5146
+ (Note that the caret must be quoted on the Windows command line.)
4968
5147
 
4969
5148
  I<TAG> may contain one or more leading family 0, 1, 2 or 7 group names,
4970
5149
  prefixed by optional family numbers, and separated colons. If no group name
@@ -5039,6 +5218,10 @@ tag, overriding any other values previously assigned to the tag on the same
5039
5218
  command line. To shift a date/time value and copy it to another tag in the
5040
5219
  same operation, use the B<-globalTimeShift> option.
5041
5220
 
5221
+ 7) The C<+=> operator may not be used to shift a List-type date/time tag
5222
+ (eg. XMP-dc:Date) because C<+=> is used to add elements to the list.
5223
+ Instead, the B<-globalTimeShift> option should be used.
5224
+
5042
5225
  Special feature: Integer values may be specified in hexadecimal with a
5043
5226
  leading C<0x>, and simple rational values may be specified as fractions.
5044
5227
 
@@ -5662,7 +5845,7 @@ with this command:
5662
5845
 
5663
5846
  produces output like this:
5664
5847
 
5665
- -- Generated by ExifTool 12.97 --
5848
+ -- Generated by ExifTool 13.00 --
5666
5849
  File: a.jpg - 2003:10:31 15:44:19
5667
5850
  (f/5.6, 1/60s, ISO 100)
5668
5851
  File: b.jpg - 2006:05:23 11:57:38
@@ -6204,9 +6387,10 @@ B<-fileNUM> option is used to read an alternate file and the corresponding
6204
6387
  family 8 group name is specified for the tag. See the B<-fileNUM> option
6205
6388
  details for more information.
6206
6389
 
6207
- 5) The B<-a> option has no effect on the evaluation of the expression, and
6208
- the values of duplicate tags are accessible only by specifying a group name
6209
- (such as a family 4 instance number, eg. C<$Copy1:TAG>, C<$Copy2:TAG>, etc).
6390
+ 5) The B<-a> (Duplicates) option is implied when B<-if> is used without a
6391
+ fast I<NUM>, and the values of duplicate tags are accessible by specifying a
6392
+ group name in the expression (such as a family 4 instance number, eg.
6393
+ C<$Copy1:TAG>, C<$Copy2:TAG>, etc).
6210
6394
 
6211
6395
  6) A special "OK" UserParam is available to test the success of the previous
6212
6396
  command when B<-execute> was used, and may be used like any other tag in the
@@ -6472,9 +6656,9 @@ B<-f> adds 'flags' and 'struct' attributes if applicable. The flags are
6472
6656
  formatted as a comma-separated list of the following possible values:
6473
6657
  Avoid, Binary, List, Mandatory, Permanent, Protected, Unknown and Unsafe
6474
6658
  (see the L<Tag Name documentation|Image::ExifTool::TagNames>). For XMP List
6475
- tags, the list type (Alt, Bag or Seq) is added to the flags, and flattened
6476
- structure tags are indicated by a Flattened flag with 'struct' giving the ID
6477
- of the parent structure.
6659
+ tags, the list type (Alt, Bag or Seq) is also given, and flattened structure
6660
+ tags are indicated by a Flattened flag with 'struct' giving the ID of the
6661
+ parent structure.
6478
6662
 
6479
6663
  Note that none of the B<-list> options require an input I<FILE>.
6480
6664
 
@@ -6496,6 +6680,23 @@ names, even if they begin with a dash (C<->).
6496
6680
 
6497
6681
  =over 5
6498
6682
 
6683
+ =item B<-diff> I<FILE2>
6684
+
6685
+ Compare metadata in I<FILE> with I<FILE2>. The I<FILE2> name may include
6686
+ filename formatting codes (see the B<-w> option). All extracted tags from
6687
+ the files are compared, but the extracted tags may be controlled by adding
6688
+ B<-TAG> or B<--TAG> options. For example, below is a command to compare all
6689
+ the same-named files in two different directories, ignoring the System tags:
6690
+
6691
+ exiftool DIR1 -diff DIR2/%f.%e --system:all
6692
+
6693
+ The B<-g> and B<-G> options may be used to organize the output by the
6694
+ specified family of groups, with B<-G1> being the default. The B<-a> option
6695
+ is implied. Adding B<-v> includes a count of the number of tags that are
6696
+ the same in each group. The following text formatting options are valid
6697
+ when B<-diff> is used: B<-c>, B<-charset>, B<-d>, B<-E>, B<-ec>, B<-ex>,
6698
+ B<-L>, B<-lang>, B<-n>, B<-s>, B<-sep>, B<-struct> and B<-w>.
6699
+
6499
6700
  =item B<-geotag> I<TRKFILE>
6500
6701
 
6501
6702
  Geotag images from the specified GPS track log file. Using the B<-geotag>