exiftool_vendored 12.42.0 → 12.50.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 +189 -6
- data/bin/MANIFEST +12 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +45 -44
- data/bin/config_files/acdsee.config +2 -1
- data/bin/config_files/frameCount.config +56 -0
- data/bin/config_files/tiff_version.config +1 -1
- data/bin/exiftool +113 -95
- data/bin/fmt_files/gpx.fmt +3 -0
- data/bin/fmt_files/gpx_wpt.fmt +3 -0
- data/bin/lib/Image/ExifTool/Apple.pm +16 -3
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +23 -12
- data/bin/lib/Image/ExifTool/Canon.pm +66 -37
- data/bin/lib/Image/ExifTool/CanonRaw.pm +8 -1
- data/bin/lib/Image/ExifTool/CanonVRD.pm +7 -8
- data/bin/lib/Image/ExifTool/DJI.pm +2 -1
- data/bin/lib/Image/ExifTool/DarwinCore.pm +13 -1
- data/bin/lib/Image/ExifTool/EXE.pm +9 -1
- data/bin/lib/Image/ExifTool/Exif.pm +17 -12
- data/bin/lib/Image/ExifTool/FLAC.pm +17 -3
- data/bin/lib/Image/ExifTool/FLIR.pm +4 -3
- data/bin/lib/Image/ExifTool/FlashPix.pm +26 -3
- data/bin/lib/Image/ExifTool/FujiFilm.pm +51 -4
- data/bin/lib/Image/ExifTool/GPS.pm +21 -1
- data/bin/lib/Image/ExifTool/Geotag.pm +25 -5
- data/bin/lib/Image/ExifTool/ICC_Profile.pm +3 -2
- data/bin/lib/Image/ExifTool/ICO.pm +143 -0
- data/bin/lib/Image/ExifTool/ID3.pm +6 -6
- data/bin/lib/Image/ExifTool/IPTC.pm +5 -1
- data/bin/lib/Image/ExifTool/LNK.pm +5 -2
- data/bin/lib/Image/ExifTool/M2TS.pm +98 -8
- data/bin/lib/Image/ExifTool/MIE.pm +9 -3
- data/bin/lib/Image/ExifTool/MISB.pm +494 -0
- data/bin/lib/Image/ExifTool/MakerNotes.pm +3 -1
- data/bin/lib/Image/ExifTool/Matroska.pm +24 -16
- data/bin/lib/Image/ExifTool/Motorola.pm +8 -2
- data/bin/lib/Image/ExifTool/Nikon.pm +288 -122
- data/bin/lib/Image/ExifTool/NikonSettings.pm +5 -3
- data/bin/lib/Image/ExifTool/Olympus.pm +3 -2
- data/bin/lib/Image/ExifTool/Panasonic.pm +21 -4
- data/bin/lib/Image/ExifTool/PanasonicRaw.pm +25 -5
- data/bin/lib/Image/ExifTool/Parrot.pm +96 -2
- data/bin/lib/Image/ExifTool/Pentax.pm +7 -2
- data/bin/lib/Image/ExifTool/Photoshop.pm +29 -3
- data/bin/lib/Image/ExifTool/QuickTime.pm +163 -13
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +119 -13
- data/bin/lib/Image/ExifTool/README +13 -3
- data/bin/lib/Image/ExifTool/RIFF.pm +106 -9
- data/bin/lib/Image/ExifTool/Samsung.pm +2 -2
- data/bin/lib/Image/ExifTool/Sigma.pm +27 -1
- data/bin/lib/Image/ExifTool/SigmaRaw.pm +37 -13
- data/bin/lib/Image/ExifTool/Sony.pm +71 -43
- data/bin/lib/Image/ExifTool/TagInfoXML.pm +3 -1
- data/bin/lib/Image/ExifTool/TagLookup.pm +4737 -4517
- data/bin/lib/Image/ExifTool/TagNames.pod +1837 -1417
- data/bin/lib/Image/ExifTool/Text.pm +3 -4
- data/bin/lib/Image/ExifTool/Torrent.pm +2 -3
- data/bin/lib/Image/ExifTool/Validate.pm +3 -3
- data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +7 -0
- data/bin/lib/Image/ExifTool/WriteExif.pl +100 -23
- data/bin/lib/Image/ExifTool/WriteIPTC.pl +2 -6
- data/bin/lib/Image/ExifTool/WritePhotoshop.pl +5 -5
- data/bin/lib/Image/ExifTool/WriteRIFF.pl +359 -0
- data/bin/lib/Image/ExifTool/Writer.pl +13 -5
- data/bin/lib/Image/ExifTool/XMP.pm +78 -59
- data/bin/lib/Image/ExifTool/XMP2.pl +19 -4
- data/bin/lib/Image/ExifTool.pm +111 -24
- data/bin/lib/Image/ExifTool.pod +83 -69
- data/bin/perl-Image-ExifTool.spec +43 -43
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +9 -4
@@ -0,0 +1,359 @@
|
|
1
|
+
#------------------------------------------------------------------------------
|
2
|
+
# File: WriteRIFF.pl
|
3
|
+
#
|
4
|
+
# Description: Write RIFF-format files
|
5
|
+
#
|
6
|
+
# Revisions: 2020-09-26 - P. Harvey Created
|
7
|
+
#
|
8
|
+
# Notes: Currently writes only WEBP files
|
9
|
+
#
|
10
|
+
# References: https://developers.google.com/speed/webp/docs/riff_container
|
11
|
+
#------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
package Image::ExifTool::RIFF;
|
14
|
+
|
15
|
+
use strict;
|
16
|
+
|
17
|
+
# map of where information is stored in WebP image
|
18
|
+
my %webpMap = (
|
19
|
+
'XMP ' => 'RIFF', # (the RIFF chunk name is 'XMP ')
|
20
|
+
EXIF => 'RIFF',
|
21
|
+
ICCP => 'RIFF',
|
22
|
+
XMP => 'XMP ',
|
23
|
+
IFD0 => 'EXIF',
|
24
|
+
IFD1 => 'IFD0',
|
25
|
+
ICC_Profile => 'ICCP',
|
26
|
+
ExifIFD => 'IFD0',
|
27
|
+
GPS => 'IFD0',
|
28
|
+
SubIFD => 'IFD0',
|
29
|
+
GlobParamIFD => 'IFD0',
|
30
|
+
PrintIM => 'IFD0',
|
31
|
+
InteropIFD => 'ExifIFD',
|
32
|
+
MakerNotes => 'ExifIFD',
|
33
|
+
);
|
34
|
+
|
35
|
+
#------------------------------------------------------------------------------
|
36
|
+
# Write RIFF file (currently WebP-type only)
|
37
|
+
# Inputs: 0) ExifTool object ref, 1) dirInfo ref
|
38
|
+
# Returns: 1 on success, 0 if this wasn't a valid RIFF file, or -1 if
|
39
|
+
# an output file was specified and a write error occurred
|
40
|
+
sub WriteRIFF($$)
|
41
|
+
{
|
42
|
+
my ($et, $dirInfo) = @_;
|
43
|
+
$et or return 1; # allow dummy access to autoload this package
|
44
|
+
my $outfile = $$dirInfo{OutFile};
|
45
|
+
my $outsize = 0;
|
46
|
+
my $raf = $$dirInfo{RAF};
|
47
|
+
my ($buff, $err, $pass, %has, %dirDat, $imageWidth, $imageHeight);
|
48
|
+
|
49
|
+
# do this in 2 passes so we can set the size of the containing RIFF chunk
|
50
|
+
# without having to buffer the output (also to set the WebP_Flags)
|
51
|
+
for ($pass=0; ; ++$pass) {
|
52
|
+
my %doneDir;
|
53
|
+
# verify this is a valid RIFF file
|
54
|
+
return 0 unless $raf->Read($buff, 12) == 12;
|
55
|
+
return 0 unless $buff =~ /^(RIFF|RF64)....(.{4})/s;
|
56
|
+
|
57
|
+
unless ($1 eq 'RIFF' and $2 eq 'WEBP') {
|
58
|
+
my $type = $2;
|
59
|
+
$type =~ tr/-_a-zA-Z//dc;
|
60
|
+
$et->Error("Can't currently write $1 $type files");
|
61
|
+
return 1;
|
62
|
+
}
|
63
|
+
SetByteOrder('II');
|
64
|
+
|
65
|
+
# determine which directories we must write for this file type
|
66
|
+
$et->InitWriteDirs(\%webpMap);
|
67
|
+
my $addDirs = $$et{ADD_DIRS};
|
68
|
+
my $editDirs = $$et{EDIT_DIRS};
|
69
|
+
my ($createVP8X, $deleteVP8X);
|
70
|
+
|
71
|
+
# write header
|
72
|
+
if ($pass) {
|
73
|
+
my $needsVP8X = ($has{ANIM} or $has{'XMP '} or $has{EXIF} or
|
74
|
+
$has{ALPH} or $has{ICCP});
|
75
|
+
if ($has{VP8X} and not $needsVP8X and $$et{CHANGED}) {
|
76
|
+
$deleteVP8X = 1; # delete the VP8X chunk
|
77
|
+
$outsize -= 18; # account for missing VP8X
|
78
|
+
} elsif ($needsVP8X and not $has{VP8X}) {
|
79
|
+
if (defined $imageWidth) {
|
80
|
+
++$$et{CHANGED};
|
81
|
+
$createVP8X = 1; # add VP8X chunk
|
82
|
+
$outsize += 18; # account for VP8X size
|
83
|
+
} else {
|
84
|
+
$et->Warn('Error getting image size for required VP8X chunk');
|
85
|
+
}
|
86
|
+
}
|
87
|
+
# finally we can set the overall RIFF chunk size:
|
88
|
+
Set32u($outsize - 8, \$buff, 4);
|
89
|
+
Write($outfile, $buff) or $err = 1;
|
90
|
+
# create VP8X chunk if necessary
|
91
|
+
if ($createVP8X) {
|
92
|
+
$et->VPrint(0," Adding required VP8X chunk (Extended WEBP)\n");
|
93
|
+
my $flags = 0;
|
94
|
+
$flags |= 0x02 if $has{ANIM};
|
95
|
+
$flags |= 0x04 if $has{'XMP '};
|
96
|
+
$flags |= 0x08 if $has{EXIF};
|
97
|
+
$flags |= 0x10 if $has{ALPH};
|
98
|
+
$flags |= 0x20 if $has{ICCP};
|
99
|
+
Write($outfile, 'VP8X', pack('V3v', 10, $flags,
|
100
|
+
($imageWidth-1) | ((($imageHeight-1) & 0xff) << 24),
|
101
|
+
($imageHeight-1) >> 8));
|
102
|
+
# write ICCP after VP8X
|
103
|
+
Write($outfile, $dirDat{ICCP}) or $err = 1 if $dirDat{ICCP};
|
104
|
+
}
|
105
|
+
} else {
|
106
|
+
$outsize += length $buff;
|
107
|
+
}
|
108
|
+
my $pos = 12;
|
109
|
+
#
|
110
|
+
# Read chunks in RIFF image
|
111
|
+
#
|
112
|
+
for (;;) {
|
113
|
+
my ($tag, $len);
|
114
|
+
my $num = $raf->Read($buff, 8);
|
115
|
+
if ($num < 8) {
|
116
|
+
$num and $et->Error('RIFF format error'), return 1;
|
117
|
+
# all done if we hit end of file unless we need to add EXIF or XMP
|
118
|
+
last unless $$addDirs{EXIF} or $$addDirs{'XMP '} or $$addDirs{ICCP};
|
119
|
+
# continue to add required EXIF or XMP chunks
|
120
|
+
$num = $len = 0;
|
121
|
+
$buff = $tag = '';
|
122
|
+
} else {
|
123
|
+
$pos += 8;
|
124
|
+
($tag, $len) = unpack('a4V', $buff);
|
125
|
+
if ($len <= 0) {
|
126
|
+
if ($len < 0) {
|
127
|
+
$et->Error('Invalid chunk length');
|
128
|
+
return 1;
|
129
|
+
} elsif ($tag eq "\0\0\0\0") {
|
130
|
+
# avoid reading through corrupted files filled with nulls because it takes forever
|
131
|
+
$et->Error('Encountered empty null chunk. Processing aborted');
|
132
|
+
return 1;
|
133
|
+
} else { # (just in case a tag may have no data)
|
134
|
+
if ($pass) {
|
135
|
+
Write($outfile, $buff) or $err = 1;
|
136
|
+
} else {
|
137
|
+
$outsize += length $buff;
|
138
|
+
}
|
139
|
+
next;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
}
|
143
|
+
# RIFF chunks are padded to an even number of bytes
|
144
|
+
my $len2 = $len + ($len & 0x01);
|
145
|
+
# edit/add/delete necessary metadata chunks (EXIF must come before XMP)
|
146
|
+
if ($$editDirs{$tag} or $tag eq '' or ($tag eq 'XMP ' and $$addDirs{EXIF})) {
|
147
|
+
my $handledTag;
|
148
|
+
if ($len2) {
|
149
|
+
$et->Warn("Duplicate '${tag}' chunk") if $doneDir{$tag} and not $pass;
|
150
|
+
$doneDir{$tag} = 1;
|
151
|
+
$raf->Read($buff, $len2) == $len2 or $et->Error("Truncated '${tag}' chunk"), last;
|
152
|
+
$pos += $len2; # update current position
|
153
|
+
} else {
|
154
|
+
$buff = '';
|
155
|
+
}
|
156
|
+
#
|
157
|
+
# add/edit/delete EXIF/XMP/ICCP (note: EXIF must come before XMP, and ICCP is written elsewhere)
|
158
|
+
#
|
159
|
+
my %dirName = ( EXIF => 'IFD0', 'XMP ' => 'XMP', ICCP => 'ICC_Profile' );
|
160
|
+
my %tblName = ( EXIF => 'Exif', 'XMP ' => 'XMP', ICCP => 'ICC_Profile' );
|
161
|
+
my $dir;
|
162
|
+
foreach $dir ('EXIF', 'XMP ', 'ICCP' ) {
|
163
|
+
next unless $tag eq $dir or ($$addDirs{$dir} and
|
164
|
+
($tag eq '' or ($tag eq 'XMP ' and $dir eq 'EXIF')));
|
165
|
+
delete $$addDirs{$dir}; # (don't try to add again)
|
166
|
+
my $start;
|
167
|
+
unless ($pass) {
|
168
|
+
# write the EXIF and save the result for the next pass
|
169
|
+
my $dataPt = \$buff;
|
170
|
+
if ($tag eq 'EXIF') {
|
171
|
+
# (only need to set directory $start for EXIF)
|
172
|
+
if ($buff =~ /^Exif\0\0/) {
|
173
|
+
$et->Warn('Improper EXIF header') unless $pass;
|
174
|
+
$start = 6;
|
175
|
+
} else {
|
176
|
+
$start = 0;
|
177
|
+
}
|
178
|
+
} elsif ($dir ne $tag) {
|
179
|
+
# create from scratch
|
180
|
+
my $buf2 = '';
|
181
|
+
$dataPt = \$buf2;
|
182
|
+
}
|
183
|
+
# write the new directory to memory
|
184
|
+
my %dirInfo = (
|
185
|
+
DataPt => $dataPt,
|
186
|
+
DataPos => 0, # (relative to Base)
|
187
|
+
DirStart => $start,
|
188
|
+
Base => $pos - $len2,
|
189
|
+
Parent => $dir,
|
190
|
+
DirName => $dirName{$dir},
|
191
|
+
);
|
192
|
+
my $tagTablePtr = GetTagTable("Image::ExifTool::$tblName{$dir}::Main");
|
193
|
+
# (override writeProc for EXIF because it has the TIFF header)
|
194
|
+
my $writeProc = $dir eq 'EXIF' ? \&Image::ExifTool::WriteTIFF : undef;
|
195
|
+
$dirDat{$dir} = $et->WriteDirectory(\%dirInfo, $tagTablePtr, $writeProc);
|
196
|
+
}
|
197
|
+
if (defined $dirDat{$dir}) {
|
198
|
+
if ($dir eq $tag) {
|
199
|
+
$handledTag = 1; # set flag indicating we edited this tag
|
200
|
+
# increment CHANGED count if we are deleting the directory
|
201
|
+
++$$et{CHANGED} unless length $dirDat{$dir};
|
202
|
+
}
|
203
|
+
if (length $dirDat{$dir}) {
|
204
|
+
if ($pass) {
|
205
|
+
# write metadata chunk now (but not ICCP because it was added earlier)
|
206
|
+
Write($outfile, $dirDat{$dir}) or $err = 1 unless $dir eq 'ICCP';
|
207
|
+
} else {
|
208
|
+
# preserve (incorrect EXIF) header if it existed
|
209
|
+
my $hdr = $start ? substr($buff,0,$start) : '';
|
210
|
+
# (don't overwrite $len here because it may be XMP length)
|
211
|
+
my $dirLen = length($dirDat{$dir}) + length($hdr);
|
212
|
+
# add chunk header and padding
|
213
|
+
my $pad = $dirLen & 0x01 ? "\0" : '';
|
214
|
+
$dirDat{$dir} = $dir . Set32u($dirLen) . $hdr . $dirDat{$dir} . $pad;
|
215
|
+
$outsize += length($dirDat{$dir});
|
216
|
+
$has{$dir} = 1;
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
}
|
221
|
+
#
|
222
|
+
# just copy XMP, EXIF or ICC if nothing changed
|
223
|
+
#
|
224
|
+
if (not $handledTag and length $buff) {
|
225
|
+
# write the chunk without changes
|
226
|
+
if ($pass) {
|
227
|
+
Write($outfile, $tag, Set32u($len), $buff) or $err = 1;
|
228
|
+
} else {
|
229
|
+
$outsize += 8 + length($buff);
|
230
|
+
$has{$tag} = 1;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
next;
|
234
|
+
}
|
235
|
+
$pos += $len2; # set read position at end of chunk data
|
236
|
+
#
|
237
|
+
# update necessary flags in VP8X chunk
|
238
|
+
#
|
239
|
+
if ($tag eq 'VP8X') {
|
240
|
+
my $buf2;
|
241
|
+
if ($len2 < 10 or $raf->Read($buf2, $len2) != $len2) {
|
242
|
+
$et->Error('Truncated VP8X chunk');
|
243
|
+
return 1;
|
244
|
+
}
|
245
|
+
if ($pass) {
|
246
|
+
if ($deleteVP8X) {
|
247
|
+
$et->VPrint(0," Deleting unnecessary VP8X chunk (Standard WEBP)\n");
|
248
|
+
next;
|
249
|
+
}
|
250
|
+
# ...but first set the VP8X flags
|
251
|
+
my $flags = Get32u(\$buf2, 0);
|
252
|
+
$flags &= ~0x2c; # (reset flags for everything we can write)
|
253
|
+
$flags |= 0x04 if $has{'XMP '};
|
254
|
+
$flags |= 0x08 if $has{EXIF};
|
255
|
+
$flags |= 0x20 if $has{ICCP};
|
256
|
+
Set32u($flags, \$buf2, 0);
|
257
|
+
Write($outfile, $buff, $buf2) or $err = 1;
|
258
|
+
} else {
|
259
|
+
# get the image size
|
260
|
+
$imageWidth = (Get32u(\$buf2, 4) & 0xffffff) + 1;
|
261
|
+
$imageHeight = (Get32u(\$buf2, 6) >> 8) + 1;
|
262
|
+
$outsize += 8 + $len2;
|
263
|
+
$has{$tag} = 1;
|
264
|
+
}
|
265
|
+
# write ICCP after VP8X
|
266
|
+
Write($outfile, $dirDat{ICCP}) or $err = 1 if $dirDat{ICCP};
|
267
|
+
next;
|
268
|
+
}
|
269
|
+
#
|
270
|
+
# just copy all other chunks
|
271
|
+
#
|
272
|
+
if ($pass) {
|
273
|
+
# write chunk header (still in $buff)
|
274
|
+
Write($outfile, $buff) or $err = 1;
|
275
|
+
} else {
|
276
|
+
$outsize += length $buff;
|
277
|
+
$has{$tag} = 1;
|
278
|
+
}
|
279
|
+
unless ($pass or defined $imageWidth) {
|
280
|
+
# get WebP image size from VP8 or VP8L header
|
281
|
+
if ($tag eq 'VP8 ' and $len2 >= 16) {
|
282
|
+
$raf->Read($buff, 16) == 16 or $et->Error('Truncated VP8 chunk'), return 1;
|
283
|
+
$outsize += 16;
|
284
|
+
if ($buff =~ /^...\x9d\x01\x2a/s) {
|
285
|
+
$imageWidth = Get16u(\$buff, 6) & 0x3fff;
|
286
|
+
$imageHeight = Get16u(\$buff, 8) & 0x3fff;
|
287
|
+
}
|
288
|
+
$len2 -= 16;
|
289
|
+
} elsif ($tag eq 'VP8L' and $len2 >= 6) {
|
290
|
+
$raf->Read($buff, 6) == 6 or $et->Error('Truncated VP8L chunk'), return 1;
|
291
|
+
$outsize += 6;
|
292
|
+
if ($buff =~ /^\x2f/s) {
|
293
|
+
$imageWidth = (Get16u(\$buff, 1) & 0x3fff) + 1;
|
294
|
+
$imageHeight = ((Get32u(\$buff, 2) >> 6) & 0x3fff) + 1;
|
295
|
+
}
|
296
|
+
$len2 -= 6;
|
297
|
+
}
|
298
|
+
}
|
299
|
+
if ($pass) {
|
300
|
+
# copy the chunk data in 64k blocks
|
301
|
+
while ($len2) {
|
302
|
+
my $num = $len2;
|
303
|
+
$num = 65536 if $num > 65536;
|
304
|
+
$raf->Read($buff, $num) == $num or $et->Error('Truncated RIFF chunk'), last;
|
305
|
+
Write($outfile, $buff) or $err = 1, last;
|
306
|
+
$len2 -= $num;
|
307
|
+
}
|
308
|
+
} else {
|
309
|
+
$raf->Seek($len2, 1) or $et->Error('Seek error'), last;
|
310
|
+
$outsize += $len2;
|
311
|
+
}
|
312
|
+
}
|
313
|
+
last if $pass;
|
314
|
+
$raf->Seek(0,0) or $et->Error('Seek error'), last;
|
315
|
+
}
|
316
|
+
return $err ? -1 : 1;
|
317
|
+
}
|
318
|
+
|
319
|
+
1; # end
|
320
|
+
|
321
|
+
__END__
|
322
|
+
|
323
|
+
=head1 NAME
|
324
|
+
|
325
|
+
Image::ExifTool::WriteRIFF.pl - Write RIFF-format files
|
326
|
+
|
327
|
+
=head1 SYNOPSIS
|
328
|
+
|
329
|
+
This file is autoloaded by Image::ExifTool::RIFF.
|
330
|
+
|
331
|
+
=head1 DESCRIPTION
|
332
|
+
|
333
|
+
This file contains routines to write metadata to RIFF-format files.
|
334
|
+
|
335
|
+
=head1 NOTES
|
336
|
+
|
337
|
+
Currently writes only WebP files.
|
338
|
+
|
339
|
+
=head1 AUTHOR
|
340
|
+
|
341
|
+
Copyright 2003-2022, Phil Harvey (philharvey66 at gmail.com)
|
342
|
+
|
343
|
+
This library is free software; you can redistribute it and/or modify it
|
344
|
+
under the same terms as Perl itself.
|
345
|
+
|
346
|
+
=head1 REFERENCES
|
347
|
+
|
348
|
+
=over 4
|
349
|
+
|
350
|
+
=item L<https://developers.google.com/speed/webp/docs/riff_container>
|
351
|
+
|
352
|
+
=back
|
353
|
+
|
354
|
+
=head1 SEE ALSO
|
355
|
+
|
356
|
+
L<Image::ExifTool::Photoshop(3pm)|Image::ExifTool::RIFF>,
|
357
|
+
L<Image::ExifTool(3pm)|Image::ExifTool>
|
358
|
+
|
359
|
+
=cut
|
@@ -115,7 +115,9 @@ my %writableType = (
|
|
115
115
|
PS => [ 'PostScript', 'WritePS' ],
|
116
116
|
PSD => 'Photoshop',
|
117
117
|
RAF => [ 'FujiFilm', 'WriteRAF' ],
|
118
|
+
RIFF=> [ 'RIFF', 'WriteRIFF'],
|
118
119
|
VRD => 'CanonVRD',
|
120
|
+
WEBP=> [ 'RIFF', 'WriteRIFF'],
|
119
121
|
X3F => 'SigmaRaw',
|
120
122
|
XMP => [ undef, 'WriteXMP' ],
|
121
123
|
);
|
@@ -240,7 +242,7 @@ my %intRange = (
|
|
240
242
|
'int64s' => [-9223372036854775808, 9223372036854775807],
|
241
243
|
);
|
242
244
|
# lookup for file types with block-writable EXIF
|
243
|
-
my %blockExifTypes = map { $_ => 1 } qw(JPEG PNG JP2 MIE EXIF FLIF MOV MP4);
|
245
|
+
my %blockExifTypes = map { $_ => 1 } qw(JPEG PNG JP2 MIE EXIF FLIF MOV MP4 RIFF);
|
244
246
|
|
245
247
|
my $maxSegmentLen = 0xfffd; # maximum length of data in a JPEG segment
|
246
248
|
my $maxXMPLen = $maxSegmentLen; # maximum length of XMP data in JPEG
|
@@ -975,7 +977,7 @@ TAG: foreach $tagInfo (@matchingTags) {
|
|
975
977
|
} else {
|
976
978
|
$wgrp = '';
|
977
979
|
}
|
978
|
-
foreach $wtag (keys %{$$tagInfo{WriteAlso}}) {
|
980
|
+
foreach $wtag (sort keys %{$$tagInfo{WriteAlso}}) {
|
979
981
|
my ($n,$e) = $self->SetNewValue($wgrp . $wtag, undef, Replace=>2);
|
980
982
|
$numSet += $n;
|
981
983
|
}
|
@@ -1123,7 +1125,7 @@ WriteAlso:
|
|
1123
1125
|
$wgrp = '';
|
1124
1126
|
}
|
1125
1127
|
local $SIG{'__WARN__'} = \&SetWarning;
|
1126
|
-
foreach $wtag (keys %$writeAlso) {
|
1128
|
+
foreach $wtag (sort keys %$writeAlso) {
|
1127
1129
|
my %opts = (
|
1128
1130
|
Type => 'ValueConv',
|
1129
1131
|
Protected => $protected | 0x02,
|
@@ -1270,6 +1272,7 @@ sub SetNewValuesFromFile($$;@)
|
|
1270
1272
|
GlobalTimeShift => $$options{GlobalTimeShift},
|
1271
1273
|
HexTagIDs => $$options{HexTagIDs},
|
1272
1274
|
IgnoreMinorErrors=>$$options{IgnoreMinorErrors},
|
1275
|
+
IgnoreTags => $$options{IgnoreTags},
|
1273
1276
|
Lang => $$options{Lang},
|
1274
1277
|
LargeFileSupport=> $$options{LargeFileSupport},
|
1275
1278
|
List => 1,
|
@@ -4241,7 +4244,12 @@ sub WriteDirectory($$$;$)
|
|
4241
4244
|
$$self{DIR_NAME} = $oldDir;
|
4242
4245
|
@$self{'Compression','SubfileType'} = @save;
|
4243
4246
|
SetByteOrder($saveOrder);
|
4244
|
-
|
4247
|
+
if ($out) {
|
4248
|
+
print $out " Deleting $name\n" if defined $newData and not length $newData;
|
4249
|
+
if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) {
|
4250
|
+
print $out "$$self{INDENT} [nothing changed in $dirName]\n";
|
4251
|
+
}
|
4252
|
+
}
|
4245
4253
|
return $newData;
|
4246
4254
|
}
|
4247
4255
|
|
@@ -4530,7 +4538,7 @@ sub DumpUnknownTrailer($$)
|
|
4530
4538
|
# add to Preview block list if valid and in the trailer
|
4531
4539
|
$image{$prePos} = [$tag, $preLen] if $prePos and $preLen and $prePos+$preLen > $pos;
|
4532
4540
|
last if $lastOne; # checked all images
|
4533
|
-
# look for MPF images (in the
|
4541
|
+
# look for MPF images (in the proper order)
|
4534
4542
|
++$mpImageNum;
|
4535
4543
|
$prePos = $$self{VALUE}{"MPImageStart ($mpImageNum)"};
|
4536
4544
|
if (defined $prePos) {
|
@@ -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.55';
|
54
54
|
@ISA = qw(Exporter);
|
55
55
|
@EXPORT_OK = qw(EscapeXML UnescapeXML);
|
56
56
|
|
@@ -128,6 +128,8 @@ my %xmpNS = (
|
|
128
128
|
stRef => 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#',
|
129
129
|
stVer => 'http://ns.adobe.com/xap/1.0/sType/Version#',
|
130
130
|
stMfs => 'http://ns.adobe.com/xap/1.0/sType/ManifestItem#',
|
131
|
+
stCamera => 'http://ns.adobe.com/photoshop/1.0/camera-profile',
|
132
|
+
crlcp => 'http://ns.adobe.com/camera-raw-embedded-lens-profile/1.0/',
|
131
133
|
tiff => 'http://ns.adobe.com/tiff/1.0/',
|
132
134
|
'x' => 'adobe:ns:meta/',
|
133
135
|
xmpG => 'http://ns.adobe.com/xap/1.0/g/',
|
@@ -1273,6 +1275,41 @@ my %sPantryItem = (
|
|
1273
1275
|
},
|
1274
1276
|
},
|
1275
1277
|
EmbeddedXMPDigest => { }, #PH (LR5)
|
1278
|
+
CameraProfiles => { #PH (2022-10-11)
|
1279
|
+
List => 'Seq',
|
1280
|
+
Struct => {
|
1281
|
+
NAMESPACE => 'stCamera',
|
1282
|
+
STRUCT_NAME => 'Camera',
|
1283
|
+
Author => { },
|
1284
|
+
Make => { },
|
1285
|
+
Model => { },
|
1286
|
+
UniqueCameraModel => { },
|
1287
|
+
CameraRawProfile => { Writable => 'boolean' },
|
1288
|
+
AutoScale => { Writable => 'boolean' },
|
1289
|
+
Lens => { },
|
1290
|
+
CameraPrettyName => { },
|
1291
|
+
LensPrettyName => { },
|
1292
|
+
ProfileName => { },
|
1293
|
+
SensorFormatFactor => { Writable => 'real' },
|
1294
|
+
FocalLength => { Writable => 'real' },
|
1295
|
+
FocusDistance => { Writable => 'real' },
|
1296
|
+
ApertureValue => { Writable => 'real' },
|
1297
|
+
PerspectiveModel => {
|
1298
|
+
Namespace => 'crlcp',
|
1299
|
+
Struct => {
|
1300
|
+
NAMESPACE => 'stCamera',
|
1301
|
+
STRUCT_NAME => 'PerspectiveModel',
|
1302
|
+
Version => { },
|
1303
|
+
ImageXCenter => { Writable => 'real' },
|
1304
|
+
ImageYCenter => { Writable => 'real' },
|
1305
|
+
ScaleFactor => { Writable => 'real' },
|
1306
|
+
RadialDistortParam1 => { Writable => 'real' },
|
1307
|
+
RadialDistortParam2 => { Writable => 'real' },
|
1308
|
+
RadialDistortParam3 => { Writable => 'real' },
|
1309
|
+
},
|
1310
|
+
},
|
1311
|
+
},
|
1312
|
+
},
|
1276
1313
|
);
|
1277
1314
|
|
1278
1315
|
# Photoshop Camera Raw namespace properties (crs) - (ref 8,PH)
|
@@ -1431,7 +1468,14 @@ my %sPantryItem = (
|
|
1431
1468
|
PostCropVignetteMidpoint => { Writable => 'integer' },
|
1432
1469
|
PostCropVignetteFeather => { Writable => 'integer' },
|
1433
1470
|
PostCropVignetteRoundness => { Writable => 'integer' },
|
1434
|
-
PostCropVignetteStyle => {
|
1471
|
+
PostCropVignetteStyle => {
|
1472
|
+
Writable => 'integer',
|
1473
|
+
PrintConv => { #forum14011
|
1474
|
+
1 => 'Highlight Priority',
|
1475
|
+
2 => 'Color Priority',
|
1476
|
+
3 => 'Paint Overlay',
|
1477
|
+
},
|
1478
|
+
},
|
1435
1479
|
# disable List behaviour of flattened Gradient/PaintBasedCorrections
|
1436
1480
|
# because these are nested in lists and the flattened tags can't
|
1437
1481
|
# do justice to this complex structure
|
@@ -1528,7 +1572,17 @@ my %sPantryItem = (
|
|
1528
1572
|
},
|
1529
1573
|
ColorNoiseReductionSmoothness => { Writable => 'integer' },
|
1530
1574
|
PerspectiveAspect => { Writable => 'integer' },
|
1531
|
-
PerspectiveUpright => {
|
1575
|
+
PerspectiveUpright => {
|
1576
|
+
Writable => 'integer',
|
1577
|
+
PrintConv => { #forum14012
|
1578
|
+
0 => 'Off', # Disable Upright
|
1579
|
+
1 => 'Auto', # Apply balanced perspective corrections
|
1580
|
+
2 => 'Full', # Apply level, horizontal, and vertical perspective corrections
|
1581
|
+
3 => 'Level', # Apply only level correction
|
1582
|
+
4 => 'Vertical',# Apply level and vertical perspective corrections
|
1583
|
+
5 => 'Guided', # Draw two or more guides to customize perspective corrections
|
1584
|
+
},
|
1585
|
+
},
|
1532
1586
|
RetouchAreas => {
|
1533
1587
|
FlatName => 'RetouchArea',
|
1534
1588
|
Struct => \%sRetouchArea,
|
@@ -1552,12 +1606,17 @@ my %sPantryItem = (
|
|
1552
1606
|
UprightPreview => { Writable => 'boolean' },
|
1553
1607
|
UprightTransformCount => { Writable => 'integer' },
|
1554
1608
|
UprightDependentDigest => { },
|
1609
|
+
UprightGuidedDependentDigest => { },
|
1555
1610
|
UprightTransform_0 => { },
|
1556
1611
|
UprightTransform_1 => { },
|
1557
1612
|
UprightTransform_2 => { },
|
1558
1613
|
UprightTransform_3 => { },
|
1559
1614
|
UprightTransform_4 => { },
|
1560
1615
|
UprightTransform_5 => { },
|
1616
|
+
UprightFourSegments_0 => { },
|
1617
|
+
UprightFourSegments_1 => { },
|
1618
|
+
UprightFourSegments_2 => { },
|
1619
|
+
UprightFourSegments_3 => { },
|
1561
1620
|
# more stuff seen in lens profile file (unknown source)
|
1562
1621
|
What => { }, # (with value "LensProfileDefaultSettings")
|
1563
1622
|
LensProfileMatchKeyExifMake => { },
|
@@ -2396,6 +2455,12 @@ my %sPantryItem = (
|
|
2396
2455
|
LateralChromaticAberrationCorrectionAlreadyApplied => { Writable => 'boolean' },
|
2397
2456
|
LensDistortInfo => { }, # (LR 7.5.1, 4 signed rational values)
|
2398
2457
|
NeutralDensityFactor => { }, # (LR 11.0 - rational value, but denominator seems significant)
|
2458
|
+
# the following are ref forum13747
|
2459
|
+
EnhanceDetailsAlreadyApplied => { Writable => 'boolean' },
|
2460
|
+
EnhanceDetailsVersion => { }, # integer?
|
2461
|
+
EnhanceSuperResolutionAlreadyApplied => { Writable => 'boolean' },
|
2462
|
+
EnhanceSuperResolutionVersion => { }, # integer?
|
2463
|
+
EnhanceSuperResolutionScale => { Writable => 'rational' },
|
2399
2464
|
);
|
2400
2465
|
|
2401
2466
|
# IPTC Core namespace properties (Iptc4xmpCore) (ref 4)
|
@@ -2620,7 +2685,7 @@ sub FullEscapeXML($)
|
|
2620
2685
|
$str =~ s/([&><'"])/&$charName{$1};/sg; # escape necessary XML characters
|
2621
2686
|
$str =~ s/\\/\/sg; # escape backslashes too
|
2622
2687
|
# then use C-escape sequences for invalid characters
|
2623
|
-
if ($str =~ /[\0-\x1f]/ or IsUTF8(\$str) < 0) {
|
2688
|
+
if ($str =~ /[\0-\x1f]/ or Image::ExifTool::IsUTF8(\$str) < 0) {
|
2624
2689
|
$str =~ s/([\0-\x1f\x80-\xff])/sprintf("\\x%.2x",ord $1)/sge;
|
2625
2690
|
}
|
2626
2691
|
return $str;
|
@@ -2665,57 +2730,6 @@ sub UnescapeChar($$;$)
|
|
2665
2730
|
return $val;
|
2666
2731
|
}
|
2667
2732
|
|
2668
|
-
#------------------------------------------------------------------------------
|
2669
|
-
# Does a string contain valid UTF-8 characters?
|
2670
|
-
# Inputs: 0) string reference, 1) true to allow last character to be truncated
|
2671
|
-
# Returns: 0=regular ASCII, -1=invalid UTF-8, 1=valid UTF-8 with maximum 16-bit
|
2672
|
-
# wide characters, 2=valid UTF-8 requiring 32-bit wide characters
|
2673
|
-
# Notes: Changes current string position
|
2674
|
-
# (see http://www.fileformat.info/info/unicode/utf8.htm for help understanding this)
|
2675
|
-
sub IsUTF8($;$)
|
2676
|
-
{
|
2677
|
-
my ($strPt, $trunc) = @_;
|
2678
|
-
pos($$strPt) = 0; # start at beginning of string
|
2679
|
-
return 0 unless $$strPt =~ /([\x80-\xff])/g;
|
2680
|
-
my $rtnVal = 1;
|
2681
|
-
for (;;) {
|
2682
|
-
my $ch = ord($1);
|
2683
|
-
# minimum lead byte for 2-byte sequence is 0xc2 (overlong sequences
|
2684
|
-
# not allowed), 0xf8-0xfd are restricted by RFC 3629 (no 5 or 6 byte
|
2685
|
-
# sequences), and 0xfe and 0xff are not valid in UTF-8 strings
|
2686
|
-
return -1 if $ch < 0xc2 or $ch >= 0xf8;
|
2687
|
-
# determine number of bytes remaining in sequence
|
2688
|
-
my $n;
|
2689
|
-
if ($ch < 0xe0) {
|
2690
|
-
$n = 1;
|
2691
|
-
} elsif ($ch < 0xf0) {
|
2692
|
-
$n = 2;
|
2693
|
-
} else {
|
2694
|
-
$n = 3;
|
2695
|
-
# character code is greater than 0xffff if more than 2 extra bytes
|
2696
|
-
# were required in the UTF-8 character
|
2697
|
-
$rtnVal = 2;
|
2698
|
-
}
|
2699
|
-
my $pos = pos $$strPt;
|
2700
|
-
unless ($$strPt =~ /\G([\x80-\xbf]{$n})/g) {
|
2701
|
-
return $rtnVal if $trunc and $pos + $n > length $$strPt;
|
2702
|
-
return -1;
|
2703
|
-
}
|
2704
|
-
# the following is ref https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c
|
2705
|
-
if ($n == 2) {
|
2706
|
-
return -1 if ($ch == 0xe0 and (ord($1) & 0xe0) == 0x80) or
|
2707
|
-
($ch == 0xed and (ord($1) & 0xe0) == 0xa0) or
|
2708
|
-
($ch == 0xef and ord($1) == 0xbf and
|
2709
|
-
(ord(substr $1, 1) & 0xfe) == 0xbe);
|
2710
|
-
} else {
|
2711
|
-
return -1 if ($ch == 0xf0 and (ord($1) & 0xf0) == 0x80) or
|
2712
|
-
($ch == 0xf4 and ord($1) > 0x8f) or $ch > 0xf4;
|
2713
|
-
}
|
2714
|
-
last unless $$strPt =~ /([\x80-\xff])/g;
|
2715
|
-
}
|
2716
|
-
return $rtnVal;
|
2717
|
-
}
|
2718
|
-
|
2719
2733
|
#------------------------------------------------------------------------------
|
2720
2734
|
# Fix malformed UTF8 (by replacing bad bytes with specified character)
|
2721
2735
|
# Inputs: 0) string reference, 1) string to replace each bad byte,
|
@@ -2730,7 +2744,7 @@ sub FixUTF8($;$)
|
|
2730
2744
|
last unless $$strPt =~ /([\x80-\xff])/g;
|
2731
2745
|
my $ch = ord($1);
|
2732
2746
|
my $pos = pos($$strPt);
|
2733
|
-
# (see comments in IsUTF8()
|
2747
|
+
# (see comments in Image::ExifTool::IsUTF8())
|
2734
2748
|
if ($ch >= 0xc2 and $ch < 0xf8) {
|
2735
2749
|
my $n = $ch < 0xe0 ? 1 : ($ch < 0xf0 ? 2 : 3);
|
2736
2750
|
if ($$strPt =~ /\G([\x80-\xbf]{$n})/g) {
|
@@ -2982,8 +2996,9 @@ sub AddFlattenedTags($;$$)
|
|
2982
2996
|
} else {
|
2983
2997
|
$$flatInfo{Groups}{2} = $tagG2;
|
2984
2998
|
}
|
2985
|
-
# save reference to top-level
|
2999
|
+
# save reference to top-level and parent structures
|
2986
3000
|
$$flatInfo{RootTagInfo} = $$tagInfo{RootTagInfo} || $tagInfo;
|
3001
|
+
$$flatInfo{ParentTagInfo} = $tagInfo;
|
2987
3002
|
# recursively generate flattened tags for sub-structures
|
2988
3003
|
next unless $$flatInfo{Struct};
|
2989
3004
|
length($flatID) > 250 and warn("Possible deep recursion for tag $flatID\n"), last;
|
@@ -3447,8 +3462,12 @@ NoLoop:
|
|
3447
3462
|
} else {
|
3448
3463
|
$val = ConvertXMPDate($val, $new) if $new or $fmt eq 'date';
|
3449
3464
|
}
|
3450
|
-
if ($$et{XmpValidate} and $fmt and $fmt eq 'boolean') {
|
3451
|
-
$
|
3465
|
+
if ($$et{XmpValidate} and $fmt and $fmt eq 'boolean' and $val!~/^True|False$/) {
|
3466
|
+
if ($val =~ /^true|false$/) {
|
3467
|
+
$et->WarnOnce("Boolean value for XMP-$ns:$$tagInfo{Name} should be capitalized",1);
|
3468
|
+
} else {
|
3469
|
+
$et->WarnOnce(qq(Boolean value for XMP-$ns:$$tagInfo{Name} should be "True" or "False"),1);
|
3470
|
+
}
|
3452
3471
|
}
|
3453
3472
|
# protect against large binary data in unknown tags
|
3454
3473
|
$$tagInfo{Binary} = 1 if $new and length($val) > 65536;
|