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.
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;