exiftool_vendored 12.42.0 → 12.52.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +226 -6
  3. data/bin/MANIFEST +14 -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 +116 -97
  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 +66 -37
  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/Casio.pm +3 -3
  19. data/bin/lib/Image/ExifTool/DJI.pm +2 -1
  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 +17 -12
  23. data/bin/lib/Image/ExifTool/FLAC.pm +17 -3
  24. data/bin/lib/Image/ExifTool/FLIR.pm +9 -7
  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 +31 -5
  28. data/bin/lib/Image/ExifTool/Geotag.pm +36 -8
  29. data/bin/lib/Image/ExifTool/ICC_Profile.pm +3 -2
  30. data/bin/lib/Image/ExifTool/ICO.pm +143 -0
  31. data/bin/lib/Image/ExifTool/ID3.pm +6 -6
  32. data/bin/lib/Image/ExifTool/IPTC.pm +5 -1
  33. data/bin/lib/Image/ExifTool/JPEG.pm +1 -0
  34. data/bin/lib/Image/ExifTool/Jpeg2000.pm +24 -3
  35. data/bin/lib/Image/ExifTool/LNK.pm +5 -2
  36. data/bin/lib/Image/ExifTool/Lang/de.pm +1 -1
  37. data/bin/lib/Image/ExifTool/Lang/fr.pm +6015 -759
  38. data/bin/lib/Image/ExifTool/Lang/sk.pm +1927 -0
  39. data/bin/lib/Image/ExifTool/M2TS.pm +98 -8
  40. data/bin/lib/Image/ExifTool/MIE.pm +9 -3
  41. data/bin/lib/Image/ExifTool/MISB.pm +494 -0
  42. data/bin/lib/Image/ExifTool/MakerNotes.pm +3 -1
  43. data/bin/lib/Image/ExifTool/Matroska.pm +272 -48
  44. data/bin/lib/Image/ExifTool/Motorola.pm +8 -2
  45. data/bin/lib/Image/ExifTool/Nikon.pm +746 -382
  46. data/bin/lib/Image/ExifTool/NikonCustom.pm +139 -106
  47. data/bin/lib/Image/ExifTool/NikonSettings.pm +5 -3
  48. data/bin/lib/Image/ExifTool/Olympus.pm +6 -4
  49. data/bin/lib/Image/ExifTool/PNG.pm +8 -1
  50. data/bin/lib/Image/ExifTool/Panasonic.pm +21 -4
  51. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +25 -5
  52. data/bin/lib/Image/ExifTool/Parrot.pm +96 -2
  53. data/bin/lib/Image/ExifTool/Pentax.pm +7 -2
  54. data/bin/lib/Image/ExifTool/Photoshop.pm +29 -3
  55. data/bin/lib/Image/ExifTool/QuickTime.pm +166 -13
  56. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +161 -22
  57. data/bin/lib/Image/ExifTool/README +15 -4
  58. data/bin/lib/Image/ExifTool/RIFF.pm +106 -9
  59. data/bin/lib/Image/ExifTool/Samsung.pm +2 -2
  60. data/bin/lib/Image/ExifTool/Sigma.pm +27 -1
  61. data/bin/lib/Image/ExifTool/SigmaRaw.pm +37 -13
  62. data/bin/lib/Image/ExifTool/Sony.pm +75 -47
  63. data/bin/lib/Image/ExifTool/TagInfoXML.pm +13 -6
  64. data/bin/lib/Image/ExifTool/TagLookup.pm +4791 -4519
  65. data/bin/lib/Image/ExifTool/TagNames.pod +2056 -1446
  66. data/bin/lib/Image/ExifTool/Text.pm +3 -4
  67. data/bin/lib/Image/ExifTool/Torrent.pm +2 -3
  68. data/bin/lib/Image/ExifTool/Validate.pm +3 -3
  69. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +7 -0
  70. data/bin/lib/Image/ExifTool/WriteExif.pl +100 -23
  71. data/bin/lib/Image/ExifTool/WriteIPTC.pl +2 -6
  72. data/bin/lib/Image/ExifTool/WritePhotoshop.pl +5 -5
  73. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +12 -7
  74. data/bin/lib/Image/ExifTool/WriteRIFF.pl +359 -0
  75. data/bin/lib/Image/ExifTool/WriteXMP.pl +15 -1
  76. data/bin/lib/Image/ExifTool/Writer.pl +46 -18
  77. data/bin/lib/Image/ExifTool/XMP.pm +78 -59
  78. data/bin/lib/Image/ExifTool/XMP2.pl +19 -4
  79. data/bin/lib/Image/ExifTool/ZIP.pm +19 -7
  80. data/bin/lib/Image/ExifTool.pm +146 -38
  81. data/bin/lib/Image/ExifTool.pod +83 -69
  82. data/bin/perl-Image-ExifTool.spec +43 -43
  83. data/lib/exiftool_vendored/version.rb +1 -1
  84. metadata +10 -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
