exiftool-vendored.exe 12.55.0 → 12.60.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.
- package/bin/exiftool_files/Changes +97 -4
- package/bin/exiftool_files/README +19 -19
- package/bin/exiftool_files/arg_files/xmp2exif.args +4 -1
- package/bin/exiftool_files/config_files/example.config +1 -0
- package/bin/exiftool_files/config_files/rotate_regions.config +1 -1
- package/bin/exiftool_files/exiftool.pl +198 -123
- package/bin/exiftool_files/fmt_files/kml.fmt +3 -0
- package/bin/exiftool_files/fmt_files/kml_track.fmt +3 -0
- package/bin/exiftool_files/lib/Image/ExifTool/AIFF.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/APE.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +25 -19
- package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +33 -7
- package/bin/exiftool_files/lib/Image/ExifTool/CanonRaw.pm +5 -1
- package/bin/exiftool_files/lib/Image/ExifTool/DJI.pm +28 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +107 -20
- package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +92 -9
- package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +9 -4
- package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +7 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +30 -7
- package/bin/exiftool_files/lib/Image/ExifTool/InfiRay.pm +227 -0
- package/bin/exiftool_files/lib/Image/ExifTool/JPEG.pm +53 -7
- package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +5 -5
- package/bin/exiftool_files/lib/Image/ExifTool/LIF.pm +10 -2
- package/bin/exiftool_files/lib/Image/ExifTool/LNK.pm +5 -4
- package/bin/exiftool_files/lib/Image/ExifTool/MIE.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/MPEG.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/MakerNotes.pm +3 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Minolta.pm +6 -7
- package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +1199 -1325
- package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/NikonSettings.pm +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +88 -6
- package/bin/exiftool_files/lib/Image/ExifTool/OpenEXR.pm +32 -15
- package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +89 -3
- package/bin/exiftool_files/lib/Image/ExifTool/PanasonicRaw.pm +27 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +8 -5
- package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +14 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Photoshop.pm +38 -7
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +44 -13
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +63 -20
- package/bin/exiftool_files/lib/Image/ExifTool/README +19 -2
- package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +34 -13
- package/bin/exiftool_files/lib/Image/ExifTool/Rawzor.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Real.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Ricoh.pm +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Sigma.pm +5 -4
- package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +9 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +28 -1
- package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +4691 -4624
- package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +511 -117
- package/bin/exiftool_files/lib/Image/ExifTool/VCard.pm +19 -5
- package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +5 -5
- package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +42 -0
- package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +150 -36
- package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +19 -4
- package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool.pm +248 -54
- package/bin/exiftool_files/lib/Image/ExifTool.pod +94 -58
- package/package.json +2 -2
|
@@ -17,7 +17,7 @@ use strict;
|
|
|
17
17
|
use vars qw($VERSION);
|
|
18
18
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
19
19
|
|
|
20
|
-
$VERSION = '1.
|
|
20
|
+
$VERSION = '1.07';
|
|
21
21
|
|
|
22
22
|
my %unescapeVCard = ( '\\'=>'\\', ','=>',', 'n'=>"\n", 'N'=>"\n" );
|
|
23
23
|
|
|
@@ -190,6 +190,15 @@ my %timeInfo = (
|
|
|
190
190
|
'X-wr-alarmuid' => 'AlarmUID',
|
|
191
191
|
);
|
|
192
192
|
|
|
193
|
+
%Image::ExifTool::VCard::VNote = (
|
|
194
|
+
GROUPS => { 1 => 'VNote', 2 => 'Document' },
|
|
195
|
+
NOTES => 'Tags extracted from V-Note VNT files.',
|
|
196
|
+
Version => { },
|
|
197
|
+
Body => { },
|
|
198
|
+
Dcreated => { Name => 'CreateDate', Groups => { 2 => 'Time' }, %timeInfo },
|
|
199
|
+
'Last-modified' => { Name => 'ModifyDate', Groups => { 2 => 'Time' }, %timeInfo },
|
|
200
|
+
);
|
|
201
|
+
|
|
193
202
|
#------------------------------------------------------------------------------
|
|
194
203
|
# Get vCard tag, creating if necessary
|
|
195
204
|
# Inputs: 0) ExifTool ref, 1) tag table ref, 2) tag ID, 3) tag Name,
|
|
@@ -254,9 +263,14 @@ sub ProcessVCard($$)
|
|
|
254
263
|
my $raf = $$dirInfo{RAF};
|
|
255
264
|
my ($buff, $val, $ok, $component, %compNum, @count);
|
|
256
265
|
|
|
257
|
-
return 0 unless $raf->Read($buff, 24) and $raf->Seek(0,0) and $buff=~/^BEGIN:(VCARD|VCALENDAR)\r\n/i;
|
|
258
|
-
my
|
|
259
|
-
|
|
266
|
+
return 0 unless $raf->Read($buff, 24) and $raf->Seek(0,0) and $buff=~/^BEGIN:(VCARD|VCALENDAR|VNOTE)\r\n/i;
|
|
267
|
+
my %info = (
|
|
268
|
+
VCARD => [ qw(VCard vCard Main VCF) ],
|
|
269
|
+
VCALENDAR => [ qw(ICS iCalendar VCalendar ICS) ],
|
|
270
|
+
VNOTE => [ qw(VNote vNote VNote VNT text/v-note) ],
|
|
271
|
+
);
|
|
272
|
+
my ($type, $lbl, $tbl, $ext, $mime) = @{$info{uc($1)}};
|
|
273
|
+
$et->SetFileType($type, $mime, $ext);
|
|
260
274
|
return 1 if $$et{OPTIONS}{FastScan} and $$et{OPTIONS}{FastScan} == 3;
|
|
261
275
|
local $/ = "\r\n";
|
|
262
276
|
my $tagTablePtr = GetTagTable("Image::ExifTool::VCard::$tbl");
|
|
@@ -274,7 +288,7 @@ sub ProcessVCard($$)
|
|
|
274
288
|
}
|
|
275
289
|
if ($val =~ /^(BEGIN|END):(V?)(\w+)$/i) {
|
|
276
290
|
my ($begin, $v, $what) = ((lc($1) eq 'begin' ? 1 : 0), $2, ucfirst lc $3);
|
|
277
|
-
if ($what eq 'Card' or $what eq 'Calendar') {
|
|
291
|
+
if ($what eq 'Card' or $what eq 'Calendar' or $what eq 'Note') {
|
|
278
292
|
if ($begin) {
|
|
279
293
|
@count = ( { } ); # reset group counters
|
|
280
294
|
} else {
|
|
@@ -17,7 +17,7 @@ package Image::ExifTool::Validate;
|
|
|
17
17
|
use strict;
|
|
18
18
|
use vars qw($VERSION %exifSpec);
|
|
19
19
|
|
|
20
|
-
$VERSION = '1.
|
|
20
|
+
$VERSION = '1.20';
|
|
21
21
|
|
|
22
22
|
use Image::ExifTool qw(:Utils);
|
|
23
23
|
use Image::ExifTool::Exif;
|
|
@@ -437,8 +437,8 @@ sub ValidateExif($$$$$$$$)
|
|
|
437
437
|
$et->Warn(sprintf('Wrong IFD for 0x%.4x %s (should be %s not %s)', $tag, $$ti{Name}, $wgp, $ifd));
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
|
-
} elsif (not $otherSpec{$$et{
|
|
441
|
-
(not $otherSpec{$$et{
|
|
440
|
+
} elsif (not $otherSpec{$$et{FileType}} or
|
|
441
|
+
(not $otherSpec{$$et{FileType}}{$tag} and not $otherSpec{$$et{FileType}}{All}))
|
|
442
442
|
{
|
|
443
443
|
if ($tagTablePtr eq \%Image::ExifTool::Exif::Main or $$tagInfo{Unknown}) {
|
|
444
444
|
$et->Warn(sprintf('Non-standard %s tag 0x%.4x %s', $ifd, $tag, $$ti{Name}), 1);
|
|
@@ -459,8 +459,8 @@ sub ValidateExif($$$$$$$$)
|
|
|
459
459
|
$et->Warn(sprintf('Non-standard count (%d) for %s 0x%.4x %s', $count, $ifd, $tag, $$ti{Name}));
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
|
-
} elsif (not $otherSpec{$$et{
|
|
463
|
-
(not $otherSpec{$$et{
|
|
462
|
+
} elsif (not $otherSpec{$$et{FileType}} or
|
|
463
|
+
(not $otherSpec{$$et{FileType}}{$tag} and not $otherSpec{$$et{FileType}}{All}))
|
|
464
464
|
{
|
|
465
465
|
$et->Warn(sprintf('Unknown %s tag 0x%.4x', $ifd, $tag), 1);
|
|
466
466
|
}
|
|
@@ -419,6 +419,48 @@ sub ValidateImageData($$$;$)
|
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
+
#------------------------------------------------------------------------------
|
|
423
|
+
# Add specified image data to ImageDataMD5 hash
|
|
424
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) lookup for [tagInfo,value] based on tagID
|
|
425
|
+
sub AddImageDataMD5($$$)
|
|
426
|
+
{
|
|
427
|
+
my ($et, $dirInfo, $offsetInfo) = @_;
|
|
428
|
+
my ($tagID, $offset, $buff);
|
|
429
|
+
|
|
430
|
+
my $verbose = $et->Options('Verbose');
|
|
431
|
+
my $md5 = $$et{ImageDataMD5};
|
|
432
|
+
my $raf = $$dirInfo{RAF};
|
|
433
|
+
|
|
434
|
+
foreach $tagID (sort keys %$offsetInfo) {
|
|
435
|
+
next unless ref $$offsetInfo{$tagID} eq 'ARRAY'; # ignore scalar tag values used for Validate
|
|
436
|
+
my $tagInfo = $$offsetInfo{$tagID}[0];
|
|
437
|
+
next unless $$tagInfo{IsImageData}; # only consider image data
|
|
438
|
+
my $sizeID = $$tagInfo{OffsetPair};
|
|
439
|
+
my @sizes;
|
|
440
|
+
if ($$tagInfo{NotRealPair}) {
|
|
441
|
+
@sizes = 999999999; # (Panasonic hack: raw data runs to end of file)
|
|
442
|
+
} elsif ($sizeID and $$offsetInfo{$sizeID}) {
|
|
443
|
+
@sizes = split ' ', $$offsetInfo{$sizeID}[1];
|
|
444
|
+
} else {
|
|
445
|
+
next;
|
|
446
|
+
}
|
|
447
|
+
my @offsets = split ' ', $$offsetInfo{$tagID}[1];
|
|
448
|
+
$sizes[0] = 999999999 if $$tagInfo{NotRealPair};
|
|
449
|
+
my $total = 0;
|
|
450
|
+
foreach $offset (@offsets) {
|
|
451
|
+
my $size = shift @sizes;
|
|
452
|
+
next unless $offset =~ /^\d+$/ and $size and $size =~ /^\d+$/ and $size;
|
|
453
|
+
next unless $raf->Seek($offset, 0); # (offset is absolute)
|
|
454
|
+
$total += $et->ImageDataMD5($raf, $size);
|
|
455
|
+
}
|
|
456
|
+
if ($verbose) {
|
|
457
|
+
my $name = "$$dirInfo{DirName}:$$tagInfo{Name}";
|
|
458
|
+
$name =~ s/Offsets?|Start$//;
|
|
459
|
+
$et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $total bytes of $name data)\n");
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
422
464
|
#------------------------------------------------------------------------------
|
|
423
465
|
# Handle error while writing EXIF
|
|
424
466
|
# Inputs: 0) ExifTool ref, 1) error string, 2) tag table ref
|
|
@@ -1490,7 +1490,7 @@ sub WriteXMP($$;$)
|
|
|
1490
1490
|
my @ns = sort keys %nsCur;
|
|
1491
1491
|
$long[-2] .= "$nl$sp<$prop rdf:about='${about}'";
|
|
1492
1492
|
# generate et:toolkit attribute if this is an exiftool RDF/XML output file
|
|
1493
|
-
if (@ns and $nsCur{$ns[0]} =~ m{^http://ns.exiftool.(?:ca|org)/}) {
|
|
1493
|
+
if ($$et{XMP_NO_XMPMETA} and @ns and $nsCur{$ns[0]} =~ m{^http://ns.exiftool.(?:ca|org)/}) {
|
|
1494
1494
|
$long[-2] .= "\n$sp${sp}xmlns:et='http://ns.exiftool.org/1.0/'" .
|
|
1495
1495
|
" et:toolkit='Image::ExifTool $Image::ExifTool::VERSION'";
|
|
1496
1496
|
}
|
|
@@ -1321,6 +1321,7 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1321
1321
|
XMPAutoConv => $$options{XMPAutoConv},
|
|
1322
1322
|
);
|
|
1323
1323
|
$$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
|
|
1324
|
+
$$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL};
|
|
1324
1325
|
foreach $tag (@setTags) {
|
|
1325
1326
|
next if ref $tag;
|
|
1326
1327
|
if ($tag =~ /^-(.*)/) {
|
|
@@ -1387,7 +1388,7 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1387
1388
|
# transfer specified tags in the proper order
|
|
1388
1389
|
#
|
|
1389
1390
|
# 1) loop through input list of tags to set, and build @setList
|
|
1390
|
-
my (@setList, $set, %setMatches, $t);
|
|
1391
|
+
my (@setList, $set, %setMatches, $t, %altFiles);
|
|
1391
1392
|
foreach $t (@setTags) {
|
|
1392
1393
|
if (ref $t eq 'HASH') {
|
|
1393
1394
|
# update current options
|
|
@@ -1419,9 +1420,12 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1419
1420
|
$tag =~ s/(.+?)\s*(>|<) ?//;
|
|
1420
1421
|
$$opts{EXPR} = 1; # flag this expression
|
|
1421
1422
|
} else {
|
|
1423
|
+
# (not sure why this is here because sign should be before '<')
|
|
1424
|
+
# (--> allows "<+" or "<-", which is an undocumented feature)
|
|
1422
1425
|
$opt = $1 if $tag =~ s/^([-+])\s*//;
|
|
1423
1426
|
}
|
|
1424
1427
|
}
|
|
1428
|
+
$$opts{Replace} = 0 if $dstTag =~ s/^\+//;
|
|
1425
1429
|
# validate tag name(s)
|
|
1426
1430
|
unless ($$opts{EXPR} or ValidTagName($tag)) {
|
|
1427
1431
|
$self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value");
|
|
@@ -1438,6 +1442,8 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1438
1442
|
$$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//;
|
|
1439
1443
|
# replace tag name of 'all' with '*'
|
|
1440
1444
|
$dstTag = '*' if $dstTag eq 'all';
|
|
1445
|
+
} else {
|
|
1446
|
+
$$opts{Replace} = 0 if $tag =~ s/^\+//;
|
|
1441
1447
|
}
|
|
1442
1448
|
unless ($$opts{EXPR}) {
|
|
1443
1449
|
$isExclude = ($tag =~ s/^-//);
|
|
@@ -1447,7 +1453,17 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1447
1453
|
# save family/groups in list (ignoring 'all' and '*')
|
|
1448
1454
|
next unless length($_) and /^(\d+)?(.*)/;
|
|
1449
1455
|
my ($f, $g) = ($1, $2);
|
|
1450
|
-
$f = 7 if $g =~ s/^ID-//i;
|
|
1456
|
+
$f = 7 if (not $f or $f eq '7') and $g =~ s/^ID-//i;
|
|
1457
|
+
if ($g =~ /^file\d+$/i and (not $f or $f eq '8')) {
|
|
1458
|
+
$f = 8;
|
|
1459
|
+
my $g8 = ucfirst $g;
|
|
1460
|
+
if ($$srcExifTool{ALT_EXIFTOOL}{$g8}) {
|
|
1461
|
+
$$opts{GROUP8} = $g8;
|
|
1462
|
+
$altFiles{$g8} or $altFiles{$g8} = [ ];
|
|
1463
|
+
# save list of requested tags for this alternate ExifTool object
|
|
1464
|
+
push @{$altFiles{$g8}}, "$grp:$tag";
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1451
1467
|
push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all';
|
|
1452
1468
|
}
|
|
1453
1469
|
}
|
|
@@ -1484,26 +1500,44 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1484
1500
|
# save in reverse order so we don't set tags before an exclude
|
|
1485
1501
|
unshift @setList, [ \@fg, $tag, $dst, $opts ];
|
|
1486
1502
|
}
|
|
1503
|
+
# 1b) copy requested tags for each alternate ExifTool object
|
|
1504
|
+
my $g8;
|
|
1505
|
+
foreach $g8 (sort keys %altFiles) {
|
|
1506
|
+
# request specific alternate tags to load them from the alternate ExifTool object
|
|
1507
|
+
my $altInfo = $srcExifTool->GetInfo($altFiles{$g8});
|
|
1508
|
+
# add to tags list after dummy entry to signify start of tags for this alt file
|
|
1509
|
+
if (%$altInfo) {
|
|
1510
|
+
push @tags, 'Warning DUMMY', reverse sort keys %$altInfo;
|
|
1511
|
+
$$info{$_} = $$altInfo{$_} foreach keys %$altInfo;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1487
1514
|
# 2) initialize lists of matching tags for each setTag
|
|
1488
1515
|
foreach $set (@setList) {
|
|
1489
1516
|
$$set[2] and $setMatches{$set} = [ ];
|
|
1490
1517
|
}
|
|
1491
1518
|
# 3) loop through all tags in source image and save tags matching each setTag
|
|
1492
|
-
my %rtnInfo;
|
|
1519
|
+
my (%rtnInfo, $isAlt);
|
|
1493
1520
|
foreach $tag (@tags) {
|
|
1494
1521
|
# don't try to set errors or warnings
|
|
1495
1522
|
if ($tag =~ /^(Error|Warning)( |$)/) {
|
|
1496
|
-
$
|
|
1523
|
+
if ($tag eq 'Warning DUMMY') {
|
|
1524
|
+
$isAlt = 1; # start of the alt tags
|
|
1525
|
+
} else {
|
|
1526
|
+
$rtnInfo{$tag} = $$info{$tag};
|
|
1527
|
+
}
|
|
1497
1528
|
next;
|
|
1498
1529
|
}
|
|
1499
1530
|
# only set specified tags
|
|
1500
1531
|
my $lcTag = lc(GetTagName($tag));
|
|
1501
1532
|
my (@grp, %grp);
|
|
1502
1533
|
SET: foreach $set (@setList) {
|
|
1534
|
+
my $opts = $$set[3];
|
|
1535
|
+
next if $$opts{EXPR}; # (expressions handled in step 4)
|
|
1536
|
+
next if $$opts{GROUP8} xor $isAlt;
|
|
1503
1537
|
# check first for matching tag
|
|
1504
1538
|
unless ($$set[1] eq $lcTag or $$set[1] eq '*') {
|
|
1505
1539
|
# handle wildcards
|
|
1506
|
-
next unless $$
|
|
1540
|
+
next unless $$opts{WILD} and $lcTag =~ /^$$set[1]$/;
|
|
1507
1541
|
}
|
|
1508
1542
|
# then check for matching group
|
|
1509
1543
|
if (@{$$set[0]}) {
|
|
@@ -1835,6 +1869,27 @@ sub RestoreNewValues($)
|
|
|
1835
1869
|
$$self{DEL_GROUP} = \%delGrp;
|
|
1836
1870
|
}
|
|
1837
1871
|
|
|
1872
|
+
#------------------------------------------------------------------------------
|
|
1873
|
+
# Set alternate file for extracting information
|
|
1874
|
+
# Inputs: 0) ExifTool ref, 1) family 8 group name (of the form "File#" where # is any number)
|
|
1875
|
+
# 2) alternate file name, or undef to reset
|
|
1876
|
+
# Returns: 1 on success, or 0 on invalid group name
|
|
1877
|
+
sub SetAlternateFile($$$)
|
|
1878
|
+
{
|
|
1879
|
+
my ($self, $g8, $file) = @_;
|
|
1880
|
+
$g8 = ucfirst lc $g8;
|
|
1881
|
+
return 0 unless $g8 =~ /^File\d+$/;
|
|
1882
|
+
# keep the same file if already initialized (possibly has metadata extracted)
|
|
1883
|
+
if (not defined $file) {
|
|
1884
|
+
delete $$self{ALT_EXIFTOOL}{$g8};
|
|
1885
|
+
} elsif (not ($$self{ALT_EXIFTOOL}{$g8} and $$self{ALT_EXIFTOOL}{$g8}{ALT_FILE} eq $file)) {
|
|
1886
|
+
my $altExifTool = Image::ExifTool->new;
|
|
1887
|
+
$$altExifTool{ALT_FILE} = $file;
|
|
1888
|
+
$$self{ALT_EXIFTOOL}{$g8} = $altExifTool;
|
|
1889
|
+
}
|
|
1890
|
+
return 1;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1838
1893
|
#------------------------------------------------------------------------------
|
|
1839
1894
|
# Set filesystem time from from FileModifyDate or FileCreateDate tag
|
|
1840
1895
|
# Inputs: 0) ExifTool object reference, 1) file name or file ref
|
|
@@ -2722,13 +2777,16 @@ sub GetAllGroups($;$)
|
|
|
2722
2777
|
$family == 4 and return('Copy#');
|
|
2723
2778
|
$family == 5 and return('[too many possibilities to list]');
|
|
2724
2779
|
$family == 6 and return(@Image::ExifTool::Exif::formatName[1..$#Image::ExifTool::Exif::formatName]);
|
|
2780
|
+
$family == 8 and return('File#');
|
|
2725
2781
|
|
|
2726
2782
|
LoadAllTables(); # first load all our tables
|
|
2727
2783
|
|
|
2728
2784
|
my @tableNames = keys %allTables;
|
|
2729
2785
|
|
|
2730
|
-
# loop through all tag tables and get all group names
|
|
2731
2786
|
my %allGroups;
|
|
2787
|
+
# add family 1 groups not in tables
|
|
2788
|
+
$family == 1 and map { $allGroups{$_} = 1 } qw(Garmin);
|
|
2789
|
+
# loop through all tag tables and get all group names
|
|
2732
2790
|
while (@tableNames) {
|
|
2733
2791
|
my $table = GetTagTable(pop @tableNames);
|
|
2734
2792
|
my ($grps, $grp, $tag, $tagInfo);
|
|
@@ -3159,42 +3217,46 @@ sub InsertTagValues($$$;$$$)
|
|
|
3159
3217
|
$tag = $docGrp . ':' . $tag;
|
|
3160
3218
|
$lcTag = lc $tag;
|
|
3161
3219
|
}
|
|
3220
|
+
my $et = $self;
|
|
3221
|
+
if ($tag =~ s/(\bfile\d+)://i) {
|
|
3222
|
+
$et = $$self{ALT_EXIFTOOL}{ucfirst lc $1} or $et=$self, $tag = 'no_alt_file';
|
|
3223
|
+
}
|
|
3162
3224
|
if ($lcTag eq 'all') {
|
|
3163
3225
|
$val = 1; # always some tag available
|
|
3164
|
-
} elsif (defined $$
|
|
3165
|
-
$val = $$
|
|
3226
|
+
} elsif (defined $$et{OPTIONS}{UserParam}{$lcTag}) {
|
|
3227
|
+
$val = $$et{OPTIONS}{UserParam}{$lcTag};
|
|
3166
3228
|
} elsif ($tag =~ /(.*):(.+)/) {
|
|
3167
3229
|
my $group;
|
|
3168
3230
|
($group, $tag) = ($1, $2);
|
|
3169
3231
|
if (lc $tag eq 'all') {
|
|
3170
3232
|
# see if any tag from the specified group exists
|
|
3171
|
-
my $match = $
|
|
3233
|
+
my $match = $et->GroupMatches($group, $foundTags);
|
|
3172
3234
|
$val = $match ? 1 : 0;
|
|
3173
3235
|
} else {
|
|
3174
3236
|
# find the specified tag
|
|
3175
3237
|
my @matches = grep /^$tag(\s|$)/i, @$foundTags;
|
|
3176
|
-
@matches = $
|
|
3238
|
+
@matches = $et->GroupMatches($group, \@matches);
|
|
3177
3239
|
foreach $tg (@matches) {
|
|
3178
3240
|
if (defined $val and $tg =~ / \((\d+)\)$/) {
|
|
3179
3241
|
# take the most recently extracted tag
|
|
3180
3242
|
my $tagNum = $1;
|
|
3181
3243
|
next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum;
|
|
3182
3244
|
}
|
|
3183
|
-
$val = $
|
|
3245
|
+
$val = $et->GetValue($tg, $type);
|
|
3184
3246
|
$tag = $tg;
|
|
3185
3247
|
last unless $tag =~ / /; # all done if we got our best match
|
|
3186
3248
|
}
|
|
3187
3249
|
}
|
|
3188
3250
|
} elsif ($tag eq 'self') {
|
|
3189
|
-
$val = $
|
|
3251
|
+
$val = $et; # ("$self{var}" or "$file1:self{var}" in string)
|
|
3190
3252
|
} else {
|
|
3191
3253
|
# get the tag value
|
|
3192
|
-
$val = $
|
|
3254
|
+
$val = $et->GetValue($tag, $type);
|
|
3193
3255
|
unless (defined $val) {
|
|
3194
3256
|
# check for tag name with different case
|
|
3195
3257
|
($tg) = grep /^$tag$/i, @$foundTags;
|
|
3196
3258
|
if (defined $tg) {
|
|
3197
|
-
$val = $
|
|
3259
|
+
$val = $et->GetValue($tg, $type);
|
|
3198
3260
|
$tag = $tg;
|
|
3199
3261
|
}
|
|
3200
3262
|
}
|
|
@@ -3262,15 +3324,19 @@ sub InsertTagValues($$$;$$$)
|
|
|
3262
3324
|
undef $advFmtSelf;
|
|
3263
3325
|
$didExpr = 1; # set flag indicating an expression was evaluated
|
|
3264
3326
|
}
|
|
3265
|
-
unless (defined $val
|
|
3327
|
+
unless (defined $val) {
|
|
3266
3328
|
$val = $$self{OPTIONS}{MissingTagValue};
|
|
3267
3329
|
unless (defined $val) {
|
|
3268
3330
|
my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : '';
|
|
3269
3331
|
my $msg = $didExpr ? "Advanced formatting expression returned undef for '$g3${var}'" :
|
|
3270
3332
|
"Tag '$g3${var}' not defined";
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
$
|
|
3333
|
+
if (ref $opt) {
|
|
3334
|
+
$self->Warn($msg,2) or $val = '';
|
|
3335
|
+
} elsif ($opt) {
|
|
3336
|
+
no strict 'refs';
|
|
3337
|
+
($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef;
|
|
3338
|
+
$val = '';
|
|
3339
|
+
}
|
|
3274
3340
|
}
|
|
3275
3341
|
}
|
|
3276
3342
|
if (ref $opt eq 'HASH') {
|
|
@@ -4202,7 +4268,7 @@ sub WriteDirectory($$$;$)
|
|
|
4202
4268
|
return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed
|
|
4203
4269
|
# don't allow MakerNotes to be removed from RAW files
|
|
4204
4270
|
if ($blockName eq 'MakerNotes' and $rawType{$$self{FileType}}) {
|
|
4205
|
-
$self->Warn("Can't delete MakerNotes from $$self{
|
|
4271
|
+
$self->Warn("Can't delete MakerNotes from $$self{FileType}",1);
|
|
4206
4272
|
return undef;
|
|
4207
4273
|
}
|
|
4208
4274
|
$verb = 'Deleting';
|
|
@@ -4267,7 +4333,7 @@ sub WriteDirectory($$$;$)
|
|
|
4267
4333
|
if ($out) {
|
|
4268
4334
|
print $out " Deleting $name\n" if defined $newData and not length $newData;
|
|
4269
4335
|
if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) {
|
|
4270
|
-
print $out "$$self{INDENT} [nothing changed in $
|
|
4336
|
+
print $out "$$self{INDENT} [nothing changed in $name]\n";
|
|
4271
4337
|
}
|
|
4272
4338
|
}
|
|
4273
4339
|
return $newData;
|
|
@@ -5432,7 +5498,6 @@ sub WriteJPEG($$)
|
|
|
5432
5498
|
my $verbose = $$self{OPTIONS}{Verbose};
|
|
5433
5499
|
my $out = $$self{OPTIONS}{TextOut};
|
|
5434
5500
|
my $rtnVal = 0;
|
|
5435
|
-
my %dumpParms = ( Out => $out );
|
|
5436
5501
|
my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known
|
|
5437
5502
|
|
|
5438
5503
|
# check to be sure this is a valid JPG or EXV file
|
|
@@ -5447,7 +5512,6 @@ sub WriteJPEG($$)
|
|
|
5447
5512
|
Write($outfile,"\xff\x01") or $err = 1;
|
|
5448
5513
|
$isEXV = 1;
|
|
5449
5514
|
}
|
|
5450
|
-
$dumpParms{MaxLen} = 128 unless $verbose > 3;
|
|
5451
5515
|
|
|
5452
5516
|
delete $$self{PREVIEW_INFO}; # reset preview information
|
|
5453
5517
|
delete $$self{DEL_PREVIEW}; # reset flag to delete preview
|
|
@@ -5506,7 +5570,7 @@ sub WriteJPEG($$)
|
|
|
5506
5570
|
$s =~ /^JFXX\0\x10/ and $dirName = 'JFXX';
|
|
5507
5571
|
$s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF';
|
|
5508
5572
|
} elsif ($marker == 0xe1) {
|
|
5509
|
-
if ($s =~ /^(.{0,4})
|
|
5573
|
+
if ($s =~ /^(.{0,4})Exif\0.(.{1,4})/is) {
|
|
5510
5574
|
$dirName = 'IFD0';
|
|
5511
5575
|
my ($junk, $bytes) = ($1, $2);
|
|
5512
5576
|
# support multi-segment EXIF
|
|
@@ -6017,12 +6081,7 @@ sub WriteJPEG($$)
|
|
|
6017
6081
|
#
|
|
6018
6082
|
my $segDataPt = \$segData;
|
|
6019
6083
|
$length = length($segData);
|
|
6020
|
-
|
|
6021
|
-
print $out "JPEG $markerName ($length bytes):\n";
|
|
6022
|
-
if ($verbose > 2 and $markerName =~ /^APP/) {
|
|
6023
|
-
HexDump($segDataPt, undef, %dumpParms);
|
|
6024
|
-
}
|
|
6025
|
-
}
|
|
6084
|
+
print $out "JPEG $markerName ($length bytes)\n" if $verbose;
|
|
6026
6085
|
# group delete of APP segments
|
|
6027
6086
|
if ($$delGroup{$dirName}) {
|
|
6028
6087
|
$verbose and print $out " Deleting $dirName segment\n";
|
|
@@ -6077,7 +6136,7 @@ sub WriteJPEG($$)
|
|
|
6077
6136
|
}
|
|
6078
6137
|
} elsif ($marker == 0xe1) { # APP1 (EXIF, XMP)
|
|
6079
6138
|
# check for EXIF data
|
|
6080
|
-
if ($$segDataPt =~ /^(.{0,4})
|
|
6139
|
+
if ($$segDataPt =~ /^(.{0,4})Exif\0./is) {
|
|
6081
6140
|
my $hdrLen = length $exifAPP1hdr;
|
|
6082
6141
|
if (length $1) {
|
|
6083
6142
|
$hdrLen += length $1;
|
|
@@ -6812,6 +6871,36 @@ sub SetFileTime($$;$$$$)
|
|
|
6812
6871
|
return 1; # (nothing to do)
|
|
6813
6872
|
}
|
|
6814
6873
|
|
|
6874
|
+
#------------------------------------------------------------------------------
|
|
6875
|
+
# Add data to MD5 checksum
|
|
6876
|
+
# Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file),
|
|
6877
|
+
# 3) data name (or undef for no warnings or messages), 4) flag for no verbose message
|
|
6878
|
+
# Returns: number of bytes read and MD5'd
|
|
6879
|
+
sub ImageDataMD5($$$;$$)
|
|
6880
|
+
{
|
|
6881
|
+
my ($self, $raf, $size, $type, $noMsg) = @_;
|
|
6882
|
+
my $md5 = $$self{ImageDataMD5} or return;
|
|
6883
|
+
my ($bytesRead, $n) = (0, 65536);
|
|
6884
|
+
my $buff;
|
|
6885
|
+
for (;;) {
|
|
6886
|
+
if (defined $size) {
|
|
6887
|
+
last unless $size;
|
|
6888
|
+
$n = $size > 65536 ? 65536 : $size;
|
|
6889
|
+
$size -= $n;
|
|
6890
|
+
}
|
|
6891
|
+
unless ($raf->Read($buff, $n)) {
|
|
6892
|
+
$self->Warn("Error reading $type data") if $type and defined $size;
|
|
6893
|
+
last;
|
|
6894
|
+
}
|
|
6895
|
+
$md5->add($buff);
|
|
6896
|
+
$bytesRead += length $buff;
|
|
6897
|
+
}
|
|
6898
|
+
if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) {
|
|
6899
|
+
$self->VPrint(0, "$$self{INDENT}(ImageDataMD5: $bytesRead bytes of $type data)\n");
|
|
6900
|
+
}
|
|
6901
|
+
return $bytesRead;
|
|
6902
|
+
}
|
|
6903
|
+
|
|
6815
6904
|
#------------------------------------------------------------------------------
|
|
6816
6905
|
# Copy data block from RAF to output file in max 64kB chunks
|
|
6817
6906
|
# Inputs: 0) RAF ref, 1) outfile ref, 2) block size
|
|
@@ -6870,6 +6959,7 @@ sub WriteBinaryData($$$)
|
|
|
6870
6959
|
|
|
6871
6960
|
# get default format ('int8u' unless specified)
|
|
6872
6961
|
my $dataPt = $$dirInfo{DataPt} or return undef;
|
|
6962
|
+
my $dataLen = length $$dataPt;
|
|
6873
6963
|
my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
|
|
6874
6964
|
my $increment = FormatSize($defaultFormat);
|
|
6875
6965
|
unless ($increment) {
|
|
@@ -6886,7 +6976,8 @@ sub WriteBinaryData($$$)
|
|
|
6886
6976
|
delete $$dirInfo{VarFormatData};
|
|
6887
6977
|
}
|
|
6888
6978
|
my $dirStart = $$dirInfo{DirStart} || 0;
|
|
6889
|
-
my $dirLen = $$dirInfo{DirLen}
|
|
6979
|
+
my $dirLen = $$dirInfo{DirLen};
|
|
6980
|
+
$dirLen = $dataLen - $dirStart if not defined $dirLen or $dirLen > $dataLen - $dirStart;
|
|
6890
6981
|
my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
|
|
6891
6982
|
my $dirName = $$dirInfo{DirName};
|
|
6892
6983
|
my $varSize = 0;
|
|
@@ -7022,11 +7113,34 @@ sub WriteBinaryData($$$)
|
|
|
7022
7113
|
$tagInfo = $self->GetTagInfo($tagTablePtr, $tagID, \$v);
|
|
7023
7114
|
next unless $tagInfo;
|
|
7024
7115
|
}
|
|
7025
|
-
|
|
7026
|
-
my
|
|
7027
|
-
my $
|
|
7028
|
-
|
|
7029
|
-
|
|
7116
|
+
my $subdir = $$tagInfo{SubDirectory} or next;
|
|
7117
|
+
my $start = $$subdir{Start};
|
|
7118
|
+
my $len;
|
|
7119
|
+
if (not $start) {
|
|
7120
|
+
$start = $entry;
|
|
7121
|
+
$len = $dirLen - $start;
|
|
7122
|
+
} elsif ($start =~ /\$/) {
|
|
7123
|
+
my $count = 1;
|
|
7124
|
+
my $format = $$tagInfo{Format} || $defaultFormat;
|
|
7125
|
+
$format =~ /(.*)\[(.*)\]/ and ($format, $count) = ($1, $2);
|
|
7126
|
+
my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen - $entry);
|
|
7127
|
+
# ignore directories with a zero offset (ie. missing Nikon ShotInfo entries)
|
|
7128
|
+
next unless $val;
|
|
7129
|
+
my $dirStart = 0;
|
|
7130
|
+
#### eval Start ($val, $dirStart)
|
|
7131
|
+
$start = eval($start);
|
|
7132
|
+
next if $start < $dirStart or $start > $dataLen;
|
|
7133
|
+
$len = $$subdir{DirLen};
|
|
7134
|
+
$len = $dataLen - $start unless $len and $len <= $dataLen - $start;
|
|
7135
|
+
}
|
|
7136
|
+
my %subdirInfo = (
|
|
7137
|
+
DataPt => \$newData,
|
|
7138
|
+
DirStart => $start,
|
|
7139
|
+
DirLen => $len,
|
|
7140
|
+
TagInfo => $tagInfo,
|
|
7141
|
+
);
|
|
7142
|
+
my $dat = $self->WriteDirectory(\%subdirInfo, GetTagTable($$subdir{TagTable}));
|
|
7143
|
+
substr($newData, $start, $len) = $dat if defined $dat and length $dat;
|
|
7030
7144
|
}
|
|
7031
7145
|
}
|
|
7032
7146
|
return $newData;
|
|
@@ -50,7 +50,7 @@ use Image::ExifTool::Exif;
|
|
|
50
50
|
use Image::ExifTool::GPS;
|
|
51
51
|
require Exporter;
|
|
52
52
|
|
|
53
|
-
$VERSION = '3.
|
|
53
|
+
$VERSION = '3.58';
|
|
54
54
|
@ISA = qw(Exporter);
|
|
55
55
|
@EXPORT_OK = qw(EscapeXML UnescapeXML);
|
|
56
56
|
|
|
@@ -753,6 +753,10 @@ my %sRangeMask = (
|
|
|
753
753
|
Name => 'album',
|
|
754
754
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::Album' },
|
|
755
755
|
},
|
|
756
|
+
et => {
|
|
757
|
+
Name => 'et',
|
|
758
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::ExifTool' },
|
|
759
|
+
},
|
|
756
760
|
prism => {
|
|
757
761
|
Name => 'prism',
|
|
758
762
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::prism' },
|
|
@@ -2550,6 +2554,14 @@ my %sPantryItem = (
|
|
|
2550
2554
|
Notes => { },
|
|
2551
2555
|
);
|
|
2552
2556
|
|
|
2557
|
+
# ExifTool namespace properties (et)
|
|
2558
|
+
%Image::ExifTool::XMP::ExifTool = (
|
|
2559
|
+
%xmpTableDefaults,
|
|
2560
|
+
GROUPS => { 1 => 'XMP-et', 2 => 'Image' },
|
|
2561
|
+
NAMESPACE => 'et',
|
|
2562
|
+
OriginalImageMD5 => { Notes => 'used to store ExifTool ImageDataMD5 digest' },
|
|
2563
|
+
);
|
|
2564
|
+
|
|
2553
2565
|
# table to add tags in other namespaces
|
|
2554
2566
|
%Image::ExifTool::XMP::other = (
|
|
2555
2567
|
GROUPS => { 2 => 'Unknown' },
|
|
@@ -3097,7 +3109,7 @@ sub ScanForXMP($$)
|
|
|
3097
3109
|
undef $buff;
|
|
3098
3110
|
}
|
|
3099
3111
|
}
|
|
3100
|
-
unless ($$et{
|
|
3112
|
+
unless ($$et{FileType}) {
|
|
3101
3113
|
$$et{FILE_TYPE} = $$et{FILE_EXT};
|
|
3102
3114
|
$et->SetFileType('<unknown file containing XMP>', undef, '');
|
|
3103
3115
|
}
|
|
@@ -3539,6 +3551,7 @@ NoLoop:
|
|
|
3539
3551
|
DirLen => length $$dataPt,
|
|
3540
3552
|
IgnoreProp => $$subdir{IgnoreProp}, # (allow XML to ignore specified properties)
|
|
3541
3553
|
IsExtended => 1, # (hack to avoid Duplicate warning for embedded XMP)
|
|
3554
|
+
NoStruct => 1, # (don't try to build structures since this isn't true XMP)
|
|
3542
3555
|
);
|
|
3543
3556
|
my $oldOrder = GetByteOrder();
|
|
3544
3557
|
SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder};
|
|
@@ -4375,8 +4388,10 @@ sub ProcessXMP($$;$)
|
|
|
4375
4388
|
|
|
4376
4389
|
# restore structures if necessary
|
|
4377
4390
|
if ($$et{IsStruct}) {
|
|
4378
|
-
|
|
4379
|
-
|
|
4391
|
+
unless ($$dirInfo{NoStruct}) {
|
|
4392
|
+
require 'Image/ExifTool/XMPStruct.pl';
|
|
4393
|
+
RestoreStruct($et, $keepFlat);
|
|
4394
|
+
}
|
|
4380
4395
|
delete $$et{IsStruct};
|
|
4381
4396
|
}
|
|
4382
4397
|
# reset NO_LIST flag (must do this _after_ RestoreStruct() above)
|
|
@@ -843,7 +843,8 @@ my %prismPublicationDate = (
|
|
|
843
843
|
AVOID => 1,
|
|
844
844
|
NOTES => q{
|
|
845
845
|
Publishing Requirements for Industry Standard Metadata 3.0 namespace
|
|
846
|
-
tags. (see
|
|
846
|
+
tags. (see
|
|
847
|
+
L<https://www.w3.org/Submission/2020/SUBM-prism-20200910/prism-basic.html/>)
|
|
847
848
|
},
|
|
848
849
|
academicField => { }, # (3.0)
|
|
849
850
|
aggregateIssueNumber => { Writable => 'integer' }, # (3.0)
|