exiftool_vendored 12.41.0 → 12.50.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.
- checksums.yaml +4 -4
- data/bin/Changes +209 -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 +115 -96
- 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 +71 -33
- 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 +60 -1
- data/bin/lib/Image/ExifTool/DNG.pm +8 -2
- 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 +26 -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 +12 -9
- data/bin/lib/Image/ExifTool/ICO.pm +143 -0
- data/bin/lib/Image/ExifTool/ID3.pm +11 -11
- 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 +8 -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 +293 -122
- data/bin/lib/Image/ExifTool/NikonCustom.pm +4 -1
- data/bin/lib/Image/ExifTool/NikonSettings.pm +5 -3
- data/bin/lib/Image/ExifTool/Olympus.pm +22 -2
- data/bin/lib/Image/ExifTool/PDF.pm +2 -1
- data/bin/lib/Image/ExifTool/Panasonic.pm +30 -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 +8 -3
- data/bin/lib/Image/ExifTool/Photoshop.pm +35 -8
- 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 +234 -3
- data/bin/lib/Image/ExifTool/Shortcuts.pm +2 -1
- 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 +4752 -4516
- data/bin/lib/Image/ExifTool/TagNames.pod +1885 -1434
- 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 +14 -6
- data/bin/lib/Image/ExifTool/XMP.pm +78 -59
- data/bin/lib/Image/ExifTool/XMP2.pl +19 -4
- data/bin/lib/Image/ExifTool.pm +120 -33
- 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
|
@@ -858,7 +860,7 @@ TAG: foreach $tagInfo (@matchingTags) {
|
|
858
860
|
($shift or ($shiftable eq '0' and $options{DelValue})))
|
859
861
|
{
|
860
862
|
$err = "$wgrp1:$tag is not shiftable";
|
861
|
-
$verbose
|
863
|
+
$verbose and print $out "$err\n";
|
862
864
|
next;
|
863
865
|
}
|
864
866
|
}
|
@@ -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;
|