File Coverage

blib/lib/Image/ExifTool/Writer.pl
Criterion Covered Total %
statement 2692 4042 66.6
branch 1881 3354 56.0
condition 882 1715 51.4
subroutine 85 115 73.9
pod 17 99 17.1
total 5557 9325 59.5


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: Writer.pl
3             #
4             # Description: ExifTool write routines
5             #
6             # Notes: Also contains some less used ExifTool functions
7             #
8             # URL: https://exiftool.org/
9             #
10             # Revisions: 12/16/2004 - P. Harvey Created
11             #------------------------------------------------------------------------------
12              
13             package Image::ExifTool;
14              
15 61     61   904 use strict;
  61         150  
  61         3588  
16              
17 61     61   211473 use Image::ExifTool::TagLookup qw(FindTagInfo TagExists);
  61         4279  
  61         84704  
18 61     61   60195 use Image::ExifTool::Fixup;
  61         226  
  61         175429  
19              
20             sub AssembleRational($$@);
21             sub LastInList($);
22             sub NextFreeTagKey($$);
23             sub RemoveNewValueHash($$$);
24             sub RemoveNewValuesForGroup($$);
25             sub GetWriteGroup1($$);
26             sub Sanitize($$);
27             sub ConvInv($$$$$;$$);
28             sub PushValue($$$;$);
29              
30             my $loadedAllTables; # flag indicating we loaded all tables
31              
32             # the following is a road map of where we write each directory
33             # in the different types of files.
34             my %tiffMap = (
35             IFD0 => 'TIFF',
36             IFD1 => 'IFD0',
37             XMP => 'IFD0',
38             ICC_Profile => 'IFD0',
39             ExifIFD => 'IFD0',
40             GPS => 'IFD0',
41             SubIFD => 'IFD0',
42             GlobParamIFD => 'IFD0',
43             PrintIM => 'IFD0',
44             IPTC => 'IFD0',
45             Photoshop => 'IFD0',
46             SEAL => 'IFD0',
47             InteropIFD => 'ExifIFD',
48             MakerNotes => 'ExifIFD',
49             CanonVRD => 'MakerNotes', # (so VRDOffset will get updated)
50             NikonCapture => 'MakerNotes', # (to allow delete by group)
51             PhaseOne => 'MakerNotes', # (for editing PhaseOne SensorCalibration tags)
52             );
53             my %exifMap = (
54             IFD1 => 'IFD0',
55             EXIF => 'IFD0', # to write EXIF as a block
56             ExifIFD => 'IFD0',
57             GPS => 'IFD0',
58             SubIFD => 'IFD0',
59             GlobParamIFD => 'IFD0',
60             PrintIM => 'IFD0',
61             InteropIFD => 'ExifIFD',
62             MakerNotes => 'ExifIFD',
63             NikonCapture => 'MakerNotes', # (to allow delete by group)
64             # (no CanonVRD trailer allowed)
65             );
66             my %jpegMap = (
67             %exifMap, # covers all JPEG EXIF mappings
68             JFIF => 'APP0',
69             CIFF => 'APP0',
70             IFD0 => 'APP1',
71             XMP => 'APP1',
72             ICC_Profile => 'APP2',
73             FlashPix => 'APP2',
74             MPF => 'APP2',
75             Meta => 'APP3',
76             MetaIFD => 'Meta',
77             RMETA => 'APP5',
78             SEAL => ['APP8','APP9'], # (note: add 'IFD0' if this is a possibility)
79             AROT => 'APP10',
80             JUMBF => 'APP11',
81             Ducky => 'APP12',
82             Photoshop => 'APP13',
83             Adobe => 'APP14',
84             IPTC => 'Photoshop',
85             MakerNotes => ['ExifIFD', 'CIFF'], # (first parent is the default)
86             CanonVRD => 'MakerNotes', # (so VRDOffset will get updated)
87             NikonCapture => 'MakerNotes', # (to allow delete by group)
88             Comment => 'COM',
89             );
90             my %dirMap = (
91             JPEG => \%jpegMap,
92             EXV => \%jpegMap,
93             TIFF => \%tiffMap,
94             ORF => \%tiffMap,
95             RAW => \%tiffMap,
96             EXIF => \%exifMap,
97             );
98              
99             # module names and write functions for each writable file type
100             # (defaults to "$type" and "Process$type" if not defined)
101             # - types that are handled specially will not appear in this list
102             my %writableType = (
103             CRW => [ 'CanonRaw', 'WriteCRW' ],
104             DR4 => 'CanonVRD',
105             EPS => [ 'PostScript', 'WritePS' ],
106             FLIF=> [ undef, 'WriteFLIF'],
107             GIF => undef,
108             ICC => [ 'ICC_Profile', 'WriteICC' ],
109             IND => 'InDesign',
110             JP2 => 'Jpeg2000',
111             JXL => 'Jpeg2000',
112             MIE => undef,
113             MOV => [ 'QuickTime', 'WriteMOV' ],
114             MRW => 'MinoltaRaw',
115             PDF => [ undef, 'WritePDF' ],
116             PNG => undef,
117             PPM => undef,
118             PS => [ 'PostScript', 'WritePS' ],
119             PSD => 'Photoshop',
120             RAF => [ 'FujiFilm', 'WriteRAF' ],
121             RIFF=> [ 'RIFF', 'WriteRIFF'],
122             VRD => 'CanonVRD',
123             WEBP=> [ 'RIFF', 'WriteRIFF'],
124             X3F => 'SigmaRaw',
125             XMP => [ undef, 'WriteXMP' ],
126             );
127              
128             # RAW file types (2 = raw file where we can delete maker notes from ExifIFD)
129             my %rawType = (
130             '3FR'=> 1, CR3 => 2, IIQ => 1, NEF => 1, RW2 => 1,
131             ARQ => 1, CRW => 1, K25 => 1, NRW => 1, RWL => 1,
132             ARW => 1, DCR => 1, KDC => 1, ORF => 1, SR2 => 1,
133             ARW => 1, ERF => 1, MEF => 1, PEF => 1, SRF => 1,
134             CR2 => 1, FFF => 1, MOS => 1, RAW => 1, SRW => 1,
135             );
136              
137             # groups we are allowed to delete
138             # Notes:
139             # 1) these names must either exist in %dirMap, or be translated in InitWriteDirs())
140             # 2) any dependencies must be added to %excludeGroups
141             my @delGroups = qw(
142             Adobe AFCP APP0 APP1 APP2 APP3 APP4 APP5 APP6 APP7 APP8 APP9 APP10 APP11 APP12
143             APP13 APP14 APP15 AROT AudioKeys CanonVRD CIFF Ducky EXIF ExifIFD File FlashPix
144             FotoStation GlobParamIFD GPS ICC_Profile IFD0 IFD1 Insta360 InteropIFD IPTC
145             ItemList iTunes JFIF Jpeg2000 JUMBF Keys MakerNotes Meta MetaIFD Microsoft MIE
146             MPF Nextbase NikonApp NikonCapture PDF PDF-update PhotoMechanic Photoshop PNG
147             PNG-pHYs PrintIM QuickTime RMETA RSRC SEAL SubIFD Trailer UserData VideoKeys
148             Vivo XML XML-* XMP XMP-*
149             );
150             # family 2 group names that we can delete
151             my @delGroup2 = qw(
152             Audio Author Camera Document ExifTool Image Location Other Preview Printing
153             Time Video
154             );
155             # Extra groups to delete when deleting another group
156             my %delMore = (
157             QuickTime => [ qw(ItemList UserData Keys) ],
158             XMP => [ 'XMP-*' ],
159             XML => [ 'XML-*' ],
160             SEAL => [ 'XMP-SEAL' ],
161             );
162              
163             # family 0 groups where directories should never be deleted
164             my %permanentDir = ( QuickTime => 1, Jpeg2000 => 1 );
165              
166             # lookup for all valid family 2 groups (lower case)
167             my %family2groups = map { lc $_ => 1 } @delGroup2, 'Unknown';
168              
169             # groups we don't delete when deleting all information
170             my $protectedGroups = '(IFD1|SubIFD|InteropIFD|GlobParamIFD|PDF-update|Adobe)';
171              
172             # other group names of new tag values to remove when deleting an entire group
173             my %removeGroups = (
174             IFD0 => [ 'EXIF', 'MakerNotes' ],
175             EXIF => [ 'MakerNotes' ],
176             ExifIFD => [ 'MakerNotes', 'InteropIFD' ],
177             Trailer => [ 'CanonVRD' ], #(because we can add back CanonVRD as a block)
178             );
179             # related family 0/1 groups in @delGroups (and not already in %jpegMap)
180             # that must be removed from delete list when excluding a group
181             my %excludeGroups = (
182             EXIF => [ qw(IFD0 IFD1 ExifIFD GPS MakerNotes GlobParamIFD InteropIFD PrintIM SubIFD) ],
183             IFD0 => [ 'EXIF' ],
184             IFD1 => [ 'EXIF' ],
185             ExifIFD => [ 'EXIF' ],
186             GPS => [ 'EXIF' ],
187             MakerNotes => [ 'EXIF' ],
188             InteropIFD => [ 'EXIF' ],
189             GlobParamIFD => [ 'EXIF' ],
190             PrintIM => [ 'EXIF' ],
191             CIFF => [ 'MakerNotes' ],
192             # technically correct, but very uncommon and not a good reason to avoid deleting trailer
193             # IPTC => [ qw(AFCP FotoStation Trailer) ],
194             AFCP => [ 'Trailer' ],
195             FotoStation => [ 'Trailer' ],
196             CanonVRD => [ 'Trailer' ],
197             PhotoMechanic=> [ 'Trailer' ],
198             MIE => [ 'Trailer' ],
199             QuickTime => [ qw(ItemList UserData Keys) ],
200             );
201             # translate (lower case) wanted group when writing for tags where group name may change
202             my %translateWantGroup = (
203             ciff => 'canonraw',
204             );
205             # group names to translate for writing
206             my %translateWriteGroup = (
207             EXIF => 'ExifIFD',
208             Meta => 'MetaIFD',
209             File => 'Comment',
210             # any entry in this table causes the write group to be set from the
211             # tag information instead of whatever the user specified...
212             MIE => 'MIE',
213             APP14 => 'APP14',
214             );
215             # names of valid EXIF and Meta directories (lower case keys):
216             my %exifDirs = (
217             gps => 'GPS',
218             exififd => 'ExifIFD',
219             subifd => 'SubIFD',
220             globparamifd => 'GlobParamIFD',
221             interopifd => 'InteropIFD',
222             previewifd => 'PreviewIFD', # (in MakerNotes)
223             metaifd => 'MetaIFD', # Kodak APP3 Meta
224             makernotes => 'MakerNotes',
225             );
226             # valid family 0 groups when WriteGroup is set to "All"
227             my %allFam0 = (
228             exif => 1,
229             makernotes => 1,
230             );
231              
232             my @writableMacOSTags = qw(
233             FileCreateDate MDItemFinderComment MDItemFSCreationDate MDItemFSLabel MDItemUserTags
234             XAttrQuarantine XAttrMDItemWhereFroms
235             );
236              
237             # min/max values for integer formats
238             my %intRange = (
239             'int8u' => [0, 0xff],
240             'int8s' => [-0x80, 0x7f],
241             'int16u' => [0, 0xffff],
242             'int16uRev' => [0, 0xffff],
243             'int16s' => [-0x8000, 0x7fff],
244             'int32u' => [0, 0xffffffff],
245             'int32s' => [-0x80000000, 0x7fffffff],
246             'int64u' => [0, 18446744073709551615],
247             'int64s' => [-9223372036854775808, 9223372036854775807],
248             );
249             # lookup for file types with block-writable EXIF
250             my %blockExifTypes = map { $_ => 1 } qw(JPEG PNG JP2 JXL MIE EXIF FLIF MOV MP4 RIFF);
251              
252             my $maxSegmentLen = 0xfffd; # maximum length of data in a JPEG segment
253             my $maxXMPLen = $maxSegmentLen; # maximum length of XMP data in JPEG
254              
255             # value separators when conversion list is used (in SetNewValue)
256             my %listSep = ( PrintConv => '; ?', ValueConv => ' ' );
257              
258             # printConv hash keys to ignore when doing reverse lookup
259             my %ignorePrintConv = map { $_ => 1 } qw(OTHER BITMASK Notes);
260              
261             #------------------------------------------------------------------------------
262             # Set tag value
263             # Inputs: 0) ExifTool object reference
264             # 1) tag key, tag name, or '*' (optionally prefixed by group name),
265             # or undef to reset all previous SetNewValue() calls
266             # 2) new value (scalar, scalar ref or list ref), or undef to delete tag
267             # 3-N) Options:
268             # Type => PrintConv, ValueConv or Raw - specifies value type
269             # AddValue => true to add to list of existing values instead of overwriting
270             # DelValue => true to delete this existing value value from a list, or
271             # or doing a conditional delete, or to shift a time value
272             # Group => family 0 or 1 group name (case insensitive)
273             # Replace => 0, 1 or 2 - overwrite previous new values (2=reset)
274             # Protected => bitmask to write tags with specified protections
275             # EditOnly => true to only edit existing tags (don't create new tag)
276             # EditGroup => true to only edit existing groups (don't create new group)
277             # Shift => undef, 0, +1 or -1 - shift value if possible
278             # NoFlat => treat flattened tags as 'unsafe'
279             # NoShortcut => true to prevent looking up shortcut tags
280             # ProtectSaved => protect existing new values with a save count greater than this
281             # IgnorePermanent => ignore attempts to delete a permanent tag
282             # CreateGroups => [internal use] createGroups hash ref from related tags
283             # ListOnly => [internal use] set only list or non-list tags
284             # SetTags => [internal use] hash ref to return tagInfo refs of set tags
285             # Sanitized => [internal use] set to avoid double-sanitizing the value
286             # Fixup => [internal use] fixup information when writing maker notes
287             # Returns: number of tags set (plus error string in list context)
288             # Notes: For tag lists (like Keywords), call repeatedly with the same tag name for
289             # each value in the list. Internally, the new information is stored in
290             # the following members of the $$self{NEW_VALUE}{$tagInfo} hash:
291             # TagInfo - tag info ref
292             # DelValue - list ref for raw values to delete
293             # Value - list ref for raw values to add (not defined if deleting the tag)
294             # IsCreating - must be set for the tag to be added for the standard file types,
295             # otherwise just changed if it already exists. This may be
296             # overridden for file types with a PREFERRED metadata type.
297             # Set to 2 to create individual tags but not new groups
298             # EditOnly - flag set if tag should never be created (regardless of file type).
299             # If this is set, then IsCreating must be false
300             # CreateOnly - flag set if creating only (never edit existing tag)
301             # CreateGroups - hash of all family 0 group names where tag may be created
302             # WriteGroup - group name where information is being written (correct case)
303             # WantGroup - group name as specified in call to function (case insensitive)
304             # Next - pointer to next new value hash (if more than one for this tag)
305             # NoReplace - set if value was created with Replace=0
306             # AddBefore - number of list items added by a subsequent Replace=0 call
307             # IsNVH - flag indicating this is a new value hash
308             # Order - counter to indicate the order that new value hashes were created
309             # Shift - shift value
310             # Save - counter used by SaveNewValues()/RestoreNewValues()
311             # MAKER_NOTE_FIXUP - pointer to fixup if necessary for a maker note value
312             sub SetNewValue($;$$%)
313             {
314 5774     5774 1 60934 local $_;
315 5774         23419 my ($self, $tag, $value, %options) = @_;
316 5774         10500 my ($err, $tagInfo, $family);
317 5774         18461 my $verbose = $$self{OPTIONS}{Verbose};
318 5774         13153 my $out = $$self{OPTIONS}{TextOut};
319 5774   100     22667 my $protected = $options{Protected} || 0;
320 5774         12810 my $listOnly = $options{ListOnly};
321 5774         11024 my $setTags = $options{SetTags};
322 5774         11305 my $noFlat = $options{NoFlat};
323 5774         9654 my $numSet = 0;
324              
325 5774 100       14085 unless (defined $tag) {
326 43         1377 delete $$self{NEW_VALUE};
327 43         203 $$self{SAVE_COUNT} = $$self{NV_COUNT} = 0;
328 43         237 $$self{DEL_GROUP} = { };
329 43         234 return 1;
330             }
331             # allow value to be scalar or list reference
332 5731 100       14479 if (ref $value) {
333 218 100       1134 if (ref $value eq 'ARRAY') {
    100          
334             # value is an ARRAY so it may have more than one entry
335             # - set values both separately and as a combined string if there are more than one
336 78 100       314 if (@$value > 1) {
337             # set all list-type tags first
338 51         127 my $replace = $options{Replace};
339 51         98 my $noJoin;
340 51         156 foreach (@$value) {
341 153 100       402 $noJoin = 1 if ref $_;
342 153         1024 my ($n, $e) = SetNewValue($self, $tag, $_, %options, ListOnly => 1);
343 153 100       530 $err = $e if $e;
344 153         255 $numSet += $n;
345 153         452 delete $options{Replace}; # don't replace earlier values in list
346             }
347 51 100       177 return $numSet if $noJoin; # don't join if list contains objects
348             # and now set only non-list tags
349 50         305 $value = join $$self{OPTIONS}{ListSep}, @$value;
350 50         158 $options{Replace} = $replace;
351 50         161 $listOnly = $options{ListOnly} = 0;
352             } else {
353 27         86 $value = $$value[0];
354 27 50       103 $value = $$value if ref $value eq 'SCALAR'; # (handle single scalar ref in a list)
355             }
356             } elsif (ref $value eq 'SCALAR') {
357 127         375 $value = $$value;
358             }
359             }
360             # un-escape as necessary and make sure the Perl UTF-8 flag is OFF for the value
361             # if perl is 5.6 or greater (otherwise our byte manipulations get corrupted!!)
362 5730 100 100     46557 $self->Sanitize(\$value) if defined $value and not ref $value and not $options{Sanitized};
      100        
363              
364             # set group name in options if specified
365 5730 100       23208 ($options{Group}, $tag) = ($1, $2) if $tag =~ /(.*):(.+)/;
366              
367             # allow trailing '#' for ValueConv value
368 5730 100       16742 $options{Type} = 'ValueConv' if $tag =~ s/#$//;
369 5730   66     39504 my $convType = $options{Type} || ($$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv');
370              
371             # filter value if necessary
372 5730 100 50     31872 $self->Filter($$self{OPTIONS}{FilterW}, \$value) or return 0 if $convType eq 'PrintConv';
373              
374 5730         10517 my (@wantGroup, $family2);
375 5730         12919 my $wantGroup = $options{Group};
376 5730 100       17740 if ($wantGroup) {
377 2469         8692 foreach (split /:/, $wantGroup) {
378 2497 50 33     19456 next unless length($_) and /^(\d+)?(.*)/; # separate family number and group name
379 2497         9006 my ($f, $g) = ($1, $2);
380 2497         5674 my $lcg = lc $g;
381             # save group/family unless '*' or 'all'
382 2497 100 66     13710 push @wantGroup, [ $f, $lcg ] unless $lcg eq '*' or $lcg eq 'all';
383 2497 100       9025 if ($g =~ s/^ID-//i) { # family 7 is a tag ID
    100          
384 1 50 33     11 return 0 if defined $f and $f ne 7;
385 1         6 $wantGroup[-1] = [ 7, $g ]; # group name with 'ID-' removed and case preserved
386             } elsif (defined $f) {
387 30 50       93 $f > 2 and return 0; # only allow family 0, 1 or 2
388 30 100       95 $family2 = 1 if $f == 2; # set flag indicating family 2 was used
389             } else {
390 2466 100       9625 $family2 = 1 if $family2groups{$lcg};
391             }
392             }
393 2469 100       6239 undef $wantGroup unless @wantGroup;
394             }
395              
396 5730         13446 $tag =~ s/ .*//; # convert from tag key to tag name if necessary
397 5730 100       15582 $tag = '*' if lc($tag) eq 'all'; # use '*' instead of 'all'
398             #
399             # handle group delete
400             #
401 5730   100     18409 while ($tag eq '*' and not defined $value and not $family2 and @wantGroup < 2) {
      100        
      66        
402             # set groups to delete
403 49         137 my (@del, $grp);
404 49   66     242 my $remove = ($options{Replace} and $options{Replace} > 1);
405 49 100       169 if ($wantGroup) {
406 35 50       3195 @del = grep /^$wantGroup$/i, @delGroups unless $wantGroup =~ /^XM[LP]-\*$/i;
407             # remove associated groups when excluding from mass delete
408 35 100 100     229 if (@del and $remove) {
409             # remove associated groups in other family
410 4 100       19 push @del, @{$excludeGroups{$del[0]}} if $excludeGroups{$del[0]};
  2         7  
411             # remove upstream groups according to JPEG map
412 4         7 my $dirName = $del[0];
413 4         8 my @dirNames;
414 4         5 for (;;) {
415 10         20 my $parent = $jpegMap{$dirName};
416 10 50       19 if (ref $parent) {
417 0         0 push @dirNames, @$parent;
418 0         0 $parent = pop @dirNames;
419             }
420 10 100 66     37 $dirName = $parent || shift @dirNames or last;
421 6         13 push @del, $dirName; # exclude this too
422             }
423             }
424             # allow MIE groups to be deleted by number,
425             # and allow any XMP family 1 group to be deleted
426 35 100       210 push @del, uc($wantGroup) if $wantGroup =~ /^(MIE\d+|XM[LP]-[-\w]*\w)$/i;
427             } else {
428             # push all groups plus '*', except the protected groups
429 14         2259 push @del, (grep !/^$protectedGroups$/, @delGroups), '*';
430             }
431 49 50       179 if (@del) {
    0          
432 49         96 ++$numSet;
433 49         110 my @donegrps;
434 49         164 my $delGroup = $$self{DEL_GROUP};
435 49         142 foreach $grp (@del) {
436 975 100       1551 if ($remove) {
437 23         24 my $didExcl;
438 23 100       49 if ($grp =~ /^(XM[LP])(-.*)?$/) {
439 4         30 my $x = $1;
440 4 100 33     29 if ($grp eq $x) {
    50          
441             # exclude all related family 1 groups too
442 1         14 foreach (keys %$delGroup) {
443 67 100       135 next unless /^(-?)$x-/;
444 2 50       5 push @donegrps, $_ unless $1;
445 2         5 delete $$delGroup{$_};
446             }
447             } elsif ($$delGroup{"$x-*"} and not $$delGroup{"-$grp"}) {
448             # must also exclude XMP or XML to prevent bulk delete
449 3 100       11 if ($$delGroup{$x}) {
450 2         5 push @donegrps, $x;
451 2         6 delete $$delGroup{$x};
452             }
453             # flag XMP/XML family 1 group for exclusion with leading '-'
454 3         7 $$delGroup{"-$grp"} = 1;
455 3         6 $didExcl = 1;
456             }
457             }
458 23 100       45 if (exists $$delGroup{$grp}) {
459 15         19 delete $$delGroup{$grp};
460             } else {
461 8 100       15 next unless $didExcl;
462             }
463             } else {
464 952         2133 $$delGroup{$grp} = 1;
465             # add extra groups to delete if necessary
466 952 100       1948 if ($delMore{$grp}) {
467 66         116 $$delGroup{$_} = 1, push @donegrps, $_ foreach @{$delMore{$grp}};
  66         343  
468             }
469             # remove all of this group from previous new values
470 952         1748 $self->RemoveNewValuesForGroup($grp);
471             }
472 970         1680 push @donegrps, $grp;
473             }
474 49 100 66     342 if ($verbose > 1 and @donegrps) {
475 1         5 @donegrps = sort @donegrps;
476 1 50       6 my $msg = $remove ? 'Excluding from deletion' : 'Deleting tags in';
477 1         11 print $out " $msg: @donegrps\n";
478             }
479             } elsif (grep /^$wantGroup$/i, @delGroup2) {
480 0         0 last; # allow tags to be deleted by group2 name
481             } else {
482 0         0 $err = "Not a deletable group: $wantGroup";
483             }
484             # all done
485 49 50       202 return ($numSet, $err) if wantarray;
486 49 50       164 $err and warn "$err\n";
487 49         401 return $numSet;
488             }
489              
490             # initialize write/create flags
491 5681         8961 my $createOnly;
492 5681         10076 my $editOnly = $options{EditOnly};
493 5681         10338 my $editGroup = $options{EditGroup};
494 5681         14440 my $writeMode = $$self{OPTIONS}{WriteMode};
495 5681 100       13984 if ($writeMode ne 'wcg') {
496 27 100       99 $createOnly = 1 if $writeMode !~ /w/i; # don't write existing tags
497 27 100       198 if ($writeMode !~ /c/i) {
    100          
498 2 50       7 return 0 if $createOnly; # nothing to do unless writing existing tags
499 2         5 $editOnly = 1; # don't create new tags
500             } elsif ($writeMode !~ /g/i) {
501 8         16 $editGroup = 1; # don't create new groups
502             }
503             }
504 5681         10396 my ($ifdName, $mieGroup, $movGroup, $fg);
505             # set family 1 group names
506 5681         12309 foreach $fg (@wantGroup) {
507 2353 100 100     7310 next if defined $$fg[0] and $$fg[0] != 1;
508 2334         5216 $_ = $$fg[1];
509             # set $ifdName if this group is a valid IFD or SubIFD name
510 2334         3656 my $grpName;
511 2334 100 100     24579 if (/^IFD(\d+)$/i) {
    50          
    50          
    100          
    100          
    100          
    100          
    100          
512 131         569 $grpName = $ifdName = "IFD$1";
513             } elsif (/^SubIFD(\d+)$/i) {
514 0         0 $grpName = $ifdName = "SubIFD$1";
515             } elsif (/^Version(\d+)$/i) {
516 0         0 $grpName = $ifdName = "Version$1"; # Sony IDC VersionIFD
517             } elsif ($exifDirs{$_}) {
518 274         724 $grpName = $exifDirs{$_};
519 274 50 33     938 $ifdName = $grpName unless $ifdName and $allFam0{$_};
520             } elsif ($allFam0{$_}) {
521 293         828 $grpName = $allFam0{$_};
522             } elsif (/^Track(\d+)$/i) {
523 1         8 $grpName = $movGroup = "Track$1"; # QuickTime track
524             } elsif (/^MIE(\d*-?)(\w+)$/i) {
525 2         13 $grpName = $mieGroup = "MIE$1" . ucfirst(lc($2));
526             } elsif (not $ifdName and /^XMP\b/i) {
527             # must load XMP table to set group1 names
528 519         2083 my $table = GetTagTable('Image::ExifTool::XMP::Main');
529 519         1470 my $writeProc = $$table{WRITE_PROC};
530 519 50       1362 if ($writeProc) {
531 61     61   668 no strict 'refs';
  61         149  
  61         135137  
532 519         1994 &$writeProc();
533             }
534             }
535             # fix case for known groups
536 2334 100 66     15820 $wantGroup =~ s/$grpName/$grpName/i if $grpName and $grpName ne $_;
537             }
538             #
539             # get list of tags we want to set
540             #
541 5681         10502 my $origTag = $tag;
542 5681         21646 my @matchingTags = FindTagInfo($tag);
543 5681         17933 until (@matchingTags) {
544 1422         2507 my $langCode;
545             # allow language suffix of form "-en_CA" or "-" on tag name
546 1422 100 100     8935 if ($tag =~ /^([?*\w]+)-([a-z]{2})(_[a-z]{2})$/i or # MIE
    50          
547             $tag =~ /^([?*\w]+)-([a-z]{2,3}|[xi])(-[a-z\d]{2,8}(-[a-z\d]{1,8})*)?$/i) # XMP/PNG/QuickTime
548             {
549 55         213 $tag = $1;
550             # normalize case of language codes
551 55         160 $langCode = lc($2);
552 55 100       878 $langCode .= (length($3) == 3 ? uc($3) : lc($3)) if $3;
    100          
553 55         192 my @newMatches = FindTagInfo($tag);
554 55         140 foreach $tagInfo (@newMatches) {
555             # only allow language codes in tables which support them
556 291 50       1343 next unless $$tagInfo{Table};
557 291 100       859 my $langInfoProc = $$tagInfo{Table}{LANG_INFO} or next;
558 226         798 my $langInfo = &$langInfoProc($tagInfo, $langCode);
559 226 100       1811 push @matchingTags, $langInfo if $langInfo;
560             }
561 55 100       276 last if @matchingTags;
562             } elsif (not $options{NoShortcut}) {
563             # look for a shortcut or alias
564 1367         11822 require Image::ExifTool::Shortcuts;
565 1367         42797 my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main;
566 1367         4309 undef $err;
567 1367 100       3700 if ($match) {
568 1         5 $options{NoShortcut} = $options{Sanitized} = 1;
569 1         2 foreach $tag (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  1         5  
570 3         62 my ($n, $e) = $self->SetNewValue($tag, $value, %options);
571 3         13 $numSet += $n;
572 3 50       14 $e and $err = $e;
573             }
574 1 50       6 undef $err if $numSet; # no error if any set successfully
575 1 50       5 return ($numSet, $err) if wantarray;
576 1 50       4 $err and warn "$err\n";
577 1         11 return $numSet;
578             }
579             }
580 1368 50       3152 unless ($listOnly) {
581 1368 100       4038 if (not TagExists($tag)) {
    50          
    100          
582 47 50       189 if ($tag =~ /^[-\w*?]+$/) {
583 47 100       127 my $pre = $wantGroup ? $wantGroup . ':' : '';
584 47         129 $err = "Tag '$pre${origTag}' is not defined";
585 47 100       113 $err .= ' or has a bad language code' if $origTag =~ /-/;
586 47 50 66     151 if (not $pre and uc($origTag) eq 'TAG') {
587 0         0 $err .= " (specify a writable tag name, not '${origTag}' literally)"
588             }
589             } else {
590 0         0 $err = "Invalid tag name '${tag}'";
591 0 0       0 $err .= " (remove the leading '\$')" if $tag =~ /^\$/;
592             }
593             } elsif ($langCode) {
594 0         0 $err = "Tag '${tag}' does not support alternate languages";
595             } elsif ($wantGroup) {
596 509         1340 $err = "Sorry, $wantGroup:$origTag doesn't exist or isn't writable";
597             } else {
598 812         1942 $err = "Sorry, $origTag is not writable";
599             }
600 1368 50       3086 $verbose > 2 and print $out "$err\n";
601             }
602             # all done
603 1368 50       8731 return ($numSet, $err) if wantarray;
604 0 0       0 $err and warn "$err\n";
605 0         0 return $numSet;
606             }
607             # get group name that we're looking for
608 4312         8247 my $foundMatch = 0;
609             #
610             # determine the groups for all tags found, and the tag with
611             # the highest priority group
612             #
613 4312         12600 my (@tagInfoList, @writeAlsoList, %writeGroup, %preferred, %tagPriority);
614 4312         0 my (%avoid, $wasProtected, $noCreate, %highestPriority, %highestQT);
615              
616 4312         9961 TAG: foreach $tagInfo (@matchingTags) {
617 75695         217865 $tag = $$tagInfo{Name}; # get tag name for warnings
618 75695         126484 my $lcTag = lc $tag; # get lower-case tag name for use in variables
619             # initialize highest priority if we are starting a new tag
620 75695 100       200440 $highestPriority{$lcTag} = -999 unless defined $highestPriority{$lcTag};
621 75695         110951 my ($priority, $writeGroup);
622 75695 100       228391 my $prfTag = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED};
623 75695 100       149110 if ($wantGroup) {
624             # a WriteGroup of All is special
625 54807   100     117654 my $wgAll = ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All');
626 54807         150272 my @grp = $self->GetGroup($tagInfo);
627 54807         83673 my $hiPri = 1000;
628 54807         89743 foreach $fg (@wantGroup) {
629 54847         106844 my ($fam, $lcWant) = @$fg;
630 54847 100       119472 $lcWant = $translateWantGroup{$lcWant} if $translateWantGroup{$lcWant};
631             # only set tag in specified group
632             # bump priority of preferred tag
633 54847 100       103028 $hiPri += $prfTag if $prfTag;
634 54847 100 66     100667 if (not defined $fam) {
    100          
    100          
635 54567 100       129399 if ($lcWant eq lc $grp[0]) {
636             # don't go to more general write group of "All"
637             # if something more specific was wanted
638 2240 100 100     5871 $writeGroup = $grp[0] if $wgAll and not $writeGroup;
639 2240         5278 next;
640             }
641 52327 100       109143 next if $lcWant eq lc $grp[2];
642             } elsif ($fam == 7) {
643 2 100       13 next if IsSameID($$tagInfo{TagID}, $lcWant);
644             } elsif ($fam != 1 and not $$tagInfo{AllowGroup}) {
645 156 100       334 next if $lcWant eq lc $grp[$fam];
646 132 100 100     365 if ($wgAll and not $fam and $allFam0{$lcWant}) {
      100        
647 5 100       16 $writeGroup or $writeGroup = $allFam0{$lcWant};
648 5         10 next;
649             }
650 127         284 next TAG; # wrong group
651             }
652             # handle family 1 groups specially
653 40396 100 66     230360 if ($grp[0] eq 'EXIF' or $grp[0] eq 'SonyIDC' or $wgAll) {
    100 100        
    100 100        
    100          
654 1644 100 100     5657 unless ($ifdName and $lcWant eq lc $ifdName) {
655 1194 100 100     5328 next TAG unless $wgAll and not $fam and $allFam0{$lcWant};
      100        
656 7 100       29 $writeGroup = $allFam0{$lcWant} unless $writeGroup;
657 7         13 next;
658             }
659 450 100 100     1324 next TAG if $wgAll and $allFam0{$lcWant} and $fam;
      100        
660             # can't yet write PreviewIFD tags (except for image)
661 448 50       1111 $lcWant eq 'PreviewIFD' and ++$foundMatch, next TAG;
662 448         1088 $writeGroup = $ifdName; # write to the specified IFD
663             } elsif ($grp[0] eq 'QuickTime') {
664 1765 100       4020 if ($grp[1] eq 'Track#') {
665 16 100 66     95 next TAG unless $movGroup and $lcWant eq lc($movGroup);
666 1         4 $writeGroup = $movGroup;
667             } else {
668 1749         4348 my $grp = $$tagInfo{Table}{WRITE_GROUP};
669 1749 100 100     8836 next TAG unless $grp and $lcWant eq lc $grp;
670 48         142 $writeGroup = $grp;
671             }
672             } elsif ($grp[0] eq 'MIE') {
673 767 100 66     4173 next TAG unless $mieGroup and $lcWant eq lc($mieGroup);
674 2         6 $writeGroup = $mieGroup; # write to specific MIE group
675             # set specific write group with document number if specified
676 2 0 33     17 if ($writeGroup =~ /^MIE\d+$/ and $$tagInfo{Table}{WRITE_GROUP}) {
677 0         0 $writeGroup = $$tagInfo{Table}{WRITE_GROUP};
678 0         0 $writeGroup =~ s/^MIE/$mieGroup/;
679             }
680             } elsif (not $$tagInfo{AllowGroup} or $lcWant !~ /^$$tagInfo{AllowGroup}$/i) {
681             # allow group1 name to be specified
682 36214 100       121316 next TAG unless $lcWant eq lc $grp[1];
683             }
684             }
685 15341 100 66     85470 $writeGroup or $writeGroup = ($$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP} || $grp[0]);
686 15341         31981 $priority = $hiPri; # highest priority since group was specified
687             }
688 36229         50137 ++$foundMatch;
689             # must do a dummy call to the write proc to autoload write package
690             # before checking Writable flag
691 36229         58531 my $table = $$tagInfo{Table};
692 36229         71506 my $writeProc = $$table{WRITE_PROC};
693             # load source table if this was a user-defined table
694 36229 100       80086 if ($$table{SRC_TABLE}) {
695 9         31 my $src = GetTagTable($$table{SRC_TABLE});
696 9 50       24 $writeProc = $$src{WRITE_PROC} unless $writeProc;
697             }
698 36229 50       69572 if ($writeProc) {
699             # make sure module is loaded if the writeProc is a string
700 36229 100       70992 unless (ref $writeProc) {
701 13         35 my $module = $writeProc;
702 13 50       1700 $module =~ s/::\w+$// and eval "require $module";
703             }
704 61     61   576 no strict 'refs';
  61         142  
  61         1071431  
705 36229 100 66     132080 next unless $writeProc and &$writeProc();
706             }
707             # must still check writable flags in case of UserDefined tags
708 36110         76867 my $writable = $$tagInfo{Writable};
709             next unless $writable or ($$table{WRITABLE} and
710 36110 50 66     164458 not defined $writable and not $$tagInfo{SubDirectory});
      66        
      66        
711             # set specific write group (if we didn't already)
712 36109 100 66     108817 if (not $writeGroup or ($translateWriteGroup{$writeGroup} and
      66        
      66        
713             (not $$tagInfo{WriteGroup} or $$tagInfo{WriteGroup} ne 'All')))
714             {
715             # use default write group
716 20864   100     69682 $writeGroup = $$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP};
717             # use group 0 name if no WriteGroup specified
718 20864         62992 my $group0 = $self->GetGroup($tagInfo, 0);
719 20864 100       49386 $writeGroup or $writeGroup = $group0;
720             # get priority for this group
721 20864 100       40699 unless ($priority) {
722 20783 100 100     57567 if ($$tagInfo{Avoid} and $$tagInfo{WriteAlso}) {
723 26         78 $priority = 0;
724             } else {
725 20757         51289 $priority = $$self{WRITE_PRIORITY}{lc($writeGroup)};
726 20757 100       41878 unless ($priority) {
727 3760   100     13269 $priority = $$self{WRITE_PRIORITY}{lc($group0)} || 0;
728             }
729             }
730             }
731             # adjust priority based on Preferred level for this tag
732 20864 100       43815 $priority += $prfTag if $prfTag;
733             }
734             # don't write tag if protected
735 36109         65775 my $prot = $$tagInfo{Protected};
736 36109 100 100     83657 $prot = 1 if $noFlat and defined $$tagInfo{Flat};
737 36109 100       68808 if ($prot) {
738 2350         5017 $prot &= ~$protected;
739 2350 100       5176 if ($prot) {
740 1217         5662 my %lkup = ( 1=>'unsafe', 2=>'protected', 3=>'unsafe and protected');
741 1217         2803 $wasProtected = $lkup{$prot};
742 1217 100       2946 if ($verbose > 1) {
743 1         9 my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup);
744 1         10 print $out "Sorry, $wgrp1:$tag is $wasProtected for writing\n";
745             }
746 1217         4258 next;
747             }
748             }
749             # set priority for this tag
750 34892         113927 $tagPriority{$tagInfo} = $priority;
751             # keep track of highest priority QuickTime tag
752             $highestQT{$lcTag} = $priority if $$table{GROUPS}{0} eq 'QuickTime' and
753 34892 100 100     105645 (not defined $highestQT{$lcTag} or $highestQT{$lcTag} < $priority);
      100        
754 34892 100       100447 if ($priority > $highestPriority{$lcTag}) {
    100          
755 11013         19694 $highestPriority{$lcTag} = $priority;
756 11013         42532 $preferred{$lcTag} = { $tagInfo => 1 };
757 11013 100       39263 $avoid{$lcTag} = $$tagInfo{Avoid} ? 1 : 0;
758             } elsif ($priority == $highestPriority{$lcTag}) {
759             # create all tags with highest priority
760 14682         38507 $preferred{$lcTag}{$tagInfo} = 1;
761 14682 100       36405 ++$avoid{$lcTag} if $$tagInfo{Avoid};
762             }
763 34892 100       67357 if ($$tagInfo{WriteAlso}) {
764             # store WriteAlso tags separately so we can set them first
765 115         2248 push @writeAlsoList, $tagInfo;
766             } else {
767 34777         65756 push @tagInfoList, $tagInfo;
768             }
769             # special case to allow override of XMP WriteGroup
770 34892 100       70287 if ($writeGroup eq 'XMP') {
771 5911   33     22939 my $wg = $$tagInfo{WriteGroup} || $$table{WRITE_GROUP};
772 5911 50       13377 $writeGroup = $wg if $wg;
773             }
774 34892         110144 $writeGroup{$tagInfo} = $writeGroup;
775             }
776             # sort tag info list in reverse order of priority (highest number last)
777             # so we get the highest priority error message in the end
778 4312         17365 @tagInfoList = sort { $tagPriority{$a} <=> $tagPriority{$b} } @tagInfoList;
  56561         105055  
779             # must write any tags which also write other tags first
780 4312 100       11783 unshift @tagInfoList, @writeAlsoList if @writeAlsoList;
781              
782             # check priorities for each set of tags we are writing
783 4312         6973 my $lcTag;
784 4312         15233 foreach $lcTag (keys %preferred) {
785             # don't create tags with priority 0 if group priorities are set
786 10184 100 66     49783 if ($preferred{$lcTag} and $highestPriority{$lcTag} == 0 and
      66        
787 18         81 %{$$self{WRITE_PRIORITY}})
788             {
789 18         62 delete $preferred{$lcTag}
790             }
791             # avoid creating tags with 'Avoid' flag set if there are other alternatives
792 10184 50 66     29922 if ($avoid{$lcTag} and $preferred{$lcTag}) {
793 1538 100       3221 if ($avoid{$lcTag} < scalar(keys %{$preferred{$lcTag}})) {
  1538 100       8041  
794             # just remove the 'Avoid' tags since there are other preferred tags
795 1377         3541 foreach $tagInfo (@tagInfoList) {
796 5707122 100       13742775 next unless $lcTag eq lc $$tagInfo{Name};
797 6203 100       20941 delete $preferred{$lcTag}{$tagInfo} if $$tagInfo{Avoid};
798             }
799             } elsif ($highestPriority{$lcTag} < 1000) {
800             # look for another priority tag to create instead
801 48         110 my $nextHighest = 0;
802 48         96 my @nextBestTags;
803 48         115 foreach $tagInfo (@tagInfoList) {
804 32548 100       63262 next unless $lcTag eq lc $$tagInfo{Name};
805 122 100       386 my $priority = $tagPriority{$tagInfo} or next;
806 121 100       343 next if $priority == $highestPriority{$lcTag};
807 72 50       215 next if $priority < $nextHighest;
808 72         130 my $permanent = $$tagInfo{Permanent};
809 72 50       204 $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent;
810 72 100 100     266 next if $$tagInfo{Avoid} or $permanent;
811 67 100       198 next if $writeGroup{$tagInfo} eq 'MakerNotes';
812 23 100       69 if ($nextHighest < $priority) {
813 18         33 $nextHighest = $priority;
814 18         47 undef @nextBestTags;
815             }
816 23         60 push @nextBestTags, $tagInfo;
817             }
818 48 100       182 if (@nextBestTags) {
819             # change our preferred tags to the next best tags
820 13         34 delete $preferred{$lcTag};
821 13         31 foreach $tagInfo (@nextBestTags) {
822 14         75 $preferred{$lcTag}{$tagInfo} = 1;
823             }
824             }
825             }
826             }
827             }
828             #
829             # generate new value hash for each tag
830             #
831 4312         10279 my ($prioritySet, $createGroups, %alsoWrote);
832              
833 4312         9755 delete $$self{CHECK_WARN}; # reset CHECK_PROC warnings
834              
835             # loop through all valid tags to find the one(s) to write
836 4312         8293 foreach $tagInfo (@tagInfoList) {
837 34876 100       104930 next if $alsoWrote{$tagInfo}; # don't rewrite tags we already wrote
838             # only process List or non-List tags if specified
839 34867 100 100     82807 next if defined $listOnly and ($listOnly xor $$tagInfo{List});
      100        
840 34646         47424 my $noConv;
841 34646         88034 my $writeGroup = $writeGroup{$tagInfo};
842 34646         82485 my $permanent = $$tagInfo{Permanent};
843 34646 100       111492 $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent;
844 34646 100 66     99184 $writeGroup eq 'MakerNotes' and $permanent = 1 unless defined $permanent;
845 34646         102410 my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup);
846 34646         77362 $tag = $$tagInfo{Name}; # get tag name for warnings
847 34646         70505 my $lcTag = lc $tag;
848 34646   100     101059 my $pref = $preferred{$lcTag} || { };
849             # don't write Avoid-ed tags with side effect unless preferred
850 34646 100 100     140699 next if not $$pref{$tagInfo} and $$tagInfo{Avoid} and $$tagInfo{WriteAlso};
      100        
851 34620         63478 my $shift = $options{Shift};
852 34620         55012 my $addValue = $options{AddValue};
853 34620 100       70124 if (defined $shift) {
854             # (can't currently shift list-type tags)
855 169         267 my $shiftable;
856 169 50       503 if ($$tagInfo{List}) {
857 0         0 $shiftable = ''; # can add/delete but not shift
858             } else {
859 169         317 $shiftable = $$tagInfo{Shift};
860 169 100       454 unless ($shift) {
861             # set shift according to AddValue/DelValue
862 24 50       50 $shift = 1 if $addValue;
863             # can shift a date/time with -=, but this is
864             # a conditional delete operation for other tags
865 24 0 33     86 $shift = -1 if $options{DelValue} and defined $shiftable and $shiftable eq 'Time';
      33        
866             }
867 169 50 33     1041 if ($shift and (not defined $value or not length $value)) {
      33        
868             # (now allow -= to be used for shiftable tag - v8.05)
869             #$err = "No value for time shift of $wgrp1:$tag";
870             #$verbose > 2 and print $out "$err\n";
871             #next;
872 0         0 undef $shift;
873             }
874             }
875             # can't shift List-type tag
876 169 0 66     612 if ((defined $shiftable and not $shiftable) and
      0        
      33        
877             # and don't try to conditionally delete if Shift is "0"
878             ($shift or ($shiftable eq '0' and $options{DelValue})))
879             {
880 0         0 $err = "$wgrp1:$tag is not shiftable";
881 0 0       0 $verbose and print $out "$err\n";
882 0         0 next;
883             }
884             }
885 34620         61609 my $val = $value;
886 34620 100 33     91640 if (defined $val) {
    100          
    50          
887             # check to make sure this is a List or Shift tag if adding
888 22299 100 100     55103 if ($addValue and not ($shift or $$tagInfo{List})) {
      100        
889 9 50       26 if ($addValue eq '2') {
890 0         0 undef $addValue; # quietly reset this option
891             } else {
892 9         18 $err = "Can't add $wgrp1:$tag (not a List type)";
893 9 50       19 $verbose > 2 and print $out "$err\n";
894 9         23 next;
895             }
896             }
897 22290 100 66     125745 if ($shift) {
    100 100        
    100          
898 169 100 66     699 if ($$tagInfo{Shift} and $$tagInfo{Shift} eq 'Time') {
    100          
899             # add '+' or '-' prefix to indicate shift direction
900 51 100       178 $val = ($shift > 0 ? '+' : '-') . $val;
901             # check the shift for validity
902 51         2580 require 'Image/ExifTool/Shift.pl';
903 51         259 my $err2 = CheckShift($$tagInfo{Shift}, $val);
904 51 50       162 if ($err2) {
905 0         0 $err = "$err2 for $wgrp1:$tag";
906 0 0       0 $verbose > 2 and print $out "$err\n";
907 0         0 next;
908             }
909             } elsif (IsFloat($val)) {
910 114         263 $val *= $shift;
911             } else {
912 4         17 $err = "Shift value for $wgrp1:$tag is not a number";
913 4 50       18 $verbose > 2 and print $out "$err\n";
914 4         19 next;
915             }
916 165         284 $noConv = 1; # no conversions if shifting tag
917             } elsif (not length $val and $options{DelValue}) {
918 39         54 $noConv = 1; # no conversions for deleting empty value
919             } elsif (ref $val eq 'HASH' and not $$tagInfo{Struct}) {
920 2         8 $err = "Can't write a structure to $wgrp1:$tag";
921 2 50       12 $verbose > 2 and print $out "$err\n";
922 2         9 next;
923             }
924             } elsif ($permanent) {
925 8125 100       20636 return 0 if $options{IgnorePermanent};
926             # can't delete permanent tags, so set them to DelValue or empty string instead
927 8121 100       14840 if (defined $$tagInfo{DelValue}) {
928 33         121 $val = $$tagInfo{DelValue};
929 33         56 $noConv = 1; # DelValue is the raw value, so no conversion necessary
930             } else {
931 8088         11544 $val = '';
932             }
933             } elsif ($addValue or $options{DelValue}) {
934 0         0 $err = "No value to add or delete in $wgrp1:$tag";
935 0 0       0 $verbose > 2 and print $out "$err\n";
936 0         0 next;
937             } else {
938 4196 100       9871 if ($$tagInfo{DelCheck}) {
939             #### eval DelCheck ($self, $tagInfo, $wantGroup)
940 7         842 my $err2 = eval $$tagInfo{DelCheck};
941 7 50       47 $@ and warn($@), $err2 = 'Error evaluating DelCheck';
942 7 50       27 if (defined $err2) {
943             # (allow other tags to be set using DelCheck as a hook)
944 7 100       161 $err2 or goto WriteAlso; # GOTO!
945 3 50       17 $err2 .= ' for' unless $err2 =~ /delete$/;
946 3         7 $err = "$err2 $wgrp1:$tag";
947 3 50       9 $verbose > 2 and print $out "$err\n";
948 3         9 next;
949             }
950             }
951             # set group delete flag if this tag represents an entire group
952 4189 100 66     10675 if ($$tagInfo{DelGroup} and not $options{DelValue}) {
953 3         12 my @del = ( $tag );
954 3         15 $$self{DEL_GROUP}{$tag} = 1;
955             # delete extra groups if necessary
956 3 50       15 if ($delMore{$tag}) {
957 0         0 $$self{DEL_GROUP}{$_} = 1, push(@del,$_) foreach @{$delMore{$tag}};
  0         0  
958             }
959             # remove all of this group from previous new values
960 3         21 $self->RemoveNewValuesForGroup($tag);
961 3 50       13 $verbose and print $out " Deleting tags in: @del\n";
962 3         24 ++$numSet;
963 3         16 next;
964             }
965 4186         6997 $noConv = 1; # value is not defined, so don't do conversion
966             }
967             # apply inverse PrintConv and ValueConv conversions
968             # save ValueConv setting for use in ConvInv()
969 34591 100       76547 unless ($noConv) {
970             # set default conversion type used by ConvInv() and CHECK_PROC routines
971 30168         72640 $$self{ConvType} = $convType;
972 30168         48204 my $e;
973 30168         105369 ($val,$e) = $self->ConvInv($val,$tagInfo,$tag,$wgrp1,$$self{ConvType},$wantGroup);
974 30168 100       73534 if (defined $e) {
975             # empty error string causes error to be ignored without setting the value
976 9920 100       19459 $e or goto WriteAlso; # GOTO!
977 9902         17183 $err = $e;
978             }
979             }
980 34573 100 100     97442 if (not defined $val and defined $value) {
981             # if value conversion failed, we must still add a NEW_VALUE
982             # entry for this tag it it was a DelValue
983 3047 50       14275 next unless $options{DelValue};
984 0         0 $val = 'xxx never delete xxx';
985             }
986 31526 100       90884 $$self{NEW_VALUE} or $$self{NEW_VALUE} = { };
987 31526 100       84446 if ($options{Replace}) {
988             # delete the previous new value
989 14376         75780 $self->GetNewValueHash($tagInfo, $writeGroup, 'delete', $options{ProtectSaved});
990             # also delete related tag previous new values
991 14376 100       45536 if ($$tagInfo{WriteAlso}) {
992 25         175 $$self{INDENT2} = '+';
993 25         88 my ($wgrp, $wtag);
994 25 100 66     210 if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) {
      66        
995 6         18 $wgrp = $writeGroup . ':';
996             } else {
997 19         51 $wgrp = '';
998             }
999 25         65 foreach $wtag (sort keys %{$$tagInfo{WriteAlso}}) {
  25         233  
1000 91         509 my ($n,$e) = $self->SetNewValue($wgrp . $wtag, undef, Replace=>2);
1001 91         265 $numSet += $n;
1002             }
1003 25         143 $$self{INDENT2} = '';
1004             }
1005 14376 100       38715 $options{Replace} == 2 and ++$numSet, next;
1006             }
1007              
1008 31233 100 33     76008 if (defined $val) {
    100          
    50          
1009             # we are editing this tag, so create a NEW_VALUE hash entry
1010             my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create',
1011 20415   66     91517 $options{ProtectSaved}, ($options{DelValue} and not $shift));
1012             # ignore new values protected with ProtectSaved
1013 20415 50       53080 $nvHash or ++$numSet, next; # (increment $numSet to avoid warning)
1014 20415 100 100     58901 $$nvHash{NoReplace} = 1 if $$tagInfo{List} and not $options{Replace};
1015 20415         43688 $$nvHash{WantGroup} = $wantGroup;
1016 20415 100       44416 $$nvHash{EditOnly} = 1 if $editOnly;
1017             # save maker note fixup information if writing maker notes
1018 20415 100       50574 $$nvHash{MAKER_NOTE_FIXUP} = $options{Fixup} if $$tagInfo{MakerNotes};
1019 20415 100 100     107276 if ($createOnly) { # create only (never edit)
    100 100        
1020             # empty item in DelValue list to never edit existing value
1021 49         167 $$nvHash{DelValue} = [ '' ];
1022 49         101 $$nvHash{CreateOnly} = 1;
1023             } elsif ($options{DelValue} or $addValue or $shift) {
1024             # flag any AddValue or DelValue by creating the DelValue list
1025 236 100       925 $$nvHash{DelValue} or $$nvHash{DelValue} = [ ];
1026 236 100       503 if ($shift) {
    100          
1027             # add shift value to list
1028 165         384 $$nvHash{Shift} = $val;
1029             } elsif ($options{DelValue}) {
1030             # don't create if we are replacing a specific value
1031 58 100 100     209 $$nvHash{IsCreating} = 0 unless $val eq '' or $$tagInfo{List};
1032             # add delete value to list
1033 58 100       84 push @{$$nvHash{DelValue}}, ref $val eq 'ARRAY' ? @$val : $val;
  58         185  
1034 58 50       159 if ($verbose > 1) {
1035 0 0       0 my $verb = $permanent ? 'Replacing' : 'Deleting';
1036 0 0       0 my $fromList = $$tagInfo{List} ? ' from list' : '';
1037 0 0       0 my @vals = (ref $val eq 'ARRAY' ? @$val : $val);
1038 0         0 foreach (@vals) {
1039 0 0       0 if (ref $_ eq 'HASH') {
1040 0         0 require 'Image/ExifTool/XMPStruct.pl';
1041 0         0 $_ = Image::ExifTool::XMP::SerializeStruct($self, $_);
1042             }
1043 0         0 print $out "$$self{INDENT2}$verb $wgrp1:$tag$fromList if value is '${_}'\n";
1044             }
1045             }
1046             }
1047             }
1048             # set priority flag to add only the high priority info
1049             # (will only create the priority tag if it doesn't exist,
1050             # others get changed only if they already exist)
1051 20415 100       64695 my $prf = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED};
1052             # hack to prefer only a single tag in the QuickTime group
1053 20415 100       68911 if ($$tagInfo{Table}{GROUPS}{0} eq 'QuickTime') {
1054 793 100       3561 $prf = 0 if $tagPriority{$tagInfo} < $highestQT{$lcTag};
1055             }
1056 20415 100 100     77448 if ($$pref{$tagInfo} or $prf) {
1057 9583 100 100     49726 if ($permanent or $shift) {
    100 100        
      66        
      100        
      100        
1058             # don't create permanent or Shift-ed tag but define IsCreating
1059             # so we know that it is the preferred tag
1060 5786         19611 $$nvHash{IsCreating} = 0;
1061             } elsif (($$tagInfo{List} and not $options{DelValue}) or
1062             not ($$nvHash{DelValue} and @{$$nvHash{DelValue}}) or
1063             # also create tag if any DelValue value is empty ('')
1064 58         349 grep(/^$/,@{$$nvHash{DelValue}}))
1065             {
1066 3783 100       16906 $$nvHash{IsCreating} = $editOnly ? 0 : ($editGroup ? 2 : 1);
    100          
1067             # add to hash of groups where this tag is being created
1068 3783 100 100     15821 $createGroups or $createGroups = $options{CreateGroups} || { };
1069 3783         17193 $$createGroups{$self->GetGroup($tagInfo, 0)} = 1;
1070 3783         12680 $$nvHash{CreateGroups} = $createGroups;
1071             }
1072             }
1073 20415 100       57287 if ($$nvHash{IsCreating}) {
    100          
1074 3772 100       6090 if (%{$$self{DEL_GROUP}}) {
  3772         13692  
1075 177         347 my ($grp, @grps);
1076 177         269 foreach $grp (keys %{$$self{DEL_GROUP}}) {
  177         3490  
1077 11060 100       19336 next if $$self{DEL_GROUP}{$grp} == 2;
1078             # set flag indicating tags were written after this group was deleted
1079 409         618 $$self{DEL_GROUP}{$grp} = 2;
1080 409         736 push @grps, $grp;
1081             }
1082 177 100 66     1348 if ($verbose > 1 and @grps) {
1083 1         4 @grps = sort @grps;
1084 1         8 print $out " Writing new tags after deleting groups: @grps\n";
1085             }
1086             }
1087             } elsif ($createOnly) {
1088 22 100       66 $noCreate = $permanent ? 'permanent' : ($$tagInfo{Avoid} ? 'avoided' : '');
    100          
1089 22 50       60 $noCreate or $noCreate = $shift ? 'shifting' : 'not preferred';
    100          
1090 22 50       65 $verbose > 2 and print $out "Not creating $wgrp1:$tag ($noCreate)\n";
1091 22         67 next; # nothing to do (not creating and not editing)
1092             }
1093 20393 100 100     77190 if ($shift or not $options{DelValue}) {
1094 20335 100       81906 $$nvHash{Value} or $$nvHash{Value} = [ ];
1095 20335 100 33     47957 if (not $$tagInfo{List}) {
    50          
1096             # not a List tag -- overwrite existing value
1097 19806         50811 $$nvHash{Value}[0] = $val;
1098 0         0 } elsif (defined $$nvHash{AddBefore} and @{$$nvHash{Value}} >= $$nvHash{AddBefore}) {
1099             # values from a later argument have been added (ie. Replace=0)
1100             # to this list, so the new values should come before these
1101 0 0       0 splice @{$$nvHash{Value}}, -$$nvHash{AddBefore}, 0, ref $val eq 'ARRAY' ? @$val : $val;
  0         0  
1102             } else {
1103             # add at end of existing list
1104 529 100       914 push @{$$nvHash{Value}}, ref $val eq 'ARRAY' ? @$val : $val;
  529         2263  
1105             }
1106 20335 100       50679 if ($verbose > 1) {
1107 26         53 my $ifExists;
1108 26 50       111 if ($$tagInfo{IsComposite}) {
1109             # (composite tags don't technically exist in the file)
1110 0 0       0 if ($$tagInfo{WriteAlso}) {
1111 0         0 $ifExists = ' (+' . join(',+',sort keys %{$$tagInfo{WriteAlso}}) . '):';
  0         0  
1112             } else {
1113 0         0 $ifExists = '';
1114             }
1115             } else {
1116             $ifExists = $$nvHash{IsCreating} ? ( $createOnly ?
1117             ($$nvHash{IsCreating} == 2 ?
1118             " if $writeGroup exists and tag doesn't" :
1119             " if tag doesn't exist") :
1120             ($$nvHash{IsCreating} == 2 ? " if $writeGroup exists" : '')) :
1121 26 0 33     253 (($$nvHash{DelValue} and @{$$nvHash{DelValue}}) ?
    50          
    50          
    50          
    100          
1122             ' if tag was deleted' : ' if tag exists');
1123             }
1124 26 50       102 my $verb = ($shift ? 'Shifting' : ($addValue ? 'Adding' : 'Writing'));
    50          
1125 26         169 print $out "$$self{INDENT2}$verb $wgrp1:$tag$ifExists\n";
1126             }
1127             }
1128             } elsif ($permanent) {
1129 6749         11035 $err = "Can't delete Permanent tag $wgrp1:$tag";
1130 6749 50       12748 $verbose > 1 and print $out "$err\n";
1131 6749         18600 next;
1132             } elsif ($addValue or $options{DelValue}) {
1133 0 0       0 $verbose > 1 and print $out "Adding/Deleting nothing does nothing\n";
1134 0         0 next;
1135             } else {
1136             # create empty new value hash entry to delete this tag
1137 4069         11994 $self->GetNewValueHash($tagInfo, $writeGroup, 'delete');
1138 4069         7634 my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create');
1139 4069         8770 $$nvHash{WantGroup} = $wantGroup;
1140 4069 50       9455 $verbose > 1 and print $out "$$self{INDENT2}Deleting $wgrp1:$tag\n";
1141             }
1142 24462 100       51316 $$setTags{$tagInfo} = 1 if $setTags;
1143 24462 100       72109 $prioritySet = 1 if $$pref{$tagInfo};
1144 24484         36703 WriteAlso:
1145             ++$numSet;
1146             # also write related tags
1147 24484         42588 my $writeAlso = $$tagInfo{WriteAlso};
1148 24484 100       93189 if ($writeAlso) {
1149 76         268 $$self{INDENT2} = '+'; # indicate related tag with a leading "+"
1150 76         254 my ($wgrp, $wtag, $n);
1151 76 100 66     769 if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) {
      66        
1152 46         166 $wgrp = $writeGroup . ':';
1153             } else {
1154 30         85 $wgrp = '';
1155             }
1156 76         613 local $SIG{'__WARN__'} = \&SetWarning;
1157 76         557 foreach $wtag (sort keys %$writeAlso) {
1158             my %opts = (
1159             Type => 'ValueConv',
1160             Protected => $protected | 0x02,
1161             AddValue => $addValue,
1162             DelValue => $options{DelValue},
1163             Shift => $options{Shift},
1164             Replace => $options{Replace}, # handle lists properly
1165 243         2352 CreateGroups=> $createGroups,
1166             SetTags => \%alsoWrote, # remember tags already written
1167             );
1168 243         530 undef $evalWarning;
1169             #### eval WriteAlso ($val,%opts)
1170 243         26945 my $v = eval $$writeAlso{$wtag};
1171             # we wanted to do the eval in case there are side effect, but we
1172             # don't want to write a value for a tag that is being deleted:
1173 243 100       1325 undef $v unless defined $val;
1174 243 50       794 $@ and $evalWarning = $@;
1175 243 50       703 unless ($evalWarning) {
1176 243         2191 ($n,$evalWarning) = $self->SetNewValue($wgrp . $wtag, $v, %opts);
1177 243         846 $numSet += $n;
1178             # count this as being set if any related tag is set
1179 243 100 100     1659 $prioritySet = 1 if $n and $$pref{$tagInfo};
1180             }
1181 243 100 66     1346 if ($evalWarning and (not $err or $verbose > 2)) {
      66        
1182 9         41 my $str = CleanWarning();
1183 9 50       33 if ($str) {
1184 9 50       67 $str .= " for $wtag" unless $str =~ / for [-\w:]+$/;
1185 9         27 $str .= " in $wgrp1:$tag (WriteAlso)";
1186 9 50       34 $err or $err = $str;
1187 9 50       56 print $out "$str\n" if $verbose > 2;
1188             }
1189             }
1190             }
1191 76         793 $$self{INDENT2} = '';
1192             }
1193             }
1194             # print warning if we couldn't set our priority tag
1195 4308 100 100     28780 if (defined $err and not $prioritySet) {
    100 66        
    50          
    100          
1196 86 50 33     501 warn "$err\n" if $err and not wantarray;
1197             } elsif (not $numSet) {
1198 622 100       2314 my $pre = $wantGroup ? $wantGroup . ':' : '';
1199 622 100       1697 if ($wasProtected) {
    100          
1200 363         690 $verbose = 0; # we already printed this verbose message
1201 363 100 100     2157 unless ($options{Replace} and $options{Replace} == 2) {
1202 351         967 $err = "Sorry, $pre$tag is $wasProtected for writing";
1203             }
1204             } elsif (not $listOnly) {
1205 252 50 33     2096 if ($origTag =~ /[?*]/) {
    50          
    50          
    50          
1206 0 0       0 if ($noCreate) {
    0          
1207 0         0 $err = "No tags matching 'pre${origTag}' will be created";
1208 0         0 $verbose = 0; # (already printed)
1209             } elsif ($foundMatch) {
1210 0         0 $err = "Sorry, no writable tags matching '$pre${origTag}'";
1211             } else {
1212 0         0 $err = "No matching tags for '$pre${origTag}'";
1213             }
1214             } elsif ($noCreate) {
1215 0         0 $err = "Not creating $pre$tag";
1216 0         0 $verbose = 0; # (already printed)
1217             } elsif ($foundMatch) {
1218 0         0 $err = "Sorry, $pre$tag is not writable";
1219             } elsif ($wantGroup and @matchingTags) {
1220 252         608 $err = "Sorry, $pre$tag doesn't exist or isn't writable";
1221             } else {
1222 0         0 $err = "Tag '$pre${tag}' is not defined";
1223             }
1224             }
1225 622 100       1585 if ($err) {
1226 603 50       1683 $verbose > 2 and print $out "$err\n";
1227 603 50       1625 warn "$err\n" unless wantarray;
1228             }
1229             } elsif ($$self{CHECK_WARN}) {
1230 0         0 $err = $$self{CHECK_WARN};
1231 0 0       0 $verbose > 2 and print $out "$err\n";
1232             } elsif ($err and not $verbose) {
1233 451         1038 undef $err;
1234             }
1235 4308 100       63317 return ($numSet, $err) if wantarray;
1236 471         41490 return $numSet;
1237             }
1238              
1239             #------------------------------------------------------------------------------
1240             # set new values from information in specified file
1241             # Inputs: 0) ExifTool object reference, 1) source file name or reference, etc,
1242             # or ExifTool ref to use already-extracted tags from an ExifTool object,
1243             # 2-N) List of tags to set (or all if none specified), or reference(s) to
1244             # hash for options to pass to SetNewValue. The Replace option defaults
1245             # to 1 for SetNewValuesFromFile -- set this to 0 to allow multiple tags
1246             # to be copied to a list
1247             # Returns: Hash of information set successfully (includes Warning or Error messages)
1248             # Notes: Tag names may contain a group prefix, a leading '-' to exclude from copy,
1249             # and/or a trailing '#' to copy the ValueConv value. The tag name '*' may
1250             # be used to represent all tags in a group. An optional destination tag
1251             # may be specified with '>DSTTAG' ('DSTTAG
1252             # case the source tag may also be an expression involving tag names).
1253             # Simple assignments are also allowed: 'DSTTAG[#][+-][^]=[string]'
1254             sub SetNewValuesFromFile($$;@)
1255             {
1256 59     59 1 2408 local $_;
1257 59         293 my ($self, $srcFile, @setTags) = @_;
1258 59         194 my ($srcExifTool, $key, $tag, @exclude, @reqTags, $info);
1259              
1260             # get initial SetNewValuesFromFile options
1261 59         302 my %opts = ( Replace => 1 ); # replace existing list items by default
1262 59         331 while (ref $setTags[0] eq 'HASH') {
1263 1         3 $_ = shift @setTags;
1264 1         4 foreach $key (keys %$_) {
1265 1         4 $opts{$key} = $$_{$key};
1266             }
1267             }
1268             # expand shortcuts
1269 59 100       556 @setTags and ExpandShortcuts(\@setTags);
1270             # set options for our extraction tool
1271 59         648 my $options = $$self{OPTIONS};
1272 59         214 my $printConv = $$options{PrintConv};
1273 59 50       319 if ($opts{Type}) {
1274             # save source type separately because it may be different than dst Type
1275 0         0 $opts{SrcType} = $opts{Type};
1276             # override PrintConv option with initial Type if given
1277 0 0       0 $printConv = ($opts{Type} eq 'PrintConv' ? 1 : 0);
1278             }
1279 59 100       258 my $srcType = $printConv ? 'PrintConv' : 'ValueConv';
1280 59 50       307 my $structOpt = defined $$options{Struct} ? $$options{Struct} : 2;
1281              
1282 59 50 33     379 if (ref $srcFile and UNIVERSAL::isa($srcFile,'Image::ExifTool')) {
1283 0         0 $srcExifTool = $srcFile;
1284 0         0 $info = $srcExifTool->GetInfo({ PrintConv => $printConv });
1285             } else {
1286 59         424 $srcExifTool = Image::ExifTool->new;
1287 59         344 $srcExifTool->Options(PrintConv => $printConv);
1288             # set flag to indicate we are being called from inside SetNewValuesFromFile()
1289 59         213 $$srcExifTool{TAGS_FROM_FILE} = 1;
1290             # synchronize and increment the file sequence number
1291 59         307 $$srcExifTool{FILE_SEQUENCE} = $$self{FILE_SEQUENCE}++;
1292             # copy both structured and flattened tags by default (but flattened tags are "unsafe")
1293             # copy structures only if no tags specified (since flattened tags are "unsafe")
1294 59 100 66     585 $structOpt = 1 if $structOpt eq '2' and not @setTags;
1295             # +------------------------------------------+
1296             # ! DON'T FORGET!! Must consider each new !
1297             # ! option to decide how it is handled here. !
1298             # +------------------------------------------+
1299 59         359 foreach (qw(ByteUnit Charset CharsetEXIF CharsetFileName CharsetID3 CharsetIPTC
1300             CharsetPhotoshop Composite DateFormat Debug EncodeHangs Escape
1301             ExtendedXMP ExtractEmbedded FastScan Filter FixBase Geolocation
1302             GeolocAltNames GeolocFeature GeolocMinPop GeolocMaxDist
1303             GlobalTimeShift GPSQuadrant HexTagIDs IgnoreGroups IgnoreMinorErrors
1304             IgnoreTags ImageHashType KeepUTCTime Lang LargeFileSupport
1305             LigoGPSScale ListItem ListSep MDItemTags MissingTagValue NoPDFList
1306             NoWarning Password PrintConv QuickTimeUTC RequestTags SaveFormat
1307             SavePath ScanForXMP StructFormat SystemTags SystemTimeRes TimeZone
1308             Unknown UserParam Validate WindowsLongPath WindowsWideFile XAttrTags
1309             XMPAutoConv))
1310             {
1311 3363         9602 $srcExifTool->Options($_ => $$options{$_});
1312             }
1313             $srcExifTool->Options(
1314             Binary => 1,
1315             CoordFormat => $$options{CoordFormat} || '%d %d %.8f', # copy coordinates at high resolution unless otherwise specified
1316             Duplicates => 1,
1317             # Exclude (set below)
1318             LimitLongValues => 10000000, # (10 MB)
1319             List => 1,
1320             MakerNotes => $$options{FastScan} && $$options{FastScan} > 1 ? undef : 1,
1321             RequestAll => $$options{RequestAll} || 1, # (must request all because reqTags doesn't cover wildcards)
1322 59 50 50     1523 StrictDate => defined $$options{StrictDate} ? $$options{StrictDate} : 1,
    50 33        
      50        
1323             Struct => $structOpt,
1324             );
1325             # reset Geolocation option if we aren't copying any geolocation tags
1326 59 50 33     395 if ($$options{Geolocation} and not grep /\bGeolocation/i, @setTags) {
1327 0         0 $self->VPrint(0, '(resetting unnecessary Geolocation option)');
1328 0         0 $$srcExifTool{OPTIONS}{Geolocation} = undef;
1329             }
1330 59         255 $$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
1331 59         280 $$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL};
1332 59         182 foreach $tag (@setTags) {
1333 71 100       240 next if ref $tag;
1334             # avoid extracting tags that are excluded
1335 70 100       299 $tag =~ /^-(.*)/ and push(@exclude, $1), next;
1336             # add specified tags to list of requested tags
1337 62         175 $_ = $tag;
1338 62 100       755 if (/(.+?)\s*(>|<)\s*(.+)/) {
1339 31 100       176 if ($2 eq '>') {
1340 10         38 $_ = $1;
1341             } else {
1342 21         66 $_ = $3;
1343 21 100       161 /\$/ and push(@reqTags, /\$\{?(?:[-\w]+:)*([-\w?*]+)/g), next;
1344             }
1345             }
1346 54 50       554 push @reqTags, $2 if /(^|:)([-\w?*]+)#?$/;
1347             }
1348 59 100       238 if (@exclude) {
1349 7         73 ExpandShortcuts(\@exclude, 1);
1350 7         39 $srcExifTool->Options(Exclude => \@exclude);
1351             }
1352 59 100       372 $srcExifTool->Options(RequestTags => \@reqTags) if @reqTags;
1353             # get all tags from source file (including MakerNotes block)
1354 59         327 $info = $srcExifTool->ImageInfo($srcFile);
1355             }
1356             # (allow processing to continue if we have alternate files that may have been read OK)
1357 59 0 33     518 return $info if $$info{Error} and $$info{Error} eq 'Error opening file' and not $$self{ALT_EXIFTOOL};
      33        
1358 59         285 delete $$srcExifTool{VALUE}{Error}; # delete so we can check this later
1359              
1360             # sort tags in file order with priority tags last
1361 59         193 my (@tags, @prio);
1362 59         2215 foreach (sort { $$srcExifTool{FILE_ORDER}{$a} <=> $$srcExifTool{FILE_ORDER}{$b} } keys %$info) {
  58033         94589  
1363 9141 100       14733 if (/ /) {
1364 768         1163 push @tags, $_;
1365             } else {
1366 8373         13058 push @prio, $_;
1367             }
1368             }
1369 59         1738 push @tags, @prio;
1370             #
1371             # simply transfer all tags from source image if no tags specified
1372             #
1373 59 100       304 unless (@setTags) {
1374             # transfer maker note information to this object
1375 15         94 $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER};
1376 15         52 my $tagExtra = $$srcExifTool{TAG_EXTRA};
1377 15         44 foreach $tag (@tags) {
1378             # don't try to set errors or warnings
1379 2651 100       11159 next if $tag =~ /^(Error|Warning)\b/;
1380             # get appropriate value type if necessary
1381 2647 50 33     9417 if ($opts{SrcType} and $opts{SrcType} ne $srcType) {
1382 0         0 $$info{$tag} = $srcExifTool->GetValue($tag, $opts{SrcType});
1383             }
1384 2647         7951 my $fixup = $$tagExtra{$tag}{Fixup};
1385 2647 100       5824 $opts{Fixup} = $fixup if $fixup;
1386             # set value for this tag
1387 2647         12764 my ($n, $e) = $self->SetNewValue($tag, $$info{$tag}, %opts);
1388             # delete this tag if we couldn't set it
1389 2647 100       10361 $n or delete $$info{$tag};
1390 2647 100       9089 delete $opts{Fixup} if $fixup;
1391             }
1392 15         1934 return $info;
1393             }
1394             #
1395             # transfer specified tags in the proper order
1396             #
1397             # 1) loop through input list of tags to set, and build @setList
1398 44         1052 my (@setList, $set, %setMatches, $t, %altFiles);
1399 44         134 my $assign = 0;
1400 44         193 foreach $t (@setTags) {
1401 71 100       288 if (ref $t eq 'HASH') {
1402             # update current options
1403 1         6 foreach $key (keys %$t) {
1404 1         5 $opts{$key} = $$t{$key};
1405             }
1406 1         6 next;
1407             }
1408             # make a copy of the current options for this setTag
1409             # (also use this hash to store expression and wildcard flags, EXPR and WILD)
1410 70         350 my $opts = { %opts };
1411 70         257 $tag = lc $t; # change tag/group names to all lower case
1412 70         191 my (@fg, $grp, $dst, $dstGrp, $dstTag, $isExclude);
1413             # handle redirection to another tag
1414 70 100       917 if ($tag =~ /(.+?)\s*(>|<|=)(\s*)(.*)/) {
1415 31         78 $dstGrp = '';
1416 31         73 my ($opt, $op, $spc);
1417 31 100       147 if ($2 eq '>') {
1418 10         60 ($tag, $dstTag) = ($1, $4);
1419             # flag add and delete (eg. '+<' and '-<') redirections
1420 10 50 33     104 $opt = $1 if $tag =~ s/\s*([-+])$// or $dstTag =~ s/^([-+])\s*//;
1421             } else {
1422 21         163 ($dstTag, $op, $spc, $tag) = ($1, $2, $3, $4);
1423 21 50       136 $opt = $1 if $dstTag =~ s/\s*([-+])$//;
1424 21 50       156 if ($op eq '=') {
    100          
1425             # simple assignment ($tag will be the new value)
1426 0         0 $tag = $spc . $tag;
1427 0 0 0     0 undef $tag unless $dstTag =~ s/\^$// or length $tag;
1428 0         0 $$opts{ASSIGN} = ++$assign;
1429             } elsif ($tag =~ /\$/) { # handle expressions
1430 8         26 $tag = $t; # restore original case
1431             # recover leading whitespace (except for initial single space)
1432 8         83 $tag =~ s/(.+?)\s*(>|<) ?//;
1433 8         42 $$opts{EXPR} = 1; # flag this expression
1434             } else {
1435             # (not sure why this is here because sign should be before '<')
1436             # (--> allows "<+" or "<-", which is an undocumented feature)
1437 13 50       65 $opt = $1 if $tag =~ s/^([-+])\s*//;
1438             }
1439             }
1440 31 100       127 $$opts{Replace} = 0 if $dstTag =~ s/^\+//;
1441             # validate tag name(s)
1442 31 50 66     314 unless ($$opts{EXPR} or $$opts{ASSIGN} or ValidTagName($tag)) {
      33        
1443 0         0 $self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value");
1444 0         0 next;
1445             }
1446 31 50       129 ValidTagName($dstTag) or $self->Warn("Invalid tag name '${dstTag}'"), next;
1447             # translate '+' and '-' to appropriate SetNewValue option
1448 31 50       118 if ($opt) {
1449 0         0 $$opts{{ '+' => 'AddValue', '-' => 'DelValue' }->{$opt}} = 1;
1450 0         0 $$opts{Shift} = 0; # shift if shiftable
1451             }
1452 31 100       194 ($dstGrp, $dstTag) = ($1, $2) if $dstTag =~ /(.*):(.+)/;
1453             # ValueConv may be specified separately on the destination with '#'
1454 31 50       125 $$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//;
1455             # replace tag name of 'all' with '*'
1456 31 100       113 $dstTag = '*' if $dstTag eq 'all';
1457             } else {
1458 39 50       197 $$opts{Replace} = 0 if $tag =~ s/^\+//;
1459             }
1460 70 50 66     504 unless ($$opts{EXPR} or $$opts{ASSIGN}) {
1461 62         222 $isExclude = ($tag =~ s/^-//);
1462 62 100       352 if ($tag =~ /(.*):(.+)/) {
1463 34         154 ($grp, $tag) = ($1, $2);
1464 34         147 foreach (split /:/, $grp) {
1465             # save family/groups in list (ignoring 'all' and '*')
1466 35 50 33     293 next unless length($_) and /^(\d+)?(.*)/;
1467 35         122 my ($f, $g) = ($1, $2);
1468 35 50 33     281 $f = 7 if (not $f or $f eq '7') and $g =~ s/^ID-//i;
      33        
1469 35 50 33     154 if ($g =~ /^file\d+$/i and (not $f or $f eq '8')) {
      66        
1470 3         8 $f = 8;
1471 3         12 my $g8 = ucfirst $g;
1472 3 50       22 if ($$srcExifTool{ALT_EXIFTOOL}{$g8}) {
1473 3         13 $$opts{GROUP8} = $g8;
1474 3 100       15 $altFiles{$g8} or $altFiles{$g8} = [ ];
1475             # save list of requested tags for this alternate ExifTool object
1476 3         11 push @{$altFiles{$g8}}, "$grp:$tag";
  3         16  
1477             }
1478             }
1479 35 100 100     288 push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all';
1480             }
1481             }
1482             # allow ValueConv to be specified by a '#' on the tag name
1483 62 50       253 if ($tag =~ s/#$//) {
1484 0         0 $$opts{SrcType} = 'ValueConv';
1485 0 0       0 $$opts{Type} = 'ValueConv' unless $dstTag;
1486             }
1487             # replace 'all' with '*' in tag and group names
1488 62 100       245 $tag = '*' if $tag eq 'all';
1489             # allow wildcards in tag names (handle differently from all tags: '*')
1490 62 100 100     427 if ($tag =~ /[?*]/ and $tag ne '*') {
1491 3         15 $$opts{WILD} = 1; # set flag indicating wildcards were used in source tag
1492 3         19 $tag =~ s/\*/[-\\w]*/g;
1493 3         15 $tag =~ s/\?/[-\\w]/g;
1494             }
1495             }
1496             # redirect, exclude or set this tag (Note: @fg is empty if we don't care about the group)
1497 70 100       308 if ($dstTag) {
    100          
1498             # redirect this tag
1499 31 50       93 $isExclude and return { Error => "Can't redirect excluded tag" };
1500             # set destination group the same as source if necessary
1501             # (removed in 7.72 so '-*:*
1502             # $dstGrp = $grp if $dstGrp eq '*' and $grp;
1503             # write to specified destination group/tag
1504 31         112 $dst = [ $dstGrp, $dstTag ];
1505             } elsif ($isExclude) {
1506             # implicitly assume '*' if first entry is an exclusion
1507 8 100       37 unshift @setList, [ [ ], '*', [ '', '*' ], $opts ] unless @setList;
1508             # exclude this tag by leaving $dst undefined
1509             } else {
1510 31 100 100     302 $dst = [ $grp || '', $$opts{WILD} ? '*' : $tag ]; # use same group name for dest
1511             }
1512             # save in reverse order so we don't set tags before an exclude
1513 70         424 unshift @setList, [ \@fg, $tag, $dst, $opts ];
1514             }
1515             # 1b) copy requested tags for each alternate ExifTool object
1516 44         104 my $g8;
1517 44         185 foreach $g8 (sort keys %altFiles) {
1518             # request specific alternate tags to load them from the alternate ExifTool object
1519 1         11 my $altInfo = $srcExifTool->GetInfo($altFiles{$g8});
1520             # add to tags list after dummy entry to signify start of tags for this alt file
1521 1 50       8 if (%$altInfo) {
1522 1         8 push @tags, 'Warning DUMMY', reverse sort keys %$altInfo;
1523 1         14 $$info{$_} = $$altInfo{$_} foreach keys %$altInfo;
1524             }
1525             }
1526             # 2) initialize lists of matching tags for each setTag
1527 44         172 foreach $set (@setList) {
1528 71 100       546 $$set[2] and $setMatches{$set} = [ ];
1529             }
1530             # no need to search source tags if doing only assignments
1531 44 50       222 undef @tags if $assign == @setList;
1532             # 3) loop through all tags in source image and save tags matching each setTag
1533 44         126 my (%rtnInfo, $isAlt);
1534 44         143 foreach $tag (@tags) {
1535             # don't try to set errors or warnings
1536 6494 100       15833 if ($tag =~ /^(Error|Warning)( |$)/) {
1537 14 100       61 if ($tag eq 'Warning DUMMY') {
1538 1         4 $isAlt = 1; # start of the alt tags
1539             } else {
1540 13         64 $rtnInfo{$tag} = $$info{$tag};
1541             }
1542 14         35 next;
1543             }
1544             # only set specified tags
1545 6480         12696 my $lcTag = lc(GetTagName($tag));
1546 6480         9851 my (@grp, %grp);
1547 6480         9776 SET: foreach $set (@setList) {
1548 10534         14915 my $opts = $$set[3];
1549 10534 100       19175 next if $$opts{EXPR}; # (expressions handled in step 4)
1550 9408 100 100     26393 next if $$opts{GROUP8} xor $isAlt;
1551             # check first for matching tag
1552 8838 100 100     24631 unless ($$set[1] eq $lcTag or $$set[1] eq '*') {
1553             # handle wildcards
1554 6212 100 100     16375 next unless $$opts{WILD} and $lcTag =~ /^$$set[1]$/;
1555             }
1556             # then check for matching group
1557 2642 100       3383 if (@{$$set[0]}) {
  2642         5083  
1558             # get lower case group names if not done already
1559 1474 100       2745 unless (@grp) {
1560 1370         3750 @grp = map(lc, $srcExifTool->GetGroup($tag));
1561 1370         8361 $grp{$_} = 1 foreach @grp;
1562             }
1563 1474         2047 foreach (@{$$set[0]}) {
  1474         2808  
1564 1516         3007 my ($f, $g) = @$_;
1565 1516 100       2739 if (not defined $f) {
    50          
1566 1512 100       5691 next SET unless $grp{$g};
1567             } elsif ($f == 7) {
1568 0 0       0 next SET unless IsSameID($srcExifTool->GetTagID($tag), $g);
1569             } else {
1570 4 50 33     33 next SET unless defined $grp[$f] and $g eq $grp[$f];
1571             }
1572             }
1573             }
1574 1626 100       3327 last unless $$set[2]; # all done if we hit an exclude
1575             # add to the list of tags matching this setTag
1576 1452         2049 push @{$setMatches{$set}}, $tag;
  1452         5600  
1577             }
1578             }
1579             # 4) loop through each setTag in original order, setting new tag values
1580 44         141 foreach $set (reverse @setList) {
1581             # get options for SetNewValue
1582 71         245 my $opts = $$set[3];
1583             # handle expressions
1584 71 100 66     758 if ($$opts{EXPR} or $$opts{ASSIGN}) {
1585 8         28 my $val;
1586 8 50       66 if ($$opts{EXPR}) {
1587 8         74 $val = $srcExifTool->InsertTagValues($$set[1], \@tags, 'Error');
1588 8         60 my $err = $$srcExifTool{VALUE}{Error};
1589 8 50       28 if ($err) {
1590             # pass on any error as a warning unless it is suppressed
1591 0         0 my $noWarn = $$srcExifTool{OPTIONS}{NoWarning};
1592 0 0 0     0 unless ($noWarn and (eval { $err =~ /$noWarn/ } or
      0        
1593             # (also apply expression to warning without "[minor] " prefix)
1594             ($err =~ s/^\[minor\] //i and eval { $err =~ /$noWarn/ })))
1595             {
1596 0         0 $tag = NextFreeTagKey(\%rtnInfo, 'Warning');
1597 0         0 $rtnInfo{$tag} = $$srcExifTool{VALUE}{Error};
1598             }
1599 0         0 delete $$srcExifTool{VALUE}{Error};
1600 0 0       0 next unless defined $val;
1601             }
1602             } else {
1603 0         0 $val = $$set[1];
1604             }
1605 8         18 my ($dstGrp, $dstTag) = @{$$set[2]};
  8         44  
1606 8 50 33     68 $$opts{Protected} = 1 unless $dstTag =~ /[?*]/ and $dstTag ne '*';
1607 8 50       34 $$opts{Group} = $dstGrp if $dstGrp;
1608 8         81 my @rtnVals = $self->SetNewValue($dstTag, $val, %$opts);
1609 8 50       67 $rtnInfo{$dstTag} = $val if $rtnVals[0]; # tag was set successfully
1610             # return warning if any
1611 8 50       31 $rtnInfo{NextFreeTagKey(\%rtnInfo, 'Warning')} = $rtnVals[1] if $rtnVals[1];
1612 8         31 next;
1613             }
1614 63         233 foreach $tag (@{$setMatches{$set}}) {
  63         286  
1615 1452         2518 my ($val, $noWarn);
1616 1452 50 33     4879 if ($$opts{SrcType} and $$opts{SrcType} ne $srcType) {
1617 0         0 $val = $srcExifTool->GetValue($tag, $$opts{SrcType});
1618             } else {
1619 1452         4557 $val = $$info{$tag};
1620             }
1621 1452         2384 my ($dstGrp, $dstTag) = @{$$set[2]};
  1452         4520  
1622 1452 100       3251 if ($dstGrp) {
1623 1370         4178 my @dstGrp = split /:/, $dstGrp;
1624             # destination group of '*' writes to same group as source tag
1625             # (family 1 unless otherwise specified)
1626 1370         2751 foreach (@dstGrp) {
1627 1372 100       7540 next unless /^(\d*)(all|\*)$/i;
1628 1086 50       6444 $_ = $1 . $srcExifTool->GetGroup($tag, length $1 ? $1 : 1);
1629 1086         2883 $noWarn = 1; # don't warn on wildcard destinations
1630             }
1631 1370         5061 $$opts{Group} = join ':', @dstGrp;
1632             } else {
1633 82         228 delete $$opts{Group};
1634             }
1635             # transfer maker note information if setting this tag
1636 1452 100       5576 if ($$srcExifTool{TAG_INFO}{$tag}{MakerNotes}) {
1637 7         37 $$opts{Fixup} = $$srcExifTool{TAG_EXTRA}{$tag}{Fixup};
1638 7         32 $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER};
1639             }
1640 1452 100       3500 if ($dstTag eq '*') {
1641 1419         2281 $dstTag = $tag;
1642 1419         2293 $noWarn = 1;
1643             }
1644 1452 100 100     4957 if ($$set[1] eq '*' or $$set[3]{WILD}) {
1645             # don't copy from protected binary tags when using wildcards
1646             next if $$srcExifTool{TAG_INFO}{$tag}{Protected} and
1647 1415 100 100     4552 $$srcExifTool{TAG_INFO}{$tag}{Binary};
1648             # don't copy to protected tags when using wildcards
1649 1389         2567 delete $$opts{Protected};
1650             # don't copy flattened tags if copying structures too when copying all
1651 1389 50       3995 $$opts{NoFlat} = $structOpt eq '2' ? 1 : 0;
1652             } else {
1653             # allow protected tags to be copied if specified explicitly
1654 37 50       221 $$opts{Protected} = 1 unless $dstTag =~ /[?*]/;
1655 37         91 delete $$opts{NoFlat};
1656             }
1657             # set value(s) for this tag
1658 1426         6659 my ($rtn, $wrn) = $self->SetNewValue($dstTag, $val, %$opts);
1659             # this was added in version 9.14, and allowed actions like "-subject
1660             # write values of multiple tags into a list, but it had the side effect of
1661             # duplicating items if there were multiple list tags with the same name
1662             # (eg. -use mwg "-creator
1663             # $$opts{Replace} = 0; # accumulate values from tags matching a single argument
1664 1426 50 66     6916 if ($wrn and not $noWarn) {
1665             # return this warning
1666 0         0 $rtnInfo{NextFreeTagKey(\%rtnInfo, 'Warning')} = $wrn;
1667 0         0 $noWarn = 1;
1668             }
1669 1426         3298 delete $$opts{Fixup};
1670 1426 100       6531 $rtnInfo{$tag} = $val if $rtn; # tag was set successfully
1671             }
1672             }
1673 44         5929 return \%rtnInfo; # return information that we set
1674             }
1675              
1676             #------------------------------------------------------------------------------
1677             # Get new value(s) for tag
1678             # Inputs: 0) ExifTool object reference, 1) tag name (or tagInfo or nvHash ref, not public)
1679             # 2) optional pointer to return new value hash reference (not part of public API)
1680             # Returns: List of new Raw values (list may be empty if tag is being deleted)
1681             # Notes: 1) Preferentially returns new value from Extra table if writable Extra tag exists
1682             # 2) Must call AFTER IsOverwriting() returns 1 to get proper value for shifted times
1683             # 3) Tag name is case sensitive and may be prefixed by family 0 or 1 group name
1684             # 4) Value may have been modified by CHECK_PROC routine after ValueConv
1685             sub GetNewValue($$;$)
1686             {
1687 7306     7306 1 12944 local $_;
1688 7306         12053 my $self = shift;
1689 7306         13102 my $tag = shift;
1690 7306         10332 my $nvHash;
1691 7306 100 100     39525 if ((ref $tag eq 'HASH' and $$tag{IsNVH}) or not defined $tag) {
      100        
1692 4229         7241 $nvHash = $tag;
1693             } else {
1694 3077         4969 my $newValueHashPt = shift;
1695 3077 100       8420 if ($$self{NEW_VALUE}) {
1696 2962         4954 my ($group, $tagInfo);
1697 2962 100 66     14509 if (ref $tag) {
    100          
1698 53         217 $nvHash = $self->GetNewValueHash($tag);
1699             } elsif (defined($tagInfo = $Image::ExifTool::Extra{$tag}) and
1700             $$tagInfo{Writable})
1701             {
1702 1551         4340 $nvHash = $self->GetNewValueHash($tagInfo);
1703             } else {
1704             # separate group from tag name
1705 1358         2242 my @groups;
1706 1358 100       4134 @groups = split ':', $1 if $tag =~ s/(.*)://;
1707 1358         4546 my @tagInfoList = FindTagInfo($tag);
1708             # decide which tag we want
1709 1358         2647 GNV_TagInfo: foreach $tagInfo (@tagInfoList) {
1710 1362 100       3963 my $nvh = $self->GetNewValueHash($tagInfo) or next;
1711             # select tag in specified group(s) if necessary
1712 4         13 foreach (@groups) {
1713 2 50       11 next if $_ eq $$nvh{WriteGroup};
1714 2         10 my @grps = $self->GetGroup($tagInfo);
1715 2 50       9 if ($grps[0] eq $$nvh{WriteGroup}) {
1716             # check family 1 group only if WriteGroup is not specific
1717 0 0       0 next if $_ eq $grps[1];
1718             } else {
1719             # otherwise check family 0 group
1720 2 50       11 next if $_ eq $grps[0];
1721             }
1722             # also check family 7
1723 0 0 0     0 next if /^ID-(.*)/i and IsSameID($$tagInfo{TagID}, $1);
1724             # step to next entry in list
1725 0 0       0 $nvh = $$nvh{Next} or next GNV_TagInfo;
1726             }
1727 4         9 $nvHash = $nvh;
1728             # give priority to the one we are creating
1729 4 100       23 last if defined $$nvHash{IsCreating};
1730             }
1731             }
1732             }
1733             # return new value hash if requested
1734 3077 100       7935 $newValueHashPt and $$newValueHashPt = $nvHash;
1735             }
1736 7306 100 100     26012 unless ($nvHash and $$nvHash{Value}) {
1737 4952 100       16754 return () if wantarray; # return empty list
1738 3022         8718 return undef;
1739             }
1740 2354         4882 my $vals = $$nvHash{Value};
1741             # do inverse raw conversion if necessary
1742             # - must also check after doing a Shift
1743 2354 100 100     10985 if ($$nvHash{TagInfo}{RawConvInv} or $$nvHash{Shift}) {
1744 60         277 my @copyVals = @$vals; # modify a copy of the values
1745 60         153 $vals = \@copyVals;
1746 60         162 my $tagInfo = $$nvHash{TagInfo};
1747 60         171 my $conv = $$tagInfo{RawConvInv};
1748 60         159 my $table = $$tagInfo{Table};
1749 60         151 my ($val, $checkProc);
1750 60 100 66     398 $checkProc = $$table{CHECK_PROC} if $$nvHash{Shift} and $table;
1751 60         410 local $SIG{'__WARN__'} = \&SetWarning;
1752 60         166 undef $evalWarning;
1753 60         209 foreach $val (@$vals) {
1754             # must check value now if it was shifted
1755 60 100       217 if ($checkProc) {
1756 26         109 my $err = &$checkProc($self, $tagInfo, \$val);
1757 26 50 33     584 if ($err or not defined $val) {
1758 0 0       0 $err or $err = 'Error generating raw value';
1759 0         0 $self->Warn("$err for $$tagInfo{Name}");
1760 0         0 @$vals = ();
1761 0         0 last;
1762             }
1763 26 50       174 next unless $conv;
1764             } else {
1765 34 50       128 last unless $conv;
1766             }
1767             # do inverse raw conversion
1768 34 100       153 if (ref($conv) eq 'CODE') {
1769 2         18 $val = &$conv($val, $self);
1770             } else {
1771             #### eval RawConvInv ($self, $val, $tagInfo)
1772 32         4880 $val = eval $conv;
1773 32 50       208 $@ and $evalWarning = $@;
1774             }
1775 34 50       290 if ($evalWarning) {
1776             # an empty warning ("\n") ignores tag with no error
1777 0 0       0 if ($evalWarning ne "\n") {
1778 0         0 my $err = CleanWarning() . " in $$tagInfo{Name} (RawConvInv)";
1779 0         0 $self->Warn($err);
1780             }
1781 0         0 @$vals = ();
1782 0         0 last;
1783             }
1784             }
1785             }
1786             # return our value(s)
1787 2354 100       5710 if (wantarray) {
1788             # remove duplicates if requested
1789 1164 50 66     3715 if (@$vals > 1 and $self->Options('NoDups')) {
1790 0         0 my %seen;
1791 0         0 @$vals = grep { !$seen{$_}++ } @$vals;
  0         0  
1792             }
1793 1164         5694 return @$vals;
1794             }
1795 1190         5330 return $$vals[0];
1796             }
1797              
1798             #------------------------------------------------------------------------------
1799             # Return the total number of new values set
1800             # Inputs: 0) ExifTool object reference
1801             # Returns: Scalar context) Number of new values that have been set (incl pseudo)
1802             # List context) Number of new values (incl pseudo), number of "pseudo" values
1803             # ("pseudo" values are those which don't require rewriting the file to change)
1804             sub CountNewValues($)
1805             {
1806 245     245 1 534 my $self = shift;
1807 245         724 my $newVal = $$self{NEW_VALUE};
1808 245         775 my ($num, $pseudo) = (0, 0);
1809 245 100       941 if ($newVal) {
1810 226         670 $num = scalar keys %$newVal;
1811 226         446 my $nv;
1812 226         3078 foreach $nv (values %$newVal) {
1813 19755         37629 my $tagInfo = $$nv{TagInfo};
1814             # don't count tags that don't write anything
1815 19755 100       39092 $$tagInfo{WriteNothing} and --$num, next;
1816             # count the number of pseudo tags included
1817 19736 100       41382 $$tagInfo{WritePseudo} and ++$pseudo;
1818             }
1819             }
1820 245         638 $num += scalar keys %{$$self{DEL_GROUP}};
  245         1070  
1821 245 50       923 return $num unless wantarray;
1822 245         1250 return ($num, $pseudo);
1823             }
1824              
1825             #------------------------------------------------------------------------------
1826             # Save new values for subsequent restore
1827             # Inputs: 0) ExifTool object reference
1828             # Returns: Number of times new values have been saved
1829             # Notes: increments SAVE_COUNT flag each time routine is called
1830             sub SaveNewValues($)
1831             {
1832 1     1 1 11 my $self = shift;
1833 1         4 my $newValues = $$self{NEW_VALUE};
1834 1         5 my $saveCount = ++$$self{SAVE_COUNT};
1835 1         2 my $key;
1836 1         70 foreach $key (keys %$newValues) {
1837 114         231 my $nvHash = $$newValues{$key};
1838 114         259 while ($nvHash) {
1839             # set Save count if not done already
1840 116 50       307 $$nvHash{Save} or $$nvHash{Save} = $saveCount;
1841 116         332 $nvHash = $$nvHash{Next};
1842             }
1843             }
1844             # initialize hash for saving overwritten new values
1845 1         30 $$self{SAVE_NEW_VALUE} = { };
1846             # make a copy of the delete group hash
1847 1         5 my %delGrp = %{$$self{DEL_GROUP}};
  1         6  
1848 1         5 $$self{SAVE_DEL_GROUP} = \%delGrp;
1849 1         7 return $saveCount;
1850             }
1851              
1852             #------------------------------------------------------------------------------
1853             # Restore new values to last saved state
1854             # Inputs: 0) ExifTool object reference
1855             # Notes: Restores saved new values, but currently doesn't restore them in the
1856             # original order, so there may be some minor side-effects when restoring tags
1857             # with overlapping groups. eg) XMP:Identifier, XMP-dc:Identifier
1858             # Also, this doesn't do the right thing for list-type tags which accumulate
1859             # values across a save point
1860             sub RestoreNewValues($)
1861             {
1862 1     1 1 11 my $self = shift;
1863 1         31 my $newValues = $$self{NEW_VALUE};
1864 1         5 my $savedValues = $$self{SAVE_NEW_VALUE};
1865 1         5 my $key;
1866             # 1) remove any new values which don't have the Save flag set
1867 1 50       6 if ($newValues) {
1868 1         296 my @keys = keys %$newValues;
1869 1         7 foreach $key (@keys) {
1870 579         897 my $lastHash;
1871 579         1171 my $nvHash = $$newValues{$key};
1872 579         1190 while ($nvHash) {
1873 581 100       1305 if ($$nvHash{Save}) {
1874 27         45 $lastHash = $nvHash;
1875             } else {
1876             # remove this entry from the list
1877 554 50       1406 if ($lastHash) {
    100          
1878 0         0 $$lastHash{Next} = $$nvHash{Next};
1879             } elsif ($$nvHash{Next}) {
1880 2         9 $$newValues{$key} = $$nvHash{Next};
1881             } else {
1882 552         936 delete $$newValues{$key};
1883             }
1884             }
1885 581         3114 $nvHash = $$nvHash{Next};
1886             }
1887             }
1888             }
1889             # 2) restore saved new values
1890 1 50       21 if ($savedValues) {
1891 1 50       5 $newValues or $newValues = $$self{NEW_VALUE} = { };
1892 1         27 foreach $key (keys %$savedValues) {
1893 89 100       178 if ($$newValues{$key}) {
1894             # add saved values to end of list
1895 2         9 my $nvHash = LastInList($$newValues{$key});
1896 2         8 $$nvHash{Next} = $$savedValues{$key};
1897             } else {
1898 87         213 $$newValues{$key} = $$savedValues{$key};
1899             }
1900             }
1901 1         10 $$self{SAVE_NEW_VALUE} = { }; # reset saved new values
1902             }
1903             # 3) restore delete groups
1904 1         3 my %delGrp = %{$$self{SAVE_DEL_GROUP}};
  1         6  
1905 1         17 $$self{DEL_GROUP} = \%delGrp;
1906             }
1907              
1908             #------------------------------------------------------------------------------
1909             # Set alternate file for extracting information
1910             # Inputs: 0) ExifTool ref, 1) family 8 group name (of the form "File#" where # is any number)
1911             # 2) alternate file name (may contain tag names with leading "$"), or undef to reset
1912             # Returns: 1 on success, or 0 on invalid group name
1913             sub SetAlternateFile($$$)
1914             {
1915 6     6 1 60 my ($self, $g8, $file) = @_;
1916 6         19 $g8 = ucfirst lc $g8;
1917 6 50       45 return 0 unless $g8 =~ /^File\d+$/;
1918             # keep the same file if already initialized (possibly has metadata extracted)
1919 6 50 33     55 if (not defined $file) {
    50          
1920 0         0 delete $$self{ALT_EXIFTOOL}{$g8};
1921             } elsif (not ($$self{ALT_EXIFTOOL}{$g8} and $file !~ /\$/ and
1922             $$self{ALT_EXIFTOOL}{$g8}{ALT_FILE} eq $file))
1923             {
1924 6         25 my $altExifTool = Image::ExifTool->new;
1925 6         20 $$altExifTool{ALT_FILE} = $file;
1926 6         21 $$self{ALT_EXIFTOOL}{$g8} = $altExifTool;
1927             }
1928 6         18 return 1;
1929             }
1930              
1931             #------------------------------------------------------------------------------
1932             # Set filesystem time from from FileModifyDate or FileCreateDate tag
1933             # Inputs: 0) ExifTool object reference, 1) file name or file ref
1934             # 2) time (-M or -C) of original file (used for shift; obtained from file if not given)
1935             # 3) tag name to write (undef for 'FileModifyDate')
1936             # 4) flag set if argument 2 has already been converted to Unix seconds
1937             # Returns: 1=time changed OK, 0=nothing done, -1=error setting time
1938             # (increments CHANGED flag and sets corresponding WRITTEN tag)
1939             sub SetFileModifyDate($$;$$$)
1940             {
1941 0     0 1 0 my ($self, $file, $originalTime, $tag, $isUnixTime) = @_;
1942 0         0 my $nvHash;
1943 0 0       0 $tag = 'FileModifyDate' unless defined $tag;
1944 0         0 my $val = $self->GetNewValue($tag, \$nvHash);
1945 0 0       0 return 0 unless defined $val;
1946 0         0 my $isOverwriting = $self->IsOverwriting($nvHash);
1947 0 0       0 return 0 unless $isOverwriting;
1948             # can currently only set creation date on Windows systems
1949             # (and Mac now too, but that is handled with the MacOS tags)
1950 0 0 0     0 return 0 if $tag eq 'FileCreateDate' and $^O ne 'MSWin32';
1951 0 0       0 if ($isOverwriting < 0) { # are we shifting time?
1952             # use original time of this file if not specified
1953 0 0       0 unless (defined $originalTime) {
1954 0         0 my ($aTime, $mTime, $cTime) = $self->GetFileTime($file);
1955 0 0       0 $originalTime = ($tag eq 'FileCreateDate') ? $cTime : $mTime;
1956 0 0       0 return 0 unless defined $originalTime;
1957 0         0 $isUnixTime = 1;
1958             }
1959 0 0       0 $originalTime = int($^T - $originalTime*(24*3600) + 0.5) unless $isUnixTime;
1960 0 0       0 return 0 unless $self->IsOverwriting($nvHash, $originalTime);
1961 0         0 $val = $$nvHash{Value}[0]; # get shifted value
1962             }
1963 0         0 my ($aTime, $mTime, $cTime);
1964 0 0       0 if ($tag eq 'FileCreateDate') {
1965 0 0       0 eval { require Win32::API } or $self->Warn("Install Win32::API to set $tag"), return -1;
  0         0  
1966 0 0       0 eval { require Win32API::File } or $self->Warn("Install Win32API::File to set $tag"), return -1;
  0         0  
1967 0         0 $cTime = $val;
1968             } else {
1969 0         0 $aTime = $mTime = $val;
1970             }
1971 0 0       0 $self->SetFileTime($file, $aTime, $mTime, $cTime, 1) or $self->Warn("Error setting $tag"), return -1;
1972 0         0 ++$$self{CHANGED};
1973 0         0 $$self{WRITTEN}{$tag} = $val; # remember that we wrote this tag
1974 0         0 $self->VerboseValue("+ $tag", $val);
1975 0         0 return 1;
1976             }
1977              
1978             #------------------------------------------------------------------------------
1979             # Change file name and/or directory from FileName and Directory tags
1980             # Inputs: 0) ExifTool object reference, 1) current file name (including path)
1981             # 2) new name (or undef to build from FileName and Directory tags)
1982             # 3) option: 'HardLink'/'SymLink' to create hard/symbolic link instead of renaming
1983             # 'Test' to only print new file name
1984             # 4) 0 to indicate that a file will no longer exist (used for 'Test' only)
1985             # Returns: 1=name changed OK, 0=nothing changed, -1=error changing name
1986             # (and increments CHANGED flag if filename changed)
1987             # Notes: Will not overwrite existing file. Creates directories as necessary.
1988             sub SetFileName($$;$$$)
1989             {
1990 1     1 1 4 my ($self, $file, $newName, $opt, $usedFlag) = @_;
1991 1         2 my ($nvHash, $doName, $doDir);
1992              
1993 1 50       3 $opt or $opt = '';
1994             # determine the new file name
1995 1 50       3 unless (defined $newName) {
1996 1 50       5 if ($opt) {
1997 0 0 0     0 if ($opt eq 'HardLink' or $opt eq 'Link') {
    0          
    0          
1998 0         0 $newName = $self->GetNewValue('HardLink');
1999             } elsif ($opt eq 'SymLink') {
2000 0         0 $newName = $self->GetNewValue('SymLink');
2001             } elsif ($opt eq 'Test') {
2002 0         0 $newName = $self->GetNewValue('TestName');
2003             }
2004 0 0       0 return 0 unless defined $newName;
2005             } else {
2006 1         4 my $filename = $self->GetNewValue('FileName', \$nvHash);
2007 1 50 33     8 $doName = 1 if defined $filename and $self->IsOverwriting($nvHash, $file);
2008 1         4 my $dir = $self->GetNewValue('Directory', \$nvHash);
2009 1 50 33     6 $doDir = 1 if defined $dir and $self->IsOverwriting($nvHash, $file);
2010 1 50 33     4 return 0 unless $doName or $doDir; # nothing to do
2011 1 50       3 if ($doName) {
2012 1         4 $newName = GetNewFileName($file, $filename);
2013 1 50       3 $newName = GetNewFileName($newName, $dir) if $doDir;
2014             } else {
2015 0         0 $newName = GetNewFileName($file, $dir);
2016             }
2017             }
2018             }
2019             # validate new file name in Windows
2020 1 50       4 if ($^O eq 'MSWin32') {
2021 0 0       0 if ($newName =~ /[\0-\x1f<>"|*]/) {
2022 0         0 $self->Warn('New file name not allowed in Windows (contains reserved characters)');
2023 0         0 return -1;
2024             }
2025 0 0 0     0 if ($newName =~ /:/ and $newName !~ /^[A-Z]:[^:]*$/i) {
2026 0         0 $self->Warn("New file name not allowed in Windows (contains ':')");
2027 0         0 return -1;
2028             }
2029 0 0 0     0 if ($newName =~ /\?/ and $newName !~ m{^[\\/]{2}\?[\\/][^?]*$}) {
2030 0         0 $self->Warn("New file name not allowed in Windows (contains '?')");
2031 0         0 return -1;
2032             }
2033 0 0       0 if ($newName =~ m{(^|[\\/])(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.[^.]*)?$}i) {
2034 0         0 $self->Warn('New file name not allowed in Windows (reserved device name)');
2035 0         0 return -1;
2036             }
2037 0 0       0 if ($newName =~ /([. ])$/) {
2038 0 0       0 $self->Warn("New file name not recommended for Windows (ends with '${1}')", 2) and return -1;
2039             }
2040 0 0 0     0 if (length $newName > 259 and $newName !~ /\?/) {
2041 0 0       0 $self->Warn('New file name not recommended for Windows (exceeds 260 chars)', 2) and return -1;
2042             }
2043             } else {
2044 1         2 $newName =~ tr/\0//d; # make sure name doesn't contain nulls
2045             }
2046             # protect against empty file name
2047 1 50       4 length $newName or $self->Warn('New file name is empty'), return -1;
2048             # don't replace existing file
2049 1 0 0     6 if ($self->Exists($newName, 1) and (not defined $usedFlag or $usedFlag)) {
      33        
2050 0 0 0     0 if ($file ne $newName or $opt =~ /Link$/) {
2051             # allow for case-insensitive filesystem
2052 0 0 0     0 if ($opt =~ /Link$/ or not $self->IsSameFile($file, $newName)) {
2053 0         0 $self->Warn("File '${newName}' already exists");
2054 0         0 return -1;
2055             }
2056             } else {
2057 0         0 $self->Warn('File name is unchanged');
2058 0         0 return 0;
2059             }
2060             }
2061 1 50       6 if ($opt eq 'Test') {
2062 0         0 my $out = $$self{OPTIONS}{TextOut};
2063 0         0 print $out "'${file}' --> '${newName}'\n";
2064 0         0 return 1;
2065             }
2066             # create directory for new file if necessary
2067 1         4 my $err = $self->CreateDirectory($newName);
2068 1 50       3 if (defined $err) {
2069 0 0       0 if ($err) {
2070 0 0       0 $self->Warn($err) unless $err =~ /^Error creating/;
2071 0         0 $self->Warn("Error creating directory for '${newName}'");
2072 0         0 return -1;
2073             }
2074 0         0 $self->VPrint(0, "Created directory for '${newName}'\n");
2075             }
2076 1 50 33     8 if ($opt eq 'HardLink' or $opt eq 'Link') {
    50          
2077 0 0       0 unless (link $file, $newName) {
2078 0         0 $self->Warn("Error creating hard link '${newName}'");
2079 0         0 return -1;
2080             }
2081 0         0 ++$$self{CHANGED};
2082 0         0 $self->VerboseValue('+ HardLink', $newName);
2083 0         0 return 1;
2084             } elsif ($opt eq 'SymLink') {
2085 0 0       0 $^O eq 'MSWin32' and $self->Warn('SymLink not supported in Windows'), return -1;
2086 0         0 $newName =~ s(^\./)(); # remove leading "./" from link name if it exists
2087             # path to linked file must be relative to the $newName directory, but $file
2088             # is relative to the current directory, so convert it to an absolute path
2089             # if using a relative directory and $newName isn't in the current directory
2090 0 0 0     0 if ($file !~ m(^/) and $newName =~ m(/)) {
2091 0 0       0 unless (eval { require Cwd }) {
  0         0  
2092 0         0 $self->Warn('Install Cwd to make symlinks to other directories');
2093 0         0 return -1;
2094             }
2095 0         0 $file = eval { Cwd::abs_path($file) };
  0         0  
2096 0 0       0 unless (defined $file) {
2097 0         0 $self->Warn('Error in Cwd::abs_path when creating symlink');
2098 0         0 return -1;
2099             }
2100             }
2101 0 0       0 unless (eval { symlink $file, $newName } ) {
  0         0  
2102 0         0 $self->Warn("Error creating symbolic link '${newName}'");
2103 0         0 return -1;
2104             }
2105 0         0 ++$$self{CHANGED};
2106 0         0 $self->VerboseValue('+ SymLink', $newName);
2107 0         0 return 1;
2108             }
2109             # attempt to rename the file
2110 1 50       5 unless ($self->Rename($file, $newName)) {
2111 0         0 local (*EXIFTOOL_SFN_IN, *EXIFTOOL_SFN_OUT);
2112             # renaming didn't work, so copy the file instead
2113 0 0       0 unless ($self->Open(\*EXIFTOOL_SFN_IN, $file)) {
2114 0         0 $self->Error("Error opening '${file}'");
2115 0         0 return -1;
2116             }
2117 0 0       0 unless ($self->Open(\*EXIFTOOL_SFN_OUT, $newName, '>')) {
2118 0         0 close EXIFTOOL_SFN_IN;
2119 0         0 $self->Error("Error creating '${newName}'");
2120 0         0 return -1;
2121             }
2122 0         0 binmode EXIFTOOL_SFN_IN;
2123 0         0 binmode EXIFTOOL_SFN_OUT;
2124 0         0 my ($buff, $err);
2125 0         0 while (read EXIFTOOL_SFN_IN, $buff, 65536) {
2126 0 0       0 print EXIFTOOL_SFN_OUT $buff or $err = 1;
2127             }
2128 0 0       0 close EXIFTOOL_SFN_OUT or $err = 1;
2129 0         0 close EXIFTOOL_SFN_IN;
2130 0 0       0 if ($err) {
2131 0         0 $self->Unlink($newName); # erase bad output file
2132 0         0 $self->Error("Error writing '${newName}'");
2133 0         0 return -1;
2134             }
2135             # preserve modification time
2136 0         0 my ($aTime, $mTime, $cTime) = $self->GetFileTime($file);
2137 0         0 $self->SetFileTime($newName, $aTime, $mTime, $cTime);
2138             # remove the original file
2139 0 0       0 $self->Unlink($file) or $self->Warn('Error removing old file');
2140             }
2141 1         4 $$self{NewName} = $newName; # remember new file name
2142 1         3 ++$$self{CHANGED};
2143 1         7 $self->VerboseValue('+ FileName', $newName);
2144 1         2 return 1;
2145             }
2146              
2147             #------------------------------------------------------------------------------
2148             # Set file permissions, group/user id and various MDItem tags from new tag values
2149             # Inputs: 0) ExifTool ref, 1) file name or glob (must be a name for MDItem tags)
2150             # Returns: 1=something was set OK, 0=didn't try, -1=error (and warning set)
2151             # Notes: There may be errors even if 1 is returned
2152             sub SetSystemTags($$)
2153             {
2154 232     232 0 845 my ($self, $file) = @_;
2155 232         541 my $result = 0;
2156              
2157 232         1169 my $perm = $self->GetNewValue('FilePermissions');
2158 232 50       869 if (defined $perm) {
2159 0 0       0 if (eval { chmod($perm & 07777, $file) }) {
  0         0  
2160 0         0 $self->VerboseValue('+ FilePermissions', $perm);
2161 0         0 $result = 1;
2162             } else {
2163 0         0 $self->Warn('Error setting FilePermissions');
2164 0         0 $result = -1;
2165             }
2166             }
2167 232         835 my $uid = $self->GetNewValue('FileUserID');
2168 232         766 my $gid = $self->GetNewValue('FileGroupID');
2169 232 50 33     1429 if (defined $uid or defined $gid) {
2170 0 0       0 defined $uid or $uid = -1;
2171 0 0       0 defined $gid or $gid = -1;
2172 0 0       0 if (eval { chown($uid, $gid, $file) }) {
  0         0  
2173 0 0       0 $self->VerboseValue('+ FileUserID', $uid) if $uid >= 0;
2174 0 0       0 $self->VerboseValue('+ FileGroupID', $gid) if $gid >= 0;
2175 0         0 $result = 1;
2176             } else {
2177 0         0 $self->Warn('Error setting FileGroup/UserID');
2178 0 0       0 $result = -1 unless $result;
2179             }
2180             }
2181 232         558 my $tag;
2182 232         796 foreach $tag (@writableMacOSTags) {
2183 1624         2329 my $nvHash;
2184 1624         4163 my $val = $self->GetNewValue($tag, \$nvHash);
2185 1624 50       4636 next unless $nvHash;
2186 0 0       0 if ($^O eq 'darwin') {
    0          
2187 0 0       0 ref $file and $self->Warn('Setting MDItem tags requires a file name'), last;
2188 0         0 require Image::ExifTool::MacOS;
2189 0         0 my $res = Image::ExifTool::MacOS::SetMacOSTags($self, $file, \@writableMacOSTags);
2190 0 0 0     0 $result = $res if $res == 1 or not $result;
2191 0         0 last;
2192             } elsif ($tag ne 'FileCreateDate') {
2193 0         0 $self->Warn('Can only set MDItem tags on MacOS');
2194 0         0 last;
2195             }
2196             }
2197             # delete Windows Zone.Identifier if specified
2198 232         1235 my $zhash = $self->GetNewValueHash($Image::ExifTool::Extra{ZoneIdentifier});
2199 232 50       895 if ($zhash) {
2200 0         0 my $res = -1;
2201 0 0       0 if ($^O ne 'MSWin32') {
    0          
    0          
    0          
2202 0         0 $self->Warn('ZoneIdentifer is a Windows-only tag');
2203             } elsif (ref $file) {
2204 0         0 $self->Warn('Writing ZoneIdentifer requires a file name');
2205             } elsif (defined $self->GetNewValue('ZoneIdentifier', \$zhash)) {
2206 0         0 $self->Warn('ZoneIndentifier may only be deleted');
2207 0         0 } elsif (not eval { require Win32API::File }) {
2208 0         0 $self->Warn('Install Win32API::File to write ZoneIdentifier');
2209             } else {
2210 0         0 my ($wattr, $wide);
2211 0         0 my $zfile = "${file}:Zone.Identifier";
2212 0 0       0 if ($self->EncodeFileName($zfile)) {
2213 0         0 $wide = 1;
2214 0         0 $wattr = eval { Win32API::File::GetFileAttributesW($zfile) };
  0         0  
2215             } else {
2216 0         0 $wattr = eval { Win32API::File::GetFileAttributes($zfile) };
  0         0  
2217             }
2218 0 0       0 if ($wattr == Win32API::File::INVALID_FILE_ATTRIBUTES()) {
    0          
2219 0         0 $res = 0; # file doesn't exist, nothing to do
2220             } elsif ($wattr & Win32API::File::FILE_ATTRIBUTE_READONLY()) {
2221 0         0 $self->Warn('Zone.Identifier stream is read-only');
2222             } else {
2223 0 0       0 if ($wide) {
2224 0 0       0 $res = 1 if eval { Win32API::File::DeleteFileW($zfile) };
  0         0  
2225             } else {
2226 0 0       0 $res = 1 if eval { Win32API::File::DeleteFile($zfile) };
  0         0  
2227             }
2228 0 0       0 if ($res > 0) {
2229 0         0 $self->VPrint(0, " Deleting Zone.Identifier stream\n");
2230             } else {
2231 0         0 $self->Warn('Error deleting Zone.Identifier stream');
2232             }
2233             }
2234             }
2235 0 0 0     0 $result = $res if $res == 1 or not $result;
2236             }
2237 232         1187 return $result;
2238             }
2239              
2240             #------------------------------------------------------------------------------
2241             # Write information back to file
2242             # Inputs: 0) ExifTool object reference,
2243             # 1) input filename, file ref, RAF ref, or scalar ref (or '' or undef to create from scratch)
2244             # 2) output filename, file ref, or scalar ref (or undef to overwrite)
2245             # 3) optional output file type (required only if input file is not specified
2246             # and output file is a reference)
2247             # Returns: 1=file written OK, 2=file written but no changes made, 0=file write error
2248             sub WriteInfo($$;$$)
2249             {
2250 245     245 1 58326 local ($_, *EXIFTOOL_FILE2, *EXIFTOOL_OUTFILE);
2251 245         1011 my ($self, $infile, $outfile, $outType) = @_;
2252 245         1793 my (@fileTypeList, $fileType, $tiffType, $hdr, $seekErr, $type, $tmpfile);
2253 245         0 my ($inRef, $outRef, $closeIn, $closeOut, $outPos, $outBuff, $eraseIn, $raf, $fileExt);
2254 245         0 my ($hardLink, $symLink, $testName);
2255 245         927 my $oldRaf = $$self{RAF};
2256 245         594 my $rtnVal = 0;
2257              
2258             # initialize member variables
2259 245         1871 $self->Init();
2260 245         1773 $$self{IsWriting} = 1;
2261              
2262             # first, save original file modify date if necessary
2263             # (do this now in case we are modifying file in place and shifting date)
2264 245         762 my ($nvHash, $nvHash2, $originalTime, $createTime);
2265 245         1614 my $setModDate = defined $self->GetNewValue('FileModifyDate', \$nvHash);
2266 245         923 my $setCreateDate = defined $self->GetNewValue('FileCreateDate', \$nvHash2);
2267 245         668 my ($aTime, $mTime, $cTime);
2268 245 0 33     1135 if ($setModDate and $self->IsOverwriting($nvHash) < 0 and
      33        
      0        
2269             defined $infile and ref $infile ne 'SCALAR')
2270             {
2271 0         0 ($aTime, $mTime, $cTime) = $self->GetFileTime($infile);
2272 0         0 $originalTime = $mTime;
2273             }
2274 245 0 33     1111 if ($setCreateDate and $self->IsOverwriting($nvHash2) < 0 and
      33        
      0        
2275             defined $infile and ref $infile ne 'SCALAR')
2276             {
2277 0 0       0 ($aTime, $mTime, $cTime) = $self->GetFileTime($infile) unless defined $cTime;
2278 0         0 $createTime = $cTime;
2279             }
2280             #
2281             # do quick in-place change of file dir/name or date if that is all we are doing
2282             #
2283 245         1538 my ($numNew, $numPseudo) = $self->CountNewValues();
2284 245 100 66     1751 if (not defined $outfile and defined $infile) {
2285 4         13 $hardLink = $self->GetNewValue('HardLink');
2286 4         18 $symLink = $self->GetNewValue('SymLink');
2287 4         18 $testName = $self->GetNewValue('TestName');
2288 4 50 33     20 undef $hardLink if defined $hardLink and not length $hardLink;
2289 4 50 33     54 undef $symLink if defined $symLink and not length $symLink;
2290 4 50 33     18 undef $testName if defined $testName and not length $testName;
2291 4         14 my $newFileName = $self->GetNewValue('FileName', \$nvHash);
2292 4         11 my $newDir = $self->GetNewValue('Directory');
2293 4 50 33     18 if (defined $newDir and length $newDir) {
2294 0 0       0 $newDir .= '/' unless $newDir =~ m{/$};
2295             } else {
2296 4         10 undef $newDir;
2297             }
2298 4 100 33     26 if ($numNew == $numPseudo) {
    50          
2299 1         2 $rtnVal = 2;
2300 1 50 33     8 if ((defined $newFileName or defined $newDir) and not ref $infile) {
      33        
2301 1         5 my $result = $self->SetFileName($infile);
2302 1 50       4 if ($result > 0) {
    0          
2303 1         2 $infile = $$self{NewName}; # file name changed
2304 1         2 $rtnVal = 1;
2305             } elsif ($result < 0) {
2306 0         0 return 0; # don't try to do anything else
2307             }
2308             }
2309 1 50 33     5 if (not ref $infile or UNIVERSAL::isa($infile,'GLOB')) {
2310 1 50 0     3 $self->SetFileModifyDate($infile) > 0 and $rtnVal = 1 if $setModDate;
2311 1 50 0     2 $self->SetFileModifyDate($infile, undef, 'FileCreateDate') > 0 and $rtnVal = 1 if $setCreateDate;
2312 1 50       4 $self->SetSystemTags($infile) > 0 and $rtnVal = 1;
2313             }
2314 1 50 33     10 if (defined $hardLink or defined $symLink or defined $testName) {
      33        
2315 0 0 0     0 $hardLink and $self->SetFileName($infile, $hardLink, 'HardLink') and $rtnVal = 1;
2316 0 0 0     0 $symLink and $self->SetFileName($infile, $symLink, 'SymLink') and $rtnVal = 1;
2317 0 0 0     0 $testName and $self->SetFileName($infile, $testName, 'Test') and $rtnVal = 1;
2318             }
2319 1         5 return $rtnVal;
2320             } elsif (defined $newFileName and length $newFileName) {
2321             # can't simply rename file, so just set the output name if new FileName
2322             # --> in this case, must erase original copy
2323 0 0       0 if (ref $infile) {
    0          
2324 0         0 $outfile = $newFileName;
2325             # can't delete original
2326             } elsif ($self->IsOverwriting($nvHash, $infile)) {
2327 0         0 $outfile = GetNewFileName($infile, $newFileName);
2328 0         0 $eraseIn = 1; # delete original
2329             }
2330             }
2331             # set new directory if specified
2332 3 50       16 if (defined $newDir) {
2333 0 0 0     0 $outfile = $infile unless defined $outfile or ref $infile;
2334 0 0       0 if (defined $outfile) {
2335 0         0 $outfile = GetNewFileName($outfile, $newDir);
2336 0 0       0 $eraseIn = 1 unless ref $infile;
2337             }
2338             }
2339             }
2340             #
2341             # set up input file
2342             #
2343 244 100 66     2230 if (ref $infile) {
    100          
    50          
2344 5         14 $inRef = $infile;
2345 5 100 33     64 if (UNIVERSAL::isa($inRef,'GLOB')) {
    50 33        
    50          
2346 1         10 seek($inRef, 0, 0); # make sure we are at the start of the file
2347             } elsif (UNIVERSAL::isa($inRef,'File::RandomAccess')) {
2348 0         0 $inRef->Seek(0);
2349 0         0 $raf = $inRef;
2350             } elsif ($] >= 5.006 and ($$self{OPTIONS}{EncodeHangs} or
2351             eval { require Encode; Encode::is_utf8($$inRef) } or $@))
2352             {
2353 0         0 local $SIG{'__WARN__'} = \&SetWarning;
2354             # convert image data from UTF-8 to character stream if necessary
2355 0 0 0     0 my $buff = ($$self{OPTIONS}{EncodeHangs} or $@) ? pack('C*', unpack($] < 5.010000 ?
    0          
2356             'U0C*' : 'C0C*', $$inRef)) : Encode::encode('utf8', $$inRef);
2357 0 0       0 if (defined $outfile) {
2358 0         0 $inRef = \$buff;
2359             } else {
2360 0         0 $$inRef = $buff;
2361             }
2362             }
2363             } elsif (defined $infile and $infile ne '') {
2364             # write to a temporary file if no output file given
2365 215 100       803 $outfile = $tmpfile = "${infile}_exiftool_tmp" unless defined $outfile;
2366 215 50       1676 if ($self->Open(\*EXIFTOOL_FILE2, $infile)) {
2367 215         1674 $fileExt = GetFileExtension($infile);
2368 215         1063 $fileType = GetFileType($infile);
2369 215         826 @fileTypeList = GetFileType($infile);
2370 215         774 $tiffType = $$self{FILE_EXT} = GetFileExtension($infile);
2371 215         1751 $self->VPrint(0, "Rewriting $infile...\n");
2372 215         1982 $inRef = \*EXIFTOOL_FILE2;
2373 215         569 $closeIn = 1; # we must close the file since we opened it
2374             } else {
2375 0         0 $self->Error('Error opening file');
2376 0         0 return 0;
2377             }
2378             } elsif (not defined $outfile) {
2379 0         0 $self->Error("WriteInfo(): Must specify infile or outfile\n");
2380 0         0 return 0;
2381             } else {
2382             # create file from scratch
2383 24 100 66     253 $outType = GetFileExtension($outfile) unless $outType or ref $outfile;
2384 24 50       118 if (CanCreate($outType)) {
    0          
2385 24 50       255 if ($$self{OPTIONS}{WriteMode} =~ /g/i) {
2386 24         62 $fileType = $tiffType = $outType; # use output file type if no input file
2387 24         74 $infile = "$fileType file"; # make bogus file name
2388 24         169 $self->VPrint(0, "Creating $infile...\n");
2389 24         63 $inRef = \ ''; # set $inRef to reference to empty data
2390             } else {
2391 0         0 $self->Error("Not creating new $outType file (disallowed by WriteMode)");
2392 0         0 return 0;
2393             }
2394             } elsif ($outType) {
2395 0         0 $self->Error("Can't create $outType files");
2396 0         0 return 0;
2397             } else {
2398 0         0 $self->Error("Can't create file (unknown type)");
2399 0         0 return 0;
2400             }
2401             }
2402 244 100       929 unless (@fileTypeList) {
2403 30 100       101 if ($fileType) {
2404 24         73 @fileTypeList = ( $fileType );
2405             } else {
2406 6         220 @fileTypeList = @fileTypes;
2407 6         21 $tiffType = 'TIFF';
2408             }
2409             }
2410             #
2411             # set up output file
2412             #
2413 244 100       2161 if (ref $outfile) {
    100          
    50          
    50          
2414 13         31 $outRef = $outfile;
2415 13 50       86 if (UNIVERSAL::isa($outRef,'GLOB')) {
2416 0         0 binmode($outRef);
2417 0         0 $outPos = tell($outRef);
2418             } else {
2419             # initialize our output buffer if necessary
2420 13 50       53 defined $$outRef or $$outRef = '';
2421 13         52 $outPos = length($$outRef);
2422             }
2423             } elsif (not defined $outfile) {
2424             # editing in place, so write to memory first
2425             # (only when infile is a file ref or scalar ref)
2426 1 50       6 if ($raf) {
2427 0         0 $self->Error("Can't edit File::RandomAccess object in place");
2428 0         0 return 0;
2429             }
2430 1         3 $outBuff = '';
2431 1         2 $outRef = \$outBuff;
2432 1         3 $outPos = 0;
2433             } elsif ($self->Exists($outfile, 1)) {
2434 0         0 $self->Error("File already exists: $outfile");
2435             } elsif ($self->Open(\*EXIFTOOL_OUTFILE, $outfile, '>')) {
2436 230         1041 $outRef = \*EXIFTOOL_OUTFILE;
2437 230         595 $closeOut = 1; # we must close $outRef
2438 230         778 binmode($outRef);
2439 230         1006 $outPos = 0;
2440             } else {
2441 0 0       0 my $tmp = $tmpfile ? ' temporary' : '';
2442 0         0 $self->Error("Error creating$tmp file: $outfile");
2443             }
2444             #
2445             # write the file
2446             #
2447 244         1575 until ($$self{VALUE}{Error}) {
2448             # create random access file object (disable seek test in case of straight copy)
2449 244 50       3752 $raf or $raf = File::RandomAccess->new($inRef, 1);
2450 244         1940 $raf->BinMode();
2451 244 100 33     3263 if ($numNew == $numPseudo) {
    50 66        
2452 1         5 $rtnVal = 1;
2453             # just do a straight copy of the file (no "real" tags are being changed)
2454 1         4 my $buff;
2455 1         8 while ($raf->Read($buff, 65536)) {
2456 1 50       9 Write($outRef, $buff) or $rtnVal = -1, last;
2457             }
2458 1         5 last;
2459             } elsif (not ref $infile and ($infile eq '-' or $infile =~ /\|$/)) {
2460             # patch for Windows command shell pipe
2461 0         0 $$raf{TESTED} = -1; # force buffering
2462             } else {
2463 243         1304 $raf->SeekTest();
2464             }
2465             # $raf->Debug() and warn " RAF debugging enabled!\n";
2466 243         1255 my $inPos = $raf->Tell();
2467 243         4203 $$self{RAF} = $raf;
2468 243         1339 my %dirInfo = (
2469             RAF => $raf,
2470             OutFile => $outRef,
2471             );
2472 243 100       1368 $raf->Read($hdr, 1024) or $hdr = '';
2473 243 50       1274 $raf->Seek($inPos, 0) or $seekErr = 1;
2474 243         710 my $wrongType;
2475 243         943 until ($seekErr) {
2476 278         948 $type = shift @fileTypeList;
2477             # do quick test to see if this is the right file type
2478 278 100 66     9067 if ($magicNumber{$type} and length($hdr) and $hdr !~ /^$magicNumber{$type}/s) {
      100        
2479 35 50       115 next if @fileTypeList;
2480 0         0 $wrongType = 1;
2481 0         0 last;
2482             }
2483             # save file type in member variable
2484 243         2103 $dirInfo{Parent} = $$self{FILE_TYPE} = $$self{PATH}[0] = $type;
2485             # determine which directories we must write for this file type
2486 243         1939 $self->InitWriteDirs($type);
2487 243 100 100     2532 if ($type eq 'JPEG' or $type eq 'EXV') {
    100 33        
    100          
    50          
    50          
2488 110         817 $rtnVal = $self->WriteJPEG(\%dirInfo);
2489             } elsif ($type eq 'TIFF') {
2490             # disallow writing of some TIFF-based RAW images:
2491 13 50       34 if (grep /^$tiffType$/, @{$noWriteFile{TIFF}}) {
  13         357  
2492 0         0 $fileType = $tiffType;
2493 0         0 undef $rtnVal;
2494             } else {
2495 13 50       62 if ($tiffType eq 'FFF') {
2496             # (see https://exiftool.org/forum/index.php?topic=10848.0)
2497 0         0 $self->Error('Phocus may not properly update previews of edited FFF images', 1);
2498             }
2499 13         43 $dirInfo{Parent} = $tiffType;
2500 13         113 $rtnVal = $self->ProcessTIFF(\%dirInfo);
2501             }
2502 0         0 } elsif (exists $writableType{$type}) {
2503 118         354 my ($module, $func);
2504 118 100       544 if (ref $writableType{$type} eq 'ARRAY') {
2505 91   66     563 $module = $writableType{$type}[0] || $type;
2506 91         300 $func = $writableType{$type}[1];
2507             } else {
2508 27   66     160 $module = $writableType{$type} || $type;
2509             }
2510 118         3221 require "Image/ExifTool/$module.pm";
2511 118   66     773 $func = "Image::ExifTool::${module}::" . ($func || "Process$type");
2512 61     61   810 no strict 'refs';
  61         166  
  61         5180  
2513 118         1358 $rtnVal = &$func($self, \%dirInfo);
2514 61     61   963 use strict 'refs';
  61         153  
  61         189453  
2515             } elsif ($type eq 'ORF' or $type eq 'RAW') {
2516 0         0 $rtnVal = $self->ProcessTIFF(\%dirInfo);
2517             } elsif ($type eq 'EXIF') {
2518             # go through WriteDirectory so block writes, etc are handled
2519 2         15 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
2520 2         16 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
2521 2 50       11 if (defined $buff) {
2522 2 50       10 $rtnVal = Write($outRef, $buff) ? 1 : -1;
2523             } else {
2524 0         0 $rtnVal = 0;
2525             }
2526             } else {
2527 0         0 undef $rtnVal; # flag that we don't write this type of file
2528             }
2529             # all done unless we got the wrong type
2530 243 50       1336 last if $rtnVal;
2531 0 0       0 last unless @fileTypeList;
2532             # seek back to original position in files for next try
2533 0 0       0 $raf->Seek($inPos, 0) or $seekErr = 1, last;
2534 0 0       0 if (UNIVERSAL::isa($outRef,'GLOB')) {
2535 0         0 seek($outRef, 0, $outPos);
2536             } else {
2537 0         0 $$outRef = substr($$outRef, 0, $outPos);
2538             }
2539             }
2540             # print file format errors
2541 243 50       873 unless ($rtnVal) {
2542 0         0 my $err;
2543 0 0 0     0 if ($seekErr) {
    0          
    0          
2544 0         0 $err = 'Error seeking in file';
2545             } elsif ($fileType and defined $rtnVal) {
2546 0 0       0 if ($$self{VALUE}{Error}) {
    0          
2547             # existing error message will do
2548             } elsif ($fileType eq 'RAW') {
2549 0         0 $err = 'Writing this type of RAW file is not supported';
2550             } else {
2551 0 0       0 if ($wrongType) {
2552 0   0     0 my $type = $fileExt || ($fileType eq 'TIFF' ? $tiffType : $fileType);
2553 0         0 $err = "Not a valid $type";
2554             # do a quick check to see what this file looks like
2555 0         0 foreach $type (@fileTypes) {
2556 0 0       0 next unless $magicNumber{$type};
2557 0 0       0 next unless $hdr =~ /^$magicNumber{$type}/s;
2558 0         0 $err .= " (looks more like a $type)";
2559 0         0 last;
2560             }
2561             } else {
2562 0         0 $err = 'Format error in file';
2563             }
2564             }
2565             } elsif ($fileType) {
2566             # get specific type of file from extension
2567 0 0 0     0 $fileType = GetFileExtension($infile) if $infile and GetFileType($infile);
2568 0         0 $err = "Writing of $fileType files is not yet supported";
2569             } else {
2570 0         0 $err = 'Writing of this type of file is not supported';
2571             }
2572 0 0       0 $self->Error($err) if $err;
2573 0         0 $rtnVal = 0; # (in case it was undef)
2574             }
2575             # $raf->Close(); # only used to force debug output
2576 243         1234 last; # (didn't really want to loop)
2577             }
2578             # don't return success code if any error occurred
2579 244 50       955 if ($rtnVal > 0) {
2580 244 50 66     1133 if ($outType and $type and $outType ne $type) {
      66        
2581 0         0 my @types = GetFileType($outType);
2582 0 0       0 unless (grep /^$type$/, @types) {
2583 0         0 $self->Error("Can't create $outType file from $type");
2584 0         0 $rtnVal = 0;
2585             }
2586             }
2587 244 50 33     1646 if ($rtnVal > 0 and not Tell($outRef) and not $$self{VALUE}{Error}) {
      33        
2588             # don't write a file with zero length
2589 0 0 0     0 if (defined $hdr and length $hdr) {
2590 0 0       0 $type = '' unless defined $type;
2591 0         0 $self->Error("Can't delete all meta information from $type file");
2592             } else {
2593 0         0 $self->Error('Nothing to write');
2594             }
2595             }
2596 244 50       1356 $rtnVal = 0 if $$self{VALUE}{Error};
2597             }
2598              
2599             # rewrite original file in place if required
2600 244 100       889 if (defined $outBuff) {
2601 1 50 33     12 if ($rtnVal <= 0 or not $$self{CHANGED}) {
    50          
2602             # nothing changed, so no need to write $outBuff
2603             } elsif (UNIVERSAL::isa($inRef,'GLOB')) {
2604 1         2 my $len = length($outBuff);
2605 1         3 my $size;
2606             $rtnVal = -1 unless
2607             seek($inRef, 0, 2) and # seek to the end of file
2608             ($size = tell $inRef) >= 0 and # get the file size
2609             seek($inRef, 0, 0) and # seek back to the start
2610             print $inRef $outBuff and # write the new data
2611             ($len >= $size or # if necessary:
2612 1 50 33     24 eval { truncate($inRef, $len) }); # shorten output file
      33        
      33        
      33        
      33        
2613             } else {
2614 0         0 $$inRef = $outBuff; # replace original data
2615             }
2616 1         6 $outBuff = ''; # free memory but leave $outBuff defined
2617             }
2618             # close input file if we opened it
2619 244 100       799 if ($closeIn) {
2620             # errors on input file are significant if we edited the file in place
2621 215 50 0     6330 $rtnVal and $rtnVal = -1 unless close($inRef) or not defined $outBuff;
      33        
2622 215 50       832 if ($rtnVal > 0) {
2623             # copy Mac OS resource fork if it exists
2624 215 50 33     1372 if ($^O eq 'darwin' and -s "$infile/..namedfork/rsrc") {
2625 0 0       0 if ($$self{DEL_GROUP}{RSRC}) {
2626 0         0 $self->VPrint(0,"Deleting Mac OS resource fork\n");
2627 0         0 ++$$self{CHANGED};
2628             } else {
2629 0         0 $self->VPrint(0,"Copying Mac OS resource fork\n");
2630 0         0 my ($buf, $err);
2631 0         0 local (*SRC, *DST);
2632 0 0       0 if ($self->Open(\*SRC, "$infile/..namedfork/rsrc")) {
2633 0 0       0 if ($self->Open(\*DST, "$outfile/..namedfork/rsrc", '>')) {
2634 0         0 binmode SRC; # (not necessary for Darwin, but let's be thorough)
2635 0         0 binmode DST;
2636 0         0 while (read SRC, $buf, 65536) {
2637 0 0       0 print DST $buf or $err = 'copying', last;
2638             }
2639 0 0 0     0 close DST or $err or $err = 'closing';
2640             } else {
2641             # (this is normal if the destination filesystem isn't Mac OS)
2642 0         0 $self->Warn('Error creating Mac OS resource fork');
2643             }
2644 0         0 close SRC;
2645             } else {
2646 0         0 $err = 'opening';
2647             }
2648 0 0 0     0 $rtnVal = 0 if $err and $self->Error("Error $err Mac OS resource fork", 2);
2649             }
2650             }
2651             # erase input file if renaming while editing information in place
2652 215 50 0     718 $self->Unlink($infile) or $self->Warn('Error erasing original file') if $eraseIn;
2653             }
2654             }
2655             # close output file if we created it
2656 244 100       825 if ($closeOut) {
2657             # close file and set $rtnVal to -1 if there was an error
2658 230 50 0     15920 $rtnVal and $rtnVal = -1 unless close($outRef);
2659             # erase the output file if we weren't successful
2660 230 50       1597 if ($rtnVal <= 0) {
    100          
2661 0         0 $self->Unlink($outfile);
2662             # else rename temporary file if necessary
2663             } elsif ($tmpfile) {
2664 2         19 $self->CopyFileAttrs($infile, $tmpfile); # copy attributes to new file
2665 2 50       16 unless ($self->Rename($tmpfile, $infile)) {
2666             # some filesystems won't overwrite with 'rename', so try erasing original
2667 0 0       0 if (not $self->Unlink($infile)) {
    0          
2668 0         0 $self->Unlink($tmpfile);
2669 0         0 $self->Error('Error renaming temporary file');
2670 0         0 $rtnVal = 0;
2671             } elsif (not $self->Rename($tmpfile, $infile)) {
2672 0         0 $self->Error('Error renaming temporary file after deleting original');
2673 0         0 $rtnVal = 0;
2674             }
2675             }
2676             # the output file should now have the name of the original infile
2677 2 50       14 $outfile = $infile if $rtnVal > 0;
2678             }
2679             }
2680             # set filesystem attributes if requested (and if possible!)
2681 244 50 100     1831 if ($rtnVal > 0 and ($closeOut or (defined $outBuff and ($closeIn or UNIVERSAL::isa($infile,'GLOB'))))) {
      66        
2682 231 100       1148 my $target = $closeOut ? $outfile : $infile;
2683             # set file permissions if requested
2684 231 50       1687 ++$$self{CHANGED} if $self->SetSystemTags($target) > 0;
2685 231 100       856 if ($closeIn) { # (no use setting file times unless the input file is closed)
2686 206 50 33     977 ++$$self{CHANGED} if $setModDate and $self->SetFileModifyDate($target, $originalTime, undef, 1) > 0;
2687             # set FileCreateDate if requested (and if possible!)
2688 206 50 33     956 ++$$self{CHANGED} if $setCreateDate and $self->SetFileModifyDate($target, $createTime, 'FileCreateDate', 1) > 0;
2689             # create hard link if requested and no output filename specified (and if possible!)
2690 206 50 33     1012 ++$$self{CHANGED} if defined $hardLink and $self->SetFileName($target, $hardLink, 'HardLink');
2691 206 50 33     1321 ++$$self{CHANGED} if defined $symLink and $self->SetFileName($target, $symLink, 'SymLink');
2692 206 50       938 defined $testName and $self->SetFileName($target, $testName, 'Test');
2693             }
2694             }
2695             # check for write error and set appropriate error message and return value
2696 244 50       1354 if ($rtnVal < 0) {
    50          
2697 0 0       0 $self->Error('Error writing output file') unless $$self{VALUE}{Error};
2698 0         0 $rtnVal = 0; # return 0 on failure
2699             } elsif ($rtnVal > 0) {
2700 244 100       1203 ++$rtnVal unless $$self{CHANGED};
2701             }
2702             # set things back to the way they were
2703 244         820 $$self{RAF} = $oldRaf;
2704              
2705 244         3636 return $rtnVal;
2706             }
2707              
2708             #------------------------------------------------------------------------------
2709             # Get list of all available tags for specified group
2710             # Inputs: 0) optional group name (or string of names separated by colons)
2711             # Returns: tag list (sorted alphabetically)
2712             # Notes: Can't get tags for specific IFD
2713             sub GetAllTags(;$)
2714             {
2715 0     0 1 0 local $_;
2716 0         0 my $group = shift;
2717 0         0 my (%allTags, @groups);
2718 0 0       0 @groups = split ':', $group if $group;
2719              
2720 0         0 my $et = Image::ExifTool->new;
2721 0         0 LoadAllTables(); # first load all our tables
2722 0         0 my @tableNames = keys %allTables;
2723              
2724             # loop through all tables and save tag names to %allTags hash
2725 0         0 while (@tableNames) {
2726 0         0 my $table = GetTagTable(pop @tableNames);
2727             # generate flattened tag names for structure fields if this is an XMP table
2728 0 0 0     0 if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') {
2729 0         0 Image::ExifTool::XMP::AddFlattenedTags($table);
2730             }
2731 0         0 my $tagID;
2732 0         0 foreach $tagID (TagTableKeys($table)) {
2733 0         0 my @infoArray = GetTagInfoList($table,$tagID);
2734 0         0 my $tagInfo;
2735 0         0 GATInfo: foreach $tagInfo (@infoArray) {
2736 0         0 my $tag = $$tagInfo{Name};
2737 0 0       0 $tag or warn("no name for tag!\n"), next;
2738             # don't list subdirectories unless they are writable
2739 0 0 0     0 next if $$tagInfo{SubDirectory} and not $$tagInfo{Writable};
2740 0 0       0 next if $$tagInfo{Hidden}; # ignore hidden tags
2741 0 0       0 if (@groups) {
2742 0         0 my @tg = $et->GetGroup($tagInfo);
2743 0         0 foreach $group (@groups) {
2744 0 0       0 next GATInfo unless grep /^$group$/i, @tg;
2745             }
2746             }
2747 0         0 $allTags{$tag} = 1;
2748             }
2749             }
2750             }
2751 0         0 return sort keys %allTags;
2752             }
2753              
2754             #------------------------------------------------------------------------------
2755             # Get list of all writable tags
2756             # Inputs: 0) optional group name (or names separated by colons)
2757             # Returns: tag list (sorted alphabetically)
2758             sub GetWritableTags(;$)
2759             {
2760 0     0 1 0 local $_;
2761 0         0 my $group = shift;
2762 0         0 my (%writableTags, @groups);
2763 0 0       0 @groups = split ':', $group if $group;
2764              
2765 0         0 my $et = Image::ExifTool->new;
2766 0         0 LoadAllTables();
2767 0         0 my @tableNames = keys %allTables;
2768              
2769 0         0 while (@tableNames) {
2770 0         0 my $tableName = pop @tableNames;
2771 0         0 my $table = GetTagTable($tableName);
2772             # generate flattened tag names for structure fields if this is an XMP table
2773 0 0 0     0 if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') {
2774 0         0 Image::ExifTool::XMP::AddFlattenedTags($table);
2775             }
2776             # attempt to load Write tables if autoloaded
2777 0         0 my @parts = split(/::/,$tableName);
2778 0 0       0 if (@parts > 3) {
2779 0         0 my $i = $#parts - 1;
2780 0         0 $parts[$i] = "Write$parts[$i]"; # add 'Write' before class name
2781 0         0 my $module = join('::',@parts[0..$i]);
2782 0         0 eval { require $module }; # (fails silently if nothing loaded)
  0         0  
2783             }
2784 0         0 my $tagID;
2785 0         0 foreach $tagID (TagTableKeys($table)) {
2786 0         0 my @infoArray = GetTagInfoList($table,$tagID);
2787 0         0 my $tagInfo;
2788 0         0 GWTInfo: foreach $tagInfo (@infoArray) {
2789 0         0 my $tag = $$tagInfo{Name};
2790 0 0       0 $tag or warn("no name for tag!\n"), next;
2791 0         0 my $writable = $$tagInfo{Writable};
2792             next unless $writable or ($$table{WRITABLE} and
2793 0 0 0     0 not defined $writable and not $$tagInfo{SubDirectory});
      0        
      0        
2794 0 0       0 next if $$tagInfo{Hidden}; # ignore hidden tags
2795 0 0       0 if (@groups) {
2796 0         0 my @tg = $et->GetGroup($tagInfo);
2797 0         0 foreach $group (@groups) {
2798 0 0       0 next GWTInfo unless grep /^$group$/i, @tg;
2799             }
2800             }
2801 0         0 $writableTags{$tag} = 1;
2802             }
2803             }
2804             }
2805 0         0 return sort keys %writableTags;
2806             }
2807              
2808             #------------------------------------------------------------------------------
2809             # Get list of all group names
2810             # Inputs: 0) [optional] ExifTool ref, 1) Group family number
2811             # Returns: List of group names (sorted alphabetically)
2812             sub GetAllGroups($;$)
2813             {
2814 0     0 1 0 local $_;
2815 0   0     0 my $family = shift || 0;
2816 0         0 my $self;
2817 0 0 0     0 ref $family and $self = $family, $family = shift || 0;
2818              
2819 0 0       0 $family == 3 and return('Doc#', 'Main');
2820 0 0       0 $family == 4 and return('Copy#');
2821 0 0       0 $family == 5 and return('[too many possibilities to list]');
2822 0 0       0 if ($family == 6) {
2823 0         0 my $fn = \%Image::ExifTool::Exif::formatNumber;
2824 0         0 return(sort { $$fn{$a} <=> $$fn{$b} } keys %$fn);
  0         0  
2825             }
2826 0 0       0 $family == 8 and return('File#');
2827              
2828 0         0 LoadAllTables(); # first load all our tables
2829              
2830 0         0 my @tableNames = keys %allTables;
2831              
2832 0         0 my %allGroups;
2833             # add family 1 groups not in tables
2834 61     61   593 no warnings; # (avoid "possible attempt to put comments in qw()")
  61         151  
  61         6663  
2835             # start with family 1 groups that are missing from the tables
2836 0 0       0 $family == 1 and map { $allGroups{$_} = 1 } qw(Garmin AudioItemList AudioUserData
  0         0  
2837             VideoItemList VideoUserData Track#Keys Track#ItemList Track#UserData KFIX);
2838 61     61   450 use warnings;
  61         157  
  61         355245  
2839             # loop through all tag tables and get all group names
2840 0         0 while (@tableNames) {
2841 0         0 my $table = GetTagTable(pop @tableNames);
2842 0         0 my ($grps, $grp, $tag, $tagInfo);
2843 0 0 0     0 $allGroups{$grp} = 1 if ($grps = $$table{GROUPS}) and ($grp = $$grps{$family});
2844 0         0 foreach $tag (TagTableKeys($table)) {
2845 0         0 my @infoArray = GetTagInfoList($table, $tag);
2846 0 0       0 if ($family == 7) {
2847 0         0 foreach $tagInfo (@infoArray) {
2848 0         0 my $id = $$tagInfo{TagID};
2849 0 0       0 if (not defined $id) {
    0          
2850 0         0 $id = ''; # (just to be safe)
2851             } elsif ($id =~ /^\d+$/) {
2852 0 0 0     0 $id = sprintf('0x%x', $id) if $self and $$self{OPTIONS}{HexTagIDs};
2853             } else {
2854 0         0 $id =~ s/([^-_A-Za-z0-9])/sprintf('%.2x',ord $1)/ge;
  0         0  
2855             }
2856 0         0 $allGroups{'ID-' . $id} = 1;
2857             }
2858             } else {
2859 0         0 foreach $tagInfo (@infoArray) {
2860 0 0 0     0 next unless ($grps = $$tagInfo{Groups}) and ($grp = $$grps{$family});
2861 0         0 $allGroups{$grp} = 1;
2862             }
2863             }
2864             }
2865             }
2866 0         0 delete $allGroups{'*'}; # (not a real group)
2867 0         0 return sort { lc $a cmp lc $b } keys %allGroups;
  0         0  
2868             }
2869              
2870             #------------------------------------------------------------------------------
2871             # get priority group list for new values
2872             # Inputs: 0) ExifTool object reference
2873             # Returns: List of group names
2874             sub GetNewGroups($)
2875             {
2876 0     0 1 0 my $self = shift;
2877 0         0 return @{$$self{WRITE_GROUPS}};
  0         0  
2878             }
2879              
2880             #------------------------------------------------------------------------------
2881             # Get list of all deletable group names
2882             # Returns: List of group names (sorted alphabetically)
2883             sub GetDeleteGroups()
2884             {
2885 0     0 1 0 return sort { lc $a cmp lc $b } @delGroups, @delGroup2;
  0         0  
2886             }
2887              
2888             #------------------------------------------------------------------------------
2889             # Add user-defined tags at run time
2890             # Inputs: 0) destination table name, 1) tagID/tagInfo pairs for tags to add
2891             # Returns: number of tags added
2892             # Notes: will replace existing tags
2893             sub AddUserDefinedTags($%)
2894             {
2895 2     2 1 299 local $_;
2896 2         97 my ($tableName, %addTags) = @_;
2897 2 50       14 my $table = GetTagTable($tableName) or return 0;
2898             # add tags to writer lookup
2899 2         18 Image::ExifTool::TagLookup::AddTags(\%addTags, $tableName);
2900 2         6 my $tagID;
2901 2         5 my $num = 0;
2902 2         7 foreach $tagID (keys %addTags) {
2903 2 50       8 next if $specialTags{$tagID};
2904 2         5 delete $$table{$tagID}; # delete old entry if it existed
2905 2         17 AddTagToTable($table, $tagID, $addTags{$tagID}, 1);
2906 2         4 ++$num;
2907             }
2908 2         11 return $num;
2909             }
2910              
2911             #==============================================================================
2912             # Functions below this are not part of the public API
2913              
2914             #------------------------------------------------------------------------------
2915             # Maintain backward compatibility for old GetNewValues function name
2916             sub GetNewValues($$;$)
2917             {
2918 0     0 0 0 my ($self, $tag, $nvHashPt) = @_;
2919 0         0 return $self->GetNewValue($tag, $nvHashPt);
2920             }
2921              
2922             #------------------------------------------------------------------------------
2923             # Un-escape string according to options settings and clear UTF-8 flag
2924             # Inputs: 0) ExifTool ref, 1) string ref or string ref ref
2925             # Notes: also de-references SCALAR values
2926             sub Sanitize($$)
2927             {
2928 5467     5467 0 12910 my ($self, $valPt) = @_;
2929             # de-reference SCALAR references
2930 5467 50       14835 $$valPt = $$$valPt if ref $$valPt eq 'SCALAR';
2931             # make sure the Perl UTF-8 flag is OFF for the value if perl 5.6 or greater
2932             # (otherwise our byte manipulations get corrupted!!)
2933             # NOTE: Don't use Encode on Windows becase "require Encode" on Windows hangs if cwd is a long path name!!
2934 5467 50 33     27968 if ($] >= 5.006 and ($$self{OPTIONS}{EncodeHangs} or
      33        
2935             eval { require Encode; Encode::is_utf8($$valPt) } or $@))
2936             {
2937             # (SIG handling was added in 10.39. Not sure why, but I've added this to other similar code for 13.02)
2938 0         0 local $SIG{'__WARN__'} = \&SetWarning;
2939             # repack by hand if Encode isn't available
2940 0 0 0     0 $$valPt = ($$self{OPTIONS}{EncodeHangs} or $@) ? pack('C*', unpack($] < 5.010000 ?
    0          
2941             'U0C*' : 'C0C*', $$valPt)) : Encode::encode('utf8', $$valPt);
2942             }
2943             # un-escape value if necessary
2944 5467 100       20074 if ($$self{OPTIONS}{Escape}) {
2945             # (XMP.pm and HTML.pm were require'd as necessary when option was set)
2946 92 50       454 if ($$self{OPTIONS}{Escape} eq 'XML') {
    50          
2947 0         0 $$valPt = Image::ExifTool::XMP::UnescapeXML($$valPt);
2948             } elsif ($$self{OPTIONS}{Escape} eq 'HTML') {
2949 92         512 $$valPt = Image::ExifTool::HTML::UnescapeHTML($$valPt, $$self{OPTIONS}{Charset});
2950             }
2951             }
2952             }
2953              
2954             #------------------------------------------------------------------------------
2955             # Apply inverse conversions
2956             # Inputs: 0) ExifTool ref, 1) value, 2) tagInfo (or Struct item) ref,
2957             # 3) tag name, 4) group 1 name, 5) conversion type (or undef),
2958             # 6) [optional] want group ("" for structure field)
2959             # Returns: 0) converted value, 1) error string (or undef on success)
2960             # Notes:
2961             # - uses ExifTool "ConvType" member when conversion type is undef
2962             # - conversion types other than 'ValueConv' and 'PrintConv' are treated as 'Raw'
2963             sub ConvInv($$$$$;$$)
2964             {
2965 30241     30241 0 86502 my ($self, $val, $tagInfo, $tag, $wgrp1, $convType, $wantGroup) = @_;
2966 30241         46347 my ($err, $type);
2967              
2968 30241 100 50     62480 $convType or $convType = $$self{ConvType} || 'PrintConv';
2969              
2970 30241         43738 Conv: for (;;) {
2971 77067 100       176174 if (not defined $type) {
    100          
2972             # split value into list if necessary
2973 30241 100       74563 if ($$tagInfo{List}) {
2974 600   100     3216 my $listSplit = $$tagInfo{AutoSplit} || $$self{OPTIONS}{ListSplit};
2975 600 50 100     2555 if (defined $listSplit and not $$tagInfo{Struct} and
      66        
      100        
2976             ($wantGroup or not defined $wantGroup))
2977             {
2978 75 50 66     456 $listSplit = ',?\s+' if $listSplit eq '1' and $$tagInfo{AutoSplit};
2979 75         1330 my @splitVal = split /$listSplit/, $val, -1;
2980 75 50       367 $val = @splitVal > 1 ? \@splitVal : @splitVal ? $splitVal[0] : '';
    100          
2981             }
2982             }
2983 30241         49535 $type = $convType;
2984             } elsif ($type eq 'PrintConv') {
2985 22688         35686 $type = 'ValueConv';
2986             } else {
2987             # split raw value if necessary
2988 24138 50 66     63093 if ($$tagInfo{RawJoin} and $$tagInfo{List} and not ref $val) {
      33        
2989 13         56 my @splitVal = split ' ', $val;
2990 13 50       93 $val = \@splitVal if @splitVal > 1;
2991             }
2992             # finally, do our value check
2993 24138         38282 my ($err2, $v);
2994 24138 100       61938 if ($$tagInfo{WriteCheck}) {
2995             #### eval WriteCheck ($self, $tagInfo, $val)
2996 296         30375 $err2 = eval $$tagInfo{WriteCheck};
2997 296 50       1618 $@ and warn($@), $err2 = 'Error evaluating WriteCheck';
2998             }
2999 24138 100       51267 unless (defined $err2) {
3000 24096         48346 my $table = $$tagInfo{Table};
3001 24096 100 100     145490 if ($table and $$table{CHECK_PROC} and not $$tagInfo{RawConvInv}) {
      100        
3002 23154         42120 my $checkProc = $$table{CHECK_PROC};
3003 23154 100       51736 if (ref $val eq 'ARRAY') {
3004             # loop through array values
3005 47         127 foreach $v (@$val) {
3006 139         352 $err2 = &$checkProc($self, $tagInfo, \$v, $convType);
3007 139 50       372 last if $err2;
3008             }
3009             } else {
3010 23107         83544 $err2 = &$checkProc($self, $tagInfo, \$val, $convType);
3011             }
3012             }
3013             }
3014 24138 100       59438 if (defined $err2) {
3015 3817 100       6884 if ($err2) {
3016 3809         7657 $err = "$err2 for $wgrp1:$tag";
3017 3809         30519 $self->VPrint(2, "$err\n");
3018 3809         6377 undef $val; # value was invalid
3019             } else {
3020 8         17 $err = $err2; # empty error (quietly don't write tag)
3021             }
3022             }
3023 24138         46477 last;
3024             }
3025 52929         100312 my $conv = $$tagInfo{$type};
3026 52929         114833 my $convInv = $$tagInfo{"${type}Inv"};
3027             # nothing to do at this level if no conversion defined
3028 52929 100 100     155280 next unless defined $conv or defined $convInv;
3029              
3030 23821         38435 my (@valList, $index, $convList, $convInvList);
3031 23821 100 66     110518 if (ref $val eq 'ARRAY') {
    100          
3032             # handle ValueConv of ListSplit and AutoSplit values
3033 12         63 @valList = @$val;
3034 12         42 $val = $valList[$index = 0];
3035             } elsif (ref $conv eq 'ARRAY' or ref $convInv eq 'ARRAY') {
3036             # handle conversion lists
3037 153         1710 @valList = split /$listSep{$type}/, $val;
3038 153         444 $val = $valList[$index = 0];
3039 153 50       539 if (ref $conv eq 'ARRAY') {
3040 153         320 $convList = $conv;
3041 153         419 $conv = $$conv[0];
3042             }
3043 153 100       442 if (ref $convInv eq 'ARRAY') {
3044 30         66 $convInvList = $convInv;
3045 30         68 $convInv = $$convInv[0];
3046             }
3047             }
3048             # loop through multiple values if necessary
3049 23821         34545 for (;;) {
3050 23873 100       55651 if ($convInv) {
    100          
3051             # capture eval warnings too
3052 13979         69965 local $SIG{'__WARN__'} = \&SetWarning;
3053 13979         26080 undef $evalWarning;
3054 13979 100       27238 if (ref($convInv) eq 'CODE') {
3055 196         902 $val = &$convInv($val, $self);
3056             } else {
3057             #### eval PrintConvInv/ValueConvInv ($val, $self, $wantGroup)
3058 13783         1137074 $val = eval $convInv;
3059 13783 100       64800 $@ and $evalWarning = $@;
3060             }
3061 13979 100       63584 if ($evalWarning) {
    100          
3062             # an empty warning ("\n") ignores tag with no error
3063 227 100       529 if ($evalWarning eq "\n") {
3064 10 50       44 $err = '' unless defined $err;
3065             } else {
3066 217         707 $err = CleanWarning() . " in $wgrp1:$tag (${type}Inv)";
3067 217         970 $self->VPrint(2, "$err\n");
3068             }
3069 227         402 undef $val;
3070 227         937 last Conv;
3071             } elsif (not defined $val) {
3072 137         406 $err = "Error converting value for $wgrp1:$tag (${type}Inv)";
3073 137         817 $self->VPrint(2, "$err\n");
3074 137         674 last Conv;
3075             }
3076             } elsif ($conv) {
3077 9891 100 66     51326 if (ref $conv eq 'HASH' and (not exists $$tagInfo{"${type}Inv"} or $convInvList)) {
    100 66        
3078 9580         17110 my ($multi, $lc);
3079             # insert alternate language print conversions if required
3080 9580 0 33     32247 if ($$self{CUR_LANG} and $type eq 'PrintConv' and
      33        
      0        
3081             ref($lc = $$self{CUR_LANG}{$tag}) eq 'HASH' and
3082             ($lc = $$lc{PrintConv}))
3083             {
3084 0         0 my %newConv;
3085 0         0 foreach (keys %$conv) {
3086 0         0 my $val = $$conv{$_};
3087 0 0       0 defined $$lc{$val} or $newConv{$_} = $val, next;
3088 0         0 $newConv{$_} = $self->Decode($$lc{$val}, 'UTF8');
3089             }
3090 0 0       0 if ($$conv{BITMASK}) {
3091 0         0 foreach (keys %{$$conv{BITMASK}}) {
  0         0  
3092 0         0 my $val = $$conv{BITMASK}{$_};
3093 0 0       0 defined $$lc{$val} or $newConv{BITMASK}{$_} = $val, next;
3094 0         0 $newConv{BITMASK}{$_} = $self->Decode($$lc{$val}, 'UTF8');
3095             }
3096             }
3097 0         0 $conv = \%newConv;
3098             }
3099 9580         17312 undef $evalWarning;
3100 9580 100       22025 if ($$conv{BITMASK}) {
3101 105         297 my $lookupBits = $$conv{BITMASK};
3102 105         337 my ($wbits, $tbits) = @$tagInfo{'BitsPerWord','BitsTotal'};
3103 105         491 my ($val2, $err2) = EncodeBits($val, $lookupBits, $wbits, $tbits);
3104 105 100       402 if ($err2) {
    100          
3105             # ok, try matching a straight value
3106 2         6 ($val, $multi) = ReverseLookup($val, $conv);
3107 2 50       9 unless (defined $val) {
3108 2         15 $err = "Can't encode $wgrp1:$tag ($err2)";
3109 2         15 $self->VPrint(2, "$err\n");
3110 2         7 last Conv;
3111             }
3112             } elsif (defined $val2) {
3113 72         144 $val = $val2;
3114             } else {
3115 31         88 delete $$conv{BITMASK};
3116 31         93 ($val, $multi) = ReverseLookup($val, $conv);
3117 31         103 $$conv{BITMASK} = $lookupBits;
3118             }
3119             } else {
3120 9475         23524 ($val, $multi) = ReverseLookup($val, $conv);
3121             }
3122 9578 100       27521 if (not defined $val) {
    50          
3123 5449 100       18196 my $prob = $evalWarning ? lcfirst CleanWarning() : ($multi ? 'matches more than one ' : 'not in ') . $type;
    50          
3124 5449         11821 $err = "Can't convert $wgrp1:$tag ($prob)";
3125 5449         25688 $self->VPrint(2, "$err\n");
3126 5449         17250 last Conv;
3127             } elsif ($evalWarning) {
3128 0         0 $self->VPrint(2, CleanWarning() . " for $wgrp1:$tag\n");
3129             }
3130             } elsif (not $$tagInfo{WriteAlso}) {
3131 288         807 $err = "Can't convert value for $wgrp1:$tag (no ${type}Inv)";
3132 288         1575 $self->VPrint(2, "$err\n");
3133 288         561 undef $val;
3134 288         1084 last Conv;
3135             }
3136             }
3137 17770 100       58016 last unless @valList;
3138 124         371 $valList[$index] = $val;
3139 124 100       406 if (++$index >= @valList) {
3140             # leave AutoSplit lists in ARRAY form, or join conversion lists
3141 72 100       433 $val = $$tagInfo{List} ? \@valList : join ' ', @valList;
3142 72         239 last;
3143             }
3144 52 100       161 $conv = $$convList[$index] if $convList;
3145 52 100       125 $convInv = $$convInvList[$index] if $convInvList;
3146 52         112 $val = $valList[$index];
3147             }
3148             } # end ValueConv/PrintConv loop
3149              
3150 30241         95451 return($val, $err);
3151             }
3152              
3153             #------------------------------------------------------------------------------
3154             # Dereference value and push onto list
3155             # Inputs: 0) ExifTool ref, 1) value, 2) list ref, 3) flag to push MissingTagValue for undef value
3156             sub PushValue($$$;$)
3157             {
3158 21     21 0 42 local $_;
3159 21         68 my ($self, $val, $list, $missing) = @_;
3160 21 100 66     243 if (ref $val eq 'ARRAY' and ref $$val[0] ne 'HASH') {
    50 33        
    50          
    100          
3161 1         17 $self->PushValue($_, $list, $missing) foreach @$val;
3162             } elsif (ref $val eq 'SCALAR') {
3163 0 0 0     0 if ($$self{OPTIONS}{Binary} or $$val =~ /^Binary data/) {
3164 0         0 push @$list, $$val;
3165             } else {
3166 0         0 push @$list, 'Binary data ' . length($$val) . ' bytes';
3167             }
3168             } elsif (ref $val eq 'HASH' or ref $val eq 'ARRAY') {
3169 0         0 require 'Image/ExifTool/XMPStruct.pl';
3170 0         0 push @$list, Image::ExifTool::XMP::SerializeStruct($self, $val);
3171             } elsif (not defined $val) {
3172 1         4 my $mval = $$self{OPTIONS}{MissingTagValue};
3173 1 50 33     7 push @$list, $mval if $missing and defined $mval;
3174             } else {
3175 19         72 push @$list, $val;
3176             }
3177             }
3178              
3179             #------------------------------------------------------------------------------
3180             # Convert tag names to values or variables in a string
3181             # (eg. '${EXIF:ISO}x $$' --> '100x $' without hash ref, or "$info{'EXIF:ISO'}x $" with)
3182             # Inputs: 0) ExifTool object ref, 1) string with embedded tag names,
3183             # 2) reference to list of found tags or undef to use FOUND_TAGS, 3) Options:
3184             # undef - set missing tags to ''
3185             # 'Error' - issue minor error on missing tag (and return undef if error sent)
3186             # 'Warn' - issue minor warning on missing tag (and return undef if warning sent)
3187             # 'Silent' - just return undef on missing tag (no errors/warnings)
3188             # Hash ref - defined to interpolate as variables in string instead of values
3189             # --> receives tag/value pairs for interpolation of the variables
3190             # 4) document group name if extracting from a specific document
3191             # 5) hash ref to cache tag keys for subsequent calls in document loop
3192             # Returns: string with embedded tag values (or '$info{TAGNAME}' entries with Hash ref option)
3193             # Notes:
3194             # - tag names are not case sensitive and may end with '#' for ValueConv value
3195             # - uses MissingTagValue option if set
3196             # - '$GROUP:all' evaluates to 1 if any tag from GROUP exists, or 0 otherwise
3197             # - advanced feature allows Perl expressions inside braces (eg. '${model;tr/ //d}')
3198             # - an error/warning in an advanced expression ("${TAG;EXPR}") generates an error
3199             # if option set to 'Error', or a warning otherwise
3200             sub InsertTagValues($$;$$$$)
3201             {
3202 10     10 0 72 local $_;
3203 10         49 my ($self, $line, $foundTags, $opt, $docGrp, $cache) = @_;
3204 10         36 my $rtnStr = '';
3205 10         25 my ($docNum, $tag);
3206              
3207 10 50       40 if ($docGrp) {
3208 0 0       0 $docNum = $docGrp =~ /(\d+(-\d+)*)$/ ? $1 : 0;
3209             } else {
3210 10         33 undef $cache; # no cache if no document groups
3211             }
3212 10 50 0     43 $foundTags or $foundTags = $$self{FOUND_TAGS} || [];
3213 10         137 while ($line =~ s/(.*?)\$(\{\s*)?([-\w]*\w|\$|\/)//s) {
3214 14         131 my ($pre, $bra, $var) = ($1, $2, $3);
3215 14         42 my (@tags, $tg, $val, @val, $type, $expr, $didExpr, $level, $asList);
3216             # "$$" represents a "$" symbol, and "$/" is a newline
3217 14 50 33     114 if ($var eq '$' or $var eq '/') {
3218 0 0       0 $line =~ s/^\s*\}// if $bra;
3219 0 0 0     0 if ($var eq '/') {
    0          
3220 0         0 $var = "\n";
3221             } elsif ($line =~ /^self\b/ and not $rtnStr =~ /\$$/) {
3222 0         0 $var = '$$'; # ("$$self{var}" in string)
3223             }
3224 0         0 $rtnStr .= "$pre$var";
3225 0         0 next;
3226             }
3227             # allow multiple group names
3228 14         89 while ($line =~ /^:([-\w]*\w)(.*)/s) {
3229 9         28 my $group = $var;
3230 9         35 ($var, $line) = ($1, $2);
3231 9         47 $var = "$group:$var";
3232             }
3233             # allow trailing '#' to indicate ValueConv value
3234 14 50       94 $type = 'ValueConv' if $line =~ s/^#//;
3235             # special advanced formatting '@' feature to evaluate list values separately
3236 14 100 100     104 if ($bra and $line =~ s/^\@(#)?//) {
3237 2         6 $asList = 1;
3238 2 50       11 $type = 'ValueConv' if $1;
3239             }
3240             # remove trailing bracket if there was a leading one
3241             # and extract Perl expression from inside brackets if it exists
3242 14 100 100     180 if ($bra and $line !~ s/^\s*\}// and $line =~ s/^\s*;\s*(.*?)\s*\}//s) {
      66        
3243 4         15 my $part = $1;
3244 4         12 $expr = '';
3245 4         12 for ($level=0; ; --$level) {
3246             # increase nesting level for each opening brace
3247 8         32 ++$level while $part =~ /\{/g;
3248 8         20 $expr .= $part;
3249 8 100 66     40 last unless $level and $line =~ s/^(.*?)\s*\}//s; # get next part
3250 4         9 $part = $1;
3251 4         6 $expr .= '}'; # this brace was part of the expression
3252             }
3253             # use default Windows filename filter if expression is empty
3254 4 50       19 $expr = 'tr(/\\\\?*:|"<>\\0)()d' unless length $expr;
3255             }
3256 14         69 push @tags, $var;
3257 14         115 ExpandShortcuts(\@tags);
3258 14 50       64 @tags or $rtnStr .= $pre, next;
3259             # save advanced formatting expression to allow access by user-defined ValueConv
3260 14         65 $$self{FMT_EXPR} = $expr;
3261              
3262 14         32 for (;;) {
3263             # temporarily reset ListJoin option if evaluating list values separately
3264 14         28 my $oldListJoin;
3265 14 100       64 $oldListJoin = $self->Options(ListJoin => undef) if $asList;
3266 14         38 $tag = shift @tags;
3267 14         48 my $lcTag = lc $tag;
3268 14 50 33     93 if ($cache and $lcTag !~ /(^|:)all$/) {
3269             # remove group from tag name (but not lower-case version)
3270 0         0 my $group;
3271 0 0       0 $tag =~ s/^(.*):// and $group = $1;
3272             # cache tag keys to speed processing for a large number of sub-documents
3273             # (similar to code in BuildCompositeTags(), but this is case-insensitive)
3274 0         0 my $cacheTag = $$cache{$lcTag};
3275 0 0       0 unless ($cacheTag) {
3276 0         0 $cacheTag = $$cache{$lcTag} = { };
3277             # find all matching keys, organize into groups, and store in cache
3278 0         0 my $ex = $$self{TAG_EXTRA};
3279 0         0 my @matches = grep /^$tag(\s|$)/i, @$foundTags;
3280 0 0       0 @matches = $self->GroupMatches($group, \@matches) if defined $group;
3281 0         0 foreach (@matches) {
3282 0   0     0 my $doc = $$ex{$_}{G3} || 0;
3283 0 0       0 if (defined $$cacheTag{$doc}) {
3284 0 0       0 next unless $$cacheTag{$doc} =~ / \((\d+)\)$/;
3285 0         0 my $cur = $1;
3286             # keep the most recently extracted tag
3287 0 0 0     0 next if / \((\d+)\)$/ and $1 < $cur;
3288             }
3289 0         0 $$cacheTag{$doc} = $_;
3290             }
3291             }
3292 0 0 0     0 my $doc = $lcTag =~ /\b(main|doc(\d+(-\d+)*)):/ ? ($2 || 0) : $docNum;
3293 0 0       0 if ($$cacheTag{$doc}) {
3294 0         0 $tag = $$cacheTag{$doc};
3295 0         0 $val = $self->GetValue($tag, $type);
3296             }
3297             } else {
3298             # add document number to tag if specified and it doesn't already exist
3299 14 50 33     70 if ($docGrp and $lcTag !~ /\b(main|doc\d+):/) {
3300 0         0 $tag = $docGrp . ':' . $tag;
3301 0         0 $lcTag = lc $tag;
3302             }
3303 14         48 my ($et, $fileTags) = ($self, $foundTags);
3304 14 100       101 if ($tag =~ s/(\bfile\d+)://i) {
3305 3         29 $et = $$self{ALT_EXIFTOOL}{ucfirst lc $1};
3306 3 50       15 if ($et) {
3307 3         13 $fileTags = $$et{FoundTags};
3308             } else {
3309 0         0 $et = $self;
3310 0         0 $tag = 'no_alt_file';
3311             }
3312             }
3313 14 50       180 if ($lcTag eq 'all') {
    50          
    100          
    50          
3314 0         0 $val = 1; # always some tag available
3315             } elsif (defined $$et{OPTIONS}{UserParam}{$lcTag}) {
3316 0         0 $val = $$et{OPTIONS}{UserParam}{$lcTag};
3317             } elsif ($tag =~ /(.*):(.+)/) {
3318 4         14 my ($group, @matches);
3319 4         23 ($group, $tag) = ($1, $2);
3320             # join values of all matching tags if "All" group is used
3321             # (and remove "All" from group prefix)
3322 4 50       44 if ($group =~ s/(^|:)(all|\*)(:|$)/$1 and $3/ei) {
  1 100       18  
    50          
3323 1 50       9 if (lc $tag eq 'all') {
3324 1 50       13 @matches = $group ? $et->GroupMatches($group, $fileTags) : @$fileTags;
3325             } else {
3326 0         0 @matches = grep /^$tag(\s|$)/i, @$fileTags;
3327 0 0       0 @matches = $et->GroupMatches($group, \@matches) if $group;
3328             }
3329 1         6 $self->PushValue(scalar $et->GetValue($_, $type), \@val) foreach @matches;
3330             } elsif (lc $tag eq 'all') {
3331             # return "1" if any tag from the specified group exists
3332 0 0       0 $val = $et->GroupMatches($group, $fileTags) ? 1 : 0;
3333             } else {
3334             # find the specified tag
3335 3         1353 @matches = grep /^$tag(\s|$)/i, @$fileTags;
3336 3         36 @matches = $et->GroupMatches($group, \@matches);
3337 3         12 foreach $tg (@matches) {
3338 3 50 33     16 if (defined $val and $tg =~ / \((\d+)\)$/) {
3339             # take the most recently extracted tag
3340 0         0 my $tagNum = $1;
3341 0 0 0     0 next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum;
3342             }
3343 3         20 $val = $et->GetValue($tg, $type);
3344 3         11 $tag = $tg;
3345 3 100       22 last unless $tag =~ / /; # all done if we got our best match
3346             }
3347             }
3348             } elsif ($tag eq 'self') {
3349 0         0 $val = $et; # ("$self{var}" or "$file1:self{var}" in string)
3350             } else {
3351             # get the tag value (note: this direct access allows excluded tags
3352             # to be accessed if the case is correct and a group name is not used)
3353 10         62 $val = $et->GetValue($tag, $type);
3354 10 100       47 unless (defined $val) {
3355             # check for tag name with different case
3356 7         1104 ($tg) = grep /^$tag$/i, @$fileTags;
3357 7 50       43 if (defined $tg) {
3358 7         40 $val = $et->GetValue($tg, $type);
3359 7         28 $tag = $tg;
3360             }
3361             }
3362             }
3363             }
3364 14 100       63 $self->Options(ListJoin => $oldListJoin) if $asList;
3365 14         97 $self->PushValue($val, \@val, $asList);
3366 14         37 undef $val;
3367 14 50       64 last unless @tags;
3368             }
3369 14 50       48 if (@val) {
    0          
3370 14 50       52 $self->PushValue($val, \@val) if defined $val;
3371 14         86 $val = join $$self{OPTIONS}{ListSep}, @val;
3372             } elsif (defined $val) {
3373 0         0 $self->PushValue($val, \@val); # (so the eval has access to @val if required)
3374             }
3375             # evaluate advanced formatting expression if given (eg. "${TAG;EXPR}")
3376 14 100 66     97 if (defined $expr and defined $val) {
3377 4         30 local $SIG{'__WARN__'} = \&SetWarning;
3378 4         10 undef $evalWarning;
3379 4         11 $advFmtSelf = $self; # set variable for access to $self in helper functions
3380 4 100       16 if ($asList) {
3381 2         5 foreach (@val) {
3382             #### eval advanced formatting expression ($_, $self, @val, $tag, $advFmtSelf)
3383 7         646 eval $expr;
3384 7 50       34 $@ and $evalWarning = $@;
3385             }
3386             # join back together if any values are still defined
3387 2         12 @val = grep defined, @val;
3388 2 50       13 $val = @val ? join $$self{OPTIONS}{ListSep}, @val : undef;
3389             } else {
3390 2         4 $_ = $val;
3391             #### eval advanced formatting expression ($_, $self, @val, $tag, $advFmtSelf)
3392 2         254 eval $expr;
3393 2 50       12 $@ and $evalWarning = $@;
3394 2 50       12 $val = ref $_ eq 'ARRAY' ? join($$self{OPTIONS}{ListSep}, @$_): $_;
3395             }
3396 4 50       16 if ($evalWarning) {
3397 0 0 0     0 my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : '';
3398 0         0 my $str = CleanWarning() . " for '$g3${var}'";
3399 0 0       0 if ($opt) {
3400 0 0       0 if ($opt eq 'Error') {
    0          
3401 0         0 $self->Error($str);
3402             } elsif ($opt ne 'Silent') {
3403 0         0 $self->Warn($str);
3404             }
3405             }
3406             }
3407 4         9 undef $advFmtSelf;
3408 4         18 $didExpr = 1; # set flag indicating an expression was evaluated
3409             }
3410 14 0 0     7441 unless (defined $val or (ref $opt and $$self{OPTIONS}{UndefTags})) {
      33        
3411 0         0 $val = $$self{OPTIONS}{MissingTagValue};
3412 0 0       0 unless (defined $val) {
3413 0 0 0     0 my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : '';
3414 0 0       0 my $msg = $didExpr ? "Advanced formatting expression returned undef for '$g3${var}'" :
3415             "Tag '$g3${var}' not defined";
3416 0 0       0 if (ref $opt) {
    0          
3417 0 0       0 $val = '' if $$self{OPTIONS}{IgnoreMinorErrors};
3418             } elsif ($opt) {
3419 61     61   771 no strict 'refs';
  61         148  
  61         59667  
3420 0 0 0     0 ($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef;
3421 0         0 $val = '';
3422             }
3423             }
3424             }
3425 14 50       55 if (ref $opt eq 'HASH') {
3426 0 0       0 $var .= '#' if $type;
3427 0 0       0 if (defined $expr) {
3428             # generate unique variable name for this modified tag value
3429 0         0 my $i = 1;
3430 0         0 ++$i while exists $$opt{"$var.expr$i"};
3431 0         0 $var .= '.expr' . $i;
3432             }
3433 0         0 $rtnStr .= "$pre\$info{'${var}'}";
3434 0         0 $$opt{$var} = $val;
3435             } else {
3436 14         126 $rtnStr .= "$pre$val";
3437             }
3438             }
3439 10         39 $$self{FMT_EXPR} = undef;
3440 10         59 return $rtnStr . $line;
3441             }
3442              
3443             #------------------------------------------------------------------------------
3444             # Reformat date/time value in $_ based on specified format string
3445             # Inputs: 0) date/time format string
3446             # Returns: Reformatted date/time string
3447             sub DateFmt($)
3448             {
3449 0     0 0 0 my $et = bless { OPTIONS => { DateFormat => shift, StrictDate => 1 } };
3450 0         0 my $shift;
3451 0 0 0     0 if ($advFmtSelf and defined($shift = $$advFmtSelf{OPTIONS}{GlobalTimeShift})) {
3452 0         0 $$et{OPTIONS}{GlobalTimeShift} = $shift;
3453 0         0 $$et{GLOBAL_TIME_OFFSET} = $$advFmtSelf{GLOBAL_TIME_OFFSET};
3454             }
3455 0         0 $_ = $et->ConvertDateTime($_);
3456 0 0       0 defined $_ or warn "Error converting date/time\n";
3457 0 0       0 $$advFmtSelf{GLOBAL_TIME_OFFSET} = $$et{GLOBAL_TIME_OFFSET} if $shift;
3458 0         0 return $_;
3459             }
3460              
3461             #------------------------------------------------------------------------------
3462             # Utility routine to remove duplicate items from default input string
3463             # Inputs: 0) true to set $_ to undef if not changed
3464             # Notes: - for use only in advanced formatting expressions
3465             sub NoDups
3466             {
3467 0     0 1 0 my %seen;
3468 0 0       0 my $sep = $advFmtSelf ? $$advFmtSelf{OPTIONS}{ListSep} : ', ';
3469 0         0 my $new = join $sep, grep { !$seen{$_}++ } split /\Q$sep\E/, $_;
  0         0  
3470 0 0 0     0 $_ = ($_[0] and $new eq $_) ? undef : $new;
3471             }
3472              
3473             #------------------------------------------------------------------------------
3474             # Utility routine to set in $_ image from current object
3475             # Inputs: 0-N) list of tags to copy
3476             # Returns: Return value from WriteInfo
3477             # Notes: - for use only in advanced formatting expressions
3478             sub SetTags(@)
3479             {
3480 0     0 0 0 my $self = $advFmtSelf;
3481 0         0 my $et = Image::ExifTool->new;
3482 0         0 $et->SetNewValuesFromFile($self, @_);
3483 0         0 return $et->WriteInfo(\$_);
3484             }
3485              
3486             #------------------------------------------------------------------------------
3487             # Is specified tag writable
3488             # Inputs: 0) tag name, case insensitive (optional group name currently ignored)
3489             # Returns: 0=exists but not writable, 1=writable, undef=doesn't exist
3490             sub IsWritable($)
3491             {
3492 0     0 0 0 my $tag = shift;
3493 0         0 $tag =~ s/^(.*)://; # ignore group name
3494 0         0 my @tagInfo = FindTagInfo($tag);
3495 0 0       0 unless (@tagInfo) {
3496 0 0       0 return 0 if TagExists($tag);
3497 0         0 return undef;
3498             }
3499 0         0 my $tagInfo;
3500 0         0 foreach $tagInfo (@tagInfo) {
3501 0 0       0 return $$tagInfo{Writable} ? 1 : 0 if defined $$tagInfo{Writable};
    0          
3502 0 0       0 return 1 if $$tagInfo{Table}{WRITABLE};
3503             # must call WRITE_PROC to autoload writer because this may set the writable tag
3504 0         0 my $writeProc = $$tagInfo{Table}{WRITE_PROC};
3505 0 0       0 if ($writeProc) {
3506 61     61   595 no strict 'refs';
  61         203  
  61         20385  
3507 0         0 &$writeProc(); # dummy call to autoload writer
3508 0 0       0 return 1 if $$tagInfo{Writable};
3509             }
3510             }
3511 0         0 return 0;
3512             }
3513              
3514             #------------------------------------------------------------------------------
3515             # Check to see if these are the same file
3516             # Inputs: 0) ExifTool ref, 1) first file name, 2) second file name
3517             # Returns: true if file names reference the same file
3518             sub IsSameFile($$$)
3519             {
3520 0     0 0 0 my ($self, $file, $file2) = @_;
3521 0 0       0 return 0 unless lc $file eq lc $file2; # (only looking for differences in case)
3522 0         0 my ($isSame, $interrupted);
3523 0         0 my $tmp1 = "${file}_ExifTool_tmp_$$";
3524 0         0 my $tmp2 = "${file2}_ExifTool_tmp_$$";
3525             {
3526 0         0 local *TMP1;
  0         0  
3527 0     0   0 local $SIG{INT} = sub { $interrupted = 1 };
  0         0  
3528 0 0       0 if ($self->Open(\*TMP1, $tmp1, '>')) {
3529 0         0 close TMP1;
3530 0 0       0 $isSame = 1 if $self->Exists($tmp2);
3531 0         0 $self->Unlink($tmp1);
3532             }
3533             }
3534 0 0 0     0 if ($interrupted and $SIG{INT}) {
3535 61     61   501 no strict 'refs';
  61         154  
  61         205576  
3536 0         0 &{$SIG{INT}}();
  0         0  
3537             }
3538 0         0 return $isSame;
3539             }
3540              
3541             #------------------------------------------------------------------------------
3542             # Is this a raw file type?
3543             # Inputs: 0) ExifTool ref
3544             # Returns: true if FileType is a type of RAW image
3545             sub IsRawType($)
3546             {
3547 13     13 0 39 my $self = shift;
3548 13         168 return $rawType{$$self{FileType}};
3549             }
3550              
3551             #------------------------------------------------------------------------------
3552             # Copy file attributes from one file to another
3553             # Inputs: 0) ExifTool ref, 1) source file name, 2) destination file name
3554             # Notes: eventually add support for extended attributes?
3555             sub CopyFileAttrs($$$)
3556             {
3557 2     2 0 28 my ($self, $src, $dst) = @_;
3558 2         59 my ($mode, $uid, $gid) = (stat($src))[2, 4, 5];
3559             # copy file attributes unless we already set them
3560 2 50 33     24 if (defined $mode and not defined $self->GetNewValue('FilePermissions')) {
3561 2         6 eval { chmod($mode & 07777, $dst) };
  2         100  
3562             }
3563 2         16 my $newUid = $self->GetNewValue('FileUserID');
3564 2         10 my $newGid = $self->GetNewValue('FileGroupID');
3565 2 50 33     31 if (defined $uid and defined $gid and (not defined $newUid or not defined $newGid)) {
      33        
      33        
3566 2 50       8 defined $newGid and $gid = $newGid;
3567 2 50       9 defined $newUid and $uid = $newUid;
3568 2         8 eval { chown($uid, $gid, $dst) };
  2         82  
3569             }
3570             }
3571              
3572             #------------------------------------------------------------------------------
3573             # Get new file path name
3574             # Inputs: 0) existing name (may contain directory),
3575             # 1) new file name, new directory, or new path (dir+name)
3576             # Returns: new file path name
3577             sub GetNewFileName($$)
3578             {
3579 1     1 0 4 my ($oldName, $newName) = @_;
3580 1         8 my ($dir, $name) = ($oldName =~ m{(.*/)(.*)});
3581 1 50       3 ($dir, $name) = ('', $oldName) unless defined $dir;
3582 1 50       7 if ($newName =~ m{/$}) {
    50          
3583 0         0 $newName = "$newName$name"; # change dir only
3584             } elsif ($newName !~ m{/}) {
3585 1         3 $newName = "$dir$newName"; # change name only if newname doesn't specify dir
3586             } # else change dir and name
3587 1         2 return $newName;
3588             }
3589              
3590             #------------------------------------------------------------------------------
3591             # Get next available tag key
3592             # Inputs: 0) hash reference (keys are tag keys), 1) tag name
3593             # Returns: next available tag key
3594             sub NextFreeTagKey($$)
3595             {
3596 0     0 0 0 my ($info, $tag) = @_;
3597 0 0       0 return $tag unless exists $$info{$tag};
3598 0         0 my $i;
3599 0         0 for ($i=1; ; ++$i) {
3600 0         0 my $key = "$tag ($i)";
3601 0 0       0 return $key unless exists $$info{$key};
3602             }
3603             }
3604              
3605             #------------------------------------------------------------------------------
3606             # Reverse hash lookup
3607             # Inputs: 0) value, 1) hash reference
3608             # Returns: Hash key or undef if not found (plus flag for multiple matches in list context)
3609             sub ReverseLookup($$)
3610             {
3611 9575     9575 0 22147 my ($val, $conv) = @_;
3612 9575 100       22615 return undef unless defined $val;
3613 9513         17269 my $multi;
3614 9513 100       20944 if ($val =~ /^Unknown\s*\((.*)\)$/i) {
3615 40         116 $val = $1; # was unknown
3616 40 50       106 if ($val =~ /^0x([\da-fA-F]+)$/) {
3617             # disable "Hexadecimal number > 0xffffffff non-portable" warning
3618 0     0   0 local $SIG{'__WARN__'} = sub { };
3619 0         0 $val = hex($val); # convert hex value
3620             }
3621             } else {
3622 9473         15963 my $qval = $val;
3623 9473         25010 $qval =~ s/\s+$//; # remove trailing whitespace
3624 9473         17615 $qval = quotemeta $qval;
3625 9473         42129 my @patterns = (
3626             "^$qval\$", # exact match
3627             "^(?i)$qval\$", # case-insensitive
3628             "^(?i)$qval", # beginning of string
3629             "(?i)$qval", # substring
3630             );
3631             # hash entries to ignore in reverse lookup
3632 9473         14881 my ($pattern, $found, $matches);
3633 9473         18396 PAT: foreach $pattern (@patterns) {
3634 24755         669950 $matches = scalar grep /$pattern/, values(%$conv);
3635 24755 100       61893 next unless $matches;
3636             # multiple matches are bad unless they were exact
3637 7249 100 100     28921 if ($matches > 1 and $pattern !~ /\$$/) {
3638             # don't match entries that we should ignore
3639 3907         10807 foreach (keys %ignorePrintConv) {
3640 11721 100 100     27686 --$matches if defined $$conv{$_} and $$conv{$_} =~ /$pattern/;
3641             }
3642 3907 100       10183 last if $matches > 1;
3643             }
3644 3474         68281 foreach (sort keys %$conv) {
3645 10235 100 100     51128 next if $$conv{$_} !~ /$pattern/ or $ignorePrintConv{$_};
3646 3425         6633 $val = $_;
3647 3425         5426 $found = 1;
3648 3425         7718 last PAT;
3649             }
3650             }
3651 9473 100       32329 unless ($found) {
3652             # call OTHER conversion routine if available
3653 6048 100       17760 if ($$conv{OTHER}) {
3654 808         4484 local $SIG{'__WARN__'} = \&SetWarning;
3655 808         1769 undef $evalWarning;
3656 808         1322 $val = &{$$conv{OTHER}}($val,1,$conv);
  808         3624  
3657             } else {
3658 5240         8408 $val = undef;
3659             }
3660 6048 100       19261 $multi = 1 if $matches > 1;
3661             }
3662             }
3663 9513 100       36227 return ($val, $multi) if wantarray;
3664 47         132 return $val;
3665             }
3666              
3667             #------------------------------------------------------------------------------
3668             # Return true if we are deleting or overwriting the specified tag
3669             # Inputs: 0) ExifTool object ref, 1) new value hash reference
3670             # 2) optional tag value (before RawConv) if deleting specific values
3671             # Returns: >0 - tag should be overwritten
3672             # =0 - the tag should be preserved
3673             # <0 - not sure, we need the old value to tell (if there is no old value
3674             # then the tag should be written if $$nvHash{IsCreating} is true)
3675             # Notes: $$nvHash{Value} is updated with the new value when shifting a value
3676             sub IsOverwriting($$;$)
3677             {
3678 6708     6708 0 14295 my ($self, $nvHash, $val) = @_;
3679 6708 100       15612 return 0 unless $nvHash;
3680             # overwrite regardless if no DelValues specified
3681 6666 100       26960 return 1 unless $$nvHash{DelValue};
3682             # never overwrite if DelValue list exists but is empty
3683 117         271 my $shift = $$nvHash{Shift};
3684 117 100 100     170 return 0 unless @{$$nvHash{DelValue}} or defined $shift;
  117         487  
3685             # return "don't know" if we don't have a value to test
3686 104 100       393 return -1 unless defined $val;
3687             # apply raw conversion if necessary
3688 46         106 my $tagInfo = $$nvHash{TagInfo};
3689 46         143 my $conv = $$tagInfo{RawConv};
3690 46 100       121 if ($conv) {
3691 3         21 local $SIG{'__WARN__'} = \&SetWarning;
3692 3         8 undef $evalWarning;
3693 3 50       14 if (ref $conv eq 'CODE') {
3694 0         0 $val = &$conv($val, $self);
3695             } else {
3696 3         7 my ($priority, @grps);
3697 3         9 my $tag = $$tagInfo{Name};
3698             #### eval RawConv ($self, $val, $tag, $tagInfo, $priority, @grps)
3699 3         301 $val = eval $conv;
3700 3 50       18 $@ and $evalWarning = $@;
3701             }
3702 3 50       16 return -1 unless defined $val;
3703             }
3704             # do not overwrite if only creating
3705 46 100       169 return 0 if $$nvHash{CreateOnly};
3706             # apply time/number shift if necessary
3707 40 100       110 if (defined $shift) {
3708 13         44 my $shiftType = $$tagInfo{Shift};
3709 13 100 66     79 unless ($shiftType and $shiftType eq 'Time') {
3710 6 50       33 unless (IsFloat($val)) {
3711             # do the ValueConv to try to get a number
3712 0         0 my $conv = $$tagInfo{ValueConv};
3713 0 0       0 if (defined $conv) {
3714 0         0 local $SIG{'__WARN__'} = \&SetWarning;
3715 0         0 undef $evalWarning;
3716 0 0       0 if (ref $conv eq 'CODE') {
    0          
3717 0         0 $val = &$conv($val, $self);
3718             } elsif (not ref $conv) {
3719             #### eval ValueConv ($val, $self)
3720 0         0 $val = eval $conv;
3721 0 0       0 $@ and $evalWarning = $@;
3722             }
3723 0 0       0 if ($evalWarning) {
3724 0         0 $self->Warn("ValueConv $$tagInfo{Name}: " . CleanWarning());
3725 0         0 return 0;
3726             }
3727             }
3728 0 0 0     0 unless (defined $val and IsFloat($val)) {
3729 0         0 $self->Warn("Can't shift $$tagInfo{Name} (not a number)");
3730 0         0 return 0;
3731             }
3732             }
3733 6         25 $shiftType = 'Number'; # allow any number to be shifted
3734             }
3735 13         124 require 'Image/ExifTool/Shift.pl';
3736 13         99 my $err = $self->ApplyShift($shiftType, $shift, $val, $nvHash);
3737 13 50       47 if ($err) {
3738 0         0 $self->Warn("$err when shifting $$tagInfo{Name}");
3739 0         0 return 0;
3740             }
3741             # ensure that the shifted value is valid and reformat if necessary
3742 13         86 my $checkVal = $self->GetNewValue($nvHash);
3743 13 50       76 return 0 unless defined $checkVal;
3744             # don't bother overwriting if value is the same
3745 13 50       101 return 0 if $val eq $$nvHash{Value}[0];
3746 13         86 return 1;
3747             }
3748             # return 1 if value matches a DelValue
3749 27         47 my $delVal;
3750 27         40 foreach $delVal (@{$$nvHash{DelValue}}) {
  27         66  
3751 32 100       124 return 1 if $val eq $delVal;
3752             }
3753 17         57 return 0;
3754             }
3755              
3756             #------------------------------------------------------------------------------
3757             # Get write group for specified tag
3758             # Inputs: 0) new value hash reference
3759             # Returns: Write group name
3760             sub GetWriteGroup($)
3761             {
3762 0     0 0 0 return $_[0]{WriteGroup};
3763             }
3764              
3765             #------------------------------------------------------------------------------
3766             # Get name of write group or family 1 group
3767             # Inputs: 0) ExifTool ref, 1) tagInfo ref, 2) write group name
3768             # Returns: Name of group for verbose message
3769             sub GetWriteGroup1($$)
3770             {
3771 34647     34647 0 74024 my ($self, $tagInfo, $writeGroup) = @_;
3772 34647 100       170304 return $writeGroup unless $writeGroup =~ /^(MakerNotes|XMP|Composite|QuickTime)$/;
3773 28973         102818 return $self->GetGroup($tagInfo, 1);
3774             }
3775              
3776             #------------------------------------------------------------------------------
3777             # Get list of tags to write for Geolocate feature
3778             # Inputs: 0) ExifTool ref, 1) group name(s),
3779             # 2) 0=prefer writing City, 1=prefer writing GPS, undef=deleting tags
3780             # Returns: list of tags to write/delete
3781             sub GetGeolocateTags($$;$)
3782             {
3783 5     5 0 42 my ($self, $wantGroup, $writeGPS) = @_;
3784 5 100       41 my @grps = $wantGroup ? map lc, split(/:/, $wantGroup) : ();
3785 5         17 my %grps = map { $_ => $_ } @grps; # lookup for specified groups
  4         27  
3786 5 50 33     33 $grps{exif} and not $grps{gps} and $grps{gps} = 'gps', push(@grps, 'gps');
3787 5         145 my %tagGroups = (
3788             'xmp-iptcext' => [ qw(LocationShownCity LocationShownProvinceState LocationShownCountryCode
3789             LocationShownCountryName LocationShownGPSLatitude LocationShownGPSLongitude) ],
3790             'xmp-photoshop' => [ qw(City State Country) ],
3791             'xmp-iptccore' => [ 'CountryCode' ],
3792             'iptc' => [ qw(City Province-State Country-PrimaryLocationCode Country-PrimaryLocationName) ],
3793             'gps' => [ qw(GPSLatitude GPSLongitude GPSLatitudeRef GPSLongitudeRef) ],
3794             'xmp-exif' => [ qw(GPSLatitude GPSLongitude) ],
3795             'itemlist' => [ 'GPSCoordinates' ],
3796             'userdata' => [ 'GPSCoordinates' ],
3797             # more general groups not in this lookup: XMP and QuickTime
3798             );
3799 5         19 my (@tags, $grp);
3800             # set specific City and GPS tags
3801 5         16 foreach $grp (@grps) {
3802 4 100       20 $tagGroups{$grp} and push @tags, map("$grp:$_", @{$tagGroups{$grp}});
  2         23  
3803             }
3804             # set default XMP City tags if necessary
3805 5 100       21 if (not $writeGPS) {
3806 4 50       19 push @tags, 'Keys:LocationName' if $grps{'keys'};
3807 4 100 66     36 if ($grps{xmp} or (not @tags and not $grps{quicktime})) {
      100        
3808 3         15 push @tags, qw(XMP:City XMP:State XMP:CountryCode XMP:Country Keys:LocationName);
3809             }
3810             }
3811 5 100       17 $writeGPS = 1 unless defined $writeGPS; # (delete both City and GPS)
3812 5 50 66     25 push @tags, 'Keys:GPSCoordinates' if $writeGPS and $grps{'keys'};
3813             # set default QuickTime tag if necessary
3814 5         31 my $didQT = grep /GPSCoordinates$/, @tags;
3815 5 100 33     63 if (($grps{quicktime} and not $didQT) or ($writeGPS and not @tags and not $grps{xmp})) {
      100        
      66        
      66        
3816 1         39 push @tags, 'QuickTime:GPSCoordinates';
3817             }
3818             # set default GPS tags if necessary
3819 5 100       19 if ($writeGPS) {
3820 2 50 33     10 push @tags, qw(XMP:GPSLatitude XMP:GPSLongitude) if $grps{xmp} and not $grps{'xmp-exif'};
3821 2 100       17 push @tags, qw(GPSLatitude GPSLongitude GPSLatitudeRef GPSLongitudeRef) if not $wantGroup;
3822             }
3823 5         207 return @tags;
3824             }
3825              
3826             #------------------------------------------------------------------------------
3827             # Get new value hash for specified tagInfo/writeGroup
3828             # Inputs: 0) ExifTool object reference, 1) reference to tag info hash
3829             # 2) Write group name, 3) Options: 'delete' or 'create' new value hash
3830             # 4) optional ProtectSaved value, 5) true if we are deleting a value
3831             # Returns: new value hash reference for specified write group
3832             # (or first new value hash in linked list if write group not specified)
3833             # Notes: May return undef when 'create' is used with ProtectSaved
3834             sub GetNewValueHash($$;$$$$)
3835             {
3836 69883     69883 0 184641 my ($self, $tagInfo, $writeGroup, $opts) = @_;
3837 69883 100       140276 return undef unless $tagInfo;
3838 69879         180652 my $nvHash = $$self{NEW_VALUE}{$tagInfo};
3839              
3840 69879         99679 my %opts; # quick lookup for options
3841 69879 100       160505 $opts and $opts{$opts} = 1;
3842 69879 100       145344 $writeGroup = '' unless defined $writeGroup;
3843              
3844 69879 100       128389 if ($writeGroup) {
3845             # find the new value in the list with the specified write group
3846 47350   100     125898 while ($nvHash and $$nvHash{WriteGroup} ne $writeGroup) {
3847             # QuickTime and All are special cases because all group1 tags may be updated at once
3848 2007 100       7061 last if $$nvHash{WriteGroup} =~ /^(QuickTime|All)$/;
3849             # replace existing entry if WriteGroup is 'All' (avoids confusion of forum10349)
3850             #last if $$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All'; # (didn't work for forum17770)
3851             # forum17770 patch (also handles case where "EXIF" is specified as a write group)
3852 1971 100 33     6668 last if $writeGroup eq 'All' or $$nvHash{WriteGroup} eq 'EXIF' and $writeGroup =~ /IFD/;
      66        
3853 1968         5250 $nvHash = $$nvHash{Next};
3854             }
3855             }
3856             # remove this entry if deleting, or if creating a new entry and
3857             # this entry is marked with "Save" flag
3858 69879 100 100     192636 if (defined $nvHash and ($opts{'delete'} or ($opts{'create'} and $$nvHash{Save}))) {
      100        
3859 2402   33     8746 my $protect = (defined $_[4] and defined $$nvHash{Save} and $$nvHash{Save} > $_[4]);
3860             # this is a bit tricky: we want to add to a protected nvHash only if we
3861             # are adding a conditional delete ($_[5] true or DelValue with no Shift)
3862             # or accumulating List items (NoReplace true)
3863             # (NOTE: this should be looked into --> lists may be accumulated instead of being replaced
3864             # as expected when copying to the same list from different dynamic -tagsFromFile source files)
3865 2402 50 0     10902 if ($protect and not ($opts{create} and ($$nvHash{NoReplace} or $_[5] or
    100 33        
3866             ($$nvHash{DelValue} and not defined $$nvHash{Shift}))))
3867             {
3868 0         0 return undef; # honour ProtectSaved value by not writing this tag
3869             } elsif ($opts{'delete'}) {
3870 2392         9217 $self->RemoveNewValueHash($nvHash, $tagInfo);
3871 2392         11929 undef $nvHash;
3872             } else {
3873             # save a copy of this new value hash
3874 10         159 my %copy = %$nvHash;
3875             # make copy of Value and DelValue lists
3876 10         31 my $key;
3877 10         48 foreach $key (keys %copy) {
3878 85 100       269 next unless ref $copy{$key} eq 'ARRAY';
3879 10         17 $copy{$key} = [ @{$copy{$key}} ];
  10         87  
3880             }
3881 10         41 my $saveHash = $$self{SAVE_NEW_VALUE};
3882             # add to linked list of saved new value hashes
3883 10         49 $copy{Next} = $$saveHash{$tagInfo};
3884 10         38 $$saveHash{$tagInfo} = \%copy;
3885 10         25 delete $$nvHash{Save}; # don't save it again
3886 10 0 33     46 $$nvHash{AddBefore} = scalar @{$$nvHash{Value}} if $protect and $$nvHash{Value};
  0         0  
3887             }
3888             }
3889 69879 100 100     209913 if (not defined $nvHash and $opts{'create'}) {
3890             # create a new entry
3891             $nvHash = {
3892             TagInfo => $tagInfo,
3893             WriteGroup => $writeGroup,
3894             IsNVH => 1, # set flag so we can recognize a new value hash
3895 23871         134804 Order => $$self{NV_COUNT}++,
3896             };
3897             # add entry to our NEW_VALUE hash
3898 23871 100       63272 if ($$self{NEW_VALUE}{$tagInfo}) {
3899             # add to end of linked list
3900 36         184 my $lastHash = LastInList($$self{NEW_VALUE}{$tagInfo});
3901 36         105 $$lastHash{Next} = $nvHash;
3902             } else {
3903 23835         70750 $$self{NEW_VALUE}{$tagInfo} = $nvHash;
3904             }
3905             }
3906 69879         172165 return $nvHash;
3907             }
3908              
3909             #------------------------------------------------------------------------------
3910             # Load all tag tables
3911             sub LoadAllTables()
3912             {
3913 0 0   0 0 0 return if $loadedAllTables;
3914              
3915             # load all of our non-referenced tables (first our modules)
3916 0         0 my $table;
3917 0         0 foreach $table (@loadAllTables) {
3918 0         0 my $tableName = "Image::ExifTool::$table";
3919 0 0       0 $tableName .= '::Main' unless $table =~ /:/;
3920 0         0 GetTagTable($tableName);
3921             }
3922             # (then our special tables)
3923 0         0 GetTagTable('Image::ExifTool::Extra');
3924 0         0 GetTagTable('Image::ExifTool::Composite');
3925             # recursively load all tables referenced by the current tables
3926 0         0 my @tableNames = keys %allTables;
3927 0         0 my %pushedTables;
3928 0         0 while (@tableNames) {
3929 0         0 $table = GetTagTable(shift @tableNames);
3930             # call write proc if it exists in case it adds tags to the table
3931 0         0 my $writeProc = $$table{WRITE_PROC};
3932 0 0       0 if ($writeProc) {
3933 61     61   570 no strict 'refs';
  61         196  
  61         246960  
3934 0         0 &$writeProc();
3935             }
3936             # recursively scan through tables in subdirectories
3937 0         0 foreach (TagTableKeys($table)) {
3938 0         0 my @infoArray = GetTagInfoList($table,$_);
3939 0         0 my $tagInfo;
3940 0         0 foreach $tagInfo (@infoArray) {
3941 0 0       0 my $subdir = $$tagInfo{SubDirectory} or next;
3942 0 0       0 my $tableName = $$subdir{TagTable} or next;
3943             # next if table already loaded or queued for loading
3944 0 0 0     0 next if $allTables{$tableName} or $pushedTables{$tableName};
3945 0         0 push @tableNames, $tableName; # must scan this one too
3946 0         0 $pushedTables{$tableName} = 1;
3947             }
3948             }
3949             }
3950 0         0 $loadedAllTables = 1;
3951             }
3952              
3953             #------------------------------------------------------------------------------
3954             # Remove new value hash from linked list (and save if necessary)
3955             # Inputs: 0) ExifTool object reference, 1) new value hash ref, 2) tagInfo ref
3956             sub RemoveNewValueHash($$$)
3957             {
3958 2665     2665 0 6576 my ($self, $nvHash, $tagInfo) = @_;
3959 2665         6923 my $firstHash = $$self{NEW_VALUE}{$tagInfo};
3960 2665 50       7554 if ($nvHash eq $firstHash) {
3961             # remove first entry from linked list
3962 2665 50       6470 if ($$nvHash{Next}) {
3963 0         0 $$self{NEW_VALUE}{$tagInfo} = $$nvHash{Next};
3964             } else {
3965 2665         7794 delete $$self{NEW_VALUE}{$tagInfo};
3966             }
3967             } else {
3968             # find the list element pointing to this hash
3969 0         0 $firstHash = $$firstHash{Next} while $$firstHash{Next} ne $nvHash;
3970             # remove from linked list
3971 0         0 $$firstHash{Next} = $$nvHash{Next};
3972             }
3973             # save the existing entry if necessary
3974 2665 100       9164 if ($$nvHash{Save}) {
3975 79         134 my $saveHash = $$self{SAVE_NEW_VALUE};
3976             # add to linked list of saved new value hashes
3977 79         185 $$nvHash{Next} = $$saveHash{$tagInfo};
3978 79         265 $$saveHash{$tagInfo} = $nvHash;
3979             }
3980             }
3981              
3982             #------------------------------------------------------------------------------
3983             # Remove all new value entries for specified group
3984             # Inputs: 0) ExifTool object reference, 1) group name
3985             sub RemoveNewValuesForGroup($$)
3986             {
3987 955     955 0 1582 my ($self, $group) = @_;
3988              
3989 955 100       2025 return unless $$self{NEW_VALUE};
3990              
3991             # make list of all groups we must remove
3992 11         42 my @groups = ( $group );
3993 11 100       57 push @groups, @{$removeGroups{$group}} if $removeGroups{$group};
  3         13  
3994              
3995 11         28 my ($out, @keys, $hashKey);
3996 11 50       56 $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose} > 1;
3997              
3998             # loop though all new values, and remove any in this group
3999 11         29 @keys = keys %{$$self{NEW_VALUE}};
  11         1057  
4000 11         61 foreach $hashKey (@keys) {
4001 2008         6154 my $nvHash = $$self{NEW_VALUE}{$hashKey};
4002             # loop through each entry in linked list
4003 2008         3501 for (;;) {
4004 2014         4476 my $nextHash = $$nvHash{Next};
4005 2014         5043 my $tagInfo = $$nvHash{TagInfo};
4006 2014         7196 my ($grp0,$grp1) = $self->GetGroup($tagInfo);
4007 2014         6123 my $wgrp = $$nvHash{WriteGroup};
4008             # use group1 if write group is not specific
4009 2014 100       5610 $wgrp = $grp1 if $wgrp eq $grp0;
4010 2014 100 33     79557 if ($grp0 eq '*' or $wgrp eq '*' or grep /^($grp0|$wgrp)$/i, @groups) {
      66        
4011 273 50       845 $out and print $out "Removed new value for $wgrp:$$tagInfo{Name}\n";
4012             # remove from linked list
4013 273         997 $self->RemoveNewValueHash($nvHash, $tagInfo);
4014             }
4015 2014 100       10335 $nvHash = $nextHash or last;
4016             }
4017             }
4018             }
4019              
4020             #------------------------------------------------------------------------------
4021             # Get list of tagInfo hashes for all new data
4022             # Inputs: 0) ExifTool object reference, 1) optional tag table pointer
4023             # Returns: list of tagInfo hashes in no particular order
4024             sub GetNewTagInfoList($;$)
4025             {
4026 1257     1257 0 4147 my ($self, $tagTablePtr) = @_;
4027 1257         2505 my @tagInfoList;
4028 1257         3672 my $nv = $$self{NEW_VALUE};
4029 1257 100       4247 if ($nv) {
4030 1236         2195 my $hashKey;
4031 1236         33466 foreach $hashKey (keys %$nv) {
4032 91788         196457 my $tagInfo = $$nv{$hashKey}{TagInfo};
4033 91788 100 100     298031 next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table};
4034 33492         54555 push @tagInfoList, $tagInfo;
4035             }
4036             }
4037 1257         20876 return @tagInfoList;
4038             }
4039              
4040             #------------------------------------------------------------------------------
4041             # Get hash of tagInfo references keyed on tagID for a specific table
4042             # Inputs: 0) ExifTool object reference, 1-N) tag table pointers
4043             # Returns: hash reference
4044             # Notes: returns only one tagInfo ref for each conditional list
4045             sub GetNewTagInfoHash($@)
4046             {
4047 585     585 0 1259 my $self = shift;
4048 585         1236 my (%tagInfoHash, $hashKey);
4049 585         1501 my $nv = $$self{NEW_VALUE};
4050 585         1696 while ($nv) {
4051 1157   100     3281 my $tagTablePtr = shift || last;
4052 581         7905 foreach $hashKey (keys %$nv) {
4053 22281         49031 my $tagInfo = $$nv{$hashKey}{TagInfo};
4054 22281 100 66     94121 next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table};
4055 314         1456 $tagInfoHash{$$tagInfo{TagID}} = $tagInfo;
4056             }
4057             }
4058 585         2227 return \%tagInfoHash;
4059             }
4060              
4061             #------------------------------------------------------------------------------
4062             # Get a tagInfo/tagID hash for subdirectories we need to add
4063             # Inputs: 0) ExifTool object reference, 1) parent tag table reference
4064             # 2) parent directory name (taken from GROUP0 of tag table if not defined)
4065             # Returns: Reference to Hash of subdirectory tagInfo references keyed by tagID
4066             # (plus Reference to edit directory hash in list context)
4067             sub GetAddDirHash($$;$)
4068             {
4069 516     516 0 1676 my ($self, $tagTablePtr, $parent) = @_;
4070 516 100       1650 $parent or $parent = $$tagTablePtr{GROUPS}{0};
4071 516         1818 my $tagID;
4072             my %addDirHash;
4073 516         0 my %editDirHash;
4074 516         1468 my $addDirs = $$self{ADD_DIRS};
4075 516         1478 my $editDirs = $$self{EDIT_DIRS};
4076 516         2573 foreach $tagID (TagTableKeys($tagTablePtr)) {
4077 181585         313427 my @infoArray = GetTagInfoList($tagTablePtr,$tagID);
4078 181585         232176 my $tagInfo;
4079 181585         256239 foreach $tagInfo (@infoArray) {
4080 219953 100       518333 next unless $$tagInfo{SubDirectory};
4081             # get name for this sub directory
4082             # (take directory name from SubDirectory DirName if it exists,
4083             # otherwise Group0 name of SubDirectory TagTable or tag Group1 name)
4084 38658         73863 my $dirName = $$tagInfo{SubDirectory}{DirName};
4085 38658 100       67041 unless ($dirName) {
4086             # use tag name for directory name and save for next time
4087 4262         7729 $dirName = $$tagInfo{Name};
4088 4262         8154 $$tagInfo{SubDirectory}{DirName} = $dirName;
4089             }
4090             # save this directory information if we are writing it
4091 38658 100 100     100126 if ($$editDirs{$dirName} and $$editDirs{$dirName} eq $parent) {
4092 287         1248 $editDirHash{$tagID} = $tagInfo;
4093 287 100       1565 $addDirHash{$tagID} = $tagInfo if $$addDirs{$dirName};
4094             }
4095             }
4096             }
4097 516 100       16194 return (\%addDirHash, \%editDirHash) if wantarray;
4098 438         3342 return \%addDirHash;
4099             }
4100              
4101             #------------------------------------------------------------------------------
4102             # Get localized version of tagInfo hash (used by MIE, XMP, PNG and QuickTime)
4103             # Inputs: 0) tagInfo hash ref, 1) locale code (eg. "en_CA" for MIE)
4104             # Returns: new tagInfo hash ref, or undef if invalid
4105             # - sets LangCode member in new tagInfo
4106             sub GetLangInfo($$)
4107             {
4108 335     335 0 848 my ($tagInfo, $langCode) = @_;
4109             # make a new tagInfo hash for this locale
4110 335         3831 my $table = $$tagInfo{Table};
4111 335         826 my $tagID = $$tagInfo{TagID} . '-' . $langCode;
4112 335         848 my $langInfo = $$table{$tagID};
4113 335 100       849 unless ($langInfo) {
4114             # make a new tagInfo entry for this locale
4115             $langInfo = {
4116             %$tagInfo,
4117             Name => $$tagInfo{Name} . '-' . $langCode,
4118 192         1192 Description => Image::ExifTool::MakeDescription($$tagInfo{Name}) .
4119             " ($langCode)",
4120             LangCode => $langCode,
4121             SrcTagInfo => $tagInfo, # save reference to original tagInfo
4122             };
4123 192         827 AddTagToTable($table, $tagID, $langInfo);
4124             }
4125 335         1000 return $langInfo;
4126             }
4127              
4128             #------------------------------------------------------------------------------
4129             # initialize ADD_DIRS and EDIT_DIRS hashes for all directories that need
4130             # to be created or will have tags changed in them
4131             # Inputs: 0) ExifTool object reference, 1) file type string (or map hash ref)
4132             # 2) preferred family 0 group for creating tags, 3) alternate preferred group
4133             # Notes:
4134             # - the ADD_DIRS and EDIT_DIRS keys are the directory names, and the values
4135             # are the names of the parent directories (undefined for a top-level directory)
4136             # - also initializes FORCE_WRITE lookup
4137             sub InitWriteDirs($$;$$)
4138             {
4139 332     332 0 1242 my ($self, $fileType, $preferredGroup, $altGroup) = @_;
4140 332         1899 my $editDirs = $$self{EDIT_DIRS} = { };
4141 332         1369 my $addDirs = $$self{ADD_DIRS} = { };
4142 332         1273 my $fileDirs = $dirMap{$fileType};
4143 332 100       1194 unless ($fileDirs) {
4144 203 100       940 return unless ref $fileType eq 'HASH';
4145 85         239 $fileDirs = $fileType;
4146             }
4147 214         1237 my @tagInfoList = $self->GetNewTagInfoList();
4148 214         599 my ($tagInfo, $nvHash);
4149              
4150             # save the preferred group
4151 214         907 $$self{PreferredGroup} = $preferredGroup;
4152              
4153 214         655 foreach $tagInfo (@tagInfoList) {
4154             # cycle through all hashes in linked list
4155 13284         29115 for ($nvHash=$self->GetNewValueHash($tagInfo); $nvHash; $nvHash=$$nvHash{Next}) {
4156             # are we creating this tag? (otherwise just deleting or editing it)
4157 13312         25990 my $isCreating = $$nvHash{IsCreating};
4158 13312 100       23891 if ($preferredGroup) {
4159 3598         11588 my $g0 = $self->GetGroup($tagInfo, 0);
4160 3598 100       11047 if ($isCreating) {
4161             # if another group is taking priority, only create
4162             # directory if specifically adding tags to this group
4163             # or if this tag isn't being added to the priority group
4164             $isCreating = 0 if $preferredGroup ne $g0 and
4165 803 100 100     6908 $$nvHash{CreateGroups}{$preferredGroup} and
      100        
      100        
4166             (not $altGroup or $altGroup ne $g0);
4167             } else {
4168             # create this directory if any tag is preferred and has a value
4169             # (unless group creation is disabled via the WriteMode option)
4170             $isCreating = 1 if $$nvHash{Value} and $preferredGroup eq $g0 and
4171 2795 50 100     14478 not $$nvHash{EditOnly} and $$self{OPTIONS}{WriteMode} =~ /g/;
      66        
      66        
4172             }
4173             }
4174             # tag belongs to directory specified by WriteGroup, or by
4175             # the Group0 name if WriteGroup not defined
4176 13312         25773 my $dirName = $$nvHash{WriteGroup};
4177             # remove MIE copy number(s) if they exist
4178 13312 100       33728 if ($dirName =~ /^MIE\d*(-[a-z]+)?\d*$/i) {
4179 394   50     2218 $dirName = 'MIE' . ($1 || '');
4180             }
4181 13312         17985 my @dirNames;
4182             # allow a group name of '*' to force writing EXIF/IPTC/XMP/PNG (ForceWrite tag)
4183 13312 50 33     36135 if ($dirName eq '*' and $$nvHash{Value}) {
    100          
4184 0         0 my $val = $$nvHash{Value}[0];
4185 0 0       0 if ($val) {
4186 0         0 foreach (qw(EXIF IPTC XMP PNG FixBase)) {
4187 0 0       0 next unless $val =~ /\b($_|All)\b/i;
4188 0         0 push @dirNames, $_;
4189 0 0       0 push @dirNames, 'EXIF' if $_ eq 'FixBase';
4190 0         0 $$self{FORCE_WRITE}{$_} = 1;
4191             }
4192             }
4193 0         0 $dirName = shift @dirNames;
4194             } elsif ($dirName eq 'QuickTime') {
4195             # write to specific QuickTime group
4196 46         287 $dirName = $self->GetGroup($tagInfo, 1);
4197             }
4198 13312         23779 while ($dirName) {
4199 53741         80216 my $parent = $$fileDirs{$dirName};
4200 53741 100       83948 if (ref $parent) {
4201 6463         11279 push @dirNames, reverse @$parent;
4202 6463         8634 $parent = pop @dirNames;
4203             }
4204 53741         80289 $$editDirs{$dirName} = $parent;
4205 53741 100 100     99308 $$addDirs{$dirName} = $parent if $isCreating and $isCreating != 2;
4206 53741   100     145395 $dirName = $parent || shift @dirNames
4207             }
4208             }
4209             }
4210 214 100       533 if (%{$$self{DEL_GROUP}}) {
  214         1091  
4211             # add delete groups to list of edited groups
4212 39         124 foreach (keys %{$$self{DEL_GROUP}}) {
  39         514  
4213 1036 100       2239 next if /^-/; # ignore excluded groups
4214 1034         1541 my $dirName = $_;
4215             # translate necessary group 0 names
4216 1034 100       2161 $dirName = $translateWriteGroup{$dirName} if $translateWriteGroup{$dirName};
4217             # convert XMP group 1 names
4218 1034 100       2174 $dirName = 'XMP' if $dirName =~ /^XMP-/;
4219 1034         1424 my @dirNames;
4220 1034         1954 while ($dirName) {
4221 1484         2396 my $parent = $$fileDirs{$dirName};
4222 1484 100       2834 if (ref $parent) {
4223 17         41 push @dirNames, reverse @$parent;
4224 17         24 $parent = pop @dirNames;
4225             }
4226 1484         2813 $$editDirs{$dirName} = $parent;
4227 1484   100     4790 $dirName = $parent || shift @dirNames
4228             }
4229             }
4230             }
4231             # special case to edit JFIF to get resolutions if editing EXIF information
4232 214 100 100     1787 if ($$editDirs{IFD0} and $$fileDirs{JFIF}) {
4233 88         341 $$editDirs{JFIF} = 'IFD1';
4234 88         360 $$editDirs{APP0} = undef;
4235             }
4236              
4237 214 100       3543 if ($$self{OPTIONS}{Verbose}) {
4238 2         12 my $out = $$self{OPTIONS}{TextOut};
4239 2         11 print $out " Editing tags in: ";
4240 2         22 foreach (sort keys %$editDirs) { print $out "$_ "; }
  13         30  
4241 2         11 print $out "\n";
4242 2 50       14 return unless $$self{OPTIONS}{Verbose} > 1;
4243 2         6 print $out " Creating tags in: ";
4244 2         15 foreach (sort keys %$addDirs) { print $out "$_ "; }
  8         20  
4245 2         17 print $out "\n";
4246             }
4247             }
4248              
4249             #------------------------------------------------------------------------------
4250             # Write an image directory
4251             # Inputs: 0) ExifTool object reference, 1) source directory information reference
4252             # 2) tag table reference, 3) optional reference to writing procedure
4253             # Returns: New directory data or undefined on error (or empty string to delete directory)
4254             sub WriteDirectory($$$;$)
4255             {
4256 1947     1947 0 7760 my ($self, $dirInfo, $tagTablePtr, $writeProc) = @_;
4257 1947         3886 my ($out, $nvHash, $delFlag);
4258              
4259 1947 50       5257 $tagTablePtr or return undef;
4260 1947 100       8254 $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose};
4261             # set directory name from default group0 name if not done already
4262 1947         5301 my $dirName = $$dirInfo{DirName};
4263 1947   100     6900 my $parent = $$dirInfo{Parent} || '';
4264 1947         4080 my $dataPt = $$dirInfo{DataPt};
4265 1947         6354 my $grp0 = $$tagTablePtr{GROUPS}{0};
4266 1947 100       6250 $dirName or $dirName = $$dirInfo{DirName} = $grp0;
4267 1947 100       3295 if (%{$$self{DEL_GROUP}}) {
  1947         7400  
4268 287         654 my $delGroup = $$self{DEL_GROUP};
4269             # delete entire directory if specified
4270 287         541 my $grp1 = $dirName;
4271 287   100     1334 $delFlag = ($$delGroup{$grp0} or $$delGroup{$grp1});
4272 287 100 66     2170 if ($permanentDir{$grp0} and not ($$dirInfo{TagInfo} and $$dirInfo{TagInfo}{Deletable})) {
      66        
4273 186         356 undef $delFlag;
4274             }
4275 287 100       989 if ($delFlag) {
4276 42 50 100     641 if ($$dirInfo{Permanent}) {
    50 66        
    100 0        
      33        
      0        
      0        
4277 0         0 $self->Warn("Not deleting permanent $dirName directory");
4278 0         0 undef $grp1;
4279             } elsif (($grp0 =~ /^(MakerNotes)$/ or $grp1 =~ /^(IFD0|ExifIFD|MakerNotes)$/) and
4280             $self->IsRawType() and
4281             # allow non-permanent MakerNote directories to be deleted (ie. NikonCapture)
4282             (not $$dirInfo{TagInfo} or not defined $$dirInfo{TagInfo}{Permanent} or
4283             $$dirInfo{TagInfo}{Permanent}) and
4284             # allow MakerNotes to be deleted from ExifIFD of CR3 file
4285             not ($self->IsRawType() == 2 and $parent eq 'ExifIFD'))
4286             {
4287 0         0 $self->Warn("Can't delete $1 from $$self{FileType}",1);
4288 0         0 undef $grp1;
4289             } elsif (not $blockExifTypes{$$self{FILE_TYPE}}) {
4290             # restrict delete logic to prevent entire tiff image from being killed
4291             # (don't allow IFD0 to be deleted, and delete only ExifIFD if EXIF specified)
4292 10 50 33     167 if ($$self{FILE_TYPE} eq 'PSD') {
    50          
    50          
    50          
4293             # don't delete Photoshop directories from PSD image
4294 0 0       0 undef $grp1 if $grp0 eq 'Photoshop';
4295             } elsif ($$self{FILE_TYPE} =~ /^(EPS|PS)$/) {
4296             # allow anything to be deleted from PostScript files
4297             } elsif ($grp1 eq 'IFD0') {
4298 0   0     0 my $type = $$self{TIFF_TYPE} || $$self{FILE_TYPE};
4299 0 0       0 $$delGroup{IFD0} and $self->Warn("Can't delete IFD0 from $type",1);
4300 0         0 undef $grp1;
4301             } elsif ($grp0 eq 'EXIF' and $$delGroup{$grp0}) {
4302 0 0 0     0 undef $grp1 unless $$delGroup{$grp1} or $grp1 eq 'ExifIFD';
4303             }
4304             }
4305 42 50       157 if ($grp1) {
4306 42 100 66     208 if ($dataPt or $$dirInfo{RAF}) {
4307 32         92 ++$$self{CHANGED};
4308 32 100       109 $out and print $out " Deleting $grp1\n";
4309 32 100       146 $self->Warn('ICC_Profile deleted. Image colors may be affected') if $grp1 eq 'ICC_Profile';
4310             # can no longer validate TIFF_END if deleting an entire IFD
4311 32 100       167 delete $$self{TIFF_END} if $dirName =~ /IFD/;
4312             }
4313             # don't add back into the wrong location
4314 42         163 my $right = $$self{ADD_DIRS}{$grp1};
4315             # (take care because EXIF directory name may be either EXIF or IFD0,
4316             # but IFD0 will be the one that appears in the directory map)
4317 42 100 100     230 $right = $$self{ADD_DIRS}{IFD0} if not $right and $grp1 eq 'EXIF';
4318 42 100 100     208 if ($delFlag == 2 and $right) {
4319             # also check grandparent because some routines create 2 levels in 1
4320 21   100     147 my $right2 = $$self{ADD_DIRS}{$right} || '';
4321 21 50 66     137 if (not $parent or $parent eq $right or $parent eq $right2) {
      33        
4322             # prevent duplicate directories from being recreated at the same path
4323 21         36 my $path = join '-', @{$$self{PATH}}, $dirName;
  21         107  
4324 21 100       132 $$self{Recreated} or $$self{Recreated} = { };
4325 21 50       82 if ($$self{Recreated}{$path}) {
4326 0 0       0 my $p = $parent ? " in $parent" : '';
4327 0         0 $self->Warn("Not recreating duplicate $grp1$p",1);
4328 0         0 return '';
4329             }
4330 21         79 $$self{Recreated}{$path} = 1;
4331             # empty the directory
4332 21         49 my $data = '';
4333 21         64 $$dirInfo{DataPt} = \$data;
4334 21         59 $$dirInfo{DataLen} = 0;
4335 21         49 $$dirInfo{DirStart} = 0;
4336 21         60 $$dirInfo{DirLen} = 0;
4337 21         46 delete $$dirInfo{RAF};
4338 21         43 delete $$dirInfo{Base};
4339 21         69 delete $$dirInfo{DataPos};
4340             } else {
4341 0         0 $self->Warn("Not recreating $grp1 in $parent (should be in $right)",1);
4342 0         0 return '';
4343             }
4344             } else {
4345 21 100       200 return '' unless $$dirInfo{NoDelete};
4346             }
4347             }
4348             }
4349             }
4350             # use default proc from tag table if no proc specified
4351 1927 100 100     11525 $writeProc or $writeProc = $$tagTablePtr{WRITE_PROC} or return undef;
4352              
4353             # are we rewriting a pre-existing directory?
4354 1634   100     9445 my $isRewriting = ($$dirInfo{DirLen} or (defined $dataPt and length $$dataPt) or $$dirInfo{RAF});
4355              
4356             # copy or delete new directory as a block if specified
4357 1634         3287 my $blockName = $dirName;
4358 1634 100       4674 $blockName = 'EXIF' if $blockName eq 'IFD0';
4359 1634   100     8528 my $tagInfo = $Image::ExifTool::Extra{$blockName} || $$dirInfo{TagInfo};
4360 1634   100     11859 while ($tagInfo and ($nvHash = $$self{NEW_VALUE}{$tagInfo}) and
      66        
      33        
      66        
4361             $self->IsOverwriting($nvHash) and not ($$nvHash{CreateOnly} and $isRewriting))
4362             {
4363             # protect against writing EXIF to wrong file types, etc
4364 11 100       50 if ($blockName eq 'EXIF') {
4365 1 50       9 unless ($blockExifTypes{$$self{FILE_TYPE}}) {
4366 0         0 $self->Warn("Can't write EXIF as a block to $$self{FILE_TYPE} file");
4367 0         0 last;
4368             }
4369             # this can happen if we call WriteDirectory for an EXIF directory without going
4370             # through WriteTIFF as the WriteProc (which happens if conditionally replacing
4371             # the EXIF block and the condition fails), but we never want to do a block write
4372             # in this case because the EXIF block would end up with two TIFF headers
4373 1 50       9 last unless $writeProc eq \&Image::ExifTool::WriteTIFF;
4374             }
4375 11 100       92 last unless $self->IsOverwriting($nvHash, $dataPt ? $$dataPt : '');
    50          
4376 11         56 my $verb = 'Writing';
4377 11         105 my $newVal = $self->GetNewValue($nvHash);
4378 11 50 33     111 if (defined $newVal and length $newVal) {
4379             # hack to add back TIFF header when writing MakerNoteCanon to CMT3 in CR3 images
4380 11 50       64 if ($$tagInfo{Name} eq 'MakerNoteCanon') {
4381 0         0 require Image::ExifTool::Canon;
4382 0 0       0 if ($tagInfo eq $Image::ExifTool::Canon::uuid{CMT3}) {
4383 0         0 my $hdr;
4384 0 0       0 if (substr($newVal, 0, 1) eq "\0") {
4385 0         0 $hdr = "MM\0\x2a" . pack('N', 8);
4386             } else {
4387 0         0 $hdr = "II\x2a\0" . pack('V', 8);
4388             }
4389 0         0 $newVal = $hdr . $newVal;
4390             }
4391             }
4392             } else {
4393 0 0 0     0 return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed
4394             # don't allow MakerNotes to be removed from RAW files
4395 0 0 0     0 if ($blockName eq 'MakerNotes' and $self->IsRawType() and
      0        
      0        
4396             # but allow MakerNotes to be deleted from ExifIFD of CR3 image (shouldn't be there)
4397             not ($self->IsRawType() == 2 and $parent eq 'ExifIFD'))
4398             {
4399 0         0 $self->Warn("Can't delete MakerNotes from $$self{FileType}",1);
4400 0         0 return undef;
4401             }
4402 0         0 $verb = 'Deleting';
4403 0         0 $newVal = '';
4404             }
4405 11         41 $$dirInfo{BlockWrite} = 1; # set flag indicating we did a block write
4406 11 50       52 $out and print $out " $verb $blockName as a block\n";
4407 11         36 ++$$self{CHANGED};
4408 11         68 return $newVal;
4409             }
4410             # guard against writing the same directory twice
4411 1623 100 100     13868 if (defined $dataPt and defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos} and
      100        
      100        
4412             not $$dirInfo{NoRefTest})
4413             {
4414 693   100     4730 my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0) + $$self{BASE};
4415             # (Phase One P25 IIQ files have ICC_Profile duplicated in IFD0 and IFD1)
4416 693 50 0     3379 if ($$self{PROCESSED}{$addr} and ($dirName ne 'ICC_Profile' or $$self{TIFF_TYPE} ne 'IIQ')) {
      33        
4417 0 0 0     0 if (defined $$dirInfo{DirLen} and not $$dirInfo{DirLen} and $dirName ne $$self{PROCESSED}{$addr}) {
    0 0        
4418             # it is hypothetically possible to have 2 different directories
4419             # with the same address if one has a length of zero
4420             } elsif ($self->Error("$dirName pointer references previous $$self{PROCESSED}{$addr} directory", 2)) {
4421 0         0 return undef;
4422             } else {
4423 0         0 $self->Warn("Deleting duplicate $dirName directory");
4424 0 0       0 $out and print $out " Deleting $dirName\n";
4425             # delete the duplicate directory (don't recreate it when writing new
4426             # tags to prevent propagating a duplicate IFD in cases like when the
4427             # same ExifIFD exists in both IFD0 and IFD1)
4428 0         0 return '';
4429             }
4430             } else {
4431 693         2882 $$self{PROCESSED}{$addr} = $dirName;
4432             }
4433             }
4434 1623         4431 my $oldDir = $$self{DIR_NAME};
4435 1623         6383 my @save = @$self{'Compression','SubfileType'};
4436 1623         3012 my $name;
4437 1623 100       4683 if ($out) {
4438             $name = ($dirName eq 'MakerNotes' and $$dirInfo{TagInfo}) ?
4439 4 50 33     31 $$dirInfo{TagInfo}{Name} : $dirName;
4440 4 100 100     38 if (not defined $oldDir or $oldDir ne $name) {
4441 3 100       16 my $verb = $isRewriting ? 'Rewriting' : 'Creating';
4442 3         22 print $out " $verb $name\n";
4443             }
4444             }
4445 1623         5798 my $saveOrder = GetByteOrder();
4446 1623         4186 my $oldChanged = $$self{CHANGED};
4447 1623         3866 $$self{DIR_NAME} = $dirName;
4448 1623         2801 push @{$$self{PATH}}, $dirName;
  1623         5106  
4449 1623         4853 $$dirInfo{IsWriting} = 1;
4450 1623         2754 my $newData;
4451             {
4452 61     61   610 no strict 'refs';
  61         161  
  61         1752699  
  1623         2581  
4453 1623         13084 $newData = &$writeProc($self, $dirInfo, $tagTablePtr);
4454             }
4455 1623         3424 pop @{$$self{PATH}};
  1623         5432  
4456             # nothing changed if error occurred or nothing was created
4457 1623 100 100     9216 $$self{CHANGED} = $oldChanged unless defined $newData and (length($newData) or $isRewriting);
      100        
4458 1623         4807 $$self{DIR_NAME} = $oldDir;
4459 1623         5509 @$self{'Compression','SubfileType'} = @save;
4460 1623         6910 SetByteOrder($saveOrder);
4461 1623 100       4516 if ($out) {
4462 4 50 33     28 print $out " Deleting $name\n" if defined $newData and not length $newData;
4463 4 50 33     36 if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) {
4464 0         0 print $out "$$self{INDENT} [nothing changed in $name]\n";
4465             }
4466             }
4467 1623         9777 return $newData;
4468             }
4469              
4470             #------------------------------------------------------------------------------
4471             # Uncommon utility routines to for reading binary data values
4472             # Inputs: 0) data reference, 1) offset into data
4473             sub Get64s($$)
4474             {
4475 12     12 0 50 my ($dataPt, $pos) = @_;
4476 12 50       64 my $pt = GetByteOrder() eq 'MM' ? 0 : 4; # get position of high word
4477 12         42 my $hi = Get32s($dataPt, $pos + $pt); # preserve sign bit of high word
4478 12         40 my $lo = Get32u($dataPt, $pos + 4 - $pt);
4479 12         43 return $hi * 4294967296 + $lo;
4480             }
4481             sub Get64u($$)
4482             {
4483 197     197 0 532 my ($dataPt, $pos) = @_;
4484 197 100       592 my $pt = GetByteOrder() eq 'MM' ? 0 : 4; # get position of high word
4485 197         752 my $hi = Get32u($dataPt, $pos + $pt); # (unsigned this time)
4486 197         657 my $lo = Get32u($dataPt, $pos + 4 - $pt);
4487 197         915 return $hi * 4294967296 + $lo;
4488             }
4489             sub GetFixed64s($$)
4490             {
4491 0     0 0 0 my ($dataPt, $pos) = @_;
4492 0         0 my $val = Get64s($dataPt, $pos) / 4294967296;
4493             # remove insignificant digits
4494 0 0       0 return int($val * 1e10 + ($val>0 ? 0.5 : -0.5)) / 1e10;
4495             }
4496             # Decode extended 80-bit float used by Apple SANE and Intel 8087
4497             # (note: different than the IEEE standard 80-bit float)
4498             sub GetExtended($$)
4499             {
4500 1     1 0 4 my ($dataPt, $pos) = @_;
4501 1 50       5 my $pt = GetByteOrder() eq 'MM' ? 0 : 2; # get position of exponent
4502 1         7 my $exp = Get16u($dataPt, $pos + $pt);
4503 1         7 my $sig = Get64u($dataPt, $pos + 2 - $pt); # get significand as int64u
4504 1 50       5 my $sign = $exp & 0x8000 ? -1 : 1;
4505 1         5 $exp = ($exp & 0x7fff) - 16383 - 63; # (-63 to fractionalize significand)
4506 1         29 return $sign * $sig * 2 ** $exp;
4507             }
4508              
4509             #------------------------------------------------------------------------------
4510             # Dump data in hex and ASCII to console
4511             # Inputs: 0) data reference, 1) length or undef, 2-N) Options:
4512             # Options: Start => offset to start of data (default=0)
4513             # Addr => address to print for data start (default=DataPos+Base+Start)
4514             # DataPos => position of data within block (relative to Base)
4515             # Base => base offset for pointers from start of file
4516             # Width => width of printout (bytes, default=16)
4517             # Prefix => prefix to print at start of line (default='')
4518             # MaxLen => maximum length to dump
4519             # Out => output file reference
4520             # Len => data length
4521             sub HexDump($;$%)
4522             {
4523 169     169 0 360 my $dataPt = shift;
4524 169         374 my $len = shift;
4525 169         1264 my %opts = @_;
4526 169   100     543 my $start = $opts{Start} || 0;
4527 169         465 my $addr = $opts{Addr};
4528 169   50     707 my $wid = $opts{Width} || 16;
4529 169   50     571 my $prefix = $opts{Prefix} || '';
4530 169   50     500 my $out = $opts{Out} || \*STDOUT;
4531 169         294 my $maxLen = $opts{MaxLen};
4532 169         378 my $datLen = length($$dataPt) - $start;
4533 169         255 my $more;
4534 169 50       470 $len = $opts{Len} if defined $opts{Len};
4535              
4536 169 100 50     696 $addr = $start + ($opts{DataPos} || 0) + ($opts{Base} || 0) unless defined $addr;
      50        
4537 169 100       430 $len = $datLen unless defined $len;
4538 169 100 66     665 if ($maxLen and $len > $maxLen) {
4539             # print one line less to allow for $more line below
4540 5         18 $maxLen = int(($maxLen - 1) / $wid) * $wid;
4541 5         10 $more = $len - $maxLen;
4542 5         8 $len = $maxLen;
4543             }
4544 169 50       585 if ($len > $datLen) {
4545 0         0 print $out "$prefix Warning: Attempted dump outside data\n";
4546 0         0 print $out "$prefix ($len bytes specified, but only $datLen available)\n";
4547 0         0 $len = $datLen;
4548             }
4549 169         691 my $format = sprintf("%%-%ds", $wid * 3);
4550 169         463 my $tmpl = 'H2' x $wid; # ('(H2)*' would have been nice, but older perl versions don't support it)
4551 169         255 my $i;
4552 169         526 for ($i=0; $i<$len; $i+=$wid) {
4553 228 100       649 $wid > $len-$i and $wid = $len-$i, $tmpl = 'H2' x $wid;
4554 228         1123 printf $out "$prefix%8.4x: ", $addr+$i;
4555 228         647 my $dat = substr($$dataPt, $i+$start, $wid);
4556 228         1430 my $s = join(' ', unpack($tmpl, $dat));
4557 228         1042 printf $out $format, $s;
4558 228         463 $dat =~ tr /\x00-\x1f\x7f-\xff/./;
4559 228         854 print $out "[$dat]\n";
4560             }
4561 169 100       1920 $more and print $out "$prefix [snip $more bytes]\n";
4562             }
4563              
4564             #------------------------------------------------------------------------------
4565             # Print verbose tag information
4566             # Inputs: 0) ExifTool object reference, 1) tag ID
4567             # 2) tag info reference (or undef)
4568             # 3-N) extra parms:
4569             # Parms: Index => Index of tag in menu (starting at 0)
4570             # Value => Tag value
4571             # DataPt => reference to value data block
4572             # DataPos => location of data block in file
4573             # Base => base added to all offsets
4574             # Size => length of value data within block
4575             # Format => value format string
4576             # Count => number of values
4577             # Extra => Extra Verbose=2 information to put after tag number
4578             # Table => Reference to tag table
4579             # Name => Name to use for unknown tag
4580             # --> plus any of these HexDump() options: Start, Addr, Width
4581             sub VerboseInfo($$$%)
4582             {
4583 617     617 0 4855 my ($self, $tagID, $tagInfo, %parms) = @_;
4584 617         2014 my $verbose = $$self{OPTIONS}{Verbose};
4585 617         1431 my $out = $$self{OPTIONS}{TextOut};
4586 617         1251 my ($tag, $line, $hexID);
4587              
4588             # generate hex number if tagID is numerical
4589 617 100       1512 if (defined $tagID) {
4590 578 100       4860 $tagID =~ /^\d+$/ and $hexID = sprintf("0x%.4x", $tagID);
4591             } else {
4592 39         78 $tagID = 'Unknown';
4593             }
4594             # get tag name
4595 617 50 33     3445 if ($tagInfo and $$tagInfo{Name}) {
    0          
4596 617         1442 $tag = $$tagInfo{Name};
4597             } elsif ($parms{Name}) { # (used for PNG Plus FPX tags)
4598 0         0 $tag = $parms{Name};
4599 0         0 undef $hexID;
4600             } else {
4601 0         0 my $prefix;
4602 0 0       0 $prefix = $parms{Table}{TAG_PREFIX} if $parms{Table};
4603 0 0 0     0 if ($prefix or $hexID) {
4604 0 0       0 $prefix = 'Unknown' unless $prefix;
4605 0 0       0 $tag = $prefix . '_' . ($hexID ? $hexID : $tagID);
4606             } else {
4607 0         0 $tag = $tagID;
4608             }
4609             }
4610 617         1261 my $dataPt = $parms{DataPt};
4611 617         1304 my $size = $parms{Size};
4612 617 50 66     1818 $size = length $$dataPt unless defined $size or not $dataPt;
4613 617         1597 my $indent = $$self{INDENT};
4614              
4615             # Level 1: print tag/value information
4616 617         1022 $line = $indent;
4617 617         1164 my $index = $parms{Index};
4618 617 100       1489 if (defined $index) {
4619 365         709 $line .= $index . ') ';
4620 365 100       1081 $line .= ' ' if length($index) < 2;
4621 365         608 $indent .= ' '; # indent everything else to align with tag name
4622             }
4623 617         1134 $line .= $tag;
4624 617 100 66     2667 if ($tagInfo and $$tagInfo{SubDirectory}) {
4625 39         93 $line .= ' (SubDirectory) -->';
4626             } else {
4627 578         1109 my $maxLen = 90 - length($line);
4628 578         1164 my $val = $parms{Value};
4629 578 50       1193 if (defined $val) {
    0          
4630 578 50       1424 $val = '[' . join(',',@$val) . ']' if ref $val eq 'ARRAY';
4631 578         2518 $line .= ' = ' . $self->Printable($val, $maxLen);
4632             } elsif ($dataPt) {
4633 0   0     0 my $start = $parms{Start} || 0;
4634 0         0 $line .= ' = ' . $self->Printable(substr($$dataPt,$start,$size), $maxLen);
4635             }
4636             }
4637 617         2533 print $out "$line\n";
4638              
4639             # Level 2: print detailed information about the tag
4640 617 50 66     3674 if ($verbose > 1 and ($parms{Extra} or $parms{Format} or
      66        
4641             $parms{DataPt} or defined $size or $tagID =~ /\//))
4642             {
4643 390         726 $line = $indent . '- Tag ';
4644 390 100       799 if ($hexID) {
4645 389         618 $line .= $hexID;
4646             } else {
4647 1         3 $tagID =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/ge;
  0         0  
4648 1         3 $line .= "'${tagID}'";
4649             }
4650 390 50       896 $line .= $parms{Extra} if defined $parms{Extra};
4651 390         690 my $format = $parms{Format};
4652 390 50 66     1029 if ($format or defined $size) {
4653 390         641 $line .= ' (';
4654 390 50       940 if (defined $size) {
4655 390         669 $line .= "$size bytes";
4656 390 100       909 $line .= ', ' if $format;
4657             }
4658 390 100       793 if ($format) {
4659 352         625 $line .= $format;
4660 352 50       1353 $line .= '['.$parms{Count}.']' if $parms{Count};
4661             }
4662 390         613 $line .= ')';
4663             }
4664 390 50 66     1215 $line .= ':' if $verbose > 2 and $parms{DataPt};
4665 390         884 print $out "$line\n";
4666             }
4667              
4668             # Level 3: do hex dump of value
4669 617 100 100     4014 if ($verbose > 2 and $parms{DataPt} and (not $tagInfo or not $$tagInfo{ReadFromRAF})) {
      33        
      66        
4670 165         441 $parms{Out} = $out;
4671 165         419 $parms{Prefix} = $indent;
4672             # limit dump length if Verbose < 5
4673 165 50       669 $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5;
    50          
4674 165         1008 HexDump($dataPt, $size, %parms);
4675             }
4676             }
4677              
4678             #------------------------------------------------------------------------------
4679             # Dump trailer information
4680             # Inputs: 0) ExifTool object ref, 1) dirInfo hash (RAF, DirName, DataPos, DirLen)
4681             # Notes: Restores current file position before returning
4682             sub DumpTrailer($$)
4683             {
4684 1     1 0 4 my ($self, $dirInfo) = @_;
4685 1         6 my $raf = $$dirInfo{RAF};
4686 1         6 my $curPos = $raf->Tell();
4687 1   50     6 my $trailer = $$dirInfo{DirName} || 'Unknown';
4688 1         5 my $pos = $$dirInfo{DataPos};
4689 1         4 my $verbose = $$self{OPTIONS}{Verbose};
4690 1         5 my $htmlDump = $$self{HTML_DUMP};
4691 1         3 my ($buff, $buf2);
4692 1         4 my $size = $$dirInfo{DirLen};
4693 1 50       6 $pos = $curPos unless defined $pos;
4694              
4695             # get full trailer size if not specified
4696 1         3 for (;;) {
4697 1 50       7 unless ($size) {
4698 0 0       0 $raf->Seek(0, 2) or last;
4699 0         0 $size = $raf->Tell() - $pos;
4700 0 0       0 last unless $size;
4701             }
4702 1 50       6 $raf->Seek($pos, 0) or last;
4703 1 50       6 if ($htmlDump) {
4704 0 0       0 my $num = $raf->Read($buff, $size) or return;
4705 0         0 my $desc = "$trailer trailer";
4706 0 0       0 $desc = "[$desc]" if $trailer eq 'Unknown';
4707 0         0 $self->HDump($pos, $num, $desc, undef, 0x08);
4708 0         0 last;
4709             }
4710 1         5 my $out = $$self{OPTIONS}{TextOut};
4711 1         31 printf $out "$trailer trailer (%d bytes at offset 0x%.4x):\n", $size, $pos;
4712 1 50       8 last unless $verbose > 2;
4713 0         0 my $num = $size; # number of bytes to read
4714             # limit size if not very verbose
4715 0 0       0 if ($verbose < 5) {
4716 0 0       0 my $limit = $verbose < 4 ? 96 : 512;
4717 0 0       0 $num = $limit if $num > $limit;
4718             }
4719 0 0       0 $raf->Read($buff, $num) == $num or return;
4720             # read the end of the trailer too if not done already
4721 0 0       0 if ($size > 2 * $num) {
    0          
4722 0         0 $raf->Seek($pos + $size - $num, 0);
4723 0         0 $raf->Read($buf2, $num);
4724             } elsif ($size > $num) {
4725 0         0 $raf->Seek($pos + $num, 0);
4726 0         0 $raf->Read($buf2, $size - $num);
4727 0         0 $buff .= $buf2;
4728 0         0 undef $buf2;
4729             }
4730 0         0 HexDump(\$buff, undef, Addr => $pos, Out => $out);
4731 0 0       0 if (defined $buf2) {
4732 0         0 print $out " [snip ", $size - $num * 2, " bytes]\n";
4733 0         0 HexDump(\$buf2, undef, Addr => $pos + $size - $num, Out => $out);
4734             }
4735 0         0 last;
4736             }
4737 1         6 $raf->Seek($curPos, 0);
4738             }
4739              
4740             #------------------------------------------------------------------------------
4741             # Dump unknown trailer information
4742             # Inputs: 0) ExifTool ref, 1) dirInfo ref (with RAF, DataPos and DirLen defined)
4743             # Notes: changes dirInfo elements
4744             sub DumpUnknownTrailer($$)
4745             {
4746 0     0 0 0 my ($self, $dirInfo) = @_;
4747 0         0 my $pos = $$dirInfo{DataPos};
4748 0         0 my $endPos = $pos + $$dirInfo{DirLen};
4749             # account for preview/MPF image trailer
4750 0         0 my $value = $$self{VALUE};
4751 0   0     0 my $prePos = $$value{PreviewImageStart} || $$self{PreviewImageStart};
4752 0   0     0 my $preLen = $$value{PreviewImageLength} || $$self{PreviewImageLength};
4753 0         0 my $hidPos = $$value{HiddenDataOffset};
4754 0         0 my $hidLen = $$value{HiddenDataLength};
4755 0         0 my $tag = 'PreviewImage';
4756 0         0 my $mpImageNum = 0;
4757 0         0 my (%image, $lastOne);
4758             # add HiddenData to list of known trailer blocks
4759 0 0 0     0 if ($hidPos and $hidLen) {
4760             # call ReadHiddenData to validate hidden data and fix offset if necessary
4761 0         0 require Image::ExifTool::Sony;
4762 0         0 my $datPt = Image::ExifTool::Sony::ReadHiddenData($self, $hidPos, $hidLen);
4763 0 0       0 $image{$hidPos} = ['HiddenData', $hidLen] if $datPt;
4764             }
4765 0         0 for (;;) {
4766             # add to Preview block list if valid and in the trailer
4767 0 0 0     0 $image{$prePos} = [$tag, $preLen] if $prePos and $preLen and $prePos+$preLen > $pos;
      0        
4768 0 0       0 last if $lastOne; # checked all images
4769             # look for MPF images (in the proper order)
4770 0         0 ++$mpImageNum;
4771 0         0 $prePos = $$value{"MPImageStart ($mpImageNum)"};
4772 0 0       0 if (defined $prePos) {
4773 0         0 $preLen = $$value{"MPImageLength ($mpImageNum)"};
4774             } else {
4775 0         0 $prePos = $$value{MPImageStart};
4776 0         0 $preLen = $$value{MPImageLength};
4777 0         0 $lastOne = 1;
4778             }
4779 0         0 $tag = "MPImage$mpImageNum";
4780             }
4781             # dump trailer sections in order
4782 0         0 $image{$endPos} = [ '', 0 ]; # add terminator "image"
4783 0         0 foreach $prePos (sort { $a <=> $b } keys %image) {
  0         0  
4784 0 0       0 if ($pos < $prePos) {
4785             # dump unknown trailer data
4786 0         0 $$dirInfo{DirName} = 'Unknown';
4787 0         0 $$dirInfo{DataPos} = $pos;
4788 0         0 $$dirInfo{DirLen} = $prePos - $pos;
4789 0         0 $self->DumpTrailer($dirInfo);
4790             }
4791 0         0 ($tag, $preLen) = @{$image{$prePos}};
  0         0  
4792 0 0       0 last unless $preLen;
4793             # dump image if verbose (it is htmlDump'd by ExtractImage)
4794 0 0       0 if ($$self{OPTIONS}{Verbose}) {
4795 0         0 $$dirInfo{DirName} = $tag;
4796 0         0 $$dirInfo{DataPos} = $prePos;
4797 0         0 $$dirInfo{DirLen} = $preLen;
4798 0         0 $self->DumpTrailer($dirInfo);
4799             }
4800 0         0 $pos = $prePos + $preLen;
4801             }
4802             }
4803              
4804             #------------------------------------------------------------------------------
4805             # Find last element in linked list
4806             # Inputs: 0) element in list
4807             # Returns: Last element in list
4808             sub LastInList($)
4809             {
4810 38     38 0 70 my $element = shift;
4811 38         146 while ($$element{Next}) {
4812 1         6 $element = $$element{Next};
4813             }
4814 38         98 return $element;
4815             }
4816              
4817             #------------------------------------------------------------------------------
4818             # Print verbose value while writing
4819             # Inputs: 0) ExifTool object ref, 1) heading "eg. '+ IPTC:Keywords',
4820             # 2) value, 3) [optional] extra text after value
4821             sub VerboseValue($$$;$)
4822             {
4823 1089 100   1089 0 3798 return unless $_[0]{OPTIONS}{Verbose} > 1;
4824 11         33 my ($self, $str, $val, $xtra) = @_;
4825 11         35 my $out = $$self{OPTIONS}{TextOut};
4826 11 100       32 $xtra or $xtra = '';
4827 11         26 my $maxLen = 81 - length($str) - length($xtra);
4828 11         61 $val = $self->Printable($val, $maxLen);
4829 11         69 print $out " $str = '${val}'$xtra\n";
4830             }
4831              
4832             #------------------------------------------------------------------------------
4833             # Pack Unicode numbers into UTF8 string
4834             # Inputs: 0-N) list of Unicode numbers
4835             # Returns: Packed UTF-8 string
4836             sub PackUTF8(@)
4837             {
4838 0     0 0 0 my @out;
4839 0         0 while (@_) {
4840 0         0 my $ch = pop;
4841 0 0       0 unshift(@out, $ch), next if $ch < 0x80;
4842 0         0 unshift(@out, 0x80 | ($ch & 0x3f));
4843 0         0 $ch >>= 6;
4844 0 0       0 unshift(@out, 0xc0 | $ch), next if $ch < 0x20;
4845 0         0 unshift(@out, 0x80 | ($ch & 0x3f));
4846 0         0 $ch >>= 6;
4847 0 0       0 unshift(@out, 0xe0 | $ch), next if $ch < 0x10;
4848 0         0 unshift(@out, 0x80 | ($ch & 0x3f));
4849 0         0 $ch >>= 6;
4850 0         0 unshift(@out, 0xf0 | ($ch & 0x07));
4851             }
4852 0         0 return pack('C*', @out);
4853             }
4854              
4855             #------------------------------------------------------------------------------
4856             # Unpack numbers from UTF8 string
4857             # Inputs: 0) UTF-8 string
4858             # Returns: List of Unicode numbers (sets $evalWarning on error)
4859             sub UnpackUTF8($)
4860             {
4861 0     0 0 0 my (@out, $pos);
4862 0         0 pos($_[0]) = $pos = 0; # start at beginning of string
4863 0         0 for (;;) {
4864 0         0 my ($ch, $newPos, $val, $byte);
4865 0 0       0 if ($_[0] =~ /([\x80-\xff])/g) {
4866 0         0 $ch = ord($1);
4867 0         0 $newPos = pos($_[0]) - 1;
4868             } else {
4869 0         0 $newPos = length $_[0];
4870             }
4871             # unpack 7-bit characters
4872 0         0 my $len = $newPos - $pos;
4873 0 0       0 push @out, unpack("x${pos}C$len",$_[0]) if $len;
4874 0 0       0 last unless defined $ch;
4875 0         0 $pos = $newPos + 1;
4876             # minimum lead byte for 2-byte sequence is 0xc2 (overlong sequences
4877             # not allowed), 0xf8-0xfd are restricted by RFC 3629 (no 5 or 6 byte
4878             # sequences), and 0xfe and 0xff are not valid in UTF-8 strings
4879 0 0 0     0 if ($ch < 0xc2 or $ch >= 0xf8) {
4880 0         0 push @out, ord('?'); # invalid UTF-8
4881 0         0 $evalWarning = 'Bad UTF-8';
4882 0         0 next;
4883             }
4884             # decode 2, 3 and 4-byte sequences
4885 0         0 my $n = 1;
4886 0 0       0 if ($ch < 0xe0) {
    0          
4887 0         0 $val = $ch & 0x1f; # 2-byte sequence
4888             } elsif ($ch < 0xf0) {
4889 0         0 $val = $ch & 0x0f; # 3-byte sequence
4890 0         0 ++$n;
4891             } else {
4892 0         0 $val = $ch & 0x07; # 4-byte sequence
4893 0         0 $n += 2;
4894             }
4895 0 0       0 unless ($_[0] =~ /\G([\x80-\xbf]{$n})/g) {
4896 0         0 pos($_[0]) = $pos; # restore position
4897 0         0 push @out, ord('?'); # invalid UTF-8
4898 0         0 $evalWarning = 'Bad UTF-8';
4899 0         0 next;
4900             }
4901 0         0 foreach $byte (unpack 'C*', $1) {
4902 0         0 $val = ($val << 6) | ($byte & 0x3f);
4903             }
4904 0         0 push @out, $val; # save Unicode character value
4905 0         0 $pos += $n; # position at end of UTF-8 character
4906             }
4907 0         0 return @out;
4908             }
4909              
4910             #------------------------------------------------------------------------------
4911             # Generate a new, random GUID
4912             # Inputs:
4913             # Returns: GUID string
4914             my $guidCount;
4915             sub NewGUID()
4916             {
4917 61     61 0 1267 my @tm = localtime time;
4918 61 100 66     670 $guidCount = 0 unless defined $guidCount and ++$guidCount < 0x100;
4919 61         1914 return sprintf('%.4d%.2d%.2d%.2d%.2d%.2d%.2X%.4X%.4X%.4X%.4X',
4920             $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $guidCount,
4921             $$ & 0xffff, rand(0x10000), rand(0x10000), rand(0x10000));
4922             }
4923              
4924             #------------------------------------------------------------------------------
4925             # Make TIFF header for raw data
4926             # Inputs: 0) width, 1) height, 2) num colour components, 3) bits, 4) resolution
4927             # 5) color-map data for palette-color image (8 or 16 bit)
4928             # Returns: TIFF header
4929             # Notes: Multi-byte data must be little-endian
4930             sub MakeTiffHeader($$$$;$$)
4931             {
4932 0     0 0 0 my ($w, $h, $cols, $bits, $res, $cmap) = @_;
4933 0 0       0 $res or $res = 72;
4934 0         0 my $saveOrder = GetByteOrder();
4935 0         0 SetByteOrder('II');
4936 0 0       0 if (not $cmap) {
    0          
    0          
4937 0         0 $cmap = '';
4938             } elsif (length $cmap == 3 * 2**$bits) {
4939             # convert to short
4940 0         0 $cmap = pack 'v*', map { $_ | ($_<<8) } unpack 'C*', $cmap;
  0         0  
4941             } elsif (length $cmap != 6 * 2**$bits) {
4942 0         0 $cmap = '';
4943             }
4944 0 0       0 my $cmo = $cmap ? 12 : 0; # offset due to ColorMap IFD entry
4945 0 0       0 my $hdr =
    0          
    0          
    0          
4946             "\x49\x49\x2a\0\x08\0\0\0\x0e\0" . # 0x00 14 menu entries:
4947             "\xfe\x00\x04\0\x01\0\0\0\x00\0\0\0" . # 0x0a SubfileType = 0
4948             "\x00\x01\x04\0\x01\0\0\0" . Set32u($w) . # 0x16 ImageWidth
4949             "\x01\x01\x04\0\x01\0\0\0" . Set32u($h) . # 0x22 ImageHeight
4950             "\x02\x01\x03\0" . Set32u($cols) . # 0x2e BitsPerSample
4951             Set32u($cols == 1 ? $bits : 0xb6 + $cmo) .
4952             "\x03\x01\x03\0\x01\0\0\0\x01\0\0\0" . # 0x3a Compression = 1
4953             "\x06\x01\x03\0\x01\0\0\0" . # 0x46 PhotometricInterpretation
4954             Set32u($cmap ? 3 : $cols == 1 ? 1 : 2) .
4955             "\x11\x01\x04\0\x01\0\0\0" . # 0x52 StripOffsets
4956             Set32u(0xcc + $cmo + length($cmap)) .
4957             "\x15\x01\x03\0\x01\0\0\0" . Set32u($cols) . # 0x5e SamplesPerPixel
4958             "\x16\x01\x04\0\x01\0\0\0" . Set32u($h) . # 0x6a RowsPerStrip
4959             "\x17\x01\x04\0\x01\0\0\0" . # 0x76 StripByteCounts
4960             Set32u($w * $h * $cols * int(($bits+7)/8)) .
4961             "\x1a\x01\x05\0\x01\0\0\0" . Set32u(0xbc + $cmo) . # 0x82 XResolution
4962             "\x1b\x01\x05\0\x01\0\0\0" . Set32u(0xc4 + $cmo) . # 0x8e YResolution
4963             "\x1c\x01\x03\0\x01\0\0\0\x01\0\0\0" . # 0x9a PlanarConfiguration = 1
4964             "\x28\x01\x03\0\x01\0\0\0\x02\0\0\0" . # 0xa6 ResolutionUnit = 2
4965             ($cmap ? # 0xb2 ColorMap [optional]
4966             "\x40\x01\x03\0" . Set32u(3 * 2**$bits) . "\xd8\0\0\0" : '') .
4967             "\0\0\0\0" . # 0xb2+$cmo (no IFD1)
4968             (Set16u($bits) x 3) . # 0xb6+$cmo BitsPerSample value
4969             Set32u($res) . "\x01\0\0\0" . # 0xbc+$cmo XResolution = 72
4970             Set32u($res) . "\x01\0\0\0" . # 0xc4+$cmo YResolution = 72
4971             $cmap; # 0xcc or 0xd8 (cmap and data go here)
4972 0         0 SetByteOrder($saveOrder);
4973 0         0 return $hdr;
4974             }
4975              
4976             #------------------------------------------------------------------------------
4977             # Return current time in EXIF format
4978             # Inputs: 0) [optional] ExifTool ref, 1) flag to include timezone (0 to disable,
4979             # undef or 1 to include)
4980             # Returns: time string
4981             # - a consistent value is returned for each processed file
4982             sub TimeNow(;$$)
4983             {
4984 61     61 0 224 my ($self, $tzFlag) = @_;
4985 61         154 my $timeNow;
4986 61 50       244 ref $self or $tzFlag = $self, $self = { };
4987 61 50       316 if ($$self{Now}) {
4988 0         0 $timeNow = $$self{Now}[0];
4989             } else {
4990 61         201 my $time = time();
4991 61         2286 my @tm = localtime $time;
4992 61         506 my $tz = TimeZoneString(\@tm, $time);
4993 61         504 $timeNow = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d",
4994             $tm[5]+1900, $tm[4]+1, $tm[3],
4995             $tm[2], $tm[1], $tm[0]);
4996 61         341 $$self{Now} = [ $timeNow, $tz ];
4997             }
4998 61 50 33     595 $timeNow .= $$self{Now}[1] if $tzFlag or not defined $tzFlag;
4999 61         372 return $timeNow;
5000             }
5001              
5002             #------------------------------------------------------------------------------
5003             # Inverse date/time print conversion (reformat to YYYY:mm:dd HH:MM:SS[.ss][+-HH:MM|Z])
5004             # Inputs: 0) ExifTool object ref, 1) Date/Time string, 2) timezone flag:
5005             # 0 - remove timezone and sub-seconds if they exist
5006             # 1 - add timezone if it doesn't exist
5007             # undef - leave timezone alone
5008             # 3) flag to allow date-only (YYYY, YYYY:mm or YYYY:mm:dd) or time without seconds
5009             # Returns: formatted date/time string (or undef and issues warning on error)
5010             # Notes: currently accepts different separators, but doesn't use DateFormat yet
5011             my $strptimeLib; # strptime library name if available
5012             sub InverseDateTime($$;$$)
5013             {
5014 474     474 0 1505 my ($self, $val, $tzFlag, $dateOnly) = @_;
5015 474         837 my ($rtnVal, $tz, $fs);
5016 474         1730 my $fmt = $$self{OPTIONS}{DateFormat};
5017             # strip off timezone first if it exists
5018 474 100 100     4694 if (not $fmt and $val =~ s/([-+])(\d{1,2}):?(\d{2})\s*(DST)?$//i) {
    50 66        
5019 6         59 $tz = sprintf("$1%.2d:$3", $2);
5020             } elsif (not $fmt and $val =~ s/Z$//i) {
5021 0         0 $tz = 'Z';
5022             } else {
5023 468         936 $tz = '';
5024             # allow special value of 'now'
5025 468 50       1441 return $self->TimeNow($tzFlag) if lc($val) eq 'now';
5026             }
5027             # only convert date if a format was specified and the date is recognizable
5028 474 100       1250 if ($fmt) {
5029 1 50       5 unless (defined $strptimeLib) {
5030 1 50       3 if (eval { require POSIX::strptime }) {
  1 0       6  
5031 1         3 $strptimeLib = 'POSIX::strptime';
5032 0         0 } elsif (eval { require Time::Piece }) {
5033 0         0 $strptimeLib = 'Time::Piece';
5034             # (call use_locale() to convert localized date/time,
5035             # only available in Time::Piece 1.32 and later)
5036 0         0 eval { Time::Piece->use_locale() };
  0         0  
5037             } else {
5038 0         0 $strptimeLib = '';
5039             }
5040             }
5041             # handle fractional seconds (%f) and time zone (%z)
5042 1         4 ($fs, $tz) = ('', '');
5043 1 50       7 if ($fmt =~ /%(f|:?z)/) {
5044 1 50       10 if ($fmt =~ s/(.*[^%])%f/$1/) {
5045 1 50       7 $fs = $2 if $val =~ s/(.*)(\.\d+)/$1/; # (take last .### as fractional seconds)
5046             }
5047 1 50       7 if ($fmt =~ s/(.*[^%])%(:?)z/$1/) {
5048 1         4 my $colon = $2;
5049 1 50       48 $tz = "$2:$3" if $val =~ s/(.*)([-+]\d{2})$colon(\d{2})/$1/;
5050             }
5051             }
5052 1         4 my ($lib, $wrn, @a);
5053 1         2 TryLib: for ($lib=$strptimeLib; ; $lib='') {
5054             # handle %s format ourself (not supported in Fedora, see forum15032)
5055 1 50       4 if ($fmt eq '%s') {
5056 0         0 $val = ConvertUnixTime($val, 1);
5057 0         0 last;
5058             }
5059 1 50       5 if (not $lib) {
    50          
5060 0 0       0 last unless $$self{OPTIONS}{StrictDate};
5061 0   0     0 warn $wrn || "Install POSIX::strptime or Time::Piece for inverse date/time conversions\n";
5062 0         0 return undef;
5063             } elsif ($lib eq 'POSIX::strptime') {
5064 1         2 @a = eval { POSIX::strptime($val, $fmt) };
  1         53  
5065             } else {
5066             # protect against a negative epoch time, it can cause a hard crash in Windows
5067 0 0 0     0 if ($^O eq 'MSWin32' and $fmt =~ /%s/ and $val =~ /-\d/) {
      0        
5068 0         0 warn "Can't convert negative epoch time\n";
5069 0         0 return undef;
5070             }
5071 0         0 @a = eval {
5072 0         0 my $t = Time::Piece->strptime($val, $fmt);
5073 0         0 return ($t->sec, $t->min, $t->hour, $t->mday, $t->_mon, $t->_year);
5074             };
5075             }
5076 1 50 33     9 if (defined $a[5] and length $a[5]) {
5077 1         2 $a[5] += 1900; # add 1900 to year
5078             } else {
5079 0         0 $wrn = "Invalid date/time (no year) using $lib\n";
5080 0         0 next;
5081             }
5082 1 50 33     8 ++$a[4] if defined $a[4] and length $a[4]; # add 1 to month
5083 1         2 my $i;
5084 1         3 foreach $i (0..4) {
5085 5 50 33     24 if (not defined $a[$i] or not length $a[$i]) {
    100          
5086 0 0 0     0 if ($i < 2 or $dateOnly) { # (allow missing minutes/seconds)
5087 0         0 $a[$i] = ' ';
5088             } else {
5089 0         0 $wrn = "Incomplete date/time specification using $lib\n";
5090 0         0 next TryLib;
5091             }
5092             } elsif (length($a[$i]) < 2) {
5093 3         5 $a[$i] = "0$a[$i]"; # pad to 2 digits if necessary
5094             }
5095             }
5096 1         8 $val = join(':', @a[5,4,3]) . ' ' . join(':', @a[2,1,0]) . $fs . $tz;
5097 1         4 last;
5098             }
5099             }
5100 474 100       2253 if ($val =~ /(\d{4})/g) { # get YYYY
5101 465         1178 my $yr = $1;
5102 465         3002 my @a = ($val =~ /\d{1,2}/g); # get mm, dd, HH, and maybe MM, SS
5103 465   66     2809 length($_) < 2 and $_ = "0$_" foreach @a; # pad to 2 digits if necessary
5104 465 100       1233 if (@a >= 3) {
    50          
5105 439         856 my $ss = $a[4]; # get SS
5106 439         1193 push @a, '00' while @a < 5; # add MM, SS if not given
5107             # get sub-seconds if they exist (must be after SS, and have leading ".")
5108 439 100       1060 unless ($fmt) {
5109 438 100 100     1451 $fs = (@a > 5 and $val =~ /(\.\d+)\s*$/) ? $1 : '';
5110             }
5111             # add/remove timezone if necessary
5112 439 100       1316 if ($tzFlag) {
    100          
5113 34 50       108 if (not $tz) {
5114 34 50       73 if (eval { require Time::Local }) {
  34         1424  
5115             # determine timezone offset for this time
5116 34         3138 my @args = ($a[4],$a[3],$a[2],$a[1],$a[0]-1,$yr);
5117 34         170 my $diff = Time::Local::timegm(@args) - TimeLocal(@args);
5118 34         144 $tz = TimeZoneString($diff / 60);
5119             } else {
5120 0         0 $tz = 'Z'; # don't know time zone
5121             }
5122             }
5123             } elsif (defined $tzFlag) {
5124 92         226 $tz = $fs = ''; # remove timezone and sub-seconds
5125             }
5126 439 100 66     1954 if (defined $ss and $ss < 60) {
    50          
5127 438         986 $ss = ":$ss";
5128             } elsif ($dateOnly) {
5129 1         3 $ss = '';
5130             } else {
5131 0         0 $ss = ':00';
5132             }
5133             # construct properly formatted date/time string
5134 439 50 33     1861 if ($a[0] < 1 or $a[0] > 12) {
5135 0         0 warn "Month '$a[0]' out of range 1..12\n";
5136 0         0 return undef;
5137             }
5138 439 50 33     1705 if ($a[1] < 1 or $a[1] > 31) {
5139 0         0 warn "Day '$a[1]' out of range 1..31\n";
5140 0         0 return undef;
5141             }
5142 439 50       1133 $a[2] > 24 and warn("Hour '$a[2]' out of range 0..24\n"), return undef;
5143 439 50       991 $a[3] > 59 and warn("Minutes '$a[3]' out of range 0..59\n"), return undef;
5144 439         1682 $rtnVal = "$yr:$a[0]:$a[1] $a[2]:$a[3]$ss$fs$tz";
5145             } elsif ($dateOnly) {
5146 26         107 $rtnVal = join ':', $yr, @a;
5147             }
5148             }
5149 474 100       1228 $rtnVal or warn "Invalid date/time (use YYYY:mm:dd HH:MM:SS[.ss][+/-HH:MM|Z])\n";
5150 474         5143 return $rtnVal;
5151             }
5152              
5153             #------------------------------------------------------------------------------
5154             # Set byte order according to our current preferences
5155             # Inputs: 0) ExifTool object ref, 1) default byte order
5156             # Returns: new byte order ('II' or 'MM') and sets current byte order
5157             # Notes: takes the first of the following that is valid:
5158             # 1) ByteOrder option
5159             # 2) new value for ExifByteOrder
5160             # 3) default byte order passed to this routine
5161             # 4) makenote byte order from last file read
5162             # 5) big endian
5163             sub SetPreferredByteOrder($;$)
5164             {
5165 46     46 0 202 my ($self, $default) = @_;
5166             my $byteOrder = $self->Options('ByteOrder') ||
5167             $self->GetNewValue('ExifByteOrder') ||
5168 46   100     259 $default || $$self{MAKER_NOTE_BYTE_ORDER} || 'MM';
5169 46 50       278 unless (SetByteOrder($byteOrder)) {
5170 0 0       0 warn "Invalid byte order '${byteOrder}'\n" if $self->Options('Verbose');
5171 0   0     0 $byteOrder = $$self{MAKER_NOTE_BYTE_ORDER} || 'MM';
5172 0         0 SetByteOrder($byteOrder);
5173             }
5174 46         192 return GetByteOrder();
5175             }
5176              
5177             #------------------------------------------------------------------------------
5178             # Assemble a continuing fraction into a rational value
5179             # Inputs: 0) numerator, 1) denominator
5180             # 2-N) list of fraction denominators, deepest first
5181             # Returns: numerator, denominator (in list context)
5182             sub AssembleRational($$@)
5183             {
5184 4998 100   4998 0 11440 @_ < 3 and return @_;
5185 3523         6226 my ($num, $denom, $frac) = splice(@_, 0, 3);
5186 3523         6444 return AssembleRational($frac*$num+$denom, $num, @_);
5187             }
5188              
5189             #------------------------------------------------------------------------------
5190             # Convert a floating point number (or 'inf' or 'undef' or a fraction) into a rational
5191             # Inputs: 0) floating point number, 1) optional maximum value (defaults to 0x7fffffff)
5192             # Returns: numerator, denominator (in list context)
5193             # Notes:
5194             # - the returned rational will be accurate to at least 8 significant figures if possible
5195             # - eg. an input of 3.14159265358979 returns a rational of 104348/33215,
5196             # which equals 3.14159265392142 and is accurate to 10 significant figures
5197             # - the returned rational will be reduced to the lowest common denominator except when
5198             # the input is a fraction in which case the input is returned unchanged
5199             # - these routines were a bit tricky, but fun to write!
5200             sub Rationalize($;$)
5201             {
5202 634     634 0 1507 my $val = shift;
5203 634 50       1842 return (1, 0) if $val eq 'inf';
5204 634 50       1576 return (0, 0) if $val eq 'undef';
5205 634 100       2096 return ($1,$2) if $val =~ m{^([-+]?\d+)/(\d+)$}; # accept fractional values
5206             # Note: Just testing "if $val" doesn't work because '0.0' is true! (ugghh!)
5207 618 100       2585 return (0, 1) if $val == 0;
5208 579 100       1507 my $sign = $val < 0 ? ($val = -$val, -1) : 1;
5209 579         1066 my ($num, $denom, @fracs);
5210 579         1106 my $frac = $val;
5211 579   100     1665 my $maxInt = shift || 0x7fffffff;
5212 579         1085 for (;;) {
5213 1475         4194 my ($n, $d) = AssembleRational(int($frac + 0.5), 1, @fracs);
5214 1475 50 33     5513 if ($n > $maxInt or $d > $maxInt) {
5215 0 0       0 last if defined $num;
5216 0 0       0 return ($sign, $maxInt) if $val < 1;
5217 0         0 return ($sign * $maxInt, 1);
5218             }
5219 1475         2945 ($num, $denom) = ($n, $d); # save last good values
5220 1475         3295 my $err = ($n/$d-$val) / $val; # get error of this rational
5221 1475 100       3514 last if abs($err) < 1e-8; # all done if error is small
5222 896         1614 my $int = int($frac);
5223 896         1738 unshift @fracs, $int;
5224 896 50       2059 last unless $frac -= $int;
5225 896         1408 $frac = 1 / $frac;
5226             }
5227 579         2527 return ($num * $sign, $denom);
5228             }
5229              
5230             #------------------------------------------------------------------------------
5231             # Utility routines to for writing binary data values
5232             # Inputs: 0) value, 1) data ref, 2) offset
5233             # Notes: prototype is (@) so values can be passed from list if desired
5234             sub Set16s(@)
5235             {
5236 188     188 0 379 my $val = shift;
5237 188 100       701 $val < 0 and $val += 0x10000;
5238 188         541 return Set16u($val, @_);
5239             }
5240             sub Set32s(@)
5241             {
5242 70     70 0 154 my $val = shift;
5243 70 100       219 $val < 0 and $val += 0xffffffff, ++$val;
5244 70         244 return Set32u($val, @_);
5245             }
5246             sub Set64u(@)
5247             {
5248 28     28 0 63 my $val = $_[0];
5249 28         74 my $hi = int($val / 4294967296);
5250 28         93 my $lo = Set32u($val - $hi * 4294967296); # NOTE: subject to round-off errors!
5251 28         101 $hi = Set32u($hi);
5252 28 100       74 $val = GetByteOrder() eq 'MM' ? $hi . $lo : $lo . $hi;
5253 28 100       111 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  27         76  
5254 28         79 return $val;
5255             }
5256             sub Set64s(@)
5257             {
5258 0     0 0 0 my $val = shift;
5259 0 0       0 $val < 0 and $val += 4294967296 * 4294967296; # (temporary hack won't really work due to round-off errors)
5260 0         0 return Set64u($val, @_);
5261             }
5262             sub SetRational64u(@) {
5263 319     319 0 1217 my ($numer,$denom) = Rationalize($_[0],0xffffffff);
5264 319         1007 my $val = Set32u($numer) . Set32u($denom);
5265 319 50       941 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5266 319         1279 return $val;
5267             }
5268             sub SetRational64s(@) {
5269 45     45 0 201 my ($numer,$denom) = Rationalize($_[0]);
5270 45         179 my $val = Set32s($numer) . Set32u($denom);
5271 45 50       199 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5272 45         177 return $val;
5273             }
5274             sub SetRational32u(@) {
5275 0     0 0 0 my ($numer,$denom) = Rationalize($_[0],0xffff);
5276 0         0 my $val = Set16u($numer) . Set16u($denom);
5277 0 0       0 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5278 0         0 return $val;
5279             }
5280             sub SetRational32s(@) {
5281 0     0 0 0 my ($numer,$denom) = Rationalize($_[0],0x7fff);
5282 0         0 my $val = Set16s($numer) . Set16u($denom);
5283 0 0       0 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5284 0         0 return $val;
5285             }
5286             sub SetFixed16u(@) {
5287 0     0 0 0 my $val = int(shift() * 0x100 + 0.5);
5288 0         0 return Set16u($val, @_);
5289             }
5290             sub SetFixed16s(@) {
5291 0     0 0 0 my $val = shift;
5292 0 0       0 return Set16s(int($val * 0x100 + ($val < 0 ? -0.5 : 0.5)), @_);
5293             }
5294             sub SetFixed32u(@) {
5295 0     0 0 0 my $val = int(shift() * 0x10000 + 0.5);
5296 0         0 return Set32u($val, @_);
5297             }
5298             sub SetFixed32s(@) {
5299 12     12 0 25 my $val = shift;
5300 12 100       261 return Set32s(int($val * 0x10000 + ($val < 0 ? -0.5 : 0.5)), @_);
5301             }
5302             sub SetFloat(@) {
5303 64     64 0 465 my $val = SwapBytes(pack('f',$_[0]), 4);
5304 64 50       291 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5305 64         384 return $val;
5306             }
5307             sub SetDouble(@) {
5308             # swap 32-bit words (ARM quirk) and bytes if necessary
5309 66     66 0 518 my $val = SwapBytes(SwapWords(pack('d',$_[0])), 8);
5310 66 50       315 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5311 66         433 return $val;
5312             }
5313             #------------------------------------------------------------------------------
5314             # hash lookups for writing binary data values
5315             my %writeValueProc = (
5316             int8s => \&Set8s,
5317             int8u => \&Set8u,
5318             int16s => \&Set16s,
5319             int16u => \&Set16u,
5320             int16uRev => \&Set16uRev,
5321             int32s => \&Set32s,
5322             int32u => \&Set32u,
5323             int64s => \&Set64s,
5324             int64u => \&Set64u,
5325             rational32s => \&SetRational32s,
5326             rational32u => \&SetRational32u,
5327             rational64s => \&SetRational64s,
5328             rational64u => \&SetRational64u,
5329             fixed16u => \&SetFixed16u,
5330             fixed16s => \&SetFixed16s,
5331             fixed32u => \&SetFixed32u,
5332             fixed32s => \&SetFixed32s,
5333             float => \&SetFloat,
5334             double => \&SetDouble,
5335             ifd => \&Set32u,
5336             );
5337             # verify that we can write floats on this platform
5338             {
5339             my %writeTest = (
5340             float => [ -3.14159, 'c0490fd0' ],
5341             double => [ -3.14159, 'c00921f9f01b866e' ],
5342             );
5343             my $format;
5344             my $oldOrder = GetByteOrder();
5345             SetByteOrder('MM');
5346             foreach $format (keys %writeTest) {
5347             my ($val, $hex) = @{$writeTest{$format}};
5348             # add floating point entries if we can write them
5349             next if unpack('H*', &{$writeValueProc{$format}}($val)) eq $hex;
5350             delete $writeValueProc{$format}; # we can't write them
5351             }
5352             SetByteOrder($oldOrder);
5353             }
5354              
5355             #------------------------------------------------------------------------------
5356             # write binary data value (with current byte ordering)
5357             # Inputs: 0) value, 1) format string
5358             # 2) number of values:
5359             # undef = 1 for numerical types, or data length for string/undef types
5360             # -1 = number of space-delimited values in the input string
5361             # 3) optional data reference, 4) value offset (may be negative for bytes from end)
5362             # Returns: packed value (and sets value in data) or undef on error
5363             # Notes: May modify input value to round for integer formats
5364             sub WriteValue($$;$$$$)
5365             {
5366 1406     1406 0 4193 my ($val, $format, $count, $dataPt, $offset) = @_;
5367 1406         3713 my $proc = $writeValueProc{$format};
5368 1406         2399 my $packed;
5369              
5370 1406 100 66     4024 if ($proc) {
    50          
5371 1065         3741 my @vals = split(' ',$val);
5372 1065 100       2588 if ($count) {
5373 571 100       1633 $count = @vals if $count < 0;
5374             } else {
5375 494         899 $count = 1; # assume 1 if count not specified
5376             }
5377 1065         2135 $packed = '';
5378 1065         2914 while ($count--) {
5379 1570         3096 $val = shift @vals;
5380 1570 50       3426 return undef unless defined $val;
5381             # validate numerical formats
5382 1570 100       5583 if ($format =~ /^int/) {
    100          
5383 1189 50 33     3877 unless (IsInt($val) or IsHex($val)) {
5384 0 0       0 return undef unless IsFloat($val);
5385             # round to nearest integer
5386 0 0       0 $val = int($val + ($val < 0 ? -0.5 : 0.5));
5387 0         0 $_[0] = $val;
5388             }
5389             } elsif (not IsFloat($val)) {
5390 7 50 33     116 return undef unless $format =~ /^rational/ and ($val eq 'inf' or
      33        
5391             $val eq 'undef' or IsRational($val));
5392             }
5393 1570         4316 $packed .= &$proc($val);
5394             }
5395             } elsif ($format eq 'string' or $format eq 'undef') {
5396 341 100       1044 $format eq 'string' and $val .= "\0"; # null-terminate strings
5397 341 100 66     1034 if ($count and $count > 0) {
5398 61         178 my $diff = $count - length($val);
5399 61 100       236 if ($diff) {
5400             #warn "wrong string length!\n";
5401             # adjust length of string to match specified count
5402 33 100       909 if ($diff < 0) {
5403 26 50       75 if ($format eq 'string') {
5404 26 50       65 return undef unless $count;
5405 26         92 $val = substr($val, 0, $count - 1) . "\0";
5406             } else {
5407 0         0 $val = substr($val, 0, $count);
5408             }
5409             } else {
5410 7         28 $val .= "\0" x $diff;
5411             }
5412             }
5413             } else {
5414 280         485 $count = length($val);
5415             }
5416 341 100       847 $dataPt and substr($$dataPt, $offset, $count) = $val;
5417 341         1102 return $val;
5418             } else {
5419 0         0 warn "Sorry, Can't write $format values on this platform\n";
5420 0         0 return undef;
5421             }
5422 1065 100       3032 $dataPt and substr($$dataPt, $offset, length($packed)) = $packed;
5423 1065         4505 return $packed;
5424             }
5425              
5426             #------------------------------------------------------------------------------
5427             # Encode bit mask (the inverse of DecodeBits())
5428             # Inputs: 0) value to encode, 1) Reference to hash for encoding (or undef)
5429             # 2) optional number of bits per word (defaults to 32), 3) total bits
5430             # Returns: bit mask or undef on error (plus error string in list context)
5431             sub EncodeBits($$;$$)
5432             {
5433 105     105 0 314 my ($val, $lookup, $bits, $num) = @_;
5434 105 100       315 $bits or $bits = 32;
5435 105 100       301 $num or $num = $bits;
5436 105         389 my $words = int(($num + $bits - 1) / $bits);
5437 105         307 my @outVal = (0) x $words;
5438 105 100       324 if ($val ne '(none)') {
5439 86         345 my @vals = split /\s*,\s*/, $val;
5440 86         221 foreach $val (@vals) {
5441 42         75 my $bit;
5442 42 50       114 if ($lookup) {
5443 42         128 $bit = ReverseLookup($val, $lookup);
5444             # (Note: may get non-numerical $bit values from Unknown() tags)
5445 42 100       132 unless (defined $bit) {
5446 33 50       117 if ($val =~ /\[(\d+)\]/) { # numerical bit specification
5447 0         0 $bit = $1;
5448             } else {
5449             # don't return error string unless more than one value
5450 33 100 66     232 return undef unless @vals > 1 and wantarray;
5451 2         14 return (undef, "no match for '${val}'");
5452             }
5453             }
5454             } else {
5455 0         0 $bit = $val;
5456             }
5457 9 50 33     31 unless (IsInt($bit) and $bit < $num) {
5458 0 0       0 return undef unless wantarray;
5459 0 0       0 return (undef, IsInt($bit) ? 'bit number too high' : 'not an integer');
5460             }
5461 9         20 my $word = int($bit / $bits);
5462 9         33 $outVal[$word] |= (1 << ($bit - $word * $bits));
5463             }
5464             }
5465 72         439 return "@outVal";
5466             }
5467              
5468             #------------------------------------------------------------------------------
5469             # get current position in output file (or end of file if a scalar reference)
5470             # Inputs: 0) file or scalar reference
5471             # Returns: Current position or -1 on error
5472             sub Tell($)
5473             {
5474 334     334 0 703 my $outfile = shift;
5475 334 100       1434 if (UNIVERSAL::isa($outfile,'GLOB')) {
5476 305         1875 return tell($outfile);
5477             } else {
5478 29         143 return length($$outfile);
5479             }
5480             }
5481              
5482             #------------------------------------------------------------------------------
5483             # write to file or memory
5484             # Inputs: 0) file or scalar reference, 1-N) list of stuff to write
5485             # Returns: true on success
5486             sub Write($@)
5487             {
5488 4085     4085 0 8045 my $outfile = shift;
5489 4085 100       16135 if (UNIVERSAL::isa($outfile,'GLOB')) {
    50          
5490 2355         19139 return print $outfile @_;
5491             } elsif (ref $outfile eq 'SCALAR') {
5492 1730         10640 $$outfile .= join('', @_);
5493 1730         6996 return 1;
5494             }
5495 0         0 return 0;
5496             }
5497              
5498             #------------------------------------------------------------------------------
5499             # Write trailer buffer to file (applying fixups if necessary)
5500             # Inputs: 0) ExifTool object ref, 1) trailer dirInfo ref, 2) output file ref
5501             # Returns: 1 on success
5502             sub WriteTrailerBuffer($$$)
5503             {
5504 12     12 0 38 my ($self, $trailInfo, $outfile) = @_;
5505 12 50       54 if ($$self{DEL_GROUP}{Trailer}) {
5506 0         0 $self->VPrint(0, " Deleting trailer ($$trailInfo{Offset} bytes)\n");
5507 0         0 ++$$self{CHANGED};
5508 0         0 return 1;
5509             }
5510 12         53 my $pos = Tell($outfile);
5511 12         31 my $trailPt = $$trailInfo{OutFile};
5512             # apply fixup if necessary (AFCP requires this)
5513 12 100       50 if ($$trailInfo{Fixup}) {
5514 8 50       29 if ($pos > 0) {
5515             # shift offsets to final AFCP location and write it out
5516 8         26 $$trailInfo{Fixup}{Shift} += $pos;
5517 8         42 $$trailInfo{Fixup}->ApplyFixup($trailPt);
5518             } else {
5519 0         0 $self->Error("Can't get file position for trailer offset fixup",1);
5520             }
5521             }
5522 12         56 return Write($outfile, $$trailPt);
5523             }
5524              
5525             #------------------------------------------------------------------------------
5526             # Add trailers as a block
5527             # Inputs: 0) ExifTool object ref, 1) [optional] trailer data raf,
5528             # 1 or 2-N) trailer types to add (or none to add all)
5529             # Returns: new trailer ref, or undef
5530             # - increments CHANGED if trailer was added
5531             sub AddNewTrailers($;@)
5532             {
5533 133     133 0 498 my ($self, @types) = @_;
5534 133         282 my $trailPt;
5535 133 100       501 ref $types[0] and $trailPt = shift @types;
5536 133 100       521 $types[0] or shift @types; # (in case undef data ref is passed)
5537             # add all possible trailers if none specified (currently only CanonVRD)
5538 133 100       1119 @types or @types = qw(CanonVRD CanonDR4);
5539             # add trailers as a block (if not done already)
5540 133         1557 my $type;
5541 133         474 foreach $type (@types) {
5542 259 100       1461 next unless $$self{NEW_VALUE}{$Image::ExifTool::Extra{$type}};
5543 10 100       66 next if $$self{"Did$type"};
5544 9 100       59 my $val = $self->GetNewValue($type) or next;
5545             # DR4 record must be wrapped in VRD trailer package
5546 8 100       40 if ($type eq 'CanonDR4') {
5547 3 100       21 next if $$self{DidCanonVRD}; # (only allow one VRD trailer)
5548 2         28 require Image::ExifTool::CanonVRD;
5549 2         22 $val = Image::ExifTool::CanonVRD::WrapDR4($val);
5550 2         12 $$self{DidCanonVRD} = 1;
5551             }
5552 7 50       29 my $verb = $trailPt ? 'Writing' : 'Adding';
5553 7         67 $self->VPrint(0, " $verb $type as a block\n");
5554 7 50       29 if ($trailPt) {
5555 0         0 $$trailPt .= $val;
5556             } else {
5557 7         21 $trailPt = \$val;
5558             }
5559 7         38 $$self{"Did$type"} = 1;
5560 7         34 ++$$self{CHANGED};
5561             }
5562 133         427 return $trailPt;
5563             }
5564              
5565             #------------------------------------------------------------------------------
5566             # Write segment, splitting up into multiple segments if necessary
5567             # Inputs: 0) file or scalar reference, 1) segment marker
5568             # 2) segment header, 3) segment data ref, 4) segment type
5569             # Returns: number of segments written, or 0 on error
5570             # Notes: Writes a single empty segment if data is empty
5571             sub WriteMultiSegment($$$$;$)
5572             {
5573 114     114 0 498 my ($outfile, $marker, $header, $dataPt, $type) = @_;
5574 114 100       481 $type or $type = '';
5575 114         297 my $len = length($$dataPt);
5576 114         385 my $hdr = "\xff" . chr($marker);
5577 114         268 my $count = 0;
5578 114         271 my $maxLen = $maxSegmentLen - length($header);
5579 114 100       394 $maxLen -= 2 if $type eq 'ICC'; # leave room for segment counters
5580 114         416 my $num = int(($len + $maxLen - 1) / $maxLen); # number of segments to write
5581 114         257 my $n = 0;
5582             # write data, splitting into multiple segments if necessary
5583             # (each segment gets its own header)
5584 114         211 for (;;) {
5585 114         203 ++$count;
5586 114         264 my $size = $len - $n;
5587 114 50       345 if ($size > $maxLen) {
5588 0         0 $size = $maxLen;
5589             # avoid starting an Extended EXIF segment with a valid TIFF header
5590             # (because we would interpret that as a separate EXIF segment)
5591 0 0 0     0 --$size if $type eq 'EXIF' and $n+$maxLen <= $len-4 and
      0        
5592             substr($$dataPt, $n+$maxLen, 4) =~ /^(MM\0\x2a|II\x2a\0)/;
5593             }
5594 114         603 my $buff = substr($$dataPt,$n,$size);
5595 114         242 $n += $size;
5596 114         235 $size += length($header);
5597 114 100       387 if ($type eq 'ICC') {
5598 3         14 $buff = pack('CC', $count, $num) . $buff;
5599 3         9 $size += 2;
5600             }
5601             # write the new segment with appropriate header
5602 114         478 my $segHdr = $hdr . pack('n', $size + 2);
5603 114 50       480 Write($outfile, $segHdr, $header, $buff) or return 0;
5604 114 50       478 last if $n >= $len;
5605             }
5606 114         374 return $count;
5607             }
5608              
5609             #------------------------------------------------------------------------------
5610             # Write XMP segment(s) to JPEG file
5611             # Inputs: 0) ExifTool object ref, 1) outfile ref, 2) XMP data ref,
5612             # 3) extended XMP data ref, 4) 32-char extended XMP GUID (or undef if no extended data)
5613             # Returns: true on success, false on write error
5614             sub WriteMultiXMP($$$$$)
5615             {
5616 36     36 0 134 my ($self, $outfile, $dataPt, $extPt, $guid) = @_;
5617 36         80 my $success = 1;
5618              
5619             # write main XMP segment
5620 36         90 my $size = length($$dataPt) + length($xmpAPP1hdr);
5621 36 50       125 if ($size > $maxXMPLen) {
5622 0         0 $self->Error("XMP block too large for JPEG segment! ($size bytes)", 1);
5623 0         0 return 1;
5624             }
5625 36         205 my $app1hdr = "\xff\xe1" . pack('n', $size + 2);
5626 36 50       160 Write($outfile, $app1hdr, $xmpAPP1hdr, $$dataPt) or $success = 0;
5627             # write extended XMP segment(s) if necessary
5628 36 50       133 if (defined $guid) {
5629 0         0 $size = length($$extPt);
5630 0         0 my $maxLen = $maxXMPLen - 75; # maximum size without 75-byte header
5631 0         0 my $off;
5632 0         0 for ($off=0; $off<$size; $off+=$maxLen) {
5633             # header(75) = signature(35) + guid(32) + size(4) + offset(4)
5634 0         0 my $len = $size - $off;
5635 0 0       0 $len = $maxLen if $len > $maxLen;
5636 0         0 $app1hdr = "\xff\xe1" . pack('n', $len + 75 + 2);
5637 0         0 $self->VPrint(0, "Writing extended XMP segment ($len bytes)\n");
5638 0 0       0 Write($outfile, $app1hdr, $xmpExtAPP1hdr, $guid, pack('N2', $size, $off),
5639             substr($$extPt, $off, $len)) or $success = 0;
5640             }
5641             }
5642 36         224 return $success;
5643             }
5644              
5645             #------------------------------------------------------------------------------
5646             # WriteJPEG : Write JPEG image
5647             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
5648             # Returns: 1 on success, 0 if this wasn't a valid JPEG file, or -1 if
5649             # an output file was specified and a write error occurred
5650             sub WriteJPEG($$)
5651             {
5652 114     114 0 443 my ($self, $dirInfo) = @_;
5653 114         427 my $outfile = $$dirInfo{OutFile};
5654 114         339 my $raf = $$dirInfo{RAF};
5655 114         373 my ($ch, $s, $length,$err, %doneDir, $isEXV, $creatingEXV);
5656 114         354 my $verbose = $$self{OPTIONS}{Verbose};
5657 114         348 my $out = $$self{OPTIONS}{TextOut};
5658 114         244 my $rtnVal = 0;
5659 114         306 my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known
5660              
5661             # check to be sure this is a valid JPG or EXV file
5662 114 100 100     733 unless ($raf->Read($s,2) == 2 and $s eq "\xff\xd8") {
5663 2 100 66     44 if (defined $s and length $s) {
5664 1 50 33     10 return 0 unless $s eq "\xff\x01" and $raf->Read($s,5) == 5 and $s eq 'Exiv2';
      33        
5665             } else {
5666 1 50       8 return 0 unless $$self{FILE_TYPE} eq 'EXV';
5667 1         2 $s = 'Exiv2';
5668 1         2 $creatingEXV = 1;
5669             }
5670 2 50       17 Write($outfile,"\xff\x01") or $err = 1;
5671 2         6 $isEXV = 1;
5672             }
5673              
5674 114         407 delete $$self{PREVIEW_INFO}; # reset preview information
5675 114         354 delete $$self{DEL_PREVIEW}; # reset flag to delete preview
5676              
5677 114 50       665 Write($outfile, $s) or $err = 1;
5678             # figure out what segments we need to write for the tags we have set
5679 114         373 my $addDirs = $$self{ADD_DIRS};
5680 114         343 my $editDirs = $$self{EDIT_DIRS};
5681 114         286 my $delGroup = $$self{DEL_GROUP};
5682 114         299 my $path = $$self{PATH};
5683 114         273 my $pn = scalar @$path;
5684              
5685             # set input record separator to 0xff (the JPEG marker) to make reading quicker
5686 114         906 local $/ = "\xff";
5687             #
5688             # pre-scan image to determine if any create-able segment already exists
5689             #
5690 114         524 my $pos = $raf->Tell();
5691 114         323 my ($marker, @dirOrder, %dirCount);
5692 114         247 Prescan: for (;;) {
5693             # read up to next marker (JPEG markers begin with 0xff)
5694 808 100       2511 $raf->ReadLine($s) or last;
5695             # JPEG markers can be padded with unlimited 0xff's
5696 807         1251 for (;;) {
5697 807 50       2451 $raf->Read($ch, 1) or last Prescan;
5698 807         1431 $marker = ord($ch);
5699 807 50       2065 last unless $marker == 0xff;
5700             }
5701 807         1177 my $dirName;
5702             # stop pre-scan at SOS (end of meta information) or EOI (end of image)
5703 807 100 100     3133 if ($marker == 0xda or $marker == 0xd9) {
5704 113         443 $dirName = $jpegMarker{$marker};
5705 113         387 push(@dirOrder, $dirName);
5706 113         373 $dirCount{$dirName} = 1;
5707 113         305 last;
5708             }
5709             # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
5710 694 100 66     5606 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    50 100        
      33        
      66        
      33        
5711 112 50       384 last unless $raf->Seek(7, 1);
5712             # read data for all markers except stand-alone
5713             # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
5714             } elsif ($marker!=0x00 and $marker!=0x01 and ($marker<0xd0 or $marker>0xd7)) {
5715             # read record length word
5716 582 50       1528 last unless $raf->Read($s, 2) == 2;
5717 582         1653 my $len = unpack('n',$s); # get data length
5718 582 50 33     2173 last unless defined($len) and $len >= 2;
5719 582         944 $len -= 2; # subtract size of length word
5720 582 100       1374 if (($marker & 0xf0) == 0xe0) { # is this an APP segment?
5721 347 100       755 my $n = $len < 64 ? $len : 64;
5722 347 50       762 $raf->Read($s, $n) == $n or last;
5723 347         561 $len -= $n;
5724             # Note: only necessary to recognize APP segments that we can create,
5725             # or delete as a group (and the names below should match @delGroups)
5726 347 100       2371 if ($marker == 0xe0) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
5727 45 100       200 $s =~ /^JFIF\0/ and $dirName = 'JFIF';
5728 45 100       148 $s =~ /^JFXX\0\x10/ and $dirName = 'JFXX';
5729 45 100       180 $s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF';
5730             } elsif ($marker == 0xe1) {
5731 84 100       790 if ($s =~ /^(.{0,4})Exif\0.(.{1,4})/is) {
5732 60         191 $dirName = 'IFD0';
5733 60         312 my ($junk, $bytes) = ($1, $2);
5734             # support multi-segment EXIF
5735 60 0 66     326 if (@dirOrder and $dirOrder[-1] =~ /^(IFD0|ExtendedEXIF)$/ and
      33        
      33        
5736             not length $junk and $bytes !~ /^(MM\0\x2a|II\x2a\0)/)
5737             {
5738 0         0 $dirName = 'ExtendedEXIF';
5739             }
5740             }
5741 84 100       1138 $s =~ /^$xmpAPP1hdr/ and $dirName = 'XMP';
5742 84 100       785 $s =~ /^$xmpExtAPP1hdr/ and $dirName = 'XMP';
5743             } elsif ($marker == 0xe2) {
5744 55 100       190 $s =~ /^ICC_PROFILE\0/ and $dirName = 'ICC_Profile';
5745 55 100       223 $s =~ /^FPXR\0/ and $dirName = 'FlashPix';
5746 55 100       173 $s =~ /^MPF\0/ and $dirName = 'MPF';
5747             } elsif ($marker == 0xe3) {
5748 9 50       103 $s =~ /^(Meta|META|Exif)\0\0/ and $dirName = 'Meta';
5749             } elsif ($marker == 0xe5) {
5750 9 50       55 $s =~ /^RMETA\0/ and $dirName = 'RMETA';
5751             } elsif ($marker == 0xea) {
5752 9 50       52 $s =~ /^AROT\0\0/ and $dirName = 'AROT';
5753             } elsif ($marker == 0xeb) {
5754 18 100       84 $s =~ /^JP/ and $dirName = 'JUMBF';
5755             } elsif ($marker == 0xec) {
5756 19 100       98 $s =~ /^Ducky/ and $dirName = 'Ducky';
5757             } elsif ($marker == 0xed) {
5758 29 100       422 $s =~ /^$psAPP13hdr/ and $dirName = 'Photoshop';
5759             } elsif ($marker == 0xee) {
5760 16 50       106 $s =~ /^Adobe/ and $dirName = 'Adobe';
5761             }
5762             # initialize doneDir as a flag that the directory exists
5763             # (unless we are deleting it anyway)
5764 347 100 100     1671 $doneDir{$dirName} = 0 if defined $dirName and not $$delGroup{$dirName};
5765             }
5766 582 50       1610 $raf->Seek($len, 1) or last;
5767             }
5768 694 100       2618 $dirName or $dirName = JpegMarkerName($marker);
5769 694   100     3062 $dirCount{$dirName} = ($dirCount{$dirName} || 0) + 1;
5770 694         1502 push @dirOrder, $dirName;
5771             }
5772 114 100 100     760 unless ($marker and $marker == 0xda) {
5773 2 50       8 $isEXV or $self->Error('Corrupted JPEG image'), return 1;
5774 2 50 66     13 $marker and $marker != 0xd9 and $self->Error('Corrupted EXV file'), return 1;
5775             }
5776 114 50       484 $raf->Seek($pos, 0) or $self->Error('Seek error'), return 1;
5777             #
5778             # re-write the image
5779             #
5780 114         507 my ($combinedSegData, $segPos, $firstSegPos, %extendedXMP);
5781 114         0 my (@iccChunk, $iccChunkCount, $iccChunksTotal);
5782             # read through each segment in the JPEG file
5783 114         251 Marker: for (;;) {
5784              
5785             # read up to next marker (JPEG markers begin with 0xff)
5786 808         1516 my $segJunk;
5787 808 100       3423 $raf->ReadLine($segJunk) or $segJunk = '';
5788             # remove the 0xff but write the rest of the junk up to this point
5789             # (this will handle the data after the first 7 bytes of SOF segments)
5790 808         1907 chomp($segJunk);
5791 808 100       2260 Write($outfile, $segJunk) if length $segJunk;
5792             # JPEG markers can be padded with unlimited 0xff's
5793 808         1298 for (;;) {
5794 808 100       2649 if ($raf->Read($ch, 1)) {
    50          
5795 807         1491 $marker = ord($ch);
5796 807 50       2184 last unless $marker == 0xff;
5797             } elsif ($creatingEXV) {
5798             # create EXV from scratch
5799 1         3 $marker = 0xd9; # EOI
5800 1         2 push @dirOrder, 'EOI';
5801 1         4 $dirCount{EOI} = 1;
5802 1         4 last;
5803             } else {
5804 0         0 $self->Error('Format error');
5805 0         0 return 1;
5806             }
5807             }
5808             # read the segment data
5809 808         1452 my $segData;
5810             # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
5811 808 100 66     9095 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    100 100        
      33        
      66        
      66        
      66        
5812 112 50       452 last unless $raf->Read($segData, 7) == 7;
5813             # read data for all markers except stand-alone
5814             # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, EOI, RST0-RST7)
5815             } elsif ($marker!=0x00 and $marker!=0x01 and $marker!=0xd9 and
5816             ($marker<0xd0 or $marker>0xd7))
5817             {
5818             # read record length word
5819 694 50       2089 last unless $raf->Read($s, 2) == 2;
5820 694         2345 my $len = unpack('n',$s); # get data length
5821 694 50 33     3082 last unless defined($len) and $len >= 2;
5822 694         2247 $segPos = $raf->Tell();
5823 694         1290 $len -= 2; # subtract size of length word
5824 694 50       1699 last unless $raf->Read($segData, $len) == $len;
5825             }
5826             # initialize variables for this segment
5827 808         2240 my $hdr = "\xff" . chr($marker); # segment header
5828 808         2574 my $markerName = JpegMarkerName($marker);
5829 808         1984 my $dirName = shift @dirOrder; # get directory name
5830             #
5831             # create all segments that must come before this one
5832             # (nothing comes before SOI or after SOS)
5833             #
5834 808         2350 while ($markerName ne 'SOI') {
5835 808 100 100     2957 if (exists $$addDirs{JFIF} and not defined $doneDir{JFIF}) {
5836 1         4 $doneDir{JFIF} = 1;
5837 1 50       29 if (defined $doneDir{Adobe}) {
5838             # JFIF overrides Adobe APP14 colour components, so don't allow this
5839             # (ref https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html)
5840 1         8 $self->Warn('Not creating JFIF in JPEG with Adobe APP14');
5841             } else {
5842 0 0       0 if ($verbose) {
5843 0         0 print $out "Creating APP0:\n";
5844 0         0 print $out " Creating JFIF with default values\n";
5845             }
5846 0         0 my $jfif = "\x01\x02\x01\0\x48\0\x48\0\0";
5847 0         0 SetByteOrder('MM');
5848 0         0 my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
5849 0         0 my %dirInfo = (
5850             DataPt => \$jfif,
5851             DirStart => 0,
5852             DirLen => length $jfif,
5853             Parent => 'JFIF',
5854             );
5855             # must temporarily remove JFIF from DEL_GROUP so we can
5856             # delete JFIF and add it back again in a single step
5857 0         0 my $delJFIF = $$delGroup{JFIF};
5858 0         0 delete $$delGroup{JFIF};
5859 0         0 $$path[$pn] = 'JFIF';
5860 0         0 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5861 0 0       0 $$delGroup{JFIF} = $delJFIF if defined $delJFIF;
5862 0 0 0     0 if (defined $newData and length $newData) {
5863 0         0 my $app0hdr = "\xff\xe0" . pack('n', length($newData) + 7);
5864 0 0       0 Write($outfile,$app0hdr,"JFIF\0",$newData) or $err = 1;
5865             }
5866             }
5867             }
5868             # don't create anything before APP0 or APP1 EXIF (containing IFD0)
5869 808 100 100     5695 last if $markerName eq 'APP0' or $dirCount{IFD0} or $dirCount{ExtendedEXIF};
      66        
5870             # EXIF information must come immediately after APP0
5871 703 100 100     2844 if (exists $$addDirs{IFD0} and not defined $doneDir{IFD0}) {
5872 33         153 $doneDir{IFD0} = 1;
5873 33 100       154 $verbose and print $out "Creating APP1:\n";
5874             # write new EXIF data
5875 33         130 $$self{TIFF_TYPE} = 'APP1';
5876 33         209 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
5877 33         219 my %dirInfo = (
5878             DirName => 'IFD0',
5879             Parent => 'APP1',
5880             );
5881 33         156 $$path[$pn] = 'APP1';
5882 33         262 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
5883 33 100 66     292 if (defined $buff and length $buff) {
5884 31 50       185 if (length($buff) + length($exifAPP1hdr) > $maxSegmentLen) {
5885 0 0       0 if ($self->Options('NoMultiExif')) {
5886 0         0 $self->Error('EXIF is too large for JPEG segment');
5887             } else {
5888 0         0 $self->Warn('Creating multi-segment EXIF',1);
5889             }
5890             }
5891             # switch to buffered output if required
5892 31 50 33     424 if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer} or $$self{HiddenData}) and
      33        
5893             not $oldOutfile)
5894             {
5895 0         0 $writeBuffer = '';
5896 0         0 $oldOutfile = $outfile;
5897 0         0 $outfile = \$writeBuffer;
5898             # must account for segment, EXIF and TIFF headers
5899 0         0 foreach (qw(PREVIEW_INFO LeicaTrailer HiddenData)) {
5900 0 0       0 $$self{$_}{Fixup}{Start} += 18 if $$self{$_};
5901             }
5902             }
5903             # write as multi-segment
5904 31         198 my $n = WriteMultiSegment($outfile, 0xe1, $exifAPP1hdr, \$buff, 'EXIF');
5905 31 50 33     292 if (not $n) {
    50          
5906 0         0 $err = 1;
5907             } elsif ($n > 1 and $oldOutfile) {
5908             # (punt on this because updating the pointers would be a real pain)
5909 0         0 $self->Error("Can't write multi-segment EXIF with external pointers");
5910             }
5911 31         210 ++$$self{CHANGED};
5912             }
5913             }
5914             # APP13 Photoshop segment next
5915 703 100       2021 last if $dirCount{Photoshop};
5916 521 100 100     1976 if (exists $$addDirs{Photoshop} and not defined $doneDir{Photoshop}) {
5917 21         77 $doneDir{Photoshop} = 1;
5918 21 50       96 $verbose and print $out "Creating APP13:\n";
5919             # write new APP13 Photoshop record to memory
5920 21         91 my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
5921 21         105 my %dirInfo = (
5922             Parent => 'APP13',
5923             );
5924 21         70 $$path[$pn] = 'APP13';
5925 21         121 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5926 21 50 33     176 if (defined $buff and length $buff) {
5927 21 50       122 WriteMultiSegment($outfile, 0xed, $psAPP13hdr, \$buff) or $err = 1;
5928 21         112 ++$$self{CHANGED};
5929             }
5930             }
5931             # then APP1 XMP segment
5932 521 100       1443 last if $dirCount{XMP};
5933 506 100 100     1726 if (exists $$addDirs{XMP} and not defined $doneDir{XMP}) {
5934 29         94 $doneDir{XMP} = 1;
5935 29 50       108 $verbose and print $out "Creating APP1:\n";
5936             # write new XMP data
5937 29         149 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
5938 29         188 my %dirInfo = (
5939             Parent => 'APP1',
5940             # specify MaxDataLen so XMP is split if required
5941             MaxDataLen => $maxXMPLen - length($xmpAPP1hdr),
5942             );
5943 29         125 $$path[$pn] = 'APP1';
5944 29         218 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5945 29 50 33     200 if (defined $buff and length $buff) {
5946             WriteMultiXMP($self, $outfile, \$buff, $dirInfo{ExtendedXMP},
5947 29 50       182 $dirInfo{ExtendedGUID}) or $err = 1;
5948             }
5949             }
5950             # then APP2 ICC_Profile segment
5951 506 100       1441 last if $dirCount{ICC_Profile};
5952 501 100 100     1473 if (exists $$addDirs{ICC_Profile} and not defined $doneDir{ICC_Profile}) {
5953 3         10 $doneDir{ICC_Profile} = 1;
5954 3 50 66     21 next if $$delGroup{ICC_Profile} and $$delGroup{ICC_Profile} != 2;
5955 3 50       13 $verbose and print $out "Creating APP2:\n";
5956             # write new ICC_Profile data
5957 3         14 my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
5958 3         14 my %dirInfo = (
5959             Parent => 'APP2',
5960             );
5961 3         9 $$path[$pn] = 'APP2';
5962 3         18 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5963 3 50 33     20 if (defined $buff and length $buff) {
5964 3 50       17 WriteMultiSegment($outfile, 0xe2, "ICC_PROFILE\0", \$buff, 'ICC') or $err = 1;
5965 3         17 ++$$self{CHANGED};
5966             }
5967             }
5968             # then APP12 Ducky segment
5969 501 100       1291 last if $dirCount{Ducky};
5970 500 100 100     1476 if (exists $$addDirs{Ducky} and not defined $doneDir{Ducky}) {
5971 2         4 $doneDir{Ducky} = 1;
5972 2 50       7 $verbose and print $out "Creating APP12 Ducky:\n";
5973             # write new Ducky segment data
5974 2         9 my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
5975 2         8 my %dirInfo = (
5976             Parent => 'APP12',
5977             );
5978 2         5 $$path[$pn] = 'APP12';
5979 2         9 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5980 2 50 33     16 if (defined $buff and length $buff) {
5981 2         5 my $size = length($buff) + 5;
5982 2 50       11 if ($size <= $maxSegmentLen) {
5983             # write the new segment with appropriate header
5984 2         10 my $app12hdr = "\xff\xec" . pack('n', $size + 2);
5985 2 50       8 Write($outfile, $app12hdr, 'Ducky', $buff) or $err = 1;
5986             } else {
5987 0         0 $self->Warn("APP12 Ducky segment too large! ($size bytes)");
5988             }
5989             }
5990             }
5991             # then APP14 Adobe segment
5992 500 100       1236 last if $dirCount{Adobe};
5993 475 50 33     1361 if (exists $$addDirs{Adobe} and not defined $doneDir{Adobe}) {
5994 0         0 $doneDir{Adobe} = 1;
5995 0         0 my $buff = $self->GetNewValue('Adobe');
5996 0 0       0 if ($buff) {
5997 0 0       0 $verbose and print $out "Creating APP14:\n Creating Adobe segment\n";
5998 0         0 my $size = length($buff);
5999 0 0       0 if ($size <= $maxSegmentLen) {
6000             # write the new segment with appropriate header
6001 0         0 my $app14hdr = "\xff\xee" . pack('n', $size + 2);
6002 0 0       0 Write($outfile, $app14hdr, $buff) or $err = 1;
6003 0         0 ++$$self{CHANGED};
6004             } else {
6005 0         0 $self->Warn("APP14 Adobe segment too large! ($size bytes)");
6006             }
6007             }
6008             }
6009             # finally, COM segment
6010 475 100       1267 last if $dirCount{COM};
6011 455 100 100     1256 if (exists $$addDirs{COM} and not defined $doneDir{COM}) {
6012 5         11 $doneDir{COM} = 1;
6013 5 50 33     15 next if $$delGroup{File} and $$delGroup{File} != 2;
6014 5         19 my $newComment = $self->GetNewValue('Comment');
6015 5 50       13 if (defined $newComment) {
6016 5 50       11 if ($verbose) {
6017 0         0 print $out "Creating COM:\n";
6018 0         0 $self->VerboseValue('+ Comment', $newComment);
6019             }
6020 5 50       21 WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1;
6021 5         12 ++$$self{CHANGED};
6022             }
6023             }
6024 455         842 last; # didn't want to loop anyway
6025             }
6026 808         2488 $$path[$pn] = $markerName;
6027             # decrement counter for this directory since we are about to process it
6028 808         2087 --$dirCount{$dirName};
6029             #
6030             # rewrite existing segments
6031             #
6032             # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
6033 808 100 66     7992 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    100 100        
    100 66        
    50 33        
      66        
      33        
6034 112 100       430 $verbose and print $out "JPEG $markerName:\n";
6035 112 50       443 Write($outfile, $hdr, $segData) or $err = 1;
6036 112         297 next;
6037             } elsif ($marker == 0xda) { # SOS
6038 112         315 pop @$path;
6039 112 100       421 $verbose and print $out "JPEG SOS\n";
6040             # write SOS segment
6041 112         462 $s = pack('n', length($segData) + 2);
6042 112 50       436 Write($outfile, $hdr, $s, $segData) or $err = 1;
6043 112         283 my ($buff, $endPos, $trailInfo);
6044 112         440 my $delPreview = $$self{DEL_PREVIEW};
6045 112 100       999 $trailInfo = $self->IdentifyTrailer($raf) unless $$delGroup{Trailer};
6046 112         800 my $nvTrail = $self->GetNewValueHash($Image::ExifTool::Extra{Trailer});
6047 112 50 33     1820 unless ($oldOutfile or $delPreview or $trailInfo or $$delGroup{Trailer} or
      66        
      100        
      66        
      66        
6048             $nvTrail or $$self{HiddenData})
6049             {
6050             # blindly copy the rest of the file
6051 97         940 while ($raf->Read($buff, 65536)) {
6052 97 50       355 Write($outfile, $buff) or $err = 1, last;
6053             }
6054 97         252 $rtnVal = 1; # success unless we have a file write error
6055 97         294 last; # all done
6056             }
6057             # write the rest of the image (as quickly as possible) up to the EOI
6058 15         38 my $endedWithFF;
6059 15         32 for (;;) {
6060 15 50       59 my $n = $raf->Read($buff, 65536) or last Marker;
6061 15 50 33     217 if (($endedWithFF and $buff =~ m/^\xd9/sg) or
      33        
6062             $buff =~ m/\xff\xd9/sg)
6063             {
6064 15         45 $rtnVal = 1; # the JPEG is OK
6065             # write up to the EOI
6066 15         42 my $pos = pos($buff);
6067 15 50       79 Write($outfile, substr($buff, 0, $pos)) or $err = 1;
6068 15         97 $buff = substr($buff, $pos);
6069 15         42 last;
6070             }
6071 0 0       0 unless ($n == 65536) {
6072 0         0 $self->Error('JPEG EOI marker not found');
6073 0         0 last Marker;
6074             }
6075 0 0       0 Write($outfile, $buff) or $err = 1;
6076 0 0       0 $endedWithFF = substr($buff, 65535, 1) eq "\xff" ? 1 : 0;
6077             }
6078             # remember position of last data copied
6079 15         66 $endPos = $$self{TrailerStart} = $raf->Tell() - length($buff);
6080             # write new trailer if specified
6081 15 50       73 if ($nvTrail) {
6082             # access new value directly to avoid copying a potentially very large data block
6083 0 0 0     0 if ($$nvTrail{Value} and $$nvTrail{Value}[0]) { # (note: "0" will also delete the trailer)
    0 0        
6084 0         0 $self->VPrint(0, ' Writing new trailer');
6085 0 0       0 Write($outfile, $$nvTrail{Value}[0]) or $err = 1;
6086 0         0 ++$$self{CHANGED};
6087             } elsif ($raf->Seek(0, 2) and $raf->Tell() != $endPos) {
6088 0         0 $self->VPrint(0, ' Deleting trailer (', $raf->Tell() - $endPos, ' bytes)');
6089 0         0 ++$$self{CHANGED}; # changed if there was previously a trailer
6090             }
6091 0         0 last; # all done
6092             }
6093             # rewrite existing trailers into buffer
6094 15 100       60 if ($trailInfo) {
6095 11         41 my $tbuf = '';
6096 11         56 $raf->Seek(-length($buff), 1); # seek back to just after EOI
6097 11         43 $$trailInfo{OutFile} = \$tbuf; # rewrite the trailer
6098 11         46 $$trailInfo{ScanForTrailer} = 1;# scan if necessary
6099 11 50       70 $self->ProcessTrailers($trailInfo) or undef $trailInfo;
6100             }
6101 15 50       65 if ($oldOutfile) {
6102 0         0 my $previewInfo;
6103             # copy HiddenData if necessary
6104 0 0       0 if ($$self{HiddenData}) {
6105 0         0 my $pad;
6106 0         0 my $hd = $$self{HiddenData};
6107 0         0 my $hdOff = $$hd{Offset} + $$hd{Base};
6108 0         0 require Image::ExifTool::Sony;
6109             # read HiddenData, updating $hdOff with actual offset if necessary
6110 0         0 my $dataPt = Image::ExifTool::Sony::ReadHiddenData($self, $hdOff, $$hd{Size});
6111 0 0       0 if ($dataPt) {
6112             # preserve padding to avoid invalidating MPF pointers (yuk!)
6113 0         0 my $padLen = $hdOff - $endPos;
6114 0 0 0     0 unless ($padLen >= 0 and $raf->Seek($endPos,0) and $raf->Read($pad,$padLen)==$padLen) {
      0        
6115 0         0 $self->Error('Error reading HiddenData padding',1);
6116 0         0 $pad = '';
6117             }
6118 0         0 $endPos += length($pad) + length($$dataPt); # update end position
6119             } else {
6120 0         0 $$dataPt = $pad = '';
6121             }
6122 0         0 my $fixup = $$self{HiddenData}{Fixup};
6123             # set MakerNote pointer and size (subtract 10 for segment and EXIF headers)
6124 0         0 $fixup->SetMarkerPointers($outfile, 'HiddenData', length($$outfile) + length($pad) - 10);
6125 0         0 $writeBuffer .= $pad . $$dataPt; # keep padding for now
6126             }
6127 0 0       0 if ($$self{LeicaTrailer}) {
6128 0         0 my $trailLen;
6129 0 0       0 if ($trailInfo) {
6130 0         0 $trailLen = $$trailInfo{DataPos} - $endPos;
6131             } else {
6132 0 0       0 $raf->Seek(0, 2) or $err = 1;
6133 0         0 $trailLen = $raf->Tell() - $endPos;
6134             }
6135 0         0 my $fixup = $$self{LeicaTrailer}{Fixup};
6136 0         0 $$self{LeicaTrailer}{TrailPos} = $endPos;
6137 0         0 $$self{LeicaTrailer}{TrailLen} = $trailLen;
6138             # get _absolute_ position of new Leica trailer
6139 0         0 my $absPos = Tell($oldOutfile) + length($$outfile);
6140 0         0 require Image::ExifTool::Panasonic;
6141 0         0 my $dat = Image::ExifTool::Panasonic::ProcessLeicaTrailer($self, $absPos);
6142             # allow some junk before Leica trailer (just in case)
6143 0         0 my $junk = $$self{LeicaTrailerPos} - $endPos;
6144             # set MakerNote pointer and size (subtract 10 for segment and EXIF headers)
6145 0         0 $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', length($$outfile) - 10 + $junk);
6146             # use this fixup to set the size too (sneaky)
6147 0 0       0 my $trailSize = defined($dat) ? length($dat) - $junk : $$self{LeicaTrailer}{Size};
6148 0         0 $$fixup{Start} -= 4; $$fixup{Shift} += 4;
  0         0  
6149 0 0       0 $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', $trailSize) if defined $trailSize;
6150 0         0 $$fixup{Start} += 4; $$fixup{Shift} -= 4;
  0         0  
6151 0 0       0 if (defined $dat) {
6152 0 0       0 Write($outfile, $dat) or $err = 1; # write new Leica trailer
6153 0         0 $delPreview = 1; # delete existing Leica trailer
6154             }
6155             }
6156             # handle preview image last
6157 0 0       0 if ($$self{PREVIEW_INFO}) {
6158             # locate preview image and fix up preview offsets
6159 0 0       0 my $scanLen = $$self{Make} =~ /^SONY/i ? 65536 : 1024;
6160 0 0       0 if (length($buff) < $scanLen) { # make sure we have enough trailer to scan
6161 0         0 my $buf2;
6162 0 0       0 $buff .= $buf2 if $raf->Read($buf2, $scanLen - length($buff));
6163             }
6164             # get new preview image position, relative to EXIF base
6165 0         0 my $newPos = length($$outfile) - 10; # (subtract 10 for segment and EXIF headers)
6166 0         0 my $junkLen;
6167             # adjust position if image isn't at the start (eg. Olympus E-1/E-300)
6168 0 0       0 if ($buff =~ /(\xff\xd8\xff.|.\xd8\xff\xdb)(..)/sg) {
6169 0         0 my ($jpegHdr, $segLen) = ($1, $2);
6170 0         0 $junkLen = pos($buff) - 6;
6171             # Sony previewimage trailer has a 32 byte header
6172 0 0 0     0 if ($$self{Make} =~ /^SONY/i and $junkLen > 32) {
6173             # with some newer Sony models, the makernotes preview pointer
6174             # points to JPEG at end of EXIF inside MPImage preview (what a pain!)
6175 0 0       0 if ($jpegHdr eq "\xff\xd8\xff\xe1") { # is the first segment EXIF?
6176 0         0 $segLen = unpack('n', $segLen); # the EXIF segment length
6177             # Sony PreviewImage starts with last 2 bytes of EXIF segment
6178             # (and first byte is usually "\0", not "\xff", so don't check this)
6179 0 0 0     0 if (length($buff) > $junkLen + $segLen + 6 and
6180             substr($buff, $junkLen + $segLen + 3, 3) eq "\xd8\xff\xdb")
6181             {
6182 0         0 $junkLen += $segLen + 2;
6183             # (note: this will not copy the trailer after PreviewImage,
6184             # which is a 14kB block full of zeros for the A77)
6185             }
6186             }
6187 0         0 $junkLen -= 32;
6188             }
6189 0         0 $newPos += $junkLen;
6190             }
6191             # fix up the preview offsets to point to the start of the new image
6192 0         0 $previewInfo = $$self{PREVIEW_INFO};
6193 0         0 delete $$self{PREVIEW_INFO};
6194 0         0 my $fixup = $$previewInfo{Fixup};
6195 0   0     0 $newPos += ($$previewInfo{BaseShift} || 0);
6196             # adjust to absolute file offset if necessary (Samsung STMN)
6197 0 0       0 $newPos += Tell($oldOutfile) + 10 if $$previewInfo{Absolute};
6198 0 0       0 if ($$previewInfo{Relative}) {
    0          
6199             # adjust for our base by looking at how far the pointer got shifted
6200 0   0     0 $newPos -= ($fixup->GetMarkerPointers($outfile, 'PreviewImage') || 0);
6201             } elsif ($$previewInfo{ChangeBase}) {
6202             # Leica S2 uses relative offsets for the preview only (leica sucks)
6203 0         0 my $makerOffset = $fixup->GetMarkerPointers($outfile, 'LeicaTrailer');
6204 0 0       0 $newPos -= $makerOffset if $makerOffset;
6205             }
6206 0         0 $fixup->SetMarkerPointers($outfile, 'PreviewImage', $newPos);
6207 0 0       0 if ($$previewInfo{Data} ne 'LOAD_PREVIEW') {
6208             # write any junk that existed before the preview image
6209 0 0       0 $$previewInfo{Junk} = substr($buff,0,$junkLen) if $junkLen;
6210             }
6211             }
6212             # clean up and write the buffered data
6213 0         0 $outfile = $oldOutfile;
6214 0         0 undef $oldOutfile;
6215 0 0       0 Write($outfile, $writeBuffer) or $err = 1;
6216 0         0 undef $writeBuffer;
6217             # write preview image
6218 0 0 0     0 if ($previewInfo and $$previewInfo{Data} ne 'LOAD_PREVIEW') {
6219             # write any junk that existed before the preview image
6220 0 0 0     0 Write($outfile, $$previewInfo{Junk}) or $err = 1 if defined $$previewInfo{Junk};
6221             # write the saved preview image
6222 0 0       0 Write($outfile, $$previewInfo{Data}) or $err = 1;
6223 0         0 delete $$previewInfo{Data};
6224             # (don't increment CHANGED because we could be rewriting existing preview)
6225 0         0 $delPreview = 1; # remove old preview
6226             }
6227             }
6228             # copy over preview image (or other data) if necessary
6229 15 50       53 unless ($delPreview) {
6230 15         34 my $extra;
6231 15 100       48 if ($trailInfo) {
6232             # copy everything up to start of first processed trailer
6233 11 50       64 $extra = defined $$trailInfo{DataPos} ? ($$trailInfo{DataPos} - $endPos) : 0;
6234             } else {
6235             # copy everything up to end of file
6236 4 50       17 $raf->Seek(0, 2) or $err = 1;
6237 4         15 $extra = $raf->Tell() - $endPos;
6238             }
6239 15 100       58 if ($extra > 0) {
6240 3 100       13 if ($$delGroup{Trailer}) {
6241 2 50       23 $verbose and print $out " Deleting unknown trailer ($extra bytes)\n";
6242 2         7 ++$$self{CHANGED};
6243             } else {
6244             # copy over unknown trailer
6245 1 50       3 $verbose and print $out " Preserving unknown trailer ($extra bytes)\n";
6246 1 50       3 $raf->Seek($endPos, 0) or $err = 1;
6247 1 50       8 CopyBlock($raf, $outfile, $extra) or $err = 1;
6248             }
6249             }
6250             }
6251             # write trailer if necessary
6252 15 100       50 if ($trailInfo) {
6253 11 50       67 $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1;
6254 11         100 undef $trailInfo;
6255             }
6256 15         69 last; # all done parsing file
6257              
6258             } elsif ($marker==0xd9 and $isEXV) {
6259             # write EXV EOI (any trailer will be lost)
6260 2 50       11 Write($outfile, "\xff\xd9") or $err = 1;
6261 2         5 $rtnVal = 1;
6262 2         5 last;
6263              
6264             } elsif ($marker==0x00 or $marker==0x01 or ($marker>=0xd0 and $marker<=0xd7)) {
6265 0 0 0     0 $verbose and $marker and print $out "JPEG $markerName:\n";
6266             # handle stand-alone markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
6267 0 0       0 Write($outfile, $hdr) or $err = 1;
6268 0         0 next;
6269             }
6270             #
6271             # NOTE: A 'next' statement after this point will cause $$segDataPt
6272             # not to be written if there is an output file, so in this case
6273             # the $$self{CHANGED} flags must be updated
6274             #
6275 582         1238 my $segDataPt = \$segData;
6276 582         1270 $length = length($segData);
6277 582 100       1417 print $out "JPEG $markerName ($length bytes)\n" if $verbose;
6278             # group delete of APP segments
6279 582 100       1680 if ($$delGroup{$dirName}) {
6280 55 50       114 $verbose and print $out " Deleting $dirName segment\n";
6281 55 100       155 $self->Warn('ICC_Profile deleted. Image colors may be affected') if $dirName eq 'ICC_Profile';
6282 55         105 ++$$self{CHANGED};
6283 55         115 next Marker;
6284             }
6285 527         1111 my ($segType, $del);
6286             # rewrite this segment only if we are changing a tag which is contained in its
6287             # directory (or deleting '*', in which case we need to identify the segment type)
6288 527   100     2859 while (exists $$editDirs{$markerName} or $$delGroup{'*'}) {
6289 131 100 33     966 if ($marker == 0xe0) { # APP0 (JFIF, CIFF)
    100          
    50          
    100          
    50          
    50          
    50          
    50          
    100          
    100          
    100          
    100          
6290 31 100       291 if ($$segDataPt =~ /^JFIF\0/) {
    100          
    100          
6291 11         25 $segType = 'JFIF';
6292 11 50       77 $$delGroup{JFIF} and $del = 1, last;
6293 11 50       53 last unless $$editDirs{JFIF};
6294 11         61 SetByteOrder('MM');
6295 11         53 my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
6296 11         106 my %dirInfo = (
6297             DataPt => $segDataPt,
6298             DataPos => $segPos,
6299             DataLen => $length,
6300             DirStart => 5, # directory starts after identifier
6301             DirLen => $length-5,
6302             Parent => $markerName,
6303             );
6304 11         75 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6305 11 50 33     103 if (defined $newData and length $newData) {
6306 11         84 $$segDataPt = "JFIF\0" . $newData;
6307             }
6308             } elsif ($$segDataPt =~ /^JFXX\0\x10/) {
6309 8         22 $segType = 'JFXX';
6310 8 100       38 $$delGroup{JFIF} and $del = 1;
6311             } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) {
6312 6         22 $segType = 'CIFF';
6313 6 50       30 $$delGroup{CIFF} and $del = 1, last;
6314 6 100       27 last unless $$editDirs{CIFF};
6315 4         13 my $newData = '';
6316 4         31 my %dirInfo = (
6317             RAF => File::RandomAccess->new($segDataPt),
6318             OutFile => \$newData,
6319             );
6320 4         52 require Image::ExifTool::CanonRaw;
6321 4 50       38 if (Image::ExifTool::CanonRaw::WriteCRW($self, \%dirInfo) > 0) {
6322 4 50       19 if (length $newData) {
6323 4         18 $$segDataPt = $newData;
6324             } else {
6325 0         0 undef $segDataPt;
6326 0         0 $del = 1; # delete this segment
6327             }
6328             }
6329             }
6330             } elsif ($marker == 0xe1) { # APP1 (EXIF, XMP)
6331             # check for EXIF data
6332 73 100 0     1050 if ($$segDataPt =~ /^(.{0,4})Exif\0./is) {
    50          
    0          
6333 52         135 my $hdrLen = length $exifAPP1hdr;
6334 52 50       401 if (length $1) {
    50          
6335 0         0 $hdrLen += length $1;
6336 0         0 $self->Error('Unknown garbage at start of EXIF segment',1);
6337             } elsif ($$segDataPt !~ /^Exif\0/) {
6338 0         0 $self->Error('Incorrect EXIF segment identifier',1);
6339             }
6340 52         137 $segType = 'EXIF';
6341 52 100       231 last unless $$editDirs{IFD0};
6342             # add this data to the combined data if it exists
6343 51 50       603 if (defined $combinedSegData) {
6344 0         0 $combinedSegData .= substr($$segDataPt,$hdrLen);
6345 0         0 $segDataPt = \$combinedSegData;
6346 0         0 $segPos = $firstSegPos;
6347 0         0 $length = length $combinedSegData; # update length
6348             }
6349             # peek ahead to see if the next segment is extended EXIF
6350 51 50       218 if ($dirOrder[0] eq 'ExtendedEXIF') {
6351             # initialize combined data if necessary
6352 0 0       0 unless (defined $combinedSegData) {
6353 0         0 $combinedSegData = $$segDataPt;
6354 0         0 $firstSegPos = $segPos;
6355 0         0 $self->Warn('File contains multi-segment EXIF',1);
6356             }
6357 0         0 next Marker; # get the next segment to combine
6358             }
6359 51 50       258 $doneDir{IFD0} and $self->Warn('Multiple APP1 EXIF records');
6360 51         136 $doneDir{IFD0} = 1;
6361             # check del groups now so we can change byte order in one step
6362 51 100 66     369 if ($$delGroup{IFD0} or $$delGroup{EXIF}) {
6363 1         4 delete $doneDir{IFD0}; # delete so we will create a new one
6364 1         2 $del = 1;
6365 1         3 last;
6366             }
6367             # rewrite EXIF as if this were a TIFF file in memory
6368 50         544 my %dirInfo = (
6369             DataPt => $segDataPt,
6370             DataPos => -$hdrLen, # (remember: relative to Base!)
6371             DirStart => $hdrLen,
6372             Base => $segPos + $hdrLen,
6373             Parent => $markerName,
6374             DirName => 'IFD0',
6375             );
6376             # write new EXIF data to memory
6377 50         294 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
6378 50         379 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
6379 50 50       227 if (defined $buff) {
6380 50         168 undef $$segDataPt; # free the old buffer
6381 50         152 $segDataPt = \$buff;
6382             } else {
6383 0 0       0 last Marker unless $self->Options('IgnoreMinorErrors');
6384             }
6385             # delete segment if IFD contains no entries
6386 50 100       295 length $$segDataPt or $del = 1, last;
6387 46 50       237 if (length($$segDataPt) + length($exifAPP1hdr) > $maxSegmentLen) {
6388 0 0       0 if ($self->Options('NoMultiExif')) {
6389 0         0 $self->Error('EXIF is too large for JPEG segment');
6390             } else {
6391 0         0 $self->Warn('Writing multi-segment EXIF',1);
6392             }
6393             }
6394             # switch to buffered output if required
6395 46 50 33     601 if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer} or $$self{HiddenData}) and
      33        
6396             not $oldOutfile)
6397             {
6398 0         0 $writeBuffer = '';
6399 0         0 $oldOutfile = $outfile;
6400 0         0 $outfile = \$writeBuffer;
6401             # must account for segment, EXIF and TIFF headers
6402 0         0 foreach (qw(PREVIEW_INFO LeicaTrailer HiddenData)) {
6403 0 0       0 $$self{$_}{Fixup}{Start} += 18 if $$self{$_};
6404             }
6405             }
6406             # write as multi-segment
6407 46         317 my $n = WriteMultiSegment($outfile, $marker, $exifAPP1hdr, $segDataPt, 'EXIF');
6408 46 50 33     344 if (not $n) {
    50          
6409 0         0 $err = 1;
6410             } elsif ($n > 1 and $oldOutfile) {
6411             # (punt on this because updating the pointers would be a real pain)
6412 0         0 $self->Error("Can't write multi-segment EXIF with external pointers");
6413             }
6414 46         118 undef $combinedSegData;
6415 46         117 undef $$segDataPt;
6416 46         457 next Marker;
6417             # check for XMP data
6418             } elsif ($$segDataPt =~ /^($xmpAPP1hdr|$xmpExtAPP1hdr)/) {
6419 21         60 $segType = 'XMP';
6420 21 50       82 $$delGroup{XMP} and $del = 1, last;
6421 21   100     119 $doneDir{XMP} = ($doneDir{XMP} || 0) + 1;
6422 21 100       81 last unless $$editDirs{XMP};
6423 14 100       51 if ($doneDir{XMP} + $dirCount{XMP} > 1) {
6424             # must assemble all XMP segments before writing
6425 3         9 my ($guid, $extXMP);
6426 3 100       47 if ($$segDataPt =~ /^$xmpExtAPP1hdr/) {
6427             # save extended XMP data
6428 2 50       10 if (length $$segDataPt < 75) {
6429 0         0 $extendedXMP{Error} = 'Truncated data';
6430             } else {
6431 2         11 my ($size, $off) = unpack('x67N2', $$segDataPt);
6432 2         9 $guid = substr($$segDataPt, 35, 32);
6433 2 50       9 if ($guid =~ /[^A-Za-z0-9]/) { # (technically, should be uppercase)
6434 0         0 $extendedXMP{Error} = 'Invalid GUID';
6435             } else {
6436             # remember extended data for each GUID
6437 2         7 $extXMP = $extendedXMP{$guid};
6438 2 100       5 if ($extXMP) {
6439 1 50       9 $size == $$extXMP{Size} or $extendedXMP{Error} = 'Inconsistent size';
6440             } else {
6441 1         5 $extXMP = $extendedXMP{$guid} = { };
6442             }
6443 2         7 $$extXMP{Size} = $size;
6444 2         13 $$extXMP{$off} = substr($$segDataPt, 75);
6445             }
6446             }
6447             } else {
6448             # save all main XMP segments (should normally be only one)
6449 1 50       10 $extendedXMP{Main} = [] unless $extendedXMP{Main};
6450 1         3 push @{$extendedXMP{Main}}, substr($$segDataPt, length $xmpAPP1hdr);
  1         6  
6451             }
6452             # continue processing only if we have read all the segments
6453 3 100       21 next Marker if $dirCount{XMP};
6454             # reconstruct an XMP super-segment
6455 1         3 $$segDataPt = $xmpAPP1hdr;
6456 1         4 my $goodGuid = '';
6457 1         4 foreach (@{$extendedXMP{Main}}) {
  1         4  
6458             # get the HasExtendedXMP GUID if it exists
6459 1 50       9 if (/:HasExtendedXMP\s*(=\s*['"]|>)(\w{32})/) {
6460             # warn of subsequent XMP blocks specifying a different
6461             # HasExtendedXMP (have never seen this)
6462 1 50 33     5 if ($goodGuid and $goodGuid ne $2) {
6463 0         0 $self->Warn('Multiple XMP segments specifying different extended XMP GUID');
6464             }
6465 1         4 $goodGuid = $2; # GUID for the standard extended XMP
6466             }
6467 1         4 $$segDataPt .= $_;
6468             }
6469             # GUID of the extended XMP that we want to read
6470 1   50     6 my $readGuid = $$self{OPTIONS}{ExtendedXMP} || 0;
6471 1 50       8 $readGuid = $goodGuid if $readGuid eq '1';
6472 1         9 foreach $guid (sort keys %extendedXMP) {
6473 2 100       9 next unless length $guid == 32; # ignore other (internal) keys
6474 1 50 33     7 if ($guid ne $readGuid and $readGuid ne '2') {
6475 0 0       0 my $non = $guid eq $goodGuid ? '' : 'non-';
6476 0         0 $self->Warn("Ignored ${non}standard extended XMP (GUID $guid)");
6477 0         0 next;
6478             }
6479 1 50       40 if ($guid ne $goodGuid) {
6480 0         0 $self->Warn("Reading non-standard extended XMP (GUID $guid)");
6481             }
6482 1         5 $extXMP = $extendedXMP{$guid};
6483 1 50       7 next unless ref $extXMP eq 'HASH'; # (just to be safe)
6484 1         4 my $size = $$extXMP{Size};
6485 1         4 my (@offsets, $off);
6486 1         4 for ($off=0; $off<$size; ) {
6487 2 50       9 last unless defined $$extXMP{$off};
6488 2         6 push @offsets, $off;
6489 2         8 $off += length $$extXMP{$off};
6490             }
6491 1 50       7 if ($off == $size) {
6492             # add all XMP to super-segment
6493 1         13 $$segDataPt .= $$extXMP{$_} foreach @offsets;
6494             } else {
6495 0         0 $self->Error("Incomplete extended XMP (GUID $guid)", 1);
6496             }
6497             }
6498 1 50       6 $self->Error("$extendedXMP{Error} in extended XMP", 1) if $extendedXMP{Error};
6499             }
6500 12         30 my $start = length $xmpAPP1hdr;
6501 12         72 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
6502 12         85 my %dirInfo = (
6503             DataPt => $segDataPt,
6504             DirStart => $start,
6505             Parent => $markerName,
6506             # limit XMP size and create extended XMP if necessary
6507             MaxDataLen => $maxXMPLen - length($xmpAPP1hdr),
6508             );
6509 12         74 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6510 12 100       41 if (defined $newData) {
6511 9         29 undef %extendedXMP;
6512 9 100       34 if (length $newData) {
6513             # write multi-segment XMP (XMP plus extended XMP if necessary)
6514             WriteMultiXMP($self, $outfile, \$newData, $dirInfo{ExtendedXMP},
6515 7 50       41 $dirInfo{ExtendedGUID}) or $err = 1;
6516 7         20 undef $$segDataPt; # free the old buffer
6517 7         56 next Marker;
6518             } else {
6519 2         6 $$segDataPt = ''; # delete the XMP
6520             }
6521             } else {
6522 3 50       11 $verbose and print $out " [XMP rewritten with no changes]\n";
6523 3 50       14 if ($doneDir{XMP} > 1) {
6524             # re-write original multi-segment XMP
6525 0         0 my ($dat, $guid, $extXMP, $off);
6526 0         0 foreach $dat (@{$extendedXMP{Main}}) { # main XMP
  0         0  
6527 0 0       0 next unless length $dat;
6528 0         0 $s = pack('n', length($xmpAPP1hdr) + length($dat) + 2);
6529 0 0       0 Write($outfile, $hdr, $s, $xmpAPP1hdr, $dat) or $err = 1;
6530             }
6531 0         0 foreach $guid (sort keys %extendedXMP) { # extended XMP
6532 0 0       0 next unless length $guid == 32;
6533 0         0 $extXMP = $extendedXMP{$guid};
6534 0 0       0 next unless ref $extXMP eq 'HASH';
6535 0 0       0 my $size = $$extXMP{Size} or next;
6536 0         0 for ($off=0; defined $$extXMP{$off}; $off += length $$extXMP{$off}) {
6537 0         0 $s = pack('n', length($xmpExtAPP1hdr) + length($$extXMP{$off}) + 42);
6538             Write($outfile, $hdr, $s, $xmpExtAPP1hdr, $guid,
6539 0 0       0 pack('N2', $size, $off), $$extXMP{$off}) or $err = 1;
6540             }
6541             }
6542 0         0 undef $$segDataPt; # free the old buffer
6543 0         0 undef %extendedXMP;
6544 0         0 next Marker;
6545             }
6546             # continue on to re-write original single-segment XMP
6547             }
6548 5 100       27 $del = 1 unless length $$segDataPt;
6549             } elsif ($$segDataPt =~ /^http/ or $$segDataPt =~ /
6550 0         0 $self->Warn('Ignored APP1 XMP segment with non-standard header', 1);
6551             }
6552             } elsif ($marker == 0xe2) { # APP2 (ICC Profile, FPXR, MPF)
6553 0 0 0     0 if ($$segDataPt =~ /^ICC_PROFILE\0/ and $length >= 14) {
    0          
    0          
6554 0         0 $segType = 'ICC_Profile';
6555 0 0       0 $$delGroup{ICC_Profile} and $del = 1, last;
6556             # must concatenate blocks of profile
6557 0         0 my $chunkNum = Get8u($segDataPt, 12);
6558 0         0 my $chunksTot = Get8u($segDataPt, 13);
6559 0 0       0 if (defined $iccChunksTotal) {
6560             # abort parsing ICC_Profile if the total chunk count is inconsistent
6561 0 0 0     0 if ($chunksTot != $iccChunksTotal and defined $iccChunkCount) {
6562             # an error because the accumulated profile data will be lost
6563 0         0 $self->Error('Inconsistent ICC_Profile chunk count', 1);
6564 0         0 undef $iccChunkCount; # abort ICC_Profile parsing
6565 0         0 undef $chunkNum; # avoid 2nd warning below
6566 0         0 ++$$self{CHANGED}; # we are deleting the bad chunks before this one
6567             }
6568             } else {
6569 0         0 $iccChunkCount = 0;
6570 0         0 $iccChunksTotal = $chunksTot;
6571 0 0       0 $self->Warn('ICC_Profile chunk count is zero') if !$chunksTot;
6572             }
6573 0 0       0 if (defined $iccChunkCount) {
    0          
6574             # save this chunk
6575 0 0       0 if (defined $iccChunk[$chunkNum]) {
6576 0         0 $self->Warn("Duplicate ICC_Profile chunk number $chunkNum");
6577 0         0 $iccChunk[$chunkNum] .= substr($$segDataPt, 14);
6578             } else {
6579 0         0 $iccChunk[$chunkNum] = substr($$segDataPt, 14);
6580             }
6581             # continue accumulating chunks unless we have all of them
6582 0 0       0 next Marker unless ++$iccChunkCount >= $iccChunksTotal;
6583 0         0 undef $iccChunkCount; # prevent reprocessing
6584 0         0 $doneDir{ICC_Profile} = 1;
6585             # combine the ICC_Profile chunks
6586 0         0 my $icc_profile = '';
6587 0   0     0 defined $_ and $icc_profile .= $_ foreach @iccChunk;
6588 0         0 undef @iccChunk; # free memory
6589 0         0 $segDataPt = \$icc_profile;
6590 0         0 $length = length $icc_profile;
6591 0         0 my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
6592 0         0 my %dirInfo = (
6593             DataPt => $segDataPt,
6594             DataPos => $segPos + 14,
6595             DataLen => $length,
6596             DirStart => 0,
6597             DirLen => $length,
6598             Parent => $markerName,
6599             );
6600 0         0 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6601 0 0       0 if (defined $newData) {
6602 0         0 undef $$segDataPt; # free the old buffer
6603 0         0 $segDataPt = \$newData;
6604             }
6605 0 0       0 length $$segDataPt or $del = 1, last;
6606             # write as ICC multi-segment
6607 0 0       0 WriteMultiSegment($outfile, $marker, "ICC_PROFILE\0", $segDataPt, 'ICC') or $err = 1;
6608 0         0 undef $$segDataPt;
6609 0         0 next Marker;
6610             } elsif (defined $chunkNum) {
6611 0         0 $self->Warn('Invalid or extraneous ICC_Profile chunk(s)');
6612             # fall through to preserve this extra profile...
6613             }
6614             } elsif ($$segDataPt =~ /^FPXR\0/) {
6615 0         0 $segType = 'FPXR';
6616 0 0       0 $$delGroup{FlashPix} and $del = 1;
6617             } elsif ($$segDataPt =~ /^MPF\0/) {
6618 0         0 $segType = 'MPF';
6619 0 0       0 $$delGroup{MPF} and $del = 1;
6620             }
6621             } elsif ($marker == 0xe3) { # APP3 (Kodak Meta)
6622 1 50       10 if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) {
6623 1         3 $segType = 'Kodak Meta';
6624 1 50       6 $$delGroup{Meta} and $del = 1, last;
6625 1 50       6 $doneDir{Meta} and $self->Warn('Multiple APP3 Meta segments');
6626 1         4 $doneDir{Meta} = 1;
6627 1 50       5 last unless $$editDirs{Meta};
6628             # rewrite Meta IFD as if this were a TIFF file in memory
6629 1         11 my %dirInfo = (
6630             DataPt => $segDataPt,
6631             DataPos => -6, # (remember: relative to Base!)
6632             DirStart => 6,
6633             Base => $segPos + 6,
6634             Parent => $markerName,
6635             DirName => 'Meta',
6636             );
6637             # write new data to memory
6638 1         6 my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta');
6639 1         7 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
6640 1 50       6 if (defined $buff) {
6641             # update segment with new data
6642 1         6 $$segDataPt = substr($$segDataPt,0,6) . $buff;
6643             } else {
6644 0 0       0 last Marker unless $self->Options('IgnoreMinorErrors');
6645             }
6646             # delete segment if IFD contains no entries
6647 1 50       10 $del = 1 unless length($$segDataPt) > 6;
6648             }
6649             } elsif ($marker == 0xe5) { # APP5 (Ricoh RMETA)
6650 0 0       0 if ($$segDataPt =~ /^RMETA\0/) {
6651 0         0 $segType = 'Ricoh RMETA';
6652 0 0       0 $$delGroup{RMETA} and $del = 1;
6653             }
6654             } elsif ($marker == 0xe8 or $marker == 0xe9) { # APP8/9 (SEAL)
6655 0 0       0 if ($$segDataPt =~ /^SEAL\0/) {
6656 0         0 $segType = 'SEAL';
6657 0 0       0 $$delGroup{SEAL} and $del = 1;
6658             }
6659             } elsif ($marker == 0xea) { # APP10 (AROT)
6660 0 0       0 if ($$segDataPt =~ /^AROT\0\0/) {
6661 0         0 $segType = 'AROT';
6662 0 0       0 $$delGroup{AROT} and $del = 1;
6663             }
6664             } elsif ($marker == 0xeb) { # APP11 (JUMBF)
6665 0 0       0 if ($$segDataPt =~ /^JP/) {
6666 0         0 $segType = 'JUMBF';
6667 0 0       0 $$delGroup{JUMBF} and $del = 1;
6668             }
6669             } elsif ($marker == 0xec) { # APP12 (Ducky)
6670 1 50       7 if ($$segDataPt =~ /^Ducky/) {
6671 1         3 $segType = 'Ducky';
6672 1 50       4 $$delGroup{Ducky} and $del = 1, last;
6673 1 50       3 $doneDir{Ducky} and $self->Warn('Multiple APP12 Ducky segments');
6674 1         3 $doneDir{Ducky} = 1;
6675 1 50       4 last unless $$editDirs{Ducky};
6676 1         4 my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
6677 1         8 my %dirInfo = (
6678             DataPt => $segDataPt,
6679             DataPos => $segPos,
6680             DataLen => $length,
6681             DirStart => 5, # directory starts after identifier
6682             DirLen => $length-5,
6683             Parent => $markerName,
6684             );
6685 1         5 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6686 1 50       6 if (defined $newData) {
6687 1         2 undef $$segDataPt; # free the old buffer
6688             # add header to new segment unless empty
6689 1 50       3 $newData = 'Ducky' . $newData if length $newData;
6690 1         2 $segDataPt = \$newData;
6691             }
6692 1 50       8 $del = 1 unless length $$segDataPt;
6693             }
6694             } elsif ($marker == 0xed) { # APP13 (Photoshop)
6695 9 100       160 if ($$segDataPt =~ /^$psAPP13hdr/) {
6696 8         22 $segType = 'Photoshop';
6697             # add this data to the combined data if it exists
6698 8 50       30 if (defined $combinedSegData) {
6699 0         0 $combinedSegData .= substr($$segDataPt,length($psAPP13hdr));
6700 0         0 $segDataPt = \$combinedSegData;
6701 0         0 $length = length $combinedSegData; # update length
6702             }
6703             # peek ahead to see if the next segment is photoshop data too
6704 8 50       41 if ($dirOrder[0] eq 'Photoshop') {
6705             # initialize combined data if necessary
6706 0 0       0 $combinedSegData = $$segDataPt unless defined $combinedSegData;
6707 0         0 next Marker; # get the next segment to combine
6708             }
6709 8 50       34 if ($doneDir{Photoshop}) {
6710 0         0 $self->Warn('Multiple Photoshop records');
6711             # only rewrite the first Photoshop segment when deleting this group
6712             # (to remove multiples when deleting and adding back in one step)
6713 0 0       0 $$delGroup{Photoshop} and $del = 1, last;
6714             }
6715 8         17 $doneDir{Photoshop} = 1;
6716             # process APP13 Photoshop record
6717 8         35 my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
6718 8         68 my %dirInfo = (
6719             DataPt => $segDataPt,
6720             DataPos => $segPos,
6721             DataLen => $length,
6722             DirStart => 14, # directory starts after identifier
6723             DirLen => $length-14,
6724             Parent => $markerName,
6725             );
6726 8         45 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6727 8 50       36 if (defined $newData) {
6728 8         22 undef $$segDataPt; # free the old buffer
6729 8         15 $segDataPt = \$newData;
6730             }
6731 8 100       39 length $$segDataPt or $del = 1, last;
6732             # write as multi-segment
6733 6 50       50 WriteMultiSegment($outfile, $marker, $psAPP13hdr, $segDataPt) or $err = 1;
6734 6         25 undef $combinedSegData;
6735 6         14 undef $$segDataPt;
6736 6         53 next Marker;
6737             }
6738             } elsif ($marker == 0xee) { # APP14 (Adobe)
6739 4 50       21 if ($$segDataPt =~ /^Adobe/) {
6740 4         11 $segType = 'Adobe';
6741             # delete it and replace it later if editing
6742 4 50 33     29 if ($$delGroup{Adobe} or $$editDirs{Adobe}) {
6743 0         0 $del = 1;
6744 0         0 undef $doneDir{Adobe}; # so we can add it back again above
6745             }
6746             }
6747             } elsif ($marker == 0xfe) { # COM (JPEG comment)
6748 4         8 my $newComment;
6749 4 50       33 unless ($doneDir{COM}) {
6750 4         15 $doneDir{COM} = 1;
6751 4 100 100     35 unless ($$delGroup{File} and $$delGroup{File} != 2) {
6752 3         8 my $tagInfo = $Image::ExifTool::Extra{Comment};
6753 3         12 my $nvHash = $self->GetNewValueHash($tagInfo);
6754 3         6 my $val = $segData;
6755 3         10 $val =~ s/\0+$//; # allow for stupid software that adds NULL terminator
6756 3 50 33     14 if ($self->IsOverwriting($nvHash, $val) or $$delGroup{File}) {
6757 3         14 $newComment = $self->GetNewValue($nvHash);
6758             } else {
6759 0         0 delete $$editDirs{COM}; # we aren't editing COM after all
6760 0         0 last;
6761             }
6762             }
6763             }
6764 4         24 $self->VerboseValue('- Comment', $$segDataPt);
6765 4 100       14 if (defined $newComment) {
6766             # write out the comments
6767 2         9 $self->VerboseValue('+ Comment', $newComment);
6768 2 50       9 WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1;
6769             } else {
6770 2 50       9 $verbose and print $out " Deleting COM segment\n";
6771             }
6772 4         11 ++$$self{CHANGED}; # increment the changed flag
6773 4         9 undef $segDataPt; # don't write existing comment
6774             }
6775 53         168 last; # didn't want to loop anyway
6776             }
6777              
6778             # delete necessary segments (including unknown segments if deleting all)
6779 466 100 100     2159 if ($del or ($$delGroup{'*'} and not $segType and $marker>=0xe0 and $marker<=0xef)) {
      100        
      100        
      100        
6780 13 100       41 $segType = 'unknown' unless $segType;
6781 13 50       30 $verbose and print $out " Deleting $markerName $segType segment\n";
6782 13         35 ++$$self{CHANGED};
6783 13         64 next Marker;
6784             }
6785             # write out this segment if $segDataPt is still defined
6786 453 100 66     1915 if (defined $segDataPt and defined $$segDataPt) {
6787             # write the data for this record (the data could have been
6788             # modified, so recalculate the length word)
6789 449         771 my $size = length($$segDataPt);
6790 449 50       1065 if ($size > $maxSegmentLen) {
6791 0 0       0 $segType or $segType = 'Unknown';
6792 0         0 $self->Error("$segType $markerName segment too large! ($size bytes)");
6793 0         0 $err = 1;
6794             } else {
6795 449         1507 $s = pack('n', length($$segDataPt) + 2);
6796 449 50       1409 Write($outfile, $hdr, $s, $$segDataPt) or $err = 1;
6797             }
6798 449         1155 undef $$segDataPt; # free the buffer
6799 449         1030 undef $segDataPt;
6800             }
6801             }
6802             # make sure the ICC_Profile was complete
6803 114 50       414 $self->Error('Incomplete ICC_Profile record', 1) if defined $iccChunkCount;
6804 114 100       930 pop @$path if @$path > $pn;
6805             # if oldOutfile is still set, there was an error copying the JPEG
6806 114 50       353 $oldOutfile and return 0;
6807 114 50       370 if ($rtnVal) {
6808             # add any new trailers we are creating
6809 114         764 my $trailPt = $self->AddNewTrailers();
6810 114 100 33     407 Write($outfile, $$trailPt) or $err = 1 if $trailPt;
6811             }
6812             # set return value to -1 if we only had a write error
6813 114 50 33     651 $rtnVal = -1 if $rtnVal and $err;
6814 114 50 66     437 if ($creatingEXV and $rtnVal > 0 and not $$self{CHANGED}) {
      66        
6815 0         0 $self->Error('Nothing written');
6816 0         0 $rtnVal = -1;
6817             }
6818 114         1604 return $rtnVal;
6819             }
6820              
6821             #------------------------------------------------------------------------------
6822             # Validate an image for writing
6823             # Inputs: 0) ExifTool object reference, 1) raw value reference
6824             # Returns: error string or undef on success
6825             sub CheckImage($$)
6826             {
6827 138     138 0 415 my ($self, $valPtr) = @_;
6828 138 100 100     1225 if (length($$valPtr) and $$valPtr!~/^\xff\xd8/ and not
      100        
6829             $self->Options('IgnoreMinorErrors'))
6830             {
6831 25         244 return '[Minor] Not a valid image';
6832             }
6833 113         1228 return undef;
6834             }
6835              
6836             #------------------------------------------------------------------------------
6837             # check a value for validity
6838             # Inputs: 0) value reference, 1) format string, 2) optional count
6839             # Returns: error string, or undef on success
6840             # Notes: May modify value (if a count is specified for a string, it is null-padded
6841             # to the specified length, and floating point values are rounded to integer if required)
6842             sub CheckValue($$;$)
6843             {
6844 19995     19995 0 58370 my ($valPtr, $format, $count) = @_;
6845 19995         33523 my (@vals, $val, $n);
6846              
6847 19995 100 100     79304 if ($format eq 'string' or $format eq 'undef') {
6848 2646 100 66     12842 return undef unless $count and $count > 0;
6849 353         875 my $len = length($$valPtr);
6850 353 100       996 if ($format eq 'string') {
6851 217 100       638 $len >= $count and return 'String too long';
6852             } else {
6853 136 50       499 $len > $count and return 'Data too long';
6854             }
6855 343 100       909 if ($len < $count) {
6856 257         1003 $$valPtr .= "\0" x ($count - $len);
6857             }
6858 343         1362 return undef;
6859             }
6860 17349 100 66     45691 if ($count and $count != 1) {
6861 2132         5327 @vals = split(' ',$$valPtr);
6862 2132 100 100     5236 $count < 0 and ($count = @vals or return undef);
6863             } else {
6864 15217         24712 $count = 1;
6865 15217         35534 @vals = ( $$valPtr );
6866             }
6867 17311 100       40926 if (@vals != $count) {
6868 1059 100       2235 my $str = @vals > $count ? 'Too many' : 'Not enough';
6869 1059         3371 return "$str values specified ($count required)";
6870             }
6871 16252         42707 for ($n=0; $n<$count; ++$n) {
6872 19295         34183 $val = shift @vals;
6873 19295 100 100     61941 if ($format =~ /^int/) {
    100 100        
6874             # make sure the value is integer
6875 17886 100       57750 unless (IsInt($val)) {
6876 3332 100       8408 if (IsHex($val)) {
6877 7         23 $val = $$valPtr = hex($val);
6878             } else {
6879             # round single floating point values to the nearest integer
6880 3325 100 100     7145 return 'Not an integer' unless IsFloat($val) and $count == 1;
6881 1264 100       5658 $val = $$valPtr = int($val + ($val < 0 ? -0.5 : 0.5));
6882             }
6883             }
6884 15825 50       50926 my $rng = $intRange{$format} or return "Bad int format: $format";
6885 15825 100       41121 return "Value below $format minimum" if $val < $$rng[0];
6886             # (allow 0xfeedfeed code as value for 16-bit pointers)
6887 15512 100 66     54185 return "Value above $format maximum" if $val > $$rng[1] and $val != 0xfeedfeed;
6888             } elsif ($format =~ /^rational/ or $format eq 'float' or $format eq 'double') {
6889             # make sure the value is a valid floating point number
6890 1406 100       5462 unless (IsFloat($val)) {
6891             # allow 'inf', 'undef' and fractional rational values
6892 273 100       1021 if ($format =~ /^rational/) {
6893 235 100 66     1142 next if $val eq 'inf' or $val eq 'undef';
6894 234 100       1064 if ($val =~ m{^([-+]?\d+)/(\d+)$}) {
6895 70 50 66     554 next unless $1 < 0 and $format =~ /u$/;
6896 0         0 return 'Must be an unsigned rational';
6897             }
6898             }
6899 202         960 return 'Not a floating point number';
6900             }
6901 1133 50 66     8233 if ($format =~ /^rational\d+u$/ and $val < 0) {
6902 0         0 return 'Must be a positive number';
6903             }
6904             }
6905             }
6906 13670         42659 return undef; # success!
6907             }
6908              
6909             #------------------------------------------------------------------------------
6910             # check new value for binary data block
6911             # Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref
6912             # Returns: error string or undef (and may modify value) on success
6913             sub CheckBinaryData($$$)
6914             {
6915 12501     12501 0 30628 my ($self, $tagInfo, $valPtr) = @_;
6916 12501         25609 my $format = $$tagInfo{Format};
6917 12501 100       29757 unless ($format) {
6918 4693         8383 my $table = $$tagInfo{Table};
6919 4693 100 66     19603 if ($table and $$table{FORMAT}) {
6920 3324         8245 $format = $$table{FORMAT};
6921             } else {
6922             # use default 'int8u' unless specified
6923 1369         3667 $format = 'int8u';
6924             }
6925             }
6926 12501         19991 my $count;
6927 12501 100       38218 if ($format =~ /(.*)\[(.*)\]/) {
6928 1876         4480 $format = $1;
6929 1876         3207 $count = $2;
6930             # can't evaluate $count now because we don't know $size yet
6931 1876 100       3610 $count = -1 if $count =~ /\$size/; # (-1 = any count allowed)
6932             }
6933 12501         31891 return CheckValue($valPtr, $format, $count);
6934             }
6935              
6936             #------------------------------------------------------------------------------
6937             # Rename a file (with patch for Windows Unicode file names, and other problem)
6938             # Inputs: 0) ExifTool ref, 1) old name, 2) new name
6939             # Returns: true on success
6940             sub Rename($$$)
6941             {
6942 3     3 0 13 my ($self, $old, $new) = @_;
6943 3         9 my ($result, $try, $winUni);
6944              
6945 3 50       19 if ($self->EncodeFileName($old)) {
    50          
6946 0         0 $self->EncodeFileName($new, 1);
6947 0         0 $winUni = 1;
6948             } elsif ($self->EncodeFileName($new)) {
6949 0         0 $old = $_[1];
6950 0         0 $self->EncodeFileName($old, 1);
6951 0         0 $winUni = 1;
6952             }
6953 3         27 for (;;) {
6954 3 50       13 if ($winUni) {
6955 0         0 $result = eval { Win32API::File::MoveFileExW($old, $new,
  0         0  
6956             Win32API::File::MOVEFILE_REPLACE_EXISTING() |
6957             Win32API::File::MOVEFILE_COPY_ALLOWED()) };
6958             } else {
6959 3         6510 $result = rename($old, $new);
6960             }
6961 3 50 33     29 last if $result or $^O ne 'MSWin32';
6962             # keep trying for up to 0.5 seconds
6963             # (patch for Windows denial-of-service susceptibility)
6964 0   0     0 $try = ($try || 1) + 1;
6965 0 0       0 last if $try > 50;
6966 0         0 select(undef,undef,undef,0.01); # sleep for 0.01 sec
6967             }
6968 3         19 return $result;
6969             }
6970              
6971             #------------------------------------------------------------------------------
6972             # Delete a file (with patch for Windows Unicode file names)
6973             # Inputs: 0) ExifTool ref, 1-N) names of files to delete
6974             # Returns: number of files deleted
6975             sub Unlink($@)
6976             {
6977 0     0 0 0 my $self = shift;
6978 0         0 my $result = 0;
6979 0         0 while (@_) {
6980 0         0 my $file = shift;
6981 0 0       0 if ($self->EncodeFileName($file)) {
6982 0 0       0 ++$result if eval { Win32API::File::DeleteFileW($file) };
  0         0  
6983             } else {
6984 0 0       0 ++$result if unlink $file;
6985             }
6986             }
6987 0         0 return $result;
6988             }
6989              
6990             #------------------------------------------------------------------------------
6991             # Set file times (Unix seconds since the epoch)
6992             # Inputs: 0) ExifTool ref, 1) file name or ref, 2) access time, 3) modification time,
6993             # 4) inode change or creation time (or undef for any time to avoid setting)
6994             # 5) flag to suppress warning
6995             # Returns: 1 on success, 0 on error
6996             my $k32SetFileTime;
6997             sub SetFileTime($$;$$$$)
6998             {
6999 0     0 0 0 my ($self, $file, $atime, $mtime, $ctime, $noWarn) = @_;
7000 0         0 my $saveFile;
7001 0         0 local *FH;
7002              
7003             # open file by name if necessary
7004 0 0       0 unless (ref $file) {
7005             # (file will be automatically closed when *FH goes out of scope)
7006 0 0       0 unless ($self->Open(\*FH, $file, '+<')) {
7007 0         0 my $success;
7008 0 0 0     0 if (defined $atime or defined $mtime) {
7009 0         0 my ($a, $m, $c) = $self->GetFileTime($file);
7010 0 0       0 $atime = $a unless defined $atime;
7011 0 0       0 $mtime = $m unless defined $mtime;
7012 0 0 0     0 $success = eval { utime($atime, $mtime, $file) } if defined $atime and defined $mtime;
  0         0  
7013             }
7014 0 0       0 $self->Warn('Error updating file time') unless $success;
7015 0         0 return $success;
7016             }
7017 0         0 $saveFile = $file;
7018 0         0 $file = \*FH;
7019             }
7020             # on Windows, try to work around incorrect file times when daylight saving time is in effect
7021 0 0       0 if ($^O eq 'MSWin32') {
7022 0 0       0 if (not eval { require Win32::API }) {
  0 0       0  
7023 0         0 $self->Warn('Install Win32::API for proper handling of Windows file times');
7024 0         0 } elsif (not eval { require Win32API::File }) {
7025 0         0 $self->Warn('Install Win32API::File for proper handling of Windows file times');
7026             } else {
7027             # get Win32 handle, needed for SetFileTime
7028 0         0 my $win32Handle = eval { Win32API::File::GetOsFHandle($file) };
  0         0  
7029 0 0       0 unless ($win32Handle) {
7030 0         0 $self->Warn('Win32API::File GetOsFHandle returned invalid handle');
7031 0         0 return 0;
7032             }
7033             # convert Unix seconds to FILETIME structs
7034 0         0 my $time;
7035 0         0 foreach $time ($atime, $mtime, $ctime) {
7036             # set to NULL if not defined (i.e. do not change)
7037 0 0       0 defined $time or $time = 0, next;
7038             # convert to 100 ns intervals since 0:00 UTC Jan 1, 1601
7039             # (89 leap years between 1601 and 1970)
7040 0         0 my $wt = ($time + (((1970-1601)*365+89)*24*3600)) * 1e7;
7041 0         0 my $hi = int($wt / 4294967296);
7042 0         0 $time = pack 'LL', int($wt - $hi * 4294967296), $hi; # pack FILETIME struct
7043             }
7044 0 0       0 unless ($k32SetFileTime) {
7045 0 0       0 return 0 if defined $k32SetFileTime;
7046 0         0 $k32SetFileTime = Win32::API->new('KERNEL32', 'SetFileTime', 'NPPP', 'I');
7047 0 0       0 unless ($k32SetFileTime) {
7048 0         0 $self->Warn('Error loading Win32::API SetFileTime');
7049 0         0 $k32SetFileTime = 0;
7050 0         0 return 0;
7051             }
7052             }
7053 0 0       0 unless ($k32SetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) {
7054 0         0 $self->Warn('Win32::API SetFileTime returned ' . Win32::GetLastError());
7055 0         0 return 0;
7056             }
7057 0         0 return 1;
7058             }
7059             }
7060             # other OS (or Windows fallback)
7061 0 0 0     0 if (defined $atime and defined $mtime) {
7062 0         0 my $success;
7063 0         0 local $SIG{'__WARN__'} = \&SetWarning; # (this may not be necessary)
7064 0         0 for (;;) {
7065 0         0 undef $evalWarning;
7066             # (this may fail on the first try if futimes is not implemented)
7067 0         0 $success = eval { utime($atime, $mtime, $file) };
  0         0  
7068 0 0 0     0 last if $success or not defined $saveFile;
7069 0         0 close $file;
7070 0         0 $file = $saveFile;
7071 0         0 undef $saveFile;
7072             }
7073 0 0       0 unless ($noWarn) {
7074 0 0 0     0 if ($@ or $evalWarning) {
    0          
7075 0   0     0 $self->Warn(CleanWarning($@ || $evalWarning));
7076             } elsif (not $success) {
7077 0         0 $self->Warn('Error setting file time');
7078             }
7079             }
7080 0         0 return $success;
7081             }
7082 0         0 return 1; # (nothing to do)
7083             }
7084              
7085             #------------------------------------------------------------------------------
7086             # Add data to hash checksum
7087             # Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file),
7088             # 3) data name (or undef for no warnings or messages), 4) flag for no verbose message
7089             # Returns: number of bytes read and hashed
7090             sub ImageDataHash($$$;$$)
7091             {
7092 1     1 0 5 my ($self, $raf, $size, $type, $noMsg) = @_;
7093 1 50       8 my $hash = $$self{ImageDataHash} or return;
7094 0         0 my ($bytesRead, $n) = (0, 65536);
7095 0         0 my $buff;
7096 0         0 for (;;) {
7097 0 0       0 if (defined $size) {
7098 0 0       0 last unless $size;
7099 0 0       0 $n = $size > 65536 ? 65536 : $size;
7100 0         0 $size -= $n;
7101             }
7102 0 0       0 unless ($raf->Read($buff, $n)) {
7103 0 0 0     0 $self->Warn("Error reading $type data") if $type and defined $size;
7104 0         0 last;
7105             }
7106 0         0 $hash->add($buff);
7107 0         0 $bytesRead += length $buff;
7108             }
7109 0 0 0     0 if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) {
      0        
      0        
7110 0         0 $self->VPrint(0, "$$self{INDENT}(ImageDataHash: $bytesRead bytes of $type data)\n");
7111             }
7112 0         0 return $bytesRead;
7113             }
7114              
7115             #------------------------------------------------------------------------------
7116             # Copy data block from RAF to output file in max 64kB chunks
7117             # Inputs: 0) RAF ref, 1) outfile ref, 2) block size
7118             # Returns: 1 on success, 0 on read error, undef on write error
7119             sub CopyBlock($$$)
7120             {
7121 74     74 0 261 my ($raf, $outfile, $size) = @_;
7122 74         170 my $buff;
7123 74         147 for (;;) {
7124 127 100       461 last unless $size > 0;
7125 53 50       197 my $n = $size > 65536 ? 65536 : $size;
7126 53 50       207 $raf->Read($buff, $n) == $n or return 0;
7127 53 50       265 Write($outfile, $buff) or return undef;
7128 53         156 $size -= $n;
7129             }
7130 74         260 return 1;
7131             }
7132              
7133             #------------------------------------------------------------------------------
7134             # Copy image data from one file to another
7135             # Inputs: 0) ExifTool object reference
7136             # 1) reference to list of image data [ position, size, pad bytes ]
7137             # 2) output file ref
7138             # Returns: true on success
7139             sub CopyImageData($$$)
7140             {
7141 13     13 0 51 my ($self, $imageDataBlocks, $outfile) = @_;
7142 13         44 my $raf = $$self{RAF};
7143 13         34 my ($dataBlock, $err);
7144 13         35 my $num = @$imageDataBlocks;
7145 13 50       127 $self->VPrint(0, " Copying $num image data blocks\n") if $num;
7146 13         41 foreach $dataBlock (@$imageDataBlocks) {
7147 24         69 my ($pos, $size, $pad) = @$dataBlock;
7148 24 50       105 $raf->Seek($pos, 0) or $err = 'read', last;
7149 24         101 my $result = CopyBlock($raf, $outfile, $size);
7150 24 0       86 $result or $err = defined $result ? 'read' : 'writ';
    50          
7151             # pad if necessary
7152 24 100 33     106 Write($outfile, "\0" x $pad) or $err = 'writ' if $pad;
7153 24 50       116 last if $err;
7154             }
7155 13 50       46 if ($err) {
7156 0         0 $self->Error("Error ${err}ing image data");
7157 0         0 return 0;
7158             }
7159 13         64 return 1;
7160             }
7161              
7162             #------------------------------------------------------------------------------
7163             # Write to binary data block
7164             # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
7165             # Returns: Binary data block or undefined on error
7166             sub WriteBinaryData($$$)
7167             {
7168 16479     16479 0 33534 my ($self, $dirInfo, $tagTablePtr) = @_;
7169 16479 100       62254 $self or return 1; # allow dummy access to autoload this package
7170              
7171             # get default format ('int8u' unless specified)
7172 488 50       1827 my $dataPt = $$dirInfo{DataPt} or return undef;
7173 488         998 my $dataLen = length $$dataPt;
7174 488   100     2012 my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
7175 488         1746 my $increment = FormatSize($defaultFormat);
7176 488 50       1279 unless ($increment) {
7177 0         0 warn "Unknown format $defaultFormat\n";
7178 0         0 return undef;
7179             }
7180             # extract data members first if necessary
7181 488         888 my @varOffsets;
7182 488 100       1677 if ($$tagTablePtr{DATAMEMBER}) {
7183 220         831 $$dirInfo{DataMember} = $$tagTablePtr{DATAMEMBER};
7184 220         678 $$dirInfo{VarFormatData} = \@varOffsets;
7185 220         1368 $self->ProcessBinaryData($dirInfo, $tagTablePtr);
7186 220         761 delete $$dirInfo{DataMember};
7187 220         721 delete $$dirInfo{VarFormatData};
7188             }
7189 488   100     2152 my $dirStart = $$dirInfo{DirStart} || 0;
7190 488         1127 my $dirLen = $$dirInfo{DirLen};
7191 488 100 66     2770 $dirLen = $dataLen - $dirStart if not defined $dirLen or $dirLen > $dataLen - $dirStart;
7192 488 50       2362 my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
7193 488         1327 my $dirName = $$dirInfo{DirName};
7194 488         888 my $varSize = 0;
7195 488         1208 my @varInfo = @varOffsets;
7196 488         844 my $tagInfo;
7197 488         1103 $dataPt = \$newData;
7198 488         2574 foreach $tagInfo (sort { $$a{TagID} <=> $$b{TagID} } $self->GetNewTagInfoList($tagTablePtr)) {
  647         1392  
7199 227         705 my $tagID = $$tagInfo{TagID};
7200             # evaluate conditional tags now if necessary
7201 227 100 100     1520 if (ref $$tagTablePtr{$tagID} eq 'ARRAY' or $$tagInfo{Condition}) {
7202 22         128 my $writeInfo = $self->GetTagInfo($tagTablePtr, $tagID);
7203 22 100 100     161 next unless $writeInfo and $writeInfo eq $tagInfo;
7204             }
7205             # add offsets for variable-sized tags if necessary
7206 218   100     893 while (@varInfo and $varInfo[0][0] < $tagID) {
7207 10         25 $varSize = $varInfo[0][1]; # get accumulated variable size
7208 10         40 shift @varInfo;
7209             }
7210 218         406 my $count = 1;
7211 218         531 my $format = $$tagInfo{Format};
7212 218         583 my $entry = int($tagID) * $increment + $varSize; # relative offset of this entry
7213 218 100       633 if ($format) {
7214 87 100       548 if ($format =~ /(.*)\[(.*)\]/) {
    100          
7215 36         142 $format = $1;
7216 36         97 $count = $2;
7217 36         76 my $size = $dirLen; # used in eval
7218             # evaluate count to allow count to be based on previous values
7219             #### eval Format size ($size, $self) - NOTE: %val not supported for writing
7220 36         2818 $count = eval $count;
7221 36 50       246 $@ and warn($@), next;
7222             } elsif ($format eq 'string') {
7223             # string with no specified count runs to end of block
7224 1 50       6 $count = ($dirLen > $entry) ? $dirLen - $entry : 0;
7225             }
7226             } else {
7227 131         278 $format = $defaultFormat;
7228             }
7229             # read/write using variable format if changed in Hook
7230 218 100 66     716 $format = $varInfo[0][2] if @varInfo and $varInfo[0][0] == $tagID;
7231 218         952 my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen-$entry);
7232 218 100       649 next unless defined $val;
7233 215         1437 my $nvHash = $self->GetNewValueHash($tagInfo, $$self{CUR_WRITE_GROUP});
7234 215 100       964 next unless $self->IsOverwriting($nvHash, $val) > 0;
7235 214         851 my $newVal = $self->GetNewValue($nvHash);
7236 214 100       656 next unless defined $newVal; # can't delete from a binary table
7237             # update DataMember with new value if necessary
7238 213 100       744 $$self{$$tagInfo{DataMember}} = $newVal if $$tagInfo{DataMember};
7239             # only write masked bits if specified
7240 213         486 my $mask = $$tagInfo{Mask};
7241 213 100       575 $newVal = (($newVal << $$tagInfo{BitShift}) & $mask) | ($val & ~$mask) if $mask;
7242             # set the size
7243 213 50 33     666 if ($$tagInfo{DataTag} and not $$tagInfo{IsOffset}) {
7244 0 0       0 warn 'Internal error' unless $newVal == 0xfeedfeed;
7245 0         0 my $data = $self->GetNewValue($$tagInfo{DataTag});
7246 0 0       0 $newVal = length($data) if defined $data;
7247 0   0     0 my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u';
7248 0 0 0     0 if ($format =~ /^int16/ and $newVal > 0xffff) {
7249 0         0 $self->Error("$$tagInfo{DataTag} is too large (64 KiB max. for this file)");
7250             }
7251             }
7252 213         719 my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $entry);
7253 213 50       531 if (defined $rtnVal) {
7254 213         1486 $self->VerboseValue("- $dirName:$$tagInfo{Name}", $val);
7255 213         979 $self->VerboseValue("+ $dirName:$$tagInfo{Name}", $newVal);
7256 213         848 ++$$self{CHANGED};
7257             } else {
7258 0         0 $self->Warn("Error packing $$tagInfo{Name} value");
7259             }
7260             }
7261             # add necessary fixups for any offsets
7262 488 50 66     2033 if ($$tagTablePtr{IS_OFFSET} and $$dirInfo{Fixup}) {
7263 1         2 $varSize = 0;
7264 1         4 @varInfo = @varOffsets;
7265 1         3 my $fixup = $$dirInfo{Fixup};
7266 1         2 my $tagID;
7267 1         2 foreach $tagID (@{$$tagTablePtr{IS_OFFSET}}) {
  1         5  
7268 1 50       6 $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID) or next;
7269 1   33     18 while (@varInfo and $varInfo[0][0] < $tagID) {
7270 0         0 $varSize = $varInfo[0][1];
7271 0         0 shift @varInfo;
7272             }
7273 1         4 my $entry = $tagID * $increment + $varSize; # (no offset to dirStart for new dir data)
7274 1 50       7 next unless $entry <= $dirLen - 4;
7275             # (Ricoh has 16-bit preview image offsets, so can't just assume int32u)
7276 0   0     0 my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u';
7277 0         0 my $offset = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry);
7278             # ignore if offset is zero (eg. Ricoh DNG uses this to indicate no preview)
7279 0 0       0 next unless $offset;
7280 0         0 $fixup->AddFixup($entry, $$tagInfo{DataTag}, $format);
7281 0 0 0     0 next unless $$tagInfo{DataTag} and defined $$tagInfo{OffsetPair};
7282             # NOTE: here we assume there are no var-sized tags between the
7283             # OffsetPair tags. If this ever becomes possible we must recalculate
7284             # $varSize for the OffsetPair tag here!
7285 0         0 $entry = $$tagInfo{OffsetPair} * $increment + $varSize;
7286 0         0 my $size = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry);
7287 0 0       0 next unless defined $size;
7288 0 0       0 if ($$tagInfo{DataTag} eq 'HiddenData') {
7289             $$self{HiddenData} = {
7290             Offset => $offset,
7291             Size => $size,
7292             Fixup => Image::ExifTool::Fixup->new,
7293             Base => $$dirInfo{Base},
7294 0         0 };
7295 0         0 next;
7296             }
7297             # handle the preview image now if this is a JPEG file
7298 0 0 0     0 next unless $$tagInfo{DataTag} eq 'PreviewImage' and $$self{FILE_TYPE} eq 'JPEG';
7299 0         0 my $previewInfo = $$self{PREVIEW_INFO};
7300             $previewInfo or $previewInfo = $$self{PREVIEW_INFO} = {
7301 0 0       0 Fixup => Image::ExifTool::Fixup->new,
7302             };
7303             # set flag indicating we are using short pointers
7304 0 0       0 $$previewInfo{IsShort} = 1 unless $format eq 'int32u';
7305 0 0 0     0 $$previewInfo{Absolute} = 1 if $$tagInfo{IsOffset} and $$tagInfo{IsOffset} eq '3';
7306             # get the value of the Composite::PreviewImage tag
7307 0         0 $$previewInfo{Data} = $self->GetNewValue(GetCompositeTagInfo('PreviewImage'));
7308 0 0       0 unless (defined $$previewInfo{Data}) {
7309 0 0 0     0 if ($offset >= 0 and $offset + $size <= $$dirInfo{DataLen}) {
7310 0         0 $$previewInfo{Data} = substr(${$$dirInfo{DataPt}},$offset,$size);
  0         0  
7311             } else {
7312 0         0 $$previewInfo{Data} = 'LOAD_PREVIEW'; # flag to load preview later
7313             }
7314             }
7315             }
7316             }
7317             # write any necessary SubDirectories
7318 488 100       1659 if ($$tagTablePtr{IS_SUBDIR}) {
7319 12         32 $varSize = 0;
7320 12         36 @varInfo = @varOffsets;
7321 12         28 my $tagID;
7322 12         28 foreach $tagID (@{$$tagTablePtr{IS_SUBDIR}}) {
  12         52  
7323 13         70 my $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID);
7324 13 100       91 next unless defined $tagInfo;
7325 9   33     54 while (@varInfo and $varInfo[0][0] < $tagID) {
7326 0         0 $varSize = $varInfo[0][1];
7327 0         0 shift @varInfo;
7328             }
7329 9         35 my $entry = int($tagID) * $increment + $varSize;
7330 9 50       72 last if $entry >= $dirLen;
7331             # get value for Condition if necessary
7332 9 50       35 unless ($tagInfo) {
7333 0         0 my $more = $dirLen - $entry;
7334 0 0       0 $more = 128 if $more > 128;
7335 0         0 my $v = substr($newData, $entry, $more);
7336 0         0 $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID, \$v);
7337 0 0       0 next unless $tagInfo;
7338             }
7339 9 50       47 my $subdir = $$tagInfo{SubDirectory} or next;
7340 9         28 my $start = $$subdir{Start};
7341 9         25 my $len;
7342 9 50       39 if (not $start) {
    0          
7343 9         21 $start = $entry;
7344 9         44 $len = $dirLen - $start;
7345             } elsif ($start =~ /\$/) {
7346 0         0 my $count = 1;
7347 0   0     0 my $format = $$tagInfo{Format} || $defaultFormat;
7348 0 0       0 $format =~ /(.*)\[(.*)\]/ and ($format, $count) = ($1, $2);
7349 0         0 my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen - $entry);
7350             # ignore directories with a zero offset (ie. missing Nikon ShotInfo entries)
7351 0 0       0 next unless $val;
7352 0         0 my $dirStart = 0;
7353             #### eval Start ($val, $dirStart)
7354 0         0 $start = eval($start);
7355 0 0 0     0 next if $start < $dirStart or $start > $dataLen;
7356 0         0 $len = $$subdir{DirLen};
7357 0 0 0     0 $len = $dataLen - $start unless $len and $len <= $dataLen - $start;
7358             }
7359 9         62 my %subdirInfo = (
7360             DataPt => \$newData,
7361             DirStart => $start,
7362             DirLen => $len,
7363             TagInfo => $tagInfo,
7364             );
7365 9         56 my $dat = $self->WriteDirectory(\%subdirInfo, GetTagTable($$subdir{TagTable}));
7366 9 50 33     111 substr($newData, $start, $len) = $dat if defined $dat and length $dat;
7367             }
7368             }
7369 488         2612 return $newData;
7370             }
7371              
7372             #------------------------------------------------------------------------------
7373             # Write TIFF as a directory
7374             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
7375             # Returns: New directory data or undefined on error
7376             sub WriteTIFF($$$)
7377             {
7378 113     113 0 364 my ($self, $dirInfo, $tagTablePtr) = @_;
7379 113 50       423 $self or return 1; # allow dummy access
7380 113         310 my $buff = '';
7381 113         434 $$dirInfo{OutFile} = \$buff;
7382 113 50       764 return $buff if $self->ProcessTIFF($dirInfo, $tagTablePtr) > 0;
7383 0           return undef;
7384             }
7385              
7386             1; # end
7387              
7388             __END__