@@ -561,7 +561,21 @@ sub AddStructType($$$$;$)
561
561
  }
562
562
 
563
563
  #------------------------------------------------------------------------------
564
- # Hack to use XMP writer for SphericalVideoXML
564
+ # Process SphericalVideoXML (see XMP-GSpherical tags documentation)
565
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
566
+ # Returns: SphericalVideoXML data
567
+ sub ProcessGSpherical($$$)
568
+ {
569
+ my ($et, $dirInfo, $tagTablePtr) = @_;
570
+ # extract SphericalVideoXML as a block if requested
571
+ if ($$et{REQ_TAG_LOOKUP}{sphericalvideoxml}) {
572
+ $et->FoundTag(SphericalVideoXML => substr(${$$dirInfo{DataPt}}, 16));
573
+ }
574
+ return Image::ExifTool::XMP::ProcessXMP($et, $dirInfo, $tagTablePtr);
575
+ }
576
+
577
+ #------------------------------------------------------------------------------
578
+ # Hack to use XMP writer for SphericalVideoXML (see XMP-GSpherical tags documentation)
565
579
  # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
566
580
  # Returns: SphericalVideoXML data
567
581
  sub WriteGSpherical($$$)
@@ -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
@@ -705,9 +707,13 @@ TAG: foreach $tagInfo (@matchingTags) {
705
707
  $writeGroup or $writeGroup = $group0;
706
708
  # get priority for this group
707
709
  unless ($priority) {
708
- $priority = $$self{WRITE_PRIORITY}{lc($writeGroup)};
709
- unless ($priority) {
710
- $priority = $$self{WRITE_PRIORITY}{lc($group0)} || 0;
710
+ if ($$tagInfo{Avoid} and $$tagInfo{WriteAlso}) {
711
+ $priority = 0;
712
+ } else {
713
+ $priority = $$self{WRITE_PRIORITY}{lc($writeGroup)};
714
+ unless ($priority) {
715
+ $priority = $$self{WRITE_PRIORITY}{lc($group0)} || 0;
716
+ }
711
717
  }
712
718
  }
713
719
  # adjust priority based on Preferred level for this tag
@@ -828,6 +834,8 @@ TAG: foreach $tagInfo (@matchingTags) {
828
834
  $tag = $$tagInfo{Name}; # get tag name for warnings
829
835
  my $lcTag = lc $tag;
830
836
  my $pref = $preferred{$lcTag} || { };
837
+ # don't write Avoid-ed tags with side effect unless preferred
838
+ next if not $$pref{$tagInfo} and $$tagInfo{Avoid} and $$tagInfo{WriteAlso};
831
839
  my $shift = $options{Shift};
832
840
  my $addValue = $options{AddValue};
833
841
  if (defined $shift) {
@@ -969,16 +977,18 @@ TAG: foreach $tagInfo (@matchingTags) {
969
977
  $self->GetNewValueHash($tagInfo, $writeGroup, 'delete', $options{ProtectSaved});
970
978
  # also delete related tag previous new values
971
979
  if ($$tagInfo{WriteAlso}) {
980
+ $$self{INDENT2} = '+';
972
981
  my ($wgrp, $wtag);
973
982
  if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) {
974
983
  $wgrp = $writeGroup . ':';
975
984
  } else {
976
985
  $wgrp = '';
977
986
  }
978
- foreach $wtag (keys %{$$tagInfo{WriteAlso}}) {
987
+ foreach $wtag (sort keys %{$$tagInfo{WriteAlso}}) {
979
988
  my ($n,$e) = $self->SetNewValue($wgrp . $wtag, undef, Replace=>2);
980
989
  $numSet += $n;
981
990
  }
991
+ $$self{INDENT2} = '';
982
992
  }
983
993
  $options{Replace} == 2 and ++$numSet, next;
984
994
  }
@@ -1020,7 +1030,7 @@ TAG: foreach $tagInfo (@matchingTags) {
1020
1030
  require 'Image/ExifTool/XMPStruct.pl';
1021
1031
  $_ = Image::ExifTool::XMP::SerializeStruct($_);
1022
1032
  }
1023
- print $out "$verb $wgrp1:$tag$fromList if value is '${_}'\n";
1033
+ print $out "$$self{INDENT2}$verb $wgrp1:$tag$fromList if value is '${_}'\n";
1024
1034
  }
1025
1035
  }
1026
1036
  }
@@ -1084,15 +1094,25 @@ TAG: foreach $tagInfo (@matchingTags) {
1084
1094
  push @{$$nvHash{Value}}, ref $val eq 'ARRAY' ? @$val : $val;
1085
1095
  }
1086
1096
  if ($verbose > 1) {
1087
- my $ifExists = $$nvHash{IsCreating} ? ( $createOnly ?
1088
- ($$nvHash{IsCreating} == 2 ?
1089
- " if $writeGroup exists and tag doesn't" :
1090
- " if tag doesn't exist") :
1091
- ($$nvHash{IsCreating} == 2 ? " if $writeGroup exists" : '')) :
1097
+ my $ifExists;
1098
+ if ($$tagInfo{IsComposite}) {
1099
+ # (composite tags don't technically exist in the file)
1100
+ if ($$tagInfo{WriteAlso}) {
1101
+ $ifExists = ' (+' . join(',+',sort keys %{$$tagInfo{WriteAlso}}) . '):';
1102
+ } else {
1103
+ $ifExists = '';
1104
+ }
1105
+ } else {
1106
+ $ifExists = $$nvHash{IsCreating} ? ( $createOnly ?
1107
+ ($$nvHash{IsCreating} == 2 ?
1108
+ " if $writeGroup exists and tag doesn't" :
1109
+ " if tag doesn't exist") :
1110
+ ($$nvHash{IsCreating} == 2 ? " if $writeGroup exists" : '')) :
1092
1111
  (($$nvHash{DelValue} and @{$$nvHash{DelValue}}) ?
1093
- ' if tag was deleted' : ' if tag exists');
1112
+ ' if tag was deleted' : ' if tag exists');
1113
+ }
1094
1114
  my $verb = ($shift ? 'Shifting' : ($addValue ? 'Adding' : 'Writing'));
1095
- print $out "$verb $wgrp1:$tag$ifExists\n";
1115
+ print $out "$$self{INDENT2}$verb $wgrp1:$tag$ifExists\n";
1096
1116
  }
