exiftool_vendored 12.42.0 → 12.52.0

Sign up to get free protection for your applications and to get access to all the features.
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) {