exiftool_vendored 12.98.0 → 13.02.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 +73 -1
- 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/config_files/example.config +2 -1
- data/bin/exiftool +297 -66
- data/bin/lib/Image/ExifTool/APP12.pm +3 -2
- data/bin/lib/Image/ExifTool/CBOR.pm +4 -1
- data/bin/lib/Image/ExifTool/Canon.pm +36 -26
- data/bin/lib/Image/ExifTool/Exif.pm +8 -9
- data/bin/lib/Image/ExifTool/FlashPix.pm +5 -9
- 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/Import.pm +7 -3
- data/bin/lib/Image/ExifTool/JSON.pm +3 -4
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -2
- data/bin/lib/Image/ExifTool/LNK.pm +1 -1
- data/bin/lib/Image/ExifTool/Lytro.pm +2 -2
- 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/Nikon.pm +5 -2
- data/bin/lib/Image/ExifTool/PDF.pm +7 -3
- data/bin/lib/Image/ExifTool/PhaseOne.pm +2 -1
- data/bin/lib/Image/ExifTool/QuickTime.pm +11 -1
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +88 -9
- data/bin/lib/Image/ExifTool/TagLookup.pm +14 -9
- data/bin/lib/Image/ExifTool/TagNames.pod +37 -20
- data/bin/lib/Image/ExifTool/Text.pm +3 -2
- data/bin/lib/Image/ExifTool/Validate.pm +2 -2
- data/bin/lib/Image/ExifTool/WriteXMP.pl +16 -4
- data/bin/lib/Image/ExifTool/Writer.pl +42 -61
- data/bin/lib/Image/ExifTool/XMP.pm +13 -3
- data/bin/lib/Image/ExifTool/XMPStruct.pl +16 -9
- data/bin/lib/Image/ExifTool.pm +190 -77
- data/bin/lib/Image/ExifTool.pod +73 -36
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +2 -2
data/bin/exiftool
CHANGED
@@ -11,7 +11,7 @@ use strict;
|
|
11
11
|
use warnings;
|
12
12
|
require 5.004;
|
13
13
|
|
14
|
-
my $version = '
|
14
|
+
my $version = '13.02';
|
15
15
|
|
16
16
|
# add our 'lib' directory to the include list BEFORE 'use Image::ExifTool'
|
17
17
|
my $exePath;
|
@@ -56,11 +56,14 @@ 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($$);
|
62
64
|
sub DoSetFromFile($$$);
|
63
65
|
sub CleanFilename($);
|
66
|
+
sub HasWildcards($);
|
64
67
|
sub SetWindowTitle($);
|
65
68
|
sub ProcessFiles($;$);
|
66
69
|
sub ScanDir($$;$);
|
@@ -150,6 +153,7 @@ my $csvAdd; # flag to add CSV information to existing lists
|
|
150
153
|
my $csvDelim; # delimiter for CSV files
|
151
154
|
my $csvSaveCount; # save counter for last CSV file loaded
|
152
155
|
my $deleteOrig; # 0=restore original files, 1=delete originals, 2=delete w/o asking
|
156
|
+
my $diff; # file name for comparing differences
|
153
157
|
my $disableOutput; # flag to disable normal output
|
154
158
|
my $doSetFileName; # flag set if FileName may be written
|
155
159
|
my $doUnzip; # flag to extract info from .gz and .bz2 files
|
@@ -256,11 +260,13 @@ my %optArgs = (
|
|
256
260
|
'-csvdelim' => 1,
|
257
261
|
'-d' => 1, '-dateformat' => 1,
|
258
262
|
'-D' => 0, # necessary to avoid matching lower-case equivalent
|
263
|
+
'-diff' => 1,
|
259
264
|
'-echo' => 1, '-echo#' => 1,
|
260
265
|
'-efile' => 1, '-efile#' => 1, '-efile!' => 1, '-efile#!' => 1,
|
261
266
|
'-ext' => 1, '--ext' => 1, '-ext+' => 1, '--ext+' => 1,
|
262
267
|
'-extension' => 1, '--extension' => 1, '-extension+' => 1, '--extension+' => 1,
|
263
268
|
'-fileorder' => 1, '-fileorder#' => 1,
|
269
|
+
'-file#' => 1,
|
264
270
|
'-geotag' => 1,
|
265
271
|
'-globaltimeshift' => 1,
|
266
272
|
'-i' => 1, '-ignore' => 1,
|
@@ -332,7 +338,7 @@ sub Exit {
|
|
332
338
|
exit shift;
|
333
339
|
}
|
334
340
|
# my warning and error routines (NEVER say "die"!)
|
335
|
-
sub Warn
|
341
|
+
sub Warn {
|
336
342
|
if ($quiet < 2 or $_[0] =~ /^Error/) {
|
337
343
|
my $oldWarn = $SIG{'__WARN__'};
|
338
344
|
delete $SIG{'__WARN__'};
|
@@ -472,6 +478,7 @@ undef $comma;
|
|
472
478
|
undef $csv;
|
473
479
|
undef $csvAdd;
|
474
480
|
undef $deleteOrig;
|
481
|
+
undef $diff;
|
475
482
|
undef $disableOutput;
|
476
483
|
undef $doSetFileName;
|
477
484
|
undef $doUnzip;
|
@@ -850,7 +857,7 @@ for (;;) {
|
|
850
857
|
} else {
|
851
858
|
print "Available API Options:\n";
|
852
859
|
my $availableOptions = Image::ExifTool::AvailableOptions();
|
853
|
-
printf(" %-17s - %s\n", $$_[0], $$_[2]) foreach @$availableOptions;
|
860
|
+
$$_[3] or printf(" %-17s - %s\n", $$_[0], $$_[2]) foreach @$availableOptions;
|
854
861
|
$helped = 1;
|
855
862
|
}
|
856
863
|
next;
|
@@ -943,6 +950,11 @@ for (;;) {
|
|
943
950
|
next;
|
944
951
|
}
|
945
952
|
(/^D$/ or $a eq 'decimal') and $showTagID = 'D', next;
|
953
|
+
if (/^diff$/i) {
|
954
|
+
$diff = shift;
|
955
|
+
defined $diff or Error("Expecting file name for -$_ option\n"), $badCmd=1;
|
956
|
+
next;
|
957
|
+
}
|
946
958
|
/^delete_original(!?)$/i and $deleteOrig = ($1 ? 2 : 1), next;
|
947
959
|
/^list_dir$/i and $listDir = 1, next;
|
948
960
|
(/^e$/ or $a eq '-composite') and $mt->Options(Composite => 0), next;
|
@@ -1028,7 +1040,7 @@ for (;;) {
|
|
1028
1040
|
}
|
1029
1041
|
$trkfile or Error("Expecting file name for -geotag option\n"), $badCmd=1, next;
|
1030
1042
|
# allow wildcards in filename
|
1031
|
-
if ($trkfile
|
1043
|
+
if (HasWildcards($trkfile)) {
|
1032
1044
|
# CORE::glob() splits on white space, so use File::Glob if possible
|
1033
1045
|
my @trks;
|
1034
1046
|
if ($^O eq 'MSWin32' and eval { require Win32::FindFile }) {
|
@@ -1414,7 +1426,7 @@ for (;;) {
|
|
1414
1426
|
push @nextPass, $_;
|
1415
1427
|
next;
|
1416
1428
|
}
|
1417
|
-
if ($doGlob and
|
1429
|
+
if ($doGlob and HasWildcards($_)) {
|
1418
1430
|
if ($^O eq 'MSWin32' and eval { require Win32::FindFile }) {
|
1419
1431
|
push @files, FindFileWindows($mt, $_);
|
1420
1432
|
} else {
|
@@ -1501,13 +1513,17 @@ if ($overwriteOrig > 1 and $outOpt) {
|
|
1501
1513
|
next;
|
1502
1514
|
}
|
1503
1515
|
|
1504
|
-
if ($tagOut and ($csv or %printFmt or $tabFormat or $xml or
|
1505
|
-
|
1516
|
+
if (($tagOut or defined $diff) and ($csv or $json or %printFmt or $tabFormat or $xml or
|
1517
|
+
($verbose and $html)))
|
1518
|
+
{
|
1519
|
+
my $opt = $tagOut ? '-W' : '-diff';
|
1520
|
+
Warn "Sorry, $opt may not be combined with -csv, -htmlDump, -j, -p, -t or -X\n";
|
1506
1521
|
$rtnVal = 1;
|
1507
1522
|
next;
|
1508
1523
|
}
|
1509
1524
|
|
1510
1525
|
if ($csv and $csv eq 'CSV' and not $isWriting) {
|
1526
|
+
undef $json; # (not compatible)
|
1511
1527
|
if ($textOut) {
|
1512
1528
|
Warn "Sorry, -w may not be combined with -csv\n";
|
1513
1529
|
$rtnVal = 1;
|
@@ -1757,9 +1773,14 @@ if (@newValues) {
|
|
1757
1773
|
$rtnVal = 1;
|
1758
1774
|
next;
|
1759
1775
|
}
|
1760
|
-
if ($isWriting
|
1761
|
-
|
1762
|
-
|
1776
|
+
if ($isWriting) {
|
1777
|
+
if (defined $diff) {
|
1778
|
+
Error "Can't use -diff option when writing tags\n";
|
1779
|
+
next;
|
1780
|
+
} elsif (@tags and not $outOpt) {
|
1781
|
+
my ($tg, $s) = @tags > 1 ? ("$tags[0] ...", 's') : ($tags[0], '');
|
1782
|
+
Warn "Ignored superfluous tag name$s or invalid option$s: -$tg\n";
|
1783
|
+
}
|
1763
1784
|
}
|
1764
1785
|
# save current state of new values if setting values from target file
|
1765
1786
|
# or if we may be translating to a different format
|
@@ -1783,7 +1804,7 @@ if ($binaryOutput) {
|
|
1783
1804
|
}
|
1784
1805
|
|
1785
1806
|
# sort by groups to look nicer depending on options
|
1786
|
-
if (defined $showGroup and not (@tags and $allGroup) and ($sortOpt or not defined $sortOpt)) {
|
1807
|
+
if (defined $showGroup and not (@tags and ($allGroup or $csv)) and ($sortOpt or not defined $sortOpt)) {
|
1787
1808
|
$mt->Options(Sort => "Group$showGroup");
|
1788
1809
|
}
|
1789
1810
|
|
@@ -1804,8 +1825,7 @@ if ($outOpt) {
|
|
1804
1825
|
$type = Image::ExifTool::GetFileExtension($outOpt);
|
1805
1826
|
$type = uc($outOpt) unless defined $type;
|
1806
1827
|
}
|
1807
|
-
|
1808
|
-
$rtnVal = 1;
|
1828
|
+
Error "Can't write $type files\n";
|
1809
1829
|
next;
|
1810
1830
|
}
|
1811
1831
|
$scanWritable = $type unless CanCreate($type);
|
@@ -1822,7 +1842,7 @@ $altEnc = $mt->Options('Charset');
|
|
1822
1842
|
undef $altEnc if $altEnc eq 'UTF8';
|
1823
1843
|
|
1824
1844
|
# set flag to fix description lengths if necessary
|
1825
|
-
if (not $altEnc and $mt->Options('Lang') ne 'en'
|
1845
|
+
if (not $altEnc and $mt->Options('Lang') ne 'en') {
|
1826
1846
|
# (note that Unicode::GCString is part of the Unicode::LineBreak package)
|
1827
1847
|
$fixLen = eval { require Unicode::GCString } ? 2 : 1;
|
1828
1848
|
}
|
@@ -1903,10 +1923,7 @@ if (@dbKeys) {
|
|
1903
1923
|
# process all specified files
|
1904
1924
|
ProcessFiles($mt);
|
1905
1925
|
|
1906
|
-
if
|
1907
|
-
Warn "No file with specified extension\n";
|
1908
|
-
$rtnVal = 1;
|
1909
|
-
}
|
1926
|
+
Error "No file with specified extension\n" if $filtered and not $validFile;
|
1910
1927
|
|
1911
1928
|
# print CSV information if necessary
|
1912
1929
|
PrintCSV() if $csv and not $isWriting;
|
@@ -2014,7 +2031,7 @@ Exit $rtnValApp; # all done
|
|
2014
2031
|
sub GetImageInfo($$)
|
2015
2032
|
{
|
2016
2033
|
my ($et, $orig) = @_;
|
2017
|
-
my (@foundTags, $info, $file, $ind, $g8);
|
2034
|
+
my (@foundTags, @found2, $info, $info2, $et2, $file, $file2, $ind, $g8);
|
2018
2035
|
|
2019
2036
|
# set window title for this file if necessary
|
2020
2037
|
if (defined $windowTitle) {
|
@@ -2151,7 +2168,7 @@ sub GetImageInfo($$)
|
|
2151
2168
|
}
|
2152
2169
|
# can't make use of $info if verbose because we must reprocess
|
2153
2170
|
# the file anyway to generate the verbose output
|
2154
|
-
undef $info if $verbose or defined $fastCondition;
|
2171
|
+
undef $info if $verbose or defined $fastCondition or defined $diff;
|
2155
2172
|
} elsif ($file =~ s/^(\@JSON:)(.*)/$1/) {
|
2156
2173
|
# read JSON file from command line
|
2157
2174
|
my $dat = $2;
|
@@ -2183,7 +2200,7 @@ sub GetImageInfo($$)
|
|
2183
2200
|
|
2184
2201
|
my $lineCount = 0;
|
2185
2202
|
my ($fp, $outfile, $append);
|
2186
|
-
if ($textOut and ($verbose or $et->Options('PrintCSV')) and not $tagOut) {
|
2203
|
+
if ($textOut and ($verbose or $et->Options('PrintCSV')) and not ($tagOut or defined $diff)) {
|
2187
2204
|
($fp, $outfile, $append) = OpenOutputFile($orig);
|
2188
2205
|
$fp or EFile($file), ++$countBad, return;
|
2189
2206
|
# delete file if we exit prematurely (unless appending)
|
@@ -2228,7 +2245,7 @@ sub GetImageInfo($$)
|
|
2228
2245
|
require Image::ExifTool::HTML;
|
2229
2246
|
my $f = Image::ExifTool::HTML::EscapeHTML($file);
|
2230
2247
|
print "<!-- $f -->\n";
|
2231
|
-
} elsif (not ($json or $xml)) {
|
2248
|
+
} elsif (not ($json or $xml or defined $diff)) {
|
2232
2249
|
$o = \*STDOUT if ($multiFile and not $quiet) or $progress;
|
2233
2250
|
}
|
2234
2251
|
}
|
@@ -2257,10 +2274,37 @@ sub GetImageInfo($$)
|
|
2257
2274
|
} else {
|
2258
2275
|
@foundTags = @tags;
|
2259
2276
|
}
|
2277
|
+
if (defined $diff) {
|
2278
|
+
$file2 = FilenameSPrintf($diff, $orig);
|
2279
|
+
if ($file eq $file2) {
|
2280
|
+
Warn "Error: Diffing file with itself - $file2\n";
|
2281
|
+
EFile($file);
|
2282
|
+
++$countBad;
|
2283
|
+
return;
|
2284
|
+
}
|
2285
|
+
if ($et->Exists($file2)) {
|
2286
|
+
$showGroup = 1 unless defined $showGroup;
|
2287
|
+
$allGroup = 1 unless defined $allGroup;
|
2288
|
+
$et->Options(Duplicates => 1, Sort => "Group$showGroup", Verbose => 0);
|
2289
|
+
$et2 = Image::ExifTool->new;
|
2290
|
+
$et2->Options(%{$$et{OPTIONS}});
|
2291
|
+
@found2 = @foundTags;
|
2292
|
+
$info2 = $et2->ImageInfo($file2, \@found2);
|
2293
|
+
} else {
|
2294
|
+
$info2 = { Error => "Diff file not found" };
|
2295
|
+
}
|
2296
|
+
if ($$info2{Error}) {
|
2297
|
+
Warn "Error: $$info2{Error} - $file2\n";
|
2298
|
+
EFile($file);
|
2299
|
+
++$countBad;
|
2300
|
+
return;
|
2301
|
+
}
|
2302
|
+
}
|
2260
2303
|
# extract the information
|
2261
2304
|
$info = $et->ImageInfo(Infile($pipe), \@foundTags);
|
2262
2305
|
$et->Options(Duplicates => $oldDups);
|
2263
2306
|
}
|
2307
|
+
|
2264
2308
|
# all done now if we already wrote output text file (eg. verbose option)
|
2265
2309
|
if ($fp) {
|
2266
2310
|
if (defined $outfile) {
|
@@ -2274,7 +2318,7 @@ sub GetImageInfo($$)
|
|
2274
2318
|
}
|
2275
2319
|
}
|
2276
2320
|
if ($info->{Error}) {
|
2277
|
-
Warn "Error:
|
2321
|
+
Warn "Error: $$info{Error} - $file\n";
|
2278
2322
|
EFile($file);
|
2279
2323
|
++$countBad;
|
2280
2324
|
return;
|
@@ -2298,6 +2342,105 @@ sub GetImageInfo($$)
|
|
2298
2342
|
$tmpText = $outfile unless $append;
|
2299
2343
|
}
|
2300
2344
|
|
2345
|
+
# print differences if requested
|
2346
|
+
if (defined $diff) {
|
2347
|
+
my (%done2, $wasDiff, @diffs, @groupTags2);
|
2348
|
+
my $v = $verbose || 0;
|
2349
|
+
print $fp "======== diff < $file > $file2\n";
|
2350
|
+
my ($g2, $same) = (0, 0); # start with $g2 false, but not equal to '' to avoid infinite loop
|
2351
|
+
for (;;) {
|
2352
|
+
my $tag = shift @foundTags;
|
2353
|
+
my ($g, $tag2);
|
2354
|
+
if (defined $tag) {
|
2355
|
+
$g = $et->GetGroup($tag, $showGroup);
|
2356
|
+
} else {
|
2357
|
+
for (;;) {
|
2358
|
+
$tag2 = shift @found2;
|
2359
|
+
defined $tag2 or $g = '', last;
|
2360
|
+
$done2{$tag2} or $g = $et2->GetGroup($tag2, $showGroup), last;
|
2361
|
+
}
|
2362
|
+
}
|
2363
|
+
if ($g ne $g2) {
|
2364
|
+
my $t2;
|
2365
|
+
# add any outstanding tags from diff file not yet handled in previous group ($g2)
|
2366
|
+
foreach $t2 (@groupTags2) {
|
2367
|
+
next if $done2{$t2};
|
2368
|
+
my $val2 = $et2->GetValue($t2);
|
2369
|
+
next unless defined $val2;
|
2370
|
+
my $name = $outFormat < 1 ? $et2->GetDescription($t2) : GetTagName($t2);
|
2371
|
+
my $len = LengthUTF8($name);
|
2372
|
+
my $pad = $outFormat < 2 ? ' ' x ($len < 32 ? 32 - $len : 0) : '';
|
2373
|
+
if ($allGroup) {
|
2374
|
+
my $grp = "[$g2]";
|
2375
|
+
$grp .= ' ' x (15 - length($grp)) if length($grp) < 15 and $outFormat < 2;
|
2376
|
+
push @diffs, sprintf "> %s %s%s: %s\n", $grp, $name, $pad, Printable($val2);
|
2377
|
+
} else {
|
2378
|
+
push @diffs, sprintf "> %s%s: %s\n", $name, $pad, Printable($val2);
|
2379
|
+
}
|
2380
|
+
$done2{$t2} = 1;
|
2381
|
+
}
|
2382
|
+
my $str = '';
|
2383
|
+
$v and ($same or $v > 1) and $str = " ($same same tag" . ($same==1 ? '' : 's') . ')';
|
2384
|
+
if (not $allGroup) {
|
2385
|
+
print $fp "---- $g2 ----$str\n" if $g2 and ($str or @diffs);
|
2386
|
+
} elsif ($str and $g2) {
|
2387
|
+
printf $fp " %-13s%s\n", $g2, $str;
|
2388
|
+
}
|
2389
|
+
# print all differences for this group
|
2390
|
+
@diffs and print($fp @diffs), $wasDiff = 1, @diffs = ();
|
2391
|
+
last unless $g;
|
2392
|
+
($g2, $same) = ($g, 0);
|
2393
|
+
# build list of all tags in the new group of the diff file
|
2394
|
+
@groupTags2 = ();
|
2395
|
+
foreach $t2 (@found2) {
|
2396
|
+
$done2{$t2} or $g ne $et2->GetGroup($t2, $showGroup) or push @groupTags2, $t2;
|
2397
|
+
}
|
2398
|
+
}
|
2399
|
+
next unless defined $tag;
|
2400
|
+
my $val = $et->GetValue($tag);
|
2401
|
+
next unless defined $val; # (just in case)
|
2402
|
+
my $name = GetTagName($tag);
|
2403
|
+
# get matching tag key from diff file
|
2404
|
+
my @tags2 = grep /^$name( |$)/, @groupTags2;
|
2405
|
+
$name = $et->GetDescription($tag) if $outFormat < 1;
|
2406
|
+
my ($val2, $t2);
|
2407
|
+
foreach $t2 (@tags2) {
|
2408
|
+
next if $done2{$t2};
|
2409
|
+
$tag2 = $t2;
|
2410
|
+
$val2 = $et2->GetValue($t2);
|
2411
|
+
last;
|
2412
|
+
}
|
2413
|
+
if (defined $val2 and IsEqual($val, $val2)) {
|
2414
|
+
++$same;
|
2415
|
+
} else {
|
2416
|
+
my $len = LengthUTF8($name);
|
2417
|
+
my $pad = $outFormat < 2 ? ' ' x ($len < 32 ? 32 - $len : 0) : '';
|
2418
|
+
if ($allGroup) {
|
2419
|
+
my $grp = "[$g]";
|
2420
|
+
$grp .= ' ' x (15 - length($grp)) if length($grp) < 15 and $outFormat < 2;
|
2421
|
+
push @diffs, sprintf "< %s %s%s: %s\n", $grp, $name, $pad, Printable($val);
|
2422
|
+
if (defined $val2) {
|
2423
|
+
$grp = ' ' x length($grp), $name = ' ' x $len if $v < 3;
|
2424
|
+
push @diffs, sprintf "> %s %s%s: %s\n", $grp, $name, $pad, Printable($val2);
|
2425
|
+
}
|
2426
|
+
} else {
|
2427
|
+
push @diffs, sprintf "< %s%s: %s\n", $name, $pad, Printable($val);
|
2428
|
+
$name = ' ' x $len if $v < 3;
|
2429
|
+
push @diffs, sprintf "> %s%s %s\n", $name, $pad, Printable($val2) if defined $val2;
|
2430
|
+
}
|
2431
|
+
}
|
2432
|
+
$done2{$tag2} = 1 if defined $tag2;
|
2433
|
+
}
|
2434
|
+
print $fp "(no metadata differences)\n" unless $wasDiff;
|
2435
|
+
undef $tmpText;
|
2436
|
+
if (defined $outfile) {
|
2437
|
+
++$created{$outfile};
|
2438
|
+
close($fp);
|
2439
|
+
undef $tmpText;
|
2440
|
+
}
|
2441
|
+
++$count;
|
2442
|
+
return;
|
2443
|
+
}
|
2301
2444
|
# restore state of comma flag for this file if appending
|
2302
2445
|
$comma = $outComma{$outfile} if $append and ($textOverwrite & 0x02);
|
2303
2446
|
|
@@ -2480,7 +2623,13 @@ TAG: foreach $tag (@foundTags) {
|
|
2480
2623
|
# (note that the tag key may look like "TAG #(1)" when the "#" feature is used)
|
2481
2624
|
next if $noDups and $tag =~ /^(.*?) ?\(/ and defined $$info{$1} and
|
2482
2625
|
$group eq $et->GetGroup($1, $showGroup);
|
2483
|
-
|
2626
|
+
if (not $group and ($xml or $json or $csv)) {
|
2627
|
+
if ($showGroup !~ /\b4\b/) {
|
2628
|
+
$group = 'Unknown';
|
2629
|
+
} elsif ($json and not $allGroup) {
|
2630
|
+
$group = 'Copy0';
|
2631
|
+
}
|
2632
|
+
}
|
2484
2633
|
if ($fp and not ($allGroup or $csv)) {
|
2485
2634
|
if ($lastGroup ne $group) {
|
2486
2635
|
if ($html) {
|
@@ -2702,6 +2851,9 @@ TAG: foreach $tag (@foundTags) {
|
|
2702
2851
|
my $num = $et->GetValue($tag, 'ValueConv');
|
2703
2852
|
$$val{num} = $num if defined $num and not IsEqual($num, $$val{val});
|
2704
2853
|
}
|
2854
|
+
my $ex = $$et{TAG_EXTRA}{$tag};
|
2855
|
+
$$val{'hex'} = join ' ', unpack '(H2)*', $$ex{BinVal} if defined $$ex{BinVal};
|
2856
|
+
$$val{'fmt'} = $$ex{G6} if defined $$ex{G6};
|
2705
2857
|
}
|
2706
2858
|
}
|
2707
2859
|
FormatJSON($fp, $val, $ind, $quote);
|
@@ -2766,21 +2918,7 @@ TAG: foreach $tag (@foundTags) {
|
|
2766
2918
|
# pad description to a constant length
|
2767
2919
|
# (get actual character length when using alternate languages
|
2768
2920
|
# 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
|
-
}
|
2921
|
+
my $padLen = $wid - LengthUTF8($desc);
|
2784
2922
|
$padLen = 0 if $padLen < 0;
|
2785
2923
|
$buff .= $desc . (' ' x $padLen) . ": $val\n";
|
2786
2924
|
} elsif ($outFormat == 2) {
|
@@ -3011,7 +3149,7 @@ sub SetImageInfo($$$)
|
|
3011
3149
|
}
|
3012
3150
|
$found = 1;
|
3013
3151
|
$verbose and print $vout "Setting new values from $csv database\n";
|
3014
|
-
foreach $tag (
|
3152
|
+
foreach $tag (OrderedKeys($csvInfo)) {
|
3015
3153
|
next if $tag =~ /\b(SourceFile|Directory|FileName)$/i; # don't write these
|
3016
3154
|
my ($rtn, $wrn) = $et->SetNewValue($tag, $$csvInfo{$tag},
|
3017
3155
|
Protected => 1, AddValue => $csvAdd,
|
@@ -3427,8 +3565,7 @@ sub FormatXML($$$)
|
|
3427
3565
|
} elsif (ref $val eq 'HASH') {
|
3428
3566
|
$gt = " rdf:parseType='Resource'>";
|
3429
3567
|
my $val2 = '';
|
3430
|
-
|
3431
|
-
foreach (@keys) {
|
3568
|
+
foreach (OrderedKeys($val)) {
|
3432
3569
|
# (some variable-namespace XML structure fields may have a different group)
|
3433
3570
|
my ($ns, $tg) = ($grp, $_);
|
3434
3571
|
if (/^(.*?):(.*)/) {
|
@@ -3512,7 +3649,7 @@ sub FormatJSON($$$;$)
|
|
3512
3649
|
print $fp $bra;
|
3513
3650
|
foreach (@$val) {
|
3514
3651
|
print $fp ',' if $comma;
|
3515
|
-
FormatJSON($fp, $_, $ind);
|
3652
|
+
FormatJSON($fp, $_, $ind, $quote);
|
3516
3653
|
$comma = 1,
|
3517
3654
|
}
|
3518
3655
|
print $fp $ket,
|
@@ -3520,8 +3657,7 @@ sub FormatJSON($$$;$)
|
|
3520
3657
|
} elsif (ref $val eq 'HASH') {
|
3521
3658
|
my ($bra, $ket, $sep) = $json == 1 ? ('{','}',':') : ('Array(',')',' =>');
|
3522
3659
|
print $fp $bra;
|
3523
|
-
|
3524
|
-
foreach (@keys) {
|
3660
|
+
foreach (OrderedKeys($val)) {
|
3525
3661
|
print $fp ',' if $comma;
|
3526
3662
|
my $key = EscapeJSON($_, 1);
|
3527
3663
|
print $fp qq(\n$ind $key$sep );
|
@@ -3529,7 +3665,7 @@ sub FormatJSON($$$;$)
|
|
3529
3665
|
if ($showTagID and $_ eq 'id' and $showTagID eq 'H' and $$val{$_} =~ /^\d+\.\d+$/) {
|
3530
3666
|
print $fp qq{"$$val{$_}"};
|
3531
3667
|
} else {
|
3532
|
-
FormatJSON($fp, $$val{$_}, "$ind ");
|
3668
|
+
FormatJSON($fp, $$val{$_}, "$ind ", $quote);
|
3533
3669
|
}
|
3534
3670
|
$comma = 1,
|
3535
3671
|
}
|
@@ -3678,6 +3814,65 @@ sub IsEqual($$)
|
|
3678
3814
|
return 1;
|
3679
3815
|
}
|
3680
3816
|
|
3817
|
+
#------------------------------------------------------------------------------
|
3818
|
+
# Get the printable rendition of a value
|
3819
|
+
# Inputs: 0) value (may be a reference)
|
3820
|
+
# Returns: de-referenced value
|
3821
|
+
sub Printable($)
|
3822
|
+
{
|
3823
|
+
my $val = shift;
|
3824
|
+
if (ref $val) {
|
3825
|
+
if ($structOpt) {
|
3826
|
+
require Image::ExifTool::XMP;
|
3827
|
+
$val = Image::ExifTool::XMP::SerializeStruct($mt, $val);
|
3828
|
+
} elsif (ref $val eq 'ARRAY') {
|
3829
|
+
$val = join($listSep, @$val);
|
3830
|
+
} elsif (ref $val eq 'SCALAR') {
|
3831
|
+
$val = '(Binary data '.length($$val).' bytes)';
|
3832
|
+
}
|
3833
|
+
}
|
3834
|
+
if ($escapeC) {
|
3835
|
+
$val =~ s/([\0-\x1f\\\x7f])/$escC{$1} || sprintf('\x%.2x', ord $1)/eg;
|
3836
|
+
} else {
|
3837
|
+
# translate unprintable chars in value and remove trailing spaces
|
3838
|
+
$val =~ tr/\x01-\x1f\x7f/./;
|
3839
|
+
$val =~ s/\x00//g;
|
3840
|
+
$val =~ s/\s+$//;
|
3841
|
+
}
|
3842
|
+
return $val;
|
3843
|
+
}
|
3844
|
+
|
3845
|
+
#------------------------------------------------------------------------------
|
3846
|
+
# Get character length of a UTF-8 string
|
3847
|
+
# Inputs: 0) string
|
3848
|
+
# Returns: number of characters (not bytes) in the UTF-8 string
|
3849
|
+
sub LengthUTF8($)
|
3850
|
+
{
|
3851
|
+
my $str = shift;
|
3852
|
+
return length $str unless $fixLen;
|
3853
|
+
local $SIG{'__WARN__'} = sub { };
|
3854
|
+
if (not $$mt{OPTIONS}{EncodeHangs} and eval { require Encode }) {
|
3855
|
+
$str = Encode::decode_utf8($str);
|
3856
|
+
} else {
|
3857
|
+
$str = pack('U0C*', unpack 'C*', $str);
|
3858
|
+
}
|
3859
|
+
my $len;
|
3860
|
+
if ($fixLen == 1) {
|
3861
|
+
$len = length $str;
|
3862
|
+
} else {
|
3863
|
+
my $gcstr = eval { Unicode::GCString->new($str) };
|
3864
|
+
if ($gcstr) {
|
3865
|
+
$len = $gcstr->columns;
|
3866
|
+
} else {
|
3867
|
+
$len = length $str;
|
3868
|
+
delete $SIG{'__WARN__'};
|
3869
|
+
Warning($mt, 'Unicode::GCString problem. Columns may be misaligned');
|
3870
|
+
$fixLen = 1;
|
3871
|
+
}
|
3872
|
+
}
|
3873
|
+
return $len;
|
3874
|
+
}
|
3875
|
+
|
3681
3876
|
#------------------------------------------------------------------------------
|
3682
3877
|
# Add tag list for copying tags from specified file
|
3683
3878
|
# Inputs: 0) set tags file name (or FMT), 1) options for SetNewValuesFromFile()
|
@@ -3773,6 +3968,19 @@ sub CleanFilename($)
|
|
3773
3968
|
$_[0] =~ tr/\\/\// if Image::ExifTool::IsPC();
|
3774
3969
|
}
|
3775
3970
|
|
3971
|
+
#------------------------------------------------------------------------------
|
3972
|
+
# Does path name contain wildcards
|
3973
|
+
# Inputs: 0) path name
|
3974
|
+
# Returns: true if path contains wildcards
|
3975
|
+
sub HasWildcards($)
|
3976
|
+
{
|
3977
|
+
my $path = shift;
|
3978
|
+
|
3979
|
+
# if this is a Windows path with the long path prefix, then wildcards are not supported
|
3980
|
+
return 0 if $^O eq 'MSWin32' and $path =~ m{^[\\/]{2}\?[\\/]};
|
3981
|
+
return $path =~ /[*?]/;
|
3982
|
+
}
|
3983
|
+
|
3776
3984
|
#------------------------------------------------------------------------------
|
3777
3985
|
# Check for valid UTF-8 of a file name
|
3778
3986
|
# Inputs: 0) string, 1) original encoding
|
@@ -3893,7 +4101,7 @@ sub ScanDir($$;$)
|
|
3893
4101
|
return if $ignore{$dir};
|
3894
4102
|
# use Win32::FindFile on Windows if available
|
3895
4103
|
# (ReadDir will croak if there is a wildcard, so check for this)
|
3896
|
-
if ($^O eq 'MSWin32' and $dir
|
4104
|
+
if ($^O eq 'MSWin32' and not HasWildcards($dir)) {
|
3897
4105
|
undef $evalWarning;
|
3898
4106
|
local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] };;
|
3899
4107
|
if (CheckUTF8($dir, $enc) >= 0) {
|
@@ -4019,7 +4227,7 @@ sub FindFileWindows($$)
|
|
4019
4227
|
$wildfile = $et->Decode($wildfile, $enc, undef, 'UTF8') if $enc and $enc ne 'UTF8';
|
4020
4228
|
$wildfile =~ tr/\\/\//; # use forward slashes
|
4021
4229
|
my ($dir, $wildname) = ($wildfile =~ m{(.*[:/])(.*)}) ? ($1, $2) : ('', $wildfile);
|
4022
|
-
if ($dir
|
4230
|
+
if (HasWildcards($dir)) {
|
4023
4231
|
Warn "Wildcards don't work in the directory specification\n";
|
4024
4232
|
return ();
|
4025
4233
|
}
|
@@ -4867,6 +5075,7 @@ L<Other options|/Other options>
|
|
4867
5075
|
|
4868
5076
|
L<Special features|/Special features>
|
4869
5077
|
|
5078
|
+
-diff FILE2 Compare metadata with another file
|
4870
5079
|
-geotag TRKFILE Geotag images from specified GPS log
|
4871
5080
|
-globalTimeShift SHIFT Shift all formatted date/time values
|
4872
5081
|
-use MODULE Add features from plug-in module
|
@@ -4958,13 +5167,13 @@ may be used to to indicate any XMP namespace (eg. C<--xmp-all:dabs>).
|
|
4958
5167
|
Write a new value for the specified tag (eg. C<-comment=wow>), or delete the
|
4959
5168
|
tag if no I<VALUE> is given (eg. C<-comment=>). C<+=> and C<-=> are used to
|
4960
5169
|
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
|
4962
|
-
for more details). C<+=> may also be used to increment numerical
|
4963
|
-
decrement if I<VALUE> is negative), and C<-=> may be used to
|
4964
|
-
delete or replace a tag (see L</WRITING EXAMPLES> for
|
4965
|
-
used to write an empty string instead of deleting the
|
4966
|
-
is given, but otherwise it is equivalent to C<=>.
|
4967
|
-
be quoted on the Windows command line.)
|
5170
|
+
(see L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl> and notes 6 and
|
5171
|
+
7 below for more details). C<+=> may also be used to increment numerical
|
5172
|
+
values (or decrement if I<VALUE> is negative), and C<-=> may be used to
|
5173
|
+
conditionally delete or replace a tag (see L</WRITING EXAMPLES> for
|
5174
|
+
examples). C<^=> is used to write an empty string instead of deleting the
|
5175
|
+
tag when no I<VALUE> is given, but otherwise it is equivalent to C<=>.
|
5176
|
+
(Note that the caret must be quoted on the Windows command line.)
|
4968
5177
|
|
4969
5178
|
I<TAG> may contain one or more leading family 0, 1, 2 or 7 group names,
|
4970
5179
|
prefixed by optional family numbers, and separated colons. If no group name
|
@@ -5039,6 +5248,10 @@ tag, overriding any other values previously assigned to the tag on the same
|
|
5039
5248
|
command line. To shift a date/time value and copy it to another tag in the
|
5040
5249
|
same operation, use the B<-globalTimeShift> option.
|
5041
5250
|
|
5251
|
+
7) The C<+=> operator may not be used to shift a List-type date/time tag
|
5252
|
+
(eg. XMP-dc:Date) because C<+=> is used to add elements to the list.
|
5253
|
+
Instead, the B<-globalTimeShift> option should be used.
|
5254
|
+
|
5042
5255
|
Special feature: Integer values may be specified in hexadecimal with a
|
5043
5256
|
leading C<0x>, and simple rational values may be specified as fractions.
|
5044
5257
|
|
@@ -5499,16 +5712,17 @@ output as simple strings). The B<-a> option is implied when B<-json> is
|
|
5499
5712
|
used, but entries with identical JSON names are suppressed in the output.
|
5500
5713
|
(B<-G4> may be used to ensure that all tags have unique JSON names.) Adding
|
5501
5714
|
the B<-D> or B<-H> option changes tag values to JSON objects with "val" and
|
5502
|
-
"id" fields
|
5503
|
-
numerical value is different from the converted "val"
|
5504
|
-
|
5505
|
-
|
5506
|
-
|
5507
|
-
|
5508
|
-
|
5509
|
-
|
5510
|
-
|
5511
|
-
|
5715
|
+
"id" fields. Adding B<-l> adds a "desc" field, and a "num" field if the
|
5716
|
+
numerical value is different from the converted "val", and "fmt" and "hex"
|
5717
|
+
fields for EXIF metadata if the API SaveFormat and SaveBin options are set
|
5718
|
+
respectively. The B<-b> option may be added to output binary data, encoded
|
5719
|
+
in base64 if necessary (indicated by ASCII "base64:" as the first 7 bytes of
|
5720
|
+
the value), and B<-t> may be added to include tag table information (see
|
5721
|
+
B<-t> for details). The JSON output is UTF-8 regardless of any B<-L> or
|
5722
|
+
B<-charset> option setting, but the UTF-8 validation is disabled if a
|
5723
|
+
character set other than UTF-8 is specified. Note that ExifTool quotes JSON
|
5724
|
+
values only if they don't look like numbers (regardless of the original
|
5725
|
+
storage format or the relevant metadata specification).
|
5512
5726
|
|
5513
5727
|
If I<JSONFILE> is specified, the file is imported and the tag definitions
|
5514
5728
|
from the file are used to set tag values on a per-file basis. The special
|
@@ -5662,7 +5876,7 @@ with this command:
|
|
5662
5876
|
|
5663
5877
|
produces output like this:
|
5664
5878
|
|
5665
|
-
-- Generated by ExifTool
|
5879
|
+
-- Generated by ExifTool 13.02 --
|
5666
5880
|
File: a.jpg - 2003:10:31 15:44:19
|
5667
5881
|
(f/5.6, 1/60s, ISO 100)
|
5668
5882
|
File: b.jpg - 2006:05:23 11:57:38
|
@@ -6497,6 +6711,23 @@ names, even if they begin with a dash (C<->).
|
|
6497
6711
|
|
6498
6712
|
=over 5
|
6499
6713
|
|
6714
|
+
=item B<-diff> I<FILE2>
|
6715
|
+
|
6716
|
+
Compare metadata in I<FILE> with I<FILE2>. The I<FILE2> name may include
|
6717
|
+
filename formatting codes (see the B<-w> option). All extracted tags from
|
6718
|
+
the files are compared, but the extracted tags may be controlled by adding
|
6719
|
+
B<-TAG> or B<--TAG> options. For example, below is a command to compare all
|
6720
|
+
the same-named files in two different directories, ignoring the System tags:
|
6721
|
+
|
6722
|
+
exiftool DIR1 -diff DIR2/%f.%e --system:all
|
6723
|
+
|
6724
|
+
The B<-g> and B<-G> options may be used to organize the output by the
|
6725
|
+
specified family of groups, with B<-G1> being the default. The B<-a> option
|
6726
|
+
is implied. Adding B<-v> includes a count of the number of tags that are
|
6727
|
+
the same in each group. The following text formatting options are valid
|
6728
|
+
when B<-diff> is used: B<-c>, B<-charset>, B<-d>, B<-E>, B<-ec>, B<-ex>,
|
6729
|
+
B<-L>, B<-lang>, B<-n>, B<-s>, B<-sep>, B<-struct> and B<-w>.
|
6730
|
+
|
6500
6731
|
=item B<-geotag> I<TRKFILE>
|
6501
6732
|
|
6502
6733
|
Geotag images from the specified GPS track log file. Using the B<-geotag>
|