1097
1117
  }
1098
1118
  } elsif ($permanent) {
@@ -1107,7 +1127,7 @@ TAG: foreach $tagInfo (@matchingTags) {
1107
1127
  $self->GetNewValueHash($tagInfo, $writeGroup, 'delete');
1108
1128
  my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create');
1109
1129
  $$nvHash{WantGroup} = $wantGroup;
1110
- $verbose > 1 and print $out "Deleting $wgrp1:$tag\n";
1130
+ $verbose > 1 and print $out "$$self{INDENT2}Deleting $wgrp1:$tag\n";
1111
1131
  }
1112
1132
  $$setTags{$tagInfo} = 1 if $setTags;
1113
1133
  $prioritySet = 1 if $$pref{$tagInfo};
@@ -1116,6 +1136,7 @@ WriteAlso:
1116
1136
  # also write related tags
1117
1137
  my $writeAlso = $$tagInfo{WriteAlso};
1118
1138
  if ($writeAlso) {
1139
+ $$self{INDENT2} = '+'; # indicate related tag with a leading "+"
1119
1140
  my ($wgrp, $wtag, $n);
1120
1141
  if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) {
1121
1142
  $wgrp = $writeGroup . ':';
@@ -1123,7 +1144,7 @@ WriteAlso:
1123
1144
  $wgrp = '';
1124
1145
  }
1125
1146
  local $SIG{'__WARN__'} = \&SetWarning;
1126
- foreach $wtag (keys %$writeAlso) {
1147
+ foreach $wtag (sort keys %$writeAlso) {
1127
1148
  my %opts = (
1128
1149
  Type => 'ValueConv',
1129
1150
  Protected => $protected | 0x02,
@@ -1135,7 +1156,7 @@ WriteAlso:
1135
1156
  SetTags => \%alsoWrote, # remember tags already written
1136
1157
  );
1137
1158
  undef $evalWarning;
1138
- #### eval WriteAlso ($val)
1159
+ #### eval WriteAlso ($val,%opts)
1139
1160
  my $v = eval $$writeAlso{$wtag};
1140
1161
  # we wanted to do the eval in case there are side effect, but we
1141
1162
  # don't want to write a value for a tag that is being deleted:
@@ -1157,6 +1178,7 @@ WriteAlso:
1157
1178
  }
1158
1179
  }
1159
1180
  }
