exiftool_vendored 12.41.0 → 12.50.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +209 -6
  3. data/bin/MANIFEST +12 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +45 -44
  7. data/bin/config_files/acdsee.config +2 -1
  8. data/bin/config_files/frameCount.config +56 -0
  9. data/bin/config_files/tiff_version.config +1 -1
  10. data/bin/exiftool +115 -96
  11. data/bin/fmt_files/gpx.fmt +3 -0
  12. data/bin/fmt_files/gpx_wpt.fmt +3 -0
  13. data/bin/lib/Image/ExifTool/Apple.pm +16 -3
  14. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +23 -12
  15. data/bin/lib/Image/ExifTool/Canon.pm +71 -33
  16. data/bin/lib/Image/ExifTool/CanonRaw.pm +8 -1
  17. data/bin/lib/Image/ExifTool/CanonVRD.pm +7 -8
  18. data/bin/lib/Image/ExifTool/DJI.pm +60 -1
  19. data/bin/lib/Image/ExifTool/DNG.pm +8 -2
  20. data/bin/lib/Image/ExifTool/DarwinCore.pm +13 -1
  21. data/bin/lib/Image/ExifTool/EXE.pm +9 -1
  22. data/bin/lib/Image/ExifTool/Exif.pm +26 -12
  23. data/bin/lib/Image/ExifTool/FLAC.pm +17 -3
  24. data/bin/lib/Image/ExifTool/FLIR.pm +4 -3
  25. data/bin/lib/Image/ExifTool/FlashPix.pm +26 -3
  26. data/bin/lib/Image/ExifTool/FujiFilm.pm +51 -4
  27. data/bin/lib/Image/ExifTool/GPS.pm +21 -1
  28. data/bin/lib/Image/ExifTool/Geotag.pm +25 -5
  29. data/bin/lib/Image/ExifTool/ICC_Profile.pm +12 -9
  30. data/bin/lib/Image/ExifTool/ICO.pm +143 -0
  31. data/bin/lib/Image/ExifTool/ID3.pm +11 -11
  32. data/bin/lib/Image/ExifTool/IPTC.pm +5 -1
  33. data/bin/lib/Image/ExifTool/LNK.pm +5 -2
  34. data/bin/lib/Image/ExifTool/M2TS.pm +98 -8
  35. data/bin/lib/Image/ExifTool/MIE.pm +9 -3
  36. data/bin/lib/Image/ExifTool/MISB.pm +494 -0
  37. data/bin/lib/Image/ExifTool/MakerNotes.pm +8 -1
  38. data/bin/lib/Image/ExifTool/Matroska.pm +24 -16
  39. data/bin/lib/Image/ExifTool/Motorola.pm +8 -2
  40. data/bin/lib/Image/ExifTool/Nikon.pm +293 -122
  41. data/bin/lib/Image/ExifTool/NikonCustom.pm +4 -1
  42. data/bin/lib/Image/ExifTool/NikonSettings.pm +5 -3
  43. data/bin/lib/Image/ExifTool/Olympus.pm +22 -2
  44. data/bin/lib/Image/ExifTool/PDF.pm +2 -1
  45. data/bin/lib/Image/ExifTool/Panasonic.pm +30 -4
  46. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +25 -5
  47. data/bin/lib/Image/ExifTool/Parrot.pm +96 -2
  48. data/bin/lib/Image/ExifTool/Pentax.pm +8 -3
  49. data/bin/lib/Image/ExifTool/Photoshop.pm +35 -8
  50. data/bin/lib/Image/ExifTool/QuickTime.pm +163 -13
  51. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +119 -13
  52. data/bin/lib/Image/ExifTool/README +13 -3
  53. data/bin/lib/Image/ExifTool/RIFF.pm +106 -9
  54. data/bin/lib/Image/ExifTool/Samsung.pm +234 -3
  55. data/bin/lib/Image/ExifTool/Shortcuts.pm +2 -1
  56. data/bin/lib/Image/ExifTool/Sigma.pm +27 -1
  57. data/bin/lib/Image/ExifTool/SigmaRaw.pm +37 -13
  58. data/bin/lib/Image/ExifTool/Sony.pm +71 -43
  59. data/bin/lib/Image/ExifTool/TagInfoXML.pm +3 -1
  60. data/bin/lib/Image/ExifTool/TagLookup.pm +4752 -4516
  61. data/bin/lib/Image/ExifTool/TagNames.pod +1885 -1434
  62. data/bin/lib/Image/ExifTool/Text.pm +3 -4
  63. data/bin/lib/Image/ExifTool/Torrent.pm +2 -3
  64. data/bin/lib/Image/ExifTool/Validate.pm +3 -3
  65. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +7 -0
  66. data/bin/lib/Image/ExifTool/WriteExif.pl +100 -23
  67. data/bin/lib/Image/ExifTool/WriteIPTC.pl +2 -6
  68. data/bin/lib/Image/ExifTool/WritePhotoshop.pl +5 -5
  69. data/bin/lib/Image/ExifTool/WriteRIFF.pl +359 -0
  70. data/bin/lib/Image/ExifTool/Writer.pl +14 -6
  71. data/bin/lib/Image/ExifTool/XMP.pm +78 -59
  72. data/bin/lib/Image/ExifTool/XMP2.pl +19 -4
  73. data/bin/lib/Image/ExifTool.pm +120 -33
  74. data/bin/lib/Image/ExifTool.pod +83 -69
  75. data/bin/perl-Image-ExifTool.spec +43 -43
  76. data/lib/exiftool_vendored/version.rb +1 -1
  77. 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 > 2 and print $out "$err\n";
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
- print $out " Deleting $name\n" if $out and defined $newData and not length $newData;
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 the proper order)
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.51';
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 => { Writable => 'integer' },
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 => { Writable => 'integer' },
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/\\/&#92;/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() above)
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 structure
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
- $et->WarnOnce("Boolean value for XMP-$ns:$$tagInfo{Name} should be capitalized",1);
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;