exiftool-vendored.exe 12.56.0 → 12.62.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 +115 -5
- package/bin/exiftool_files/LICENSE +674 -0
- package/bin/exiftool_files/README +45 -44
- 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 +262 -160
- 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/BMP.pm +0 -1
- package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +23 -19
- package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +26 -6
- 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 +77 -19
- package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +33 -10
- package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +7 -3
- 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/JPEG.pm +14 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +36 -11
- 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 +1005 -909
- 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/PDF.pm +17 -8
- package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +10 -2
- 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 +48 -14
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +91 -27
- 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/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 +39 -10
- package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +4687 -4628
- package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +338 -117
- package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +5 -5
- package/bin/exiftool_files/lib/Image/ExifTool/WPG.pm +296 -0
- package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +42 -0
- package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +7 -8
- package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +162 -40
- package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +35 -8
- package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +159 -41
- package/bin/exiftool_files/lib/Image/ExifTool.pm +286 -65
- package/bin/exiftool_files/lib/Image/ExifTool.pod +95 -51
- package/package.json +2 -2
|
@@ -1302,6 +1302,7 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1302
1302
|
MDItemTags => $$options{MDItemTags},
|
|
1303
1303
|
MissingTagValue => $$options{MissingTagValue},
|
|
1304
1304
|
NoPDFList => $$options{NoPDFList},
|
|
1305
|
+
NoWarning => $$options{NoWarning},
|
|
1305
1306
|
Password => $$options{Password},
|
|
1306
1307
|
PrintConv => $$options{PrintConv},
|
|
1307
1308
|
QuickTimeUTC => $$options{QuickTimeUTC},
|
|
@@ -1321,6 +1322,7 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1321
1322
|
XMPAutoConv => $$options{XMPAutoConv},
|
|
1322
1323
|
);
|
|
1323
1324
|
$$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
|
|
1325
|
+
$$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL};
|
|
1324
1326
|
foreach $tag (@setTags) {
|
|
1325
1327
|
next if ref $tag;
|
|
1326
1328
|
if ($tag =~ /^-(.*)/) {
|
|
@@ -1387,7 +1389,7 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1387
1389
|
# transfer specified tags in the proper order
|
|
1388
1390
|
#
|
|
1389
1391
|
# 1) loop through input list of tags to set, and build @setList
|
|
1390
|
-
my (@setList, $set, %setMatches, $t);
|
|
1392
|
+
my (@setList, $set, %setMatches, $t, %altFiles);
|
|
1391
1393
|
foreach $t (@setTags) {
|
|
1392
1394
|
if (ref $t eq 'HASH') {
|
|
1393
1395
|
# update current options
|
|
@@ -1419,9 +1421,12 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1419
1421
|
$tag =~ s/(.+?)\s*(>|<) ?//;
|
|
1420
1422
|
$$opts{EXPR} = 1; # flag this expression
|
|
1421
1423
|
} else {
|
|
1424
|
+
# (not sure why this is here because sign should be before '<')
|
|
1425
|
+
# (--> allows "<+" or "<-", which is an undocumented feature)
|
|
1422
1426
|
$opt = $1 if $tag =~ s/^([-+])\s*//;
|
|
1423
1427
|
}
|
|
1424
1428
|
}
|
|
1429
|
+
$$opts{Replace} = 0 if $dstTag =~ s/^\+//;
|
|
1425
1430
|
# validate tag name(s)
|
|
1426
1431
|
unless ($$opts{EXPR} or ValidTagName($tag)) {
|
|
1427
1432
|
$self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value");
|
|
@@ -1438,6 +1443,8 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1438
1443
|
$$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//;
|
|
1439
1444
|
# replace tag name of 'all' with '*'
|
|
1440
1445
|
$dstTag = '*' if $dstTag eq 'all';
|
|
1446
|
+
} else {
|
|
1447
|
+
$$opts{Replace} = 0 if $tag =~ s/^\+//;
|
|
1441
1448
|
}
|
|
1442
1449
|
unless ($$opts{EXPR}) {
|
|
1443
1450
|
$isExclude = ($tag =~ s/^-//);
|
|
@@ -1447,7 +1454,17 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1447
1454
|
# save family/groups in list (ignoring 'all' and '*')
|
|
1448
1455
|
next unless length($_) and /^(\d+)?(.*)/;
|
|
1449
1456
|
my ($f, $g) = ($1, $2);
|
|
1450
|
-
$f = 7 if $g =~ s/^ID-//i;
|
|
1457
|
+
$f = 7 if (not $f or $f eq '7') and $g =~ s/^ID-//i;
|
|
1458
|
+
if ($g =~ /^file\d+$/i and (not $f or $f eq '8')) {
|
|
1459
|
+
$f = 8;
|
|
1460
|
+
my $g8 = ucfirst $g;
|
|
1461
|
+
if ($$srcExifTool{ALT_EXIFTOOL}{$g8}) {
|
|
1462
|
+
$$opts{GROUP8} = $g8;
|
|
1463
|
+
$altFiles{$g8} or $altFiles{$g8} = [ ];
|
|
1464
|
+
# save list of requested tags for this alternate ExifTool object
|
|
1465
|
+
push @{$altFiles{$g8}}, "$grp:$tag";
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1451
1468
|
push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all';
|
|
1452
1469
|
}
|
|
1453
1470
|
}
|
|
@@ -1484,26 +1501,44 @@ sub SetNewValuesFromFile($$;@)
|
|
|
1484
1501
|
# save in reverse order so we don't set tags before an exclude
|
|
1485
1502
|
unshift @setList, [ \@fg, $tag, $dst, $opts ];
|
|
1486
1503
|
}
|
|
1504
|
+
# 1b) copy requested tags for each alternate ExifTool object
|
|
1505
|
+
my $g8;
|
|
1506
|
+
foreach $g8 (sort keys %altFiles) {
|
|
1507
|
+
# request specific alternate tags to load them from the alternate ExifTool object
|
|
1508
|
+
my $altInfo = $srcExifTool->GetInfo($altFiles{$g8});
|
|
1509
|
+
# add to tags list after dummy entry to signify start of tags for this alt file
|
|
1510
|
+
if (%$altInfo) {
|
|
1511
|
+
push @tags, 'Warning DUMMY', reverse sort keys %$altInfo;
|
|
1512
|
+
$$info{$_} = $$altInfo{$_} foreach keys %$altInfo;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1487
1515
|
# 2) initialize lists of matching tags for each setTag
|
|
1488
1516
|
foreach $set (@setList) {
|
|
1489
1517
|
$$set[2] and $setMatches{$set} = [ ];
|
|
1490
1518
|
}
|
|
1491
1519
|
# 3) loop through all tags in source image and save tags matching each setTag
|
|
1492
|
-
my %rtnInfo;
|
|
1520
|
+
my (%rtnInfo, $isAlt);
|
|
1493
1521
|
foreach $tag (@tags) {
|
|
1494
1522
|
# don't try to set errors or warnings
|
|
1495
1523
|
if ($tag =~ /^(Error|Warning)( |$)/) {
|
|
1496
|
-
$
|
|
1524
|
+
if ($tag eq 'Warning DUMMY') {
|
|
1525
|
+
$isAlt = 1; # start of the alt tags
|
|
1526
|
+
} else {
|
|
1527
|
+
$rtnInfo{$tag} = $$info{$tag};
|
|
1528
|
+
}
|
|
1497
1529
|
next;
|
|
1498
1530
|
}
|
|
1499
1531
|
# only set specified tags
|
|
1500
1532
|
my $lcTag = lc(GetTagName($tag));
|
|
1501
1533
|
my (@grp, %grp);
|
|
1502
1534
|
SET: foreach $set (@setList) {
|
|
1535
|
+
my $opts = $$set[3];
|
|
1536
|
+
next if $$opts{EXPR}; # (expressions handled in step 4)
|
|
1537
|
+
next if $$opts{GROUP8} xor $isAlt;
|
|
1503
1538
|
# check first for matching tag
|
|
1504
1539
|
unless ($$set[1] eq $lcTag or $$set[1] eq '*') {
|
|
1505
1540
|
# handle wildcards
|
|
1506
|
-
next unless $$
|
|
1541
|
+
next unless $$opts{WILD} and $lcTag =~ /^$$set[1]$/;
|
|
1507
1542
|
}
|
|
1508
1543
|
# then check for matching group
|
|
1509
1544
|
if (@{$$set[0]}) {
|
|
@@ -1535,10 +1570,17 @@ SET: foreach $set (@setList) {
|
|
|
1535
1570
|
# handle expressions
|
|
1536
1571
|
if ($$opts{EXPR}) {
|
|
1537
1572
|
my $val = $srcExifTool->InsertTagValues(\@tags, $$set[1], 'Error');
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
$
|
|
1573
|
+
my $err = $$srcExifTool{VALUE}{Error};
|
|
1574
|
+
if ($err) {
|
|
1575
|
+
# pass on any error as a warning unless it is suppressed
|
|
1576
|
+
my $noWarn = $$srcExifTool{OPTIONS}{NoWarning};
|
|
1577
|
+
unless ($noWarn and (eval { $err =~ /$noWarn/ } or
|
|
1578
|
+
# (also apply expression to warning without "[minor] " prefix)
|
|
1579
|
+
($err =~ s/^\[minor\] //i and eval { $err =~ /$noWarn/ })))
|
|
1580
|
+
{
|
|
1581
|
+
$tag = NextFreeTagKey(\%rtnInfo, 'Warning');
|
|
1582
|
+
$rtnInfo{$tag} = $$srcExifTool{VALUE}{Error};
|
|
1583
|
+
}
|
|
1542
1584
|
delete $$srcExifTool{VALUE}{Error};
|
|
1543
1585
|
next unless defined $val;
|
|
1544
1586
|
}
|
|
@@ -1835,6 +1877,27 @@ sub RestoreNewValues($)
|
|
|
1835
1877
|
$$self{DEL_GROUP} = \%delGrp;
|
|
1836
1878
|
}
|
|
1837
1879
|
|
|
1880
|
+
#------------------------------------------------------------------------------
|
|
1881
|
+
# Set alternate file for extracting information
|
|
1882
|
+
# Inputs: 0) ExifTool ref, 1) family 8 group name (of the form "File#" where # is any number)
|
|
1883
|
+
# 2) alternate file name, or undef to reset
|
|
1884
|
+
# Returns: 1 on success, or 0 on invalid group name
|
|
1885
|
+
sub SetAlternateFile($$$)
|
|
1886
|
+
{
|
|
1887
|
+
my ($self, $g8, $file) = @_;
|
|
1888
|
+
$g8 = ucfirst lc $g8;
|
|
1889
|
+
return 0 unless $g8 =~ /^File\d+$/;
|
|
1890
|
+
# keep the same file if already initialized (possibly has metadata extracted)
|
|
1891
|
+
if (not defined $file) {
|
|
1892
|
+
delete $$self{ALT_EXIFTOOL}{$g8};
|
|
1893
|
+
} elsif (not ($$self{ALT_EXIFTOOL}{$g8} and $$self{ALT_EXIFTOOL}{$g8}{ALT_FILE} eq $file)) {
|
|
1894
|
+
my $altExifTool = Image::ExifTool->new;
|
|
1895
|
+
$$altExifTool{ALT_FILE} = $file;
|
|
1896
|
+
$$self{ALT_EXIFTOOL}{$g8} = $altExifTool;
|
|
1897
|
+
}
|
|
1898
|
+
return 1;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1838
1901
|
#------------------------------------------------------------------------------
|
|
1839
1902
|
# Set filesystem time from from FileModifyDate or FileCreateDate tag
|
|
1840
1903
|
# Inputs: 0) ExifTool object reference, 1) file name or file ref
|
|
@@ -2722,13 +2785,16 @@ sub GetAllGroups($;$)
|
|
|
2722
2785
|
$family == 4 and return('Copy#');
|
|
2723
2786
|
$family == 5 and return('[too many possibilities to list]');
|
|
2724
2787
|
$family == 6 and return(@Image::ExifTool::Exif::formatName[1..$#Image::ExifTool::Exif::formatName]);
|
|
2788
|
+
$family == 8 and return('File#');
|
|
2725
2789
|
|
|
2726
2790
|
LoadAllTables(); # first load all our tables
|
|
2727
2791
|
|
|
2728
2792
|
my @tableNames = keys %allTables;
|
|
2729
2793
|
|
|
2730
|
-
# loop through all tag tables and get all group names
|
|
2731
2794
|
my %allGroups;
|
|
2795
|
+
# add family 1 groups not in tables
|
|
2796
|
+
$family == 1 and map { $allGroups{$_} = 1 } qw(Garmin);
|
|
2797
|
+
# loop through all tag tables and get all group names
|
|
2732
2798
|
while (@tableNames) {
|
|
2733
2799
|
my $table = GetTagTable(pop @tableNames);
|
|
2734
2800
|
my ($grps, $grp, $tag, $tagInfo);
|
|
@@ -3159,42 +3225,46 @@ sub InsertTagValues($$$;$$$)
|
|
|
3159
3225
|
$tag = $docGrp . ':' . $tag;
|
|
3160
3226
|
$lcTag = lc $tag;
|
|
3161
3227
|
}
|
|
3228
|
+
my $et = $self;
|
|
3229
|
+
if ($tag =~ s/(\bfile\d+)://i) {
|
|
3230
|
+
$et = $$self{ALT_EXIFTOOL}{ucfirst lc $1} or $et=$self, $tag = 'no_alt_file';
|
|
3231
|
+
}
|
|
3162
3232
|
if ($lcTag eq 'all') {
|
|
3163
3233
|
$val = 1; # always some tag available
|
|
3164
|
-
} elsif (defined $$
|
|
3165
|
-
$val = $$
|
|
3234
|
+
} elsif (defined $$et{OPTIONS}{UserParam}{$lcTag}) {
|
|
3235
|
+
$val = $$et{OPTIONS}{UserParam}{$lcTag};
|
|
3166
3236
|
} elsif ($tag =~ /(.*):(.+)/) {
|
|
3167
3237
|
my $group;
|
|
3168
3238
|
($group, $tag) = ($1, $2);
|
|
3169
3239
|
if (lc $tag eq 'all') {
|
|
3170
3240
|
# see if any tag from the specified group exists
|
|
3171
|
-
my $match = $
|
|
3241
|
+
my $match = $et->GroupMatches($group, $foundTags);
|
|
3172
3242
|
$val = $match ? 1 : 0;
|
|
3173
3243
|
} else {
|
|
3174
3244
|
# find the specified tag
|
|
3175
3245
|
my @matches = grep /^$tag(\s|$)/i, @$foundTags;
|
|
3176
|
-
@matches = $
|
|
3246
|
+
@matches = $et->GroupMatches($group, \@matches);
|
|
3177
3247
|
foreach $tg (@matches) {
|
|
3178
3248
|
if (defined $val and $tg =~ / \((\d+)\)$/) {
|
|
3179
3249
|
# take the most recently extracted tag
|
|
3180
3250
|
my $tagNum = $1;
|
|
3181
3251
|
next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum;
|
|
3182
3252
|
}
|
|
3183
|
-
$val = $
|
|
3253
|
+
$val = $et->GetValue($tg, $type);
|
|
3184
3254
|
$tag = $tg;
|
|
3185
3255
|
last unless $tag =~ / /; # all done if we got our best match
|
|
3186
3256
|
}
|
|
3187
3257
|
}
|
|
3188
3258
|
} elsif ($tag eq 'self') {
|
|
3189
|
-
$val = $
|
|
3259
|
+
$val = $et; # ("$self{var}" or "$file1:self{var}" in string)
|
|
3190
3260
|
} else {
|
|
3191
3261
|
# get the tag value
|
|
3192
|
-
$val = $
|
|
3262
|
+
$val = $et->GetValue($tag, $type);
|
|
3193
3263
|
unless (defined $val) {
|
|
3194
3264
|
# check for tag name with different case
|
|
3195
3265
|
($tg) = grep /^$tag$/i, @$foundTags;
|
|
3196
3266
|
if (defined $tg) {
|
|
3197
|
-
$val = $
|
|
3267
|
+
$val = $et->GetValue($tg, $type);
|
|
3198
3268
|
$tag = $tg;
|
|
3199
3269
|
}
|
|
3200
3270
|
}
|
|
@@ -3262,15 +3332,19 @@ sub InsertTagValues($$$;$$$)
|
|
|
3262
3332
|
undef $advFmtSelf;
|
|
3263
3333
|
$didExpr = 1; # set flag indicating an expression was evaluated
|
|
3264
3334
|
}
|
|
3265
|
-
unless (defined $val
|
|
3335
|
+
unless (defined $val) {
|
|
3266
3336
|
$val = $$self{OPTIONS}{MissingTagValue};
|
|
3267
3337
|
unless (defined $val) {
|
|
3268
3338
|
my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : '';
|
|
3269
3339
|
my $msg = $didExpr ? "Advanced formatting expression returned undef for '$g3${var}'" :
|
|
3270
3340
|
"Tag '$g3${var}' not defined";
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
$
|
|
3341
|
+
if (ref $opt) {
|
|
3342
|
+
$self->Warn($msg,2) or $val = '';
|
|
3343
|
+
} elsif ($opt) {
|
|
3344
|
+
no strict 'refs';
|
|
3345
|
+
($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef;
|
|
3346
|
+
$val = '';
|
|
3347
|
+
}
|
|
3274
3348
|
}
|
|
3275
3349
|
}
|
|
3276
3350
|
if (ref $opt eq 'HASH') {
|
|
@@ -4202,7 +4276,7 @@ sub WriteDirectory($$$;$)
|
|
|
4202
4276
|
return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed
|
|
4203
4277
|
# don't allow MakerNotes to be removed from RAW files
|
|
4204
4278
|
if ($blockName eq 'MakerNotes' and $rawType{$$self{FileType}}) {
|
|
4205
|
-
$self->Warn("Can't delete MakerNotes from $$self{
|
|
4279
|
+
$self->Warn("Can't delete MakerNotes from $$self{FileType}",1);
|
|
4206
4280
|
return undef;
|
|
4207
4281
|
}
|
|
4208
4282
|
$verb = 'Deleting';
|
|
@@ -4267,7 +4341,7 @@ sub WriteDirectory($$$;$)
|
|
|
4267
4341
|
if ($out) {
|
|
4268
4342
|
print $out " Deleting $name\n" if defined $newData and not length $newData;
|
|
4269
4343
|
if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) {
|
|
4270
|
-
print $out "$$self{INDENT} [nothing changed in $
|
|
4344
|
+
print $out "$$self{INDENT} [nothing changed in $name]\n";
|
|
4271
4345
|
}
|
|
4272
4346
|
}
|
|
4273
4347
|
return $newData;
|
|
@@ -5432,7 +5506,6 @@ sub WriteJPEG($$)
|
|
|
5432
5506
|
my $verbose = $$self{OPTIONS}{Verbose};
|
|
5433
5507
|
my $out = $$self{OPTIONS}{TextOut};
|
|
5434
5508
|
my $rtnVal = 0;
|
|
5435
|
-
my %dumpParms = ( Out => $out );
|
|
5436
5509
|
my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known
|
|
5437
5510
|
|
|
5438
5511
|
# check to be sure this is a valid JPG or EXV file
|
|
@@ -5447,7 +5520,6 @@ sub WriteJPEG($$)
|
|
|
5447
5520
|
Write($outfile,"\xff\x01") or $err = 1;
|
|
5448
5521
|
$isEXV = 1;
|
|
5449
5522
|
}
|
|
5450
|
-
$dumpParms{MaxLen} = 128 unless $verbose > 3;
|
|
5451
5523
|
|
|
5452
5524
|
delete $$self{PREVIEW_INFO}; # reset preview information
|
|
5453
5525
|
delete $$self{DEL_PREVIEW}; # reset flag to delete preview
|
|
@@ -5506,7 +5578,7 @@ sub WriteJPEG($$)
|
|
|
5506
5578
|
$s =~ /^JFXX\0\x10/ and $dirName = 'JFXX';
|
|
5507
5579
|
$s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF';
|
|
5508
5580
|
} elsif ($marker == 0xe1) {
|
|
5509
|
-
if ($s =~ /^(.{0,4})
|
|
5581
|
+
if ($s =~ /^(.{0,4})Exif\0.(.{1,4})/is) {
|
|
5510
5582
|
$dirName = 'IFD0';
|
|
5511
5583
|
my ($junk, $bytes) = ($1, $2);
|
|
5512
5584
|
# support multi-segment EXIF
|
|
@@ -6017,12 +6089,7 @@ sub WriteJPEG($$)
|
|
|
6017
6089
|
#
|
|
6018
6090
|
my $segDataPt = \$segData;
|
|
6019
6091
|
$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
|
-
}
|
|
6092
|
+
print $out "JPEG $markerName ($length bytes)\n" if $verbose;
|
|
6026
6093
|
# group delete of APP segments
|
|
6027
6094
|
if ($$delGroup{$dirName}) {
|
|
6028
6095
|
$verbose and print $out " Deleting $dirName segment\n";
|
|
@@ -6077,7 +6144,7 @@ sub WriteJPEG($$)
|
|
|
6077
6144
|
}
|
|
6078
6145
|
} elsif ($marker == 0xe1) { # APP1 (EXIF, XMP)
|
|
6079
6146
|
# check for EXIF data
|
|
6080
|
-
if ($$segDataPt =~ /^(.{0,4})
|
|
6147
|
+
if ($$segDataPt =~ /^(.{0,4})Exif\0./is) {
|
|
6081
6148
|
my $hdrLen = length $exifAPP1hdr;
|
|
6082
6149
|
if (length $1) {
|
|
6083
6150
|
$hdrLen += length $1;
|
|
@@ -6812,6 +6879,36 @@ sub SetFileTime($$;$$$$)
|
|
|
6812
6879
|
return 1; # (nothing to do)
|
|
6813
6880
|
}
|
|
6814
6881
|
|
|
6882
|
+
#------------------------------------------------------------------------------
|
|
6883
|
+
# Add data to MD5 checksum
|
|
6884
|
+
# Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file),
|
|
6885
|
+
# 3) data name (or undef for no warnings or messages), 4) flag for no verbose message
|
|
6886
|
+
# Returns: number of bytes read and MD5'd
|
|
6887
|
+
sub ImageDataMD5($$$;$$)
|
|
6888
|
+
{
|
|
6889
|
+
my ($self, $raf, $size, $type, $noMsg) = @_;
|
|
6890
|
+
my $md5 = $$self{ImageDataMD5} or return;
|
|
6891
|
+
my ($bytesRead, $n) = (0, 65536);
|
|
6892
|
+
my $buff;
|
|
6893
|
+
for (;;) {
|
|
6894
|
+
if (defined $size) {
|
|
6895
|
+
last unless $size;
|
|
6896
|
+
$n = $size > 65536 ? 65536 : $size;
|
|
6897
|
+
$size -= $n;
|
|
6898
|
+
}
|
|
6899
|
+
unless ($raf->Read($buff, $n)) {
|
|
6900
|
+
$self->Warn("Error reading $type data") if $type and defined $size;
|
|
6901
|
+
last;
|
|
6902
|
+
}
|
|
6903
|
+
$md5->add($buff);
|
|
6904
|
+
$bytesRead += length $buff;
|
|
6905
|
+
}
|
|
6906
|
+
if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) {
|
|
6907
|
+
$self->VPrint(0, "$$self{INDENT}(ImageDataMD5: $bytesRead bytes of $type data)\n");
|
|
6908
|
+
}
|
|
6909
|
+
return $bytesRead;
|
|
6910
|
+
}
|
|
6911
|
+
|
|
6815
6912
|
#------------------------------------------------------------------------------
|
|
6816
6913
|
# Copy data block from RAF to output file in max 64kB chunks
|
|
6817
6914
|
# Inputs: 0) RAF ref, 1) outfile ref, 2) block size
|
|
@@ -6870,6 +6967,7 @@ sub WriteBinaryData($$$)
|
|
|
6870
6967
|
|
|
6871
6968
|
# get default format ('int8u' unless specified)
|
|
6872
6969
|
my $dataPt = $$dirInfo{DataPt} or return undef;
|
|
6970
|
+
my $dataLen = length $$dataPt;
|
|
6873
6971
|
my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
|
|
6874
6972
|
my $increment = FormatSize($defaultFormat);
|
|
6875
6973
|
unless ($increment) {
|
|
@@ -6886,7 +6984,8 @@ sub WriteBinaryData($$$)
|
|
|
6886
6984
|
delete $$dirInfo{VarFormatData};
|
|
6887
6985
|
}
|
|
6888
6986
|
my $dirStart = $$dirInfo{DirStart} || 0;
|
|
6889
|
-
my $dirLen = $$dirInfo{DirLen}
|
|
6987
|
+
my $dirLen = $$dirInfo{DirLen};
|
|
6988
|
+
$dirLen = $dataLen - $dirStart if not defined $dirLen or $dirLen > $dataLen - $dirStart;
|
|
6890
6989
|
my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
|
|
6891
6990
|
my $dirName = $$dirInfo{DirName};
|
|
6892
6991
|
my $varSize = 0;
|
|
@@ -7022,11 +7121,34 @@ sub WriteBinaryData($$$)
|
|
|
7022
7121
|
$tagInfo = $self->GetTagInfo($tagTablePtr, $tagID, \$v);
|
|
7023
7122
|
next unless $tagInfo;
|
|
7024
7123
|
}
|
|
7025
|
-
|
|
7026
|
-
my
|
|
7027
|
-
my $
|
|
7028
|
-
|
|
7029
|
-
|
|
7124
|
+
my $subdir = $$tagInfo{SubDirectory} or next;
|
|
7125
|
+
my $start = $$subdir{Start};
|
|
7126
|
+
my $len;
|
|
7127
|
+
if (not $start) {
|
|
7128
|
+
$start = $entry;
|
|
7129
|
+
$len = $dirLen - $start;
|
|
7130
|
+
} elsif ($start =~ /\$/) {
|
|
7131
|
+
my $count = 1;
|
|
7132
|
+
my $format = $$tagInfo{Format} || $defaultFormat;
|
|
7133
|
+
$format =~ /(.*)\[(.*)\]/ and ($format, $count) = ($1, $2);
|
|
7134
|
+
my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen - $entry);
|
|
7135
|
+
# ignore directories with a zero offset (ie. missing Nikon ShotInfo entries)
|
|
7136
|
+
next unless $val;
|
|
7137
|
+
my $dirStart = 0;
|
|
7138
|
+
#### eval Start ($val, $dirStart)
|
|
7139
|
+
$start = eval($start);
|
|
7140
|
+
next if $start < $dirStart or $start > $dataLen;
|
|
7141
|
+
$len = $$subdir{DirLen};
|
|
7142
|
+
$len = $dataLen - $start unless $len and $len <= $dataLen - $start;
|
|
7143
|
+
}
|
|
7144
|
+
my %subdirInfo = (
|
|
7145
|
+
DataPt => \$newData,
|
|
7146
|
+
DirStart => $start,
|
|
7147
|
+
DirLen => $len,
|
|
7148
|
+
TagInfo => $tagInfo,
|
|
7149
|
+
);
|
|
7150
|
+
my $dat = $self->WriteDirectory(\%subdirInfo, GetTagTable($$subdir{TagTable}));
|
|
7151
|
+
substr($newData, $start, $len) = $dat if defined $dat and length $dat;
|
|
7030
7152
|
}
|
|
7031
7153
|
}
|
|
7032
7154
|
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.59';
|
|
54
54
|
@ISA = qw(Exporter);
|
|
55
55
|
@EXPORT_OK = qw(EscapeXML UnescapeXML);
|
|
56
56
|
|
|
@@ -248,7 +248,11 @@ my %boolConv = (
|
|
|
248
248
|
|
|
249
249
|
# XMP namespaces which we don't want to contribute to generated EXIF tag names
|
|
250
250
|
# (Note: namespaces with non-standard prefixes aren't currently ignored)
|
|
251
|
-
my %ignoreNamespace = ( 'x'=>1, rdf=>1, xmlns=>1, xml=>1, svg=>1,
|
|
251
|
+
my %ignoreNamespace = ( 'x'=>1, rdf=>1, xmlns=>1, xml=>1, svg=>1, office=>1 );
|
|
252
|
+
|
|
253
|
+
# ExifTool properties that don't generate tag names (et:tagid is historic)
|
|
254
|
+
my %ignoreEtProp = ( 'et:desc'=>1, 'et:prt'=>1, 'et:val'=>1 , 'et:id'=>1, 'et:tagid'=>1,
|
|
255
|
+
'et:toolkit'=>1, 'et:table'=>1, 'et:index'=>1 );
|
|
252
256
|
|
|
253
257
|
# XMP properties to ignore (set dynamically via dirInfo IgnoreProp)
|
|
254
258
|
my %ignoreProp;
|
|
@@ -753,6 +757,10 @@ my %sRangeMask = (
|
|
|
753
757
|
Name => 'album',
|
|
754
758
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::Album' },
|
|
755
759
|
},
|
|
760
|
+
et => {
|
|
761
|
+
Name => 'et',
|
|
762
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::ExifTool' },
|
|
763
|
+
},
|
|
756
764
|
prism => {
|
|
757
765
|
Name => 'prism',
|
|
758
766
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::prism' },
|
|
@@ -2481,6 +2489,9 @@ my %sPantryItem = (
|
|
|
2481
2489
|
EnhanceSuperResolutionAlreadyApplied => { Writable => 'boolean' },
|
|
2482
2490
|
EnhanceSuperResolutionVersion => { }, # integer?
|
|
2483
2491
|
EnhanceSuperResolutionScale => { Writable => 'rational' },
|
|
2492
|
+
EnhanceDenoiseAlreadyApplied => { Writable => 'boolean' }, #forum14760
|
|
2493
|
+
EnhanceDenoiseVersion => { }, #forum14760 integer?
|
|
2494
|
+
EnhanceDenoiseLumaAmount => { }, #forum14760 integer?
|
|
2484
2495
|
);
|
|
2485
2496
|
|
|
2486
2497
|
# IPTC Core namespace properties (Iptc4xmpCore) (ref 4)
|
|
@@ -2550,6 +2561,14 @@ my %sPantryItem = (
|
|
|
2550
2561
|
Notes => { },
|
|
2551
2562
|
);
|
|
2552
2563
|
|
|
2564
|
+
# ExifTool namespace properties (et)
|
|
2565
|
+
%Image::ExifTool::XMP::ExifTool = (
|
|
2566
|
+
%xmpTableDefaults,
|
|
2567
|
+
GROUPS => { 1 => 'XMP-et', 2 => 'Image' },
|
|
2568
|
+
NAMESPACE => 'et',
|
|
2569
|
+
OriginalImageMD5 => { Notes => 'used to store ExifTool ImageDataMD5 digest' },
|
|
2570
|
+
);
|
|
2571
|
+
|
|
2553
2572
|
# table to add tags in other namespaces
|
|
2554
2573
|
%Image::ExifTool::XMP::other = (
|
|
2555
2574
|
GROUPS => { 2 => 'Unknown' },
|
|
@@ -2838,7 +2857,7 @@ sub GetXMPTagID($;$$)
|
|
|
2838
2857
|
# split name into namespace and property name
|
|
2839
2858
|
# (Note: namespace can be '' for property qualifiers)
|
|
2840
2859
|
my ($ns, $nm) = ($prop =~ /(.*?):(.*)/) ? ($1, $2) : ('', $prop);
|
|
2841
|
-
if ($ignoreNamespace{$ns} or $ignoreProp{$prop}) {
|
|
2860
|
+
if ($ignoreNamespace{$ns} or $ignoreProp{$prop} or $ignoreEtProp{$prop}) {
|
|
2842
2861
|
# special case: don't ignore rdf numbered items
|
|
2843
2862
|
# (not technically allowed in XMP, but used in RDF/XML)
|
|
2844
2863
|
unless ($prop =~ /^rdf:(_\d+)$/) {
|
|
@@ -3097,7 +3116,7 @@ sub ScanForXMP($$)
|
|
|
3097
3116
|
undef $buff;
|
|
3098
3117
|
}
|
|
3099
3118
|
}
|
|
3100
|
-
unless ($$et{
|
|
3119
|
+
unless ($$et{FileType}) {
|
|
3101
3120
|
$$et{FILE_TYPE} = $$et{FILE_EXT};
|
|
3102
3121
|
$et->SetFileType('<unknown file containing XMP>', undef, '');
|
|
3103
3122
|
}
|
|
@@ -3408,7 +3427,10 @@ NoLoop:
|
|
|
3408
3427
|
my %grps = ( 0 => $1, 1 => $2 );
|
|
3409
3428
|
# apply a little magic to recover original group names
|
|
3410
3429
|
# from this exiftool-written RDF/XML file
|
|
3411
|
-
if ($grps{1}
|
|
3430
|
+
if ($grps{1} eq 'System') {
|
|
3431
|
+
$grps{1} = 'XML-System';
|
|
3432
|
+
$grps{0} = 'XML';
|
|
3433
|
+
} elsif ($grps{1} =~ /^\d/) {
|
|
3412
3434
|
# URI's with only family 0 are internal tags from the source file,
|
|
3413
3435
|
# so change the group name to avoid confusion with tags from this file
|
|
3414
3436
|
$grps{1} = "XML-$grps{0}";
|
|
@@ -3539,6 +3561,7 @@ NoLoop:
|
|
|
3539
3561
|
DirLen => length $$dataPt,
|
|
3540
3562
|
IgnoreProp => $$subdir{IgnoreProp}, # (allow XML to ignore specified properties)
|
|
3541
3563
|
IsExtended => 1, # (hack to avoid Duplicate warning for embedded XMP)
|
|
3564
|
+
NoStruct => 1, # (don't try to build structures since this isn't true XMP)
|
|
3542
3565
|
);
|
|
3543
3566
|
my $oldOrder = GetByteOrder();
|
|
3544
3567
|
SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder};
|
|
@@ -3875,7 +3898,9 @@ sub ParseXMPElement($$$;$$$$)
|
|
|
3875
3898
|
}
|
|
3876
3899
|
}
|
|
3877
3900
|
my $shortVal = $attrs{$shortName};
|
|
3878
|
-
|
|
3901
|
+
# Note: $prop is the containing property in this loop (not the shorthand property)
|
|
3902
|
+
# so $ignoreProp ignores all attributes of the ignored property
|
|
3903
|
+
if ($ignoreNamespace{$ns} or $ignoreProp{$prop} or $ignoreEtProp{$propName}) {
|
|
3879
3904
|
$ignored = $propName;
|
|
3880
3905
|
# handle special attributes (extract as tags only once if not empty)
|
|
3881
3906
|
if (ref $recognizedAttrs{$propName} and $shortVal) {
|
|
@@ -4375,8 +4400,10 @@ sub ProcessXMP($$;$)
|
|
|
4375
4400
|
|
|
4376
4401
|
# restore structures if necessary
|
|
4377
4402
|
if ($$et{IsStruct}) {
|
|
4378
|
-
|
|
4379
|
-
|
|
4403
|
+
unless ($$dirInfo{NoStruct}) {
|
|
4404
|
+
require 'Image/ExifTool/XMPStruct.pl';
|
|
4405
|
+
RestoreStruct($et, $keepFlat);
|
|
4406
|
+
}
|
|
4380
4407
|
delete $$et{IsStruct};
|
|
4381
4408
|
}
|
|
4382
4409
|
# 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)
|