1181
+ $$self{INDENT2} = '';
1160
1182
  }
1161
1183
  }
1162
1184
  # print warning if we couldn't set our priority tag
@@ -1270,6 +1292,7 @@ sub SetNewValuesFromFile($$;@)
1270
1292
  GlobalTimeShift => $$options{GlobalTimeShift},
1271
1293
  HexTagIDs => $$options{HexTagIDs},
1272
1294
  IgnoreMinorErrors=>$$options{IgnoreMinorErrors},
1295
+ IgnoreTags => $$options{IgnoreTags},
1273
1296
  Lang => $$options{Lang},
1274
1297
  LargeFileSupport=> $$options{LargeFileSupport},
1275
1298
  List => 1,
@@ -4241,7 +4264,12 @@ sub WriteDirectory($$$;$)
4241
4264
  $$self{DIR_NAME} = $oldDir;
4242
4265
  @$self{'Compression','SubfileType'} = @save;
4243
4266
  SetByteOrder($saveOrder);
4244
- print $out " Deleting $name\n" if $out and defined $newData and not length $newData;
4267
+ if ($out) {
4268
+ print $out " Deleting $name\n" if defined $newData and not length $newData;
4269
+ if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) {
4270
+ print $out "$$self{INDENT} [nothing changed in $dirName]\n";
4271
+ }
4272
+ }
4245
4273
  return $newData;
4246
4274
  }
4247
4275
 
@@ -4530,7 +4558,7 @@ sub DumpUnknownTrailer($$)
4530
4558
  # add to Preview block list if valid and in the trailer
4531
4559
  $image{$prePos} = [$tag, $preLen] if $prePos and $preLen and $prePos+$preLen > $pos;
4532
4560
  last if $lastOne; # checked all images
4533
- # look for MPF images (in the the proper order)
4561
+ # look for MPF images (in the proper order)
4534
4562
  ++$mpImageNum;
4535
4563
  $prePos = $$self{VALUE}{"MPImageStart ($mpImageNum)"};
4536
4564
  if (defined $prePos) {