File Coverage

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


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   361 use strict;
  61         103  
  61         2561  
16              
17 61     61   168947 use Image::ExifTool::TagLookup qw(FindTagInfo TagExists);
  61         1380  
  61         63985  
18 61     61   46674 use Image::ExifTool::Fixup;
  61         158  
  61         126140  
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 41888 local $_;
315 5774         16274 my ($self, $tag, $value, %options) = @_;
316 5774         7772 my ($err, $tagInfo, $family);
317 5774         13736 my $verbose = $$self{OPTIONS}{Verbose};
318 5774         8885 my $out = $$self{OPTIONS}{TextOut};
319 5774   100     15585 my $protected = $options{Protected} || 0;
320 5774         8595 my $listOnly = $options{ListOnly};
321 5774         7645 my $setTags = $options{SetTags};
322 5774         7993 my $noFlat = $options{NoFlat};
323 5774         7020 my $numSet = 0;
324              
325 5774 100       10203 unless (defined $tag) {
326 43         1154 delete $$self{NEW_VALUE};
327 43         162 $$self{SAVE_COUNT} = $$self{NV_COUNT} = 0;
328 43         200 $$self{DEL_GROUP} = { };
329 43         177 return 1;
330             }
331             # allow value to be scalar or list reference
332 5731 100       10049 if (ref $value) {
333 218 100       872 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       241 if (@$value > 1) {
337             # set all list-type tags first
338 51         108 my $replace = $options{Replace};
339 51         80 my $noJoin;
340 51         127 foreach (@$value) {
341 153 100       294 $noJoin = 1 if ref $_;
342 153         770 my ($n, $e) = SetNewValue($self, $tag, $_, %options, ListOnly => 1);
343 153 100       403 $err = $e if $e;
344 153         243 $numSet += $n;
345 153         366 delete $options{Replace}; # don't replace earlier values in list
346             }
347 51 100       138 return $numSet if $noJoin; # don't join if list contains objects
348             # and now set only non-list tags
349 50         218 $value = join $$self{OPTIONS}{ListSep}, @$value;
350 50         106 $options{Replace} = $replace;
351 50         140 $listOnly = $options{ListOnly} = 0;
352             } else {
353 27         76 $value = $$value[0];
354 27 50       77 $value = $$value if ref $value eq 'SCALAR'; # (handle single scalar ref in a list)
355             }
356             } elsif (ref $value eq 'SCALAR') {
357 127         261 $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     32454 $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       16253 ($options{Group}, $tag) = ($1, $2) if $tag =~ /(.*):(.+)/;
366              
367             # allow trailing '#' for ValueConv value
368 5730 100       12402 $options{Type} = 'ValueConv' if $tag =~ s/#$//;
369 5730   66     18783 my $convType = $options{Type} || ($$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv');
370              
371             # filter value if necessary
372 5730 100 50     21919 $self->Filter($$self{OPTIONS}{FilterW}, \$value) or return 0 if $convType eq 'PrintConv';
373              
374 5730         7257 my (@wantGroup, $family2);
375 5730         8483 my $wantGroup = $options{Group};
376 5730 100       9541 if ($wantGroup) {
377 2469         6238 foreach (split /:/, $wantGroup) {
378 2497 50 33     13762 next unless length($_) and /^(\d+)?(.*)/; # separate family number and group name
379 2497         6113 my ($f, $g) = ($1, $2);
380 2497         4138 my $lcg = lc $g;
381             # save group/family unless '*' or 'all'
382 2497 100 66     9513 push @wantGroup, [ $f, $lcg ] unless $lcg eq '*' or $lcg eq 'all';
383 2497 100       5600 if ($g =~ s/^ID-//i) { # family 7 is a tag ID
    100          
384 1 50 33     7 return 0 if defined $f and $f ne 7;
385 1         4 $wantGroup[-1] = [ 7, $g ]; # group name with 'ID-' removed and case preserved
386             } elsif (defined $f) {
387 30 50       79 $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       6888 $family2 = 1 if $family2groups{$lcg};
391             }
392             }
393 2469 100       4262 undef $wantGroup unless @wantGroup;
394             }
395              
396 5730         9129 $tag =~ s/ .*//; # convert from tag key to tag name if necessary
397 5730 100       11093 $tag = '*' if lc($tag) eq 'all'; # use '*' instead of 'all'
398             #
399             # handle group delete
400             #
401 5730   100     13912 while ($tag eq '*' and not defined $value and not $family2 and @wantGroup < 2) {
      100        
      66        
402             # set groups to delete
403 49         94 my (@del, $grp);
404 49   66     230 my $remove = ($options{Replace} and $options{Replace} > 1);
405 49 100       133 if ($wantGroup) {
406 35 50       2430 @del = grep /^$wantGroup$/i, @delGroups unless $wantGroup =~ /^XM[LP]-\*$/i;
407             # remove associated groups when excluding from mass delete
408 35 100 100     191 if (@del and $remove) {
409             # remove associated groups in other family
410 4 100       15 push @del, @{$excludeGroups{$del[0]}} if $excludeGroups{$del[0]};
  2         22  
411             # remove upstream groups according to JPEG map
412 4         8 my $dirName = $del[0];
413 4         7 my @dirNames;
414 4         6 for (;;) {
415 10         19 my $parent = $jpegMap{$dirName};
416 10 50       21 if (ref $parent) {
417 0         0 push @dirNames, @$parent;
418 0         0 $parent = pop @dirNames;
419             }
420 10 100 66     36 $dirName = $parent || shift @dirNames or last;
421 6         31 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       176 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         1719 push @del, (grep !/^$protectedGroups$/, @delGroups), '*';
430             }
431 49 50       145 if (@del) {
    0          
432 49         79 ++$numSet;
433 49         72 my @donegrps;
434 49         122 my $delGroup = $$self{DEL_GROUP};
435 49         101 foreach $grp (@del) {
436 975 100       1108 if ($remove) {
437 23         24 my $didExcl;
438 23 100       45 if ($grp =~ /^(XM[LP])(-.*)?$/) {
439 4         9 my $x = $1;
440 4 100 33     24 if ($grp eq $x) {
    50          
441             # exclude all related family 1 groups too
442 1         13 foreach (keys %$delGroup) {
443 67 100       210 next unless /^(-?)$x-/;
444 2 50       10 push @donegrps, $_ unless $1;
445 2         4 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       10 if ($$delGroup{$x}) {
450 2         3 push @donegrps, $x;
451 2         7 delete $$delGroup{$x};
452             }
453             # flag XMP/XML family 1 group for exclusion with leading '-'
454 3         9 $$delGroup{"-$grp"} = 1;
455 3         5 $didExcl = 1;
456             }
457             }
458 23 100       49 if (exists $$delGroup{$grp}) {
459 15         21 delete $$delGroup{$grp};
460             } else {
461 8 100       16 next unless $didExcl;
462             }
463             } else {
464 952         1559 $$delGroup{$grp} = 1;
465             # add extra groups to delete if necessary
466 952 100       1381 if ($delMore{$grp}) {
467 66         82 $$delGroup{$_} = 1, push @donegrps, $_ foreach @{$delMore{$grp}};
  66         240  
468             }
469             # remove all of this group from previous new values
470 952         1241 $self->RemoveNewValuesForGroup($grp);
471             }
472 970         1142 push @donegrps, $grp;
473             }
474 49 100 66     235 if ($verbose > 1 and @donegrps) {
475 1         3 @donegrps = sort @donegrps;
476 1 50       3 my $msg = $remove ? 'Excluding from deletion' : 'Deleting tags in';
477 1         6 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       130 return ($numSet, $err) if wantarray;
486 49 50       133 $err and warn "$err\n";
487 49         284 return $numSet;
488             }
489              
490             # initialize write/create flags
491 5681         6761 my $createOnly;
492 5681         7340 my $editOnly = $options{EditOnly};
493 5681         7009 my $editGroup = $options{EditGroup};
494 5681         9668 my $writeMode = $$self{OPTIONS}{WriteMode};
495 5681 100       9331 if ($writeMode ne 'wcg') {
496 27 100       104 $createOnly = 1 if $writeMode !~ /w/i; # don't write existing tags
497 27 100       158 if ($writeMode !~ /c/i) {
    100          
498 2 50       6 return 0 if $createOnly; # nothing to do unless writing existing tags
499 2         3 $editOnly = 1; # don't create new tags
500             } elsif ($writeMode !~ /g/i) {
501 8         11 $editGroup = 1; # don't create new groups
502             }
503             }
504 5681         7387 my ($ifdName, $mieGroup, $movGroup, $fg);
505             # set family 1 group names
506 5681         8063 foreach $fg (@wantGroup) {
507 2353 100 100     5457 next if defined $$fg[0] and $$fg[0] != 1;
508 2334         3588 $_ = $$fg[1];
509             # set $ifdName if this group is a valid IFD or SubIFD name
510 2334         2514 my $grpName;
511 2334 100 100     17068 if (/^IFD(\d+)$/i) {
    50          
    50          
    100          
    100          
    100          
    100          
    100          
512 131         422 $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         473 $grpName = $exifDirs{$_};
519 274 50 33     708 $ifdName = $grpName unless $ifdName and $allFam0{$_};
520             } elsif ($allFam0{$_}) {
521 293         580 $grpName = $allFam0{$_};
522             } elsif (/^Track(\d+)$/i) {
523 1         4 $grpName = $movGroup = "Track$1"; # QuickTime track
524             } elsif (/^MIE(\d*-?)(\w+)$/i) {
525 2         10 $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         1434 my $table = GetTagTable('Image::ExifTool::XMP::Main');
529 519         1092 my $writeProc = $$table{WRITE_PROC};
530 519 50       945 if ($writeProc) {
531 61     61   486 no strict 'refs';
  61         98  
  61         98104  
532 519         1485 &$writeProc();
533             }
534             }
535             # fix case for known groups
536 2334 100 66     10788 $wantGroup =~ s/$grpName/$grpName/i if $grpName and $grpName ne $_;
537             }
538             #
539             # get list of tags we want to set
540             #
541 5681         7450 my $origTag = $tag;
542 5681         14957 my @matchingTags = FindTagInfo($tag);
543 5681         12069 until (@matchingTags) {
544 1422         1634 my $langCode;
545             # allow language suffix of form "-en_CA" or "-" on tag name
546 1422 100 100     5837 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         133 $tag = $1;
550             # normalize case of language codes
551 55         117 $langCode = lc($2);
552 55 100       214 $langCode .= (length($3) == 3 ? uc($3) : lc($3)) if $3;
    100          
553 55         138 my @newMatches = FindTagInfo($tag);
554 55         119 foreach $tagInfo (@newMatches) {
555             # only allow language codes in tables which support them
556 291 50       617 next unless $$tagInfo{Table};
557 291 100       524 my $langInfoProc = $$tagInfo{Table}{LANG_INFO} or next;
558 226         561 my $langInfo = &$langInfoProc($tagInfo, $langCode);
559 226 100       453 push @matchingTags, $langInfo if $langInfo;
560             }
561 55 100       175 last if @matchingTags;
562             } elsif (not $options{NoShortcut}) {
563             # look for a shortcut or alias
564 1367         7584 require Image::ExifTool::Shortcuts;
565 1367         37887 my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main;
566 1367         2602 undef $err;
567 1367 100       2433 if ($match) {
568 1         5 $options{NoShortcut} = $options{Sanitized} = 1;
569 1         2 foreach $tag (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  1         3  
570 3         64 my ($n, $e) = $self->SetNewValue($tag, $value, %options);
571 3         7 $numSet += $n;
572 3 50       7 $e and $err = $e;
573             }
574 1 50       3 undef $err if $numSet; # no error if any set successfully
575 1 50       3 return ($numSet, $err) if wantarray;
576 1 50       3 $err and warn "$err\n";
577 1         7 return $numSet;
578             }
579             }
580 1368 50       2378 unless ($listOnly) {
581 1368 100       2571 if (not TagExists($tag)) {
    50          
    100          
582 47 50       166 if ($tag =~ /^[-\w*?]+$/) {
583 47 100       109 my $pre = $wantGroup ? $wantGroup . ':' : '';
584 47         69 $err = "Tag '$pre${origTag}' is not defined";
585 47 100       87 $err .= ' or has a bad language code' if $origTag =~ /-/;
586 47 50 66     142 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         883 $err = "Sorry, $wantGroup:$origTag doesn't exist or isn't writable";
597             } else {
598 812         1327 $err = "Sorry, $origTag is not writable";
599             }
600 1368 50       2232 $verbose > 2 and print $out "$err\n";
601             }
602             # all done
603 1368 50       5696 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         5473 my $foundMatch = 0;
609             #
610             # determine the groups for all tags found, and the tag with
611             # the highest priority group
612             #
613 4312         8948 my (@tagInfoList, @writeAlsoList, %writeGroup, %preferred, %tagPriority);
614 4312         0 my (%avoid, $wasProtected, $noCreate, %highestPriority, %highestQT);
615              
616 4312         6239 TAG: foreach $tagInfo (@matchingTags) {
617 75823         156846 $tag = $$tagInfo{Name}; # get tag name for warnings
618 75823         82472 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 75823 100       145800 $highestPriority{$lcTag} = -999 unless defined $highestPriority{$lcTag};
621 75823         78104 my ($priority, $writeGroup);
622 75823 100       145984 my $prfTag = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED};
623 75823 100       97981 if ($wantGroup) {
624             # a WriteGroup of All is special
625 54905   100     77565 my $wgAll = ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All');
626 54905         84689 my @grp = $self->GetGroup($tagInfo);
627 54905         56210 my $hiPri = 1000;
628 54905         61801 foreach $fg (@wantGroup) {
629 54945         65934 my ($fam, $lcWant) = @$fg;
630 54945 100       81594 $lcWant = $translateWantGroup{$lcWant} if $translateWantGroup{$lcWant};
631             # only set tag in specified group
632             # bump priority of preferred tag
633 54945 100       65170 $hiPri += $prfTag if $prfTag;
634 54945 100 66     64142 if (not defined $fam) {
    100          
    100          
635 54665 100       74207 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 2244 100 100     3542 $writeGroup = $grp[0] if $wgAll and not $writeGroup;
639 2244         3367 next;
640             }
641 52421 100       70256 next if $lcWant eq lc $grp[2];
642             } elsif ($fam == 7) {
643 2 100       7 next if IsSameID($$tagInfo{TagID}, $lcWant);
644             } elsif ($fam != 1 and not $$tagInfo{AllowGroup}) {
645 156 100       259 next if $lcWant eq lc $grp[$fam];
646 132 100 100     307 if ($wgAll and not $fam and $allFam0{$lcWant}) {
      100        
647 5 100       17 $writeGroup or $writeGroup = $allFam0{$lcWant};
648 5         14 next;
649             }
650 127         227 next TAG; # wrong group
651             }
652             # handle family 1 groups specially
653 40464 100 66     143045 if ($grp[0] eq 'EXIF' or $grp[0] eq 'SonyIDC' or $wgAll) {
    100 100        
    100 100        
    100          
654 1644 100 100     3896 unless ($ifdName and $lcWant eq lc $ifdName) {
655 1194 100 100     4011 next TAG unless $wgAll and not $fam and $allFam0{$lcWant};
      100        
656 7 100       24 $writeGroup = $allFam0{$lcWant} unless $writeGroup;
657 7         13 next;
658             }
659 450 100 100     900 next TAG if $wgAll and $allFam0{$lcWant} and $fam;
      100        
660             # can't yet write PreviewIFD tags (except for image)
661 448 50       764 $lcWant eq 'PreviewIFD' and ++$foundMatch, next TAG;
662 448         744 $writeGroup = $ifdName; # write to the specified IFD
663             } elsif ($grp[0] eq 'QuickTime') {
664 1765 100       2608 if ($grp[1] eq 'Track#') {
665 16 100 66     59 next TAG unless $movGroup and $lcWant eq lc($movGroup);
666 1         2 $writeGroup = $movGroup;
667             } else {
668 1749         3042 my $grp = $$tagInfo{Table}{WRITE_GROUP};
669 1749 100 100     5788 next TAG unless $grp and $lcWant eq lc $grp;
670 48         104 $writeGroup = $grp;
671             }
672             } elsif ($grp[0] eq 'MIE') {
673 768 100 66     2536 next TAG unless $mieGroup and $lcWant eq lc($mieGroup);
674 2         2 $writeGroup = $mieGroup; # write to specific MIE group
675             # set specific write group with document number if specified
676 2 0 33     9 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 36281 100       70692 next TAG unless $lcWant eq lc $grp[1];
683             }
684             }
685 15371 100 66     49129 $writeGroup or $writeGroup = ($$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP} || $grp[0]);
686 15371         21378 $priority = $hiPri; # highest priority since group was specified
687             }
688 36289         35184 ++$foundMatch;
689             # must do a dummy call to the write proc to autoload write package
690             # before checking Writable flag
691 36289         39270 my $table = $$tagInfo{Table};
692 36289         48659 my $writeProc = $$table{WRITE_PROC};
693             # load source table if this was a user-defined table
694 36289 100       54852 if ($$table{SRC_TABLE}) {
695 9         22 my $src = GetTagTable($$table{SRC_TABLE});
696 9 50       20 $writeProc = $$src{WRITE_PROC} unless $writeProc;
697             }
698 36289 50       47123 if ($writeProc) {
699             # make sure module is loaded if the writeProc is a string
700 36289 100       49579 unless (ref $writeProc) {
701 13         20 my $module = $writeProc;
702 13 50       1244 $module =~ s/::\w+$// and eval "require $module";
703             }
704 61     61   489 no strict 'refs';
  61         105  
  61         723952  
705 36289 100 66     85143 next unless $writeProc and &$writeProc();
706             }
707             # must still check writable flags in case of UserDefined tags
708 36170         49297 my $writable = $$tagInfo{Writable};
709             next unless $writable or ($$table{WRITABLE} and
710 36170 50 66     118967 not defined $writable and not $$tagInfo{SubDirectory});
      66        
      66        
711             # set specific write group (if we didn't already)
712 36169 100 66     69850 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 20894   100     45585 $writeGroup = $$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP};
717             # use group 0 name if no WriteGroup specified
718 20894         37041 my $group0 = $self->GetGroup($tagInfo, 0);
719 20894 100       32485 $writeGroup or $writeGroup = $group0;
720             # get priority for this group
721 20894 100       27817 unless ($priority) {
722 20813 100 100     36953 if ($$tagInfo{Avoid} and $$tagInfo{WriteAlso}) {
723 26         37 $priority = 0;
724             } else {
725 20787         31080 $priority = $$self{WRITE_PRIORITY}{lc($writeGroup)};
726 20787 100       28701 unless ($priority) {
727 3764   100     8820 $priority = $$self{WRITE_PRIORITY}{lc($group0)} || 0;
728             }
729             }
730             }
731             # adjust priority based on Preferred level for this tag
732 20894 100       29342 $priority += $prfTag if $prfTag;
733             }
734             # don't write tag if protected
735 36169         41163 my $prot = $$tagInfo{Protected};
736 36169 100 100     56119 $prot = 1 if $noFlat and defined $$tagInfo{Flat};
737 36169 100       45635 if ($prot) {
738 2350         3149 $prot &= ~$protected;
739 2350 100       3355 if ($prot) {
740 1217         3725 my %lkup = ( 1=>'unsafe', 2=>'protected', 3=>'unsafe and protected');
741 1217         2025 $wasProtected = $lkup{$prot};
742 1217 100       1923 if ($verbose > 1) {
743 1         5 my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup);
744 1         5 print $out "Sorry, $wgrp1:$tag is $wasProtected for writing\n";
745             }
746 1217         2648 next;
747             }
748             }
749             # set priority for this tag
750 34952         76344 $tagPriority{$tagInfo} = $priority;
751             # keep track of highest priority QuickTime tag
752             $highestQT{$lcTag} = $priority if $$table{GROUPS}{0} eq 'QuickTime' and
753 34952 100 100     68338 (not defined $highestQT{$lcTag} or $highestQT{$lcTag} < $priority);
      100        
754 34952 100       62608 if ($priority > $highestPriority{$lcTag}) {
    100          
755 11028         14243 $highestPriority{$lcTag} = $priority;
756 11028         27752 $preferred{$lcTag} = { $tagInfo => 1 };
757 11028 100       25480 $avoid{$lcTag} = $$tagInfo{Avoid} ? 1 : 0;
758             } elsif ($priority == $highestPriority{$lcTag}) {
759             # create all tags with highest priority
760 14719         25478 $preferred{$lcTag}{$tagInfo} = 1;
761 14719 100       23810 ++$avoid{$lcTag} if $$tagInfo{Avoid};
762             }
763 34952 100       47017 if ($$tagInfo{WriteAlso}) {
764             # store WriteAlso tags separately so we can set them first
765 115         230 push @writeAlsoList, $tagInfo;
766             } else {
767 34837         41688 push @tagInfoList, $tagInfo;
768             }
769             # special case to allow override of XMP WriteGroup
770 34952 100       47256 if ($writeGroup eq 'XMP') {
771 5928   33     14195 my $wg = $$tagInfo{WriteGroup} || $$table{WRITE_GROUP};
772 5928 50       10510 $writeGroup = $wg if $wg;
773             }
774 34952         71188 $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         12939 @tagInfoList = sort { $tagPriority{$a} <=> $tagPriority{$b} } @tagInfoList;
  56574         83756  
779             # must write any tags which also write other tags first
780 4312 100       8405 unshift @tagInfoList, @writeAlsoList if @writeAlsoList;
781              
782             # check priorities for each set of tags we are writing
783 4312         5595 my $lcTag;
784 4312         11598 foreach $lcTag (keys %preferred) {
785             # don't create tags with priority 0 if group priorities are set
786 10194 100 66     36866 if ($preferred{$lcTag} and $highestPriority{$lcTag} == 0 and
      66        
787 18         76 %{$$self{WRITE_PRIORITY}})
788             {
789 18         50 delete $preferred{$lcTag}
790             }
791             # avoid creating tags with 'Avoid' flag set if there are other alternatives
792 10194 50 66     20986 if ($avoid{$lcTag} and $preferred{$lcTag}) {
793 1541 100       2138 if ($avoid{$lcTag} < scalar(keys %{$preferred{$lcTag}})) {
  1541 100       9495  
794             # just remove the 'Avoid' tags since there are other preferred tags
795 1380         2196 foreach $tagInfo (@tagInfoList) {
796 5735844 100       8952436 next unless $lcTag eq lc $$tagInfo{Name};
797 6219 100       15256 delete $preferred{$lcTag}{$tagInfo} if $$tagInfo{Avoid};
798             }
799             } elsif ($highestPriority{$lcTag} < 1000) {
800             # look for another priority tag to create instead
801 48         73 my $nextHighest = 0;
802 48         64 my @nextBestTags;
803 48         83 foreach $tagInfo (@tagInfoList) {
804 32548 100       50405 next unless $lcTag eq lc $$tagInfo{Name};
805 122 100       279 my $priority = $tagPriority{$tagInfo} or next;
806 121 100       233 next if $priority == $highestPriority{$lcTag};
807 72 50       106 next if $priority < $nextHighest;
808 72         91 my $permanent = $$tagInfo{Permanent};
809 72 50       137 $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent;
810 72 100 100     205 next if $$tagInfo{Avoid} or $permanent;
811 67 100       131 next if $writeGroup{$tagInfo} eq 'MakerNotes';
812 23 100       51 if ($nextHighest < $priority) {
813 18         45 $nextHighest = $priority;
814 18         48 undef @nextBestTags;
815             }
816 23         42 push @nextBestTags, $tagInfo;
817             }
818 48 100       126 if (@nextBestTags) {
819             # change our preferred tags to the next best tags
820 13         27 delete $preferred{$lcTag};
821 13         44 foreach $tagInfo (@nextBestTags) {
822 14         63 $preferred{$lcTag}{$tagInfo} = 1;
823             }
824             }
825             }
826             }
827             }
828             #
829             # generate new value hash for each tag
830             #
831 4312         7304 my ($prioritySet, $createGroups, %alsoWrote);
832              
833 4312         7104 delete $$self{CHECK_WARN}; # reset CHECK_PROC warnings
834              
835             # loop through all valid tags to find the one(s) to write
836 4312         6118 foreach $tagInfo (@tagInfoList) {
837 34936 100       77727 next if $alsoWrote{$tagInfo}; # don't rewrite tags we already wrote
838             # only process List or non-List tags if specified
839 34927 100 100     65955 next if defined $listOnly and ($listOnly xor $$tagInfo{List});
      100        
840 34706         37886 my $noConv;
841 34706         68638 my $writeGroup = $writeGroup{$tagInfo};
842 34706         61363 my $permanent = $$tagInfo{Permanent};
843 34706 100       84636 $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent;
844 34706 100 66     77636 $writeGroup eq 'MakerNotes' and $permanent = 1 unless defined $permanent;
845 34706         80611 my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup);
846 34706         66750 $tag = $$tagInfo{Name}; # get tag name for warnings
847 34706         53007 my $lcTag = lc $tag;
848 34706   100     76943 my $pref = $preferred{$lcTag} || { };
849             # don't write Avoid-ed tags with side effect unless preferred
850 34706 100 100     96360 next if not $$pref{$tagInfo} and $$tagInfo{Avoid} and $$tagInfo{WriteAlso};
      100        
851 34680         49841 my $shift = $options{Shift};
852 34680         41774 my $addValue = $options{AddValue};
853 34680 100       52791 if (defined $shift) {
854             # (can't currently shift list-type tags)
855 169         204 my $shiftable;
856 169 50       292 if ($$tagInfo{List}) {
857 0         0 $shiftable = ''; # can add/delete but not shift
858             } else {
859 169         237 $shiftable = $$tagInfo{Shift};
860 169 100       299 unless ($shift) {
861             # set shift according to AddValue/DelValue
862 24 50       61 $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     61 $shift = -1 if $options{DelValue} and defined $shiftable and $shiftable eq 'Time';
      33        
866             }
867 169 50 33     764 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     468 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 34680         44121 my $val = $value;
886 34680 100 33     69036 if (defined $val) {
    100          
    50          
887             # check to make sure this is a List or Shift tag if adding
888 22333 100 100     41819 if ($addValue and not ($shift or $$tagInfo{List})) {
      100        
889 9 50       23 if ($addValue eq '2') {
890 0         0 undef $addValue; # quietly reset this option
891             } else {
892 9         23 $err = "Can't add $wgrp1:$tag (not a List type)";
893 9 50       18 $verbose > 2 and print $out "$err\n";
894 9         17 next;
895             }
896             }
897 22324 100 66     81601 if ($shift) {
    100 100        
    100          
898 169 100 66     525 if ($$tagInfo{Shift} and $$tagInfo{Shift} eq 'Time') {
    100          
899             # add '+' or '-' prefix to indicate shift direction
900 51 100       111 $val = ($shift > 0 ? '+' : '-') . $val;
901             # check the shift for validity
902 51         2062 require 'Image/ExifTool/Shift.pl';
903 51         143 my $err2 = CheckShift($$tagInfo{Shift}, $val);
904 51 50       107 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         228 $val *= $shift;
911             } else {
912 4         11 $err = "Shift value for $wgrp1:$tag is not a number";
913 4 50       18 $verbose > 2 and print $out "$err\n";
914 4         9 next;
915             }
916 165         218 $noConv = 1; # no conversions if shifting tag
917             } elsif (not length $val and $options{DelValue}) {
918 39         82 $noConv = 1; # no conversions for deleting empty value
919             } elsif (ref $val eq 'HASH' and not $$tagInfo{Struct}) {
920 2         5 $err = "Can't write a structure to $wgrp1:$tag";
921 2 50       25 $verbose > 2 and print $out "$err\n";
922 2         4 next;
923             }
924             } elsif ($permanent) {
925 8138 100       14550 return 0 if $options{IgnorePermanent};
926             # can't delete permanent tags, so set them to DelValue or empty string instead
927 8134 100       13834 if (defined $$tagInfo{DelValue}) {
928 33         58 $val = $$tagInfo{DelValue};
929 33         45 $noConv = 1; # DelValue is the raw value, so no conversion necessary
930             } else {
931 8101         9172 $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 4209 100       8447 if ($$tagInfo{DelCheck}) {
939             #### eval DelCheck ($self, $tagInfo, $wantGroup)
940 7         715 my $err2 = eval $$tagInfo{DelCheck};
941 7 50       43 $@ and warn($@), $err2 = 'Error evaluating DelCheck';
942 7 50       21 if (defined $err2) {
943             # (allow other tags to be set using DelCheck as a hook)
944 7 100       135 $err2 or goto WriteAlso; # GOTO!
945 3 50       13 $err2 .= ' for' unless $err2 =~ /delete$/;
946 3         12 $err = "$err2 $wgrp1:$tag";
947 3 50       9 $verbose > 2 and print $out "$err\n";
948 3         10 next;
949             }
950             }
951             # set group delete flag if this tag represents an entire group
952 4202 100 66     8343 if ($$tagInfo{DelGroup} and not $options{DelValue}) {
953 3         7 my @del = ( $tag );
954 3         18 $$self{DEL_GROUP}{$tag} = 1;
955             # delete extra groups if necessary
956 3 50       10 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         12 $self->RemoveNewValuesForGroup($tag);
961 3 50       9 $verbose and print $out " Deleting tags in: @del\n";
962 3         4 ++$numSet;
963 3         9 next;
964             }
965 4199         4686 $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 34651 100       50407 unless ($noConv) {
970             # set default conversion type used by ConvInv() and CHECK_PROC routines
971 30215         46868 $$self{ConvType} = $convType;
972 30215         34258 my $e;
973 30215         70167 ($val,$e) = $self->ConvInv($val,$tagInfo,$tag,$wgrp1,$$self{ConvType},$wantGroup);
974 30215 100       53480 if (defined $e) {
975             # empty error string causes error to be ignored without setting the value
976 9927 100       14729 $e or goto WriteAlso; # GOTO!
977 9909         13886 $err = $e;
978             }
979             }
980 34633 100 100     79799 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 3052 50       8794 next unless $options{DelValue};
984 0         0 $val = 'xxx never delete xxx';
985             }
986 31581 100       62239 $$self{NEW_VALUE} or $$self{NEW_VALUE} = { };
987 31581 100       60201 if ($options{Replace}) {
988             # delete the previous new value
989 14399         52678 $self->GetNewValueHash($tagInfo, $writeGroup, 'delete', $options{ProtectSaved});
990             # also delete related tag previous new values
991 14399 100       31305 if ($$tagInfo{WriteAlso}) {
992 25         78 $$self{INDENT2} = '+';
993 25         44 my ($wgrp, $wtag);
994 25 100 66     149 if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) {
      66        
995 6         15 $wgrp = $writeGroup . ':';
996             } else {
997 19         35 $wgrp = '';
998             }
999 25         43 foreach $wtag (sort keys %{$$tagInfo{WriteAlso}}) {
  25         159  
1000 91         366 my ($n,$e) = $self->SetNewValue($wgrp . $wtag, undef, Replace=>2);
1001 91         179 $numSet += $n;
1002             }
1003 25         108 $$self{INDENT2} = '';
1004             }
1005 14399 100       27879 $options{Replace} == 2 and ++$numSet, next;
1006             }
1007              
1008 31288 100 33     56547 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 20455   66     65681 $options{ProtectSaved}, ($options{DelValue} and not $shift));
1012             # ignore new values protected with ProtectSaved
1013 20455 50       37687 $nvHash or ++$numSet, next; # (increment $numSet to avoid warning)
1014 20455 100 100     42762 $$nvHash{NoReplace} = 1 if $$tagInfo{List} and not $options{Replace};
1015 20455         33569 $$nvHash{WantGroup} = $wantGroup;
1016 20455 100       30462 $$nvHash{EditOnly} = 1 if $editOnly;
1017             # save maker note fixup information if writing maker notes
1018 20455 100       35905 $$nvHash{MAKER_NOTE_FIXUP} = $options{Fixup} if $$tagInfo{MakerNotes};
1019 20455 100 100     79584 if ($createOnly) { # create only (never edit)
    100 100        
1020             # empty item in DelValue list to never edit existing value
1021 49         134 $$nvHash{DelValue} = [ '' ];
1022 49         97 $$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       624 $$nvHash{DelValue} or $$nvHash{DelValue} = [ ];
1026 236 100       407 if ($shift) {
    100          
1027             # add shift value to list
1028 165         290 $$nvHash{Shift} = $val;
1029             } elsif ($options{DelValue}) {
1030             # don't create if we are replacing a specific value
1031 58 100 100     164 $$nvHash{IsCreating} = 0 unless $val eq '' or $$tagInfo{List};
1032             # add delete value to list
1033 58 100       71 push @{$$nvHash{DelValue}}, ref $val eq 'ARRAY' ? @$val : $val;
  58         158  
1034 58 50       136 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 20455 100       45237 my $prf = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED};
1052             # hack to prefer only a single tag in the QuickTime group
1053 20455 100       46897 if ($$tagInfo{Table}{GROUPS}{0} eq 'QuickTime') {
1054 793 100       2192 $prf = 0 if $tagPriority{$tagInfo} < $highestQT{$lcTag};
1055             }
1056 20455 100 100     54877 if ($$pref{$tagInfo} or $prf) {
1057 9601 100 100     36755 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 5796         12303 $$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         330 grep(/^$/,@{$$nvHash{DelValue}}))
1065             {
1066 3791 100       10843 $$nvHash{IsCreating} = $editOnly ? 0 : ($editGroup ? 2 : 1);
    100          
1067             # add to hash of groups where this tag is being created
1068 3791 100 100     10361 $createGroups or $createGroups = $options{CreateGroups} || { };
1069 3791         11713 $$createGroups{$self->GetGroup($tagInfo, 0)} = 1;
1070 3791         9811 $$nvHash{CreateGroups} = $createGroups;
1071             }
1072             }
1073 20455 100       43940 if ($$nvHash{IsCreating}) {
    100          
1074 3780 100       4458 if (%{$$self{DEL_GROUP}}) {
  3780         9241  
1075 177         268 my ($grp, @grps);
1076 177         264 foreach $grp (keys %{$$self{DEL_GROUP}}) {
  177         3698  
1077 11060 100       14450 next if $$self{DEL_GROUP}{$grp} == 2;
1078             # set flag indicating tags were written after this group was deleted
1079 409         469 $$self{DEL_GROUP}{$grp} = 2;
1080 409         467 push @grps, $grp;
1081             }
1082 177 100 66     936 if ($verbose > 1 and @grps) {
1083 1         3 @grps = sort @grps;
1084 1         5 print $out " Writing new tags after deleting groups: @grps\n";
1085             }
1086             }
1087             } elsif ($createOnly) {
1088 22 100       68 $noCreate = $permanent ? 'permanent' : ($$tagInfo{Avoid} ? 'avoided' : '');
    100          
1089 22 50       57 $noCreate or $noCreate = $shift ? 'shifting' : 'not preferred';
    100          
1090 22 50       45 $verbose > 2 and print $out "Not creating $wgrp1:$tag ($noCreate)\n";
1091 22         61 next; # nothing to do (not creating and not editing)
1092             }
1093 20433 100 100     52441 if ($shift or not $options{DelValue}) {
1094 20375 100       56017 $$nvHash{Value} or $$nvHash{Value} = [ ];
1095 20375 100 33     38829 if (not $$tagInfo{List}) {
    50          
1096             # not a List tag -- overwrite existing value
1097 19846         34934 $$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       675 push @{$$nvHash{Value}}, ref $val eq 'ARRAY' ? @$val : $val;
  529         1585  
1105             }
1106 20375 100       39015 if ($verbose > 1) {
1107 26         32 my $ifExists;
1108 26 50       59 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     103 (($$nvHash{DelValue} and @{$$nvHash{DelValue}}) ?
    50          
    50          
    50          
    100          
1122             ' if tag was deleted' : ' if tag exists');
1123             }
1124 26 50       64 my $verb = ($shift ? 'Shifting' : ($addValue ? 'Adding' : 'Writing'));
    50          
1125 26         99 print $out "$$self{INDENT2}$verb $wgrp1:$tag$ifExists\n";
1126             }
1127             }
1128             } elsif ($permanent) {
1129 6751         9025 $err = "Can't delete Permanent tag $wgrp1:$tag";
1130 6751 50       10378 $verbose > 1 and print $out "$err\n";
1131 6751         15999 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 4082         9518 $self->GetNewValueHash($tagInfo, $writeGroup, 'delete');
1138 4082         5453 my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create');
1139 4082         7506 $$nvHash{WantGroup} = $wantGroup;
1140 4082 50       6890 $verbose > 1 and print $out "$$self{INDENT2}Deleting $wgrp1:$tag\n";
1141             }
1142 24515 100       35070 $$setTags{$tagInfo} = 1 if $setTags;
1143 24515 100       42352 $prioritySet = 1 if $$pref{$tagInfo};
1144 24537         26098 WriteAlso:
1145             ++$numSet;
1146             # also write related tags
1147 24537         31892 my $writeAlso = $$tagInfo{WriteAlso};
1148 24537 100       59592 if ($writeAlso) {
1149 76         190 $$self{INDENT2} = '+'; # indicate related tag with a leading "+"
1150 76         145 my ($wgrp, $wtag, $n);
1151 76 100 66     524 if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) {
      66        
1152 46         110 $wgrp = $writeGroup . ':';
1153             } else {
1154 30         66 $wgrp = '';
1155             }
1156 76         405 local $SIG{'__WARN__'} = \&SetWarning;
1157 76         395 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         1853 CreateGroups=> $createGroups,
1166             SetTags => \%alsoWrote, # remember tags already written
1167             );
1168 243         434 undef $evalWarning;
1169             #### eval WriteAlso ($val,%opts)
1170 243         18589 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       923 undef $v unless defined $val;
1174 243 50       524 $@ and $evalWarning = $@;
1175 243 50       476 unless ($evalWarning) {
1176 243         1503 ($n,$evalWarning) = $self->SetNewValue($wgrp . $wtag, $v, %opts);
1177 243         536 $numSet += $n;
1178             # count this as being set if any related tag is set
1179 243 100 100     1215 $prioritySet = 1 if $n and $$pref{$tagInfo};
1180             }
1181 243 100 66     970 if ($evalWarning and (not $err or $verbose > 2)) {
      66        
1182 9         28 my $str = CleanWarning();
1183 9 50       29 if ($str) {
1184 9 50       57 $str .= " for $wtag" unless $str =~ / for [-\w:]+$/;
1185 9         18 $str .= " in $wgrp1:$tag (WriteAlso)";
1186 9 50       30 $err or $err = $str;
1187 9 50       45 print $out "$str\n" if $verbose > 2;
1188             }
1189             }
1190             }
1191 76         528 $$self{INDENT2} = '';
1192             }
1193             }
1194             # print warning if we couldn't set our priority tag
1195 4308 100 100     18897 if (defined $err and not $prioritySet) {
    100 66        
    50          
    100          
1196 86 50 33     337 warn "$err\n" if $err and not wantarray;
1197             } elsif (not $numSet) {
1198 622 100       1556 my $pre = $wantGroup ? $wantGroup . ':' : '';
1199 622 100       1201 if ($wasProtected) {
    100          
1200 363         514 $verbose = 0; # we already printed this verbose message
1201 363 100 100     1555 unless ($options{Replace} and $options{Replace} == 2) {
1202 351         730 $err = "Sorry, $pre$tag is $wasProtected for writing";
1203             }
1204             } elsif (not $listOnly) {
1205 252 50 33     1308 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         476 $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       1084 if ($err) {
1226 603 50       1128 $verbose > 2 and print $out "$err\n";
1227 603 50       1023 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 456         887 undef $err;
1234             }
1235 4308 100       43467 return ($numSet, $err) if wantarray;
1236 471         30761 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 1307 local $_;
1257 59         231 my ($self, $srcFile, @setTags) = @_;
1258 59         153 my ($srcExifTool, $key, $tag, @exclude, @reqTags, $info);
1259              
1260             # get initial SetNewValuesFromFile options
1261 59         243 my %opts = ( Replace => 1 ); # replace existing list items by default
1262 59         283 while (ref $setTags[0] eq 'HASH') {
1263 1         2 $_ = shift @setTags;
1264 1         5 foreach $key (keys %$_) {
1265 1         4 $opts{$key} = $$_{$key};
1266             }
1267             }
1268             # expand shortcuts
1269 59 100       381 @setTags and ExpandShortcuts(\@setTags);
1270             # set options for our extraction tool
1271 59         438 my $options = $$self{OPTIONS};
1272 59         185 my $printConv = $$options{PrintConv};
1273 59 50       239 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       238 my $srcType = $printConv ? 'PrintConv' : 'ValueConv';
1280 59 50       247 my $structOpt = defined $$options{Struct} ? $$options{Struct} : 2;
1281              
1282 59 50 33     270 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         334 $srcExifTool = Image::ExifTool->new;
1287 59         261 $srcExifTool->Options(PrintConv => $printConv);
1288             # set flag to indicate we are being called from inside SetNewValuesFromFile()
1289 59         184 $$srcExifTool{TAGS_FROM_FILE} = 1;
1290             # synchronize and increment the file sequence number
1291 59         203 $$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     438 $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         272 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         6009 $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     937 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     252 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         200 $$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
1331 59         191 $$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL};
1332 59         164 foreach $tag (@setTags) {
1333 71 100       201 next if ref $tag;
1334             # avoid extracting tags that are excluded
1335 70 100       267 $tag =~ /^-(.*)/ and push(@exclude, $1), next;
1336             # add specified tags to list of requested tags
1337 62         119 $_ = $tag;
1338 62 100       593 if (/(.+?)\s*(>|<)\s*(.+)/) {
1339 31 100       99 if ($2 eq '>') {
1340 10         27 $_ = $1;
1341             } else {
1342 21         44 $_ = $3;
1343 21 100       125 /\$/ and push(@reqTags, /\$\{?(?:[-\w]+:)*([-\w?*]+)/g), next;
1344             }
1345             }
1346 54 50       447 push @reqTags, $2 if /(^|:)([-\w?*]+)#?$/;
1347             }
1348 59 100       188 if (@exclude) {
1349 7         42 ExpandShortcuts(\@exclude, 1);
1350 7         34 $srcExifTool->Options(Exclude => \@exclude);
1351             }
1352 59 100       270 $srcExifTool->Options(RequestTags => \@reqTags) if @reqTags;
1353             # get all tags from source file (including MakerNotes block)
1354 59         237 $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     411 return $info if $$info{Error} and $$info{Error} eq 'Error opening file' and not $$self{ALT_EXIFTOOL};
      33        
1358 59         828 delete $$srcExifTool{VALUE}{Error}; # delete so we can check this later
1359              
1360             # sort tags in file order with priority tags last
1361 59         159 my (@tags, @prio);
1362 59         1447 foreach (sort { $$srcExifTool{FILE_ORDER}{$a} <=> $$srcExifTool{FILE_ORDER}{$b} } keys %$info) {
  58081         65623  
1363 9141 100       10170 if (/ /) {
1364 768         891 push @tags, $_;
1365             } else {
1366 8373         9205 push @prio, $_;
1367             }
1368             }
1369 59         1032 push @tags, @prio;
1370             #
1371             # simply transfer all tags from source image if no tags specified
1372             #
1373 59 100       223 unless (@setTags) {
1374             # transfer maker note information to this object
1375 15         78 $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER};
1376 15         47 my $tagExtra = $$srcExifTool{TAG_EXTRA};
1377 15         42 foreach $tag (@tags) {
1378             # don't try to set errors or warnings
1379 2651 100       7599 next if $tag =~ /^(Error|Warning)\b/;
1380             # get appropriate value type if necessary
1381 2647 50 33     6384 if ($opts{SrcType} and $opts{SrcType} ne $srcType) {
1382 0         0 $$info{$tag} = $srcExifTool->GetValue($tag, $opts{SrcType});
1383             }
1384 2647         5855 my $fixup = $$tagExtra{$tag}{Fixup};
1385 2647 100       4349 $opts{Fixup} = $fixup if $fixup;
1386             # set value for this tag
1387 2647         8541 my ($n, $e) = $self->SetNewValue($tag, $$info{$tag}, %opts);
1388             # delete this tag if we couldn't set it
1389 2647 100       6433 $n or delete $$info{$tag};
1390 2647 100       6694 delete $opts{Fixup} if $fixup;
1391             }
1392 15         1479 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         136 my (@setList, $set, %setMatches, $t, %altFiles);
1399 44         99 my $assign = 0;
1400 44         151 foreach $t (@setTags) {
1401 71 100       200 if (ref $t eq 'HASH') {
1402             # update current options
1403 1         4 foreach $key (keys %$t) {
1404 1         3 $opts{$key} = $$t{$key};
1405             }
1406 1         3 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         257 my $opts = { %opts };
1411 70         184 $tag = lc $t; # change tag/group names to all lower case
1412 70         130 my (@fg, $grp, $dst, $dstGrp, $dstTag, $isExclude);
1413             # handle redirection to another tag
1414 70 100       665 if ($tag =~ /(.+?)\s*(>|<|=)(\s*)(.*)/) {
1415 31         53 $dstGrp = '';
1416 31         73 my ($opt, $op, $spc);
1417 31 100       103 if ($2 eq '>') {
1418 10         54 ($tag, $dstTag) = ($1, $4);
1419             # flag add and delete (eg. '+<' and '-<') redirections
1420 10 50 33     86 $opt = $1 if $tag =~ s/\s*([-+])$// or $dstTag =~ s/^([-+])\s*//;
1421             } else {
1422 21         105 ($dstTag, $op, $spc, $tag) = ($1, $2, $3, $4);
1423 21 50       80 $opt = $1 if $dstTag =~ s/\s*([-+])$//;
1424 21 50       80 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         15 $tag = $t; # restore original case
1431             # recover leading whitespace (except for initial single space)
1432 8         49 $tag =~ s/(.+?)\s*(>|<) ?//;
1433 8         28 $$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       43 $opt = $1 if $tag =~ s/^([-+])\s*//;
1438             }
1439             }
1440 31 100       97 $$opts{Replace} = 0 if $dstTag =~ s/^\+//;
1441             # validate tag name(s)
1442 31 50 66     200 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       84 ValidTagName($dstTag) or $self->Warn("Invalid tag name '${dstTag}'"), next;
1447             # translate '+' and '-' to appropriate SetNewValue option
1448 31 50       69 if ($opt) {
1449 0         0 $$opts{{ '+' => 'AddValue', '-' => 'DelValue' }->{$opt}} = 1;
1450 0         0 $$opts{Shift} = 0; # shift if shiftable
1451             }
1452 31 100       112 ($dstGrp, $dstTag) = ($1, $2) if $dstTag =~ /(.*):(.+)/;
1453             # ValueConv may be specified separately on the destination with '#'
1454 31 50       104 $$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//;
1455             # replace tag name of 'all' with '*'
1456 31 100       80 $dstTag = '*' if $dstTag eq 'all';
1457             } else {
1458 39 50       162 $$opts{Replace} = 0 if $tag =~ s/^\+//;
1459             }
1460 70 50 66     404 unless ($$opts{EXPR} or $$opts{ASSIGN}) {
1461 62         161 $isExclude = ($tag =~ s/^-//);
1462 62 100       258 if ($tag =~ /(.*):(.+)/) {
1463 34         124 ($grp, $tag) = ($1, $2);
1464 34         106 foreach (split /:/, $grp) {
1465             # save family/groups in list (ignoring 'all' and '*')
1466 35 50 33     255 next unless length($_) and /^(\d+)?(.*)/;
1467 35         86 my ($f, $g) = ($1, $2);
1468 35 50 33     212 $f = 7 if (not $f or $f eq '7') and $g =~ s/^ID-//i;
      33        
1469 35 50 33     130 if ($g =~ /^file\d+$/i and (not $f or $f eq '8')) {
      66        
1470 3         4 $f = 8;
1471 3         5 my $g8 = ucfirst $g;
1472 3 50       14 if ($$srcExifTool{ALT_EXIFTOOL}{$g8}) {
1473 3         8 $$opts{GROUP8} = $g8;
1474 3 100       10 $altFiles{$g8} or $altFiles{$g8} = [ ];
1475             # save list of requested tags for this alternate ExifTool object
1476 3         5 push @{$altFiles{$g8}}, "$grp:$tag";
  3         8  
1477             }
1478             }
1479 35 100 100     211 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       192 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       292 $tag = '*' if $tag eq 'all';
1489             # allow wildcards in tag names (handle differently from all tags: '*')
1490 62 100 100     316 if ($tag =~ /[?*]/ and $tag ne '*') {
1491 3         10 $$opts{WILD} = 1; # set flag indicating wildcards were used in source tag
1492 3         8 $tag =~ s/\*/[-\\w]*/g;
1493 3         11 $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       226 if ($dstTag) {
    100          
1498             # redirect this tag
1499 31 50       72 $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         90 $dst = [ $dstGrp, $dstTag ];
1505             } elsif ($isExclude) {
1506             # implicitly assume '*' if first entry is an exclusion
1507 8 100       33 unshift @setList, [ [ ], '*', [ '', '*' ], $opts ] unless @setList;
1508             # exclude this tag by leaving $dst undefined
1509             } else {
1510 31 100 100     207 $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         258 unshift @setList, [ \@fg, $tag, $dst, $opts ];
1514             }
1515             # 1b) copy requested tags for each alternate ExifTool object
1516 44         86 my $g8;
1517 44         151 foreach $g8 (sort keys %altFiles) {
1518             # request specific alternate tags to load them from the alternate ExifTool object
1519 1         8 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       4 if (%$altInfo) {
1522 1         7 push @tags, 'Warning DUMMY', reverse sort keys %$altInfo;
1523 1         46 $$info{$_} = $$altInfo{$_} foreach keys %$altInfo;
1524             }
1525             }
1526             # 2) initialize lists of matching tags for each setTag
1527 44         106 foreach $set (@setList) {
1528 71 100       413 $$set[2] and $setMatches{$set} = [ ];
1529             }
1530             # no need to search source tags if doing only assignments
1531 44 50       125 undef @tags if $assign == @setList;
1532             # 3) loop through all tags in source image and save tags matching each setTag
1533 44         106 my (%rtnInfo, $isAlt);
1534 44         86 foreach $tag (@tags) {
1535             # don't try to set errors or warnings
1536 6494 100       10224 if ($tag =~ /^(Error|Warning)( |$)/) {
1537 14 100       47 if ($tag eq 'Warning DUMMY') {
1538 1         3 $isAlt = 1; # start of the alt tags
1539             } else {
1540 13         47 $rtnInfo{$tag} = $$info{$tag};
1541             }
1542 14         23 next;
1543             }
1544             # only set specified tags
1545 6480         8040 my $lcTag = lc(GetTagName($tag));
1546 6480         6847 my (@grp, %grp);
1547 6480         6747 SET: foreach $set (@setList) {
1548 10534         10499 my $opts = $$set[3];
1549 10534 100       12957 next if $$opts{EXPR}; # (expressions handled in step 4)
1550 9408 100 100     18047 next if $$opts{GROUP8} xor $isAlt;
1551             # check first for matching tag
1552 8838 100 100     17261 unless ($$set[1] eq $lcTag or $$set[1] eq '*') {
1553             # handle wildcards
1554 6212 100 100     11120 next unless $$opts{WILD} and $lcTag =~ /^$$set[1]$/;
1555             }
1556             # then check for matching group
1557 2642 100       2513 if (@{$$set[0]}) {
  2642         3462  
1558             # get lower case group names if not done already
1559 1474 100       1897 unless (@grp) {
1560 1370         2000 @grp = map(lc, $srcExifTool->GetGroup($tag));
1561 1370         5440 $grp{$_} = 1 foreach @grp;
1562             }
1563 1474         1515 foreach (@{$$set[0]}) {
  1474         1831  
1564 1516         1873 my ($f, $g) = @$_;
1565 1516 100       1787 if (not defined $f) {
    50          
1566 1512 100       3483 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     15 next SET unless defined $grp[$f] and $g eq $grp[$f];
1571             }
1572             }
1573             }
1574 1626 100       2304 last unless $$set[2]; # all done if we hit an exclude
1575             # add to the list of tags matching this setTag
1576 1452         1398 push @{$setMatches{$set}}, $tag;
  1452         3138  
1577             }
1578             }
1579             # 4) loop through each setTag in original order, setting new tag values
1580 44         131 foreach $set (reverse @setList) {
1581             # get options for SetNewValue
1582 71         164 my $opts = $$set[3];
1583             # handle expressions
1584 71 100 66     381 if ($$opts{EXPR} or $$opts{ASSIGN}) {
1585 8         16 my $val;
1586 8 50       26 if ($$opts{EXPR}) {
1587 8         52 $val = $srcExifTool->InsertTagValues($$set[1], \@tags, 'Error');
1588 8         24 my $err = $$srcExifTool{VALUE}{Error};
1589 8 50       27 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         34 my ($dstGrp, $dstTag) = @{$$set[2]};
  8         30  
1606 8 50 33     57 $$opts{Protected} = 1 unless $dstTag =~ /[?*]/ and $dstTag ne '*';
1607 8 50       5623 $$opts{Group} = $dstGrp if $dstGrp;
1608 8         52 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       23 $rtnInfo{NextFreeTagKey(\%rtnInfo, 'Warning')} = $rtnVals[1] if $rtnVals[1];
1612 8         25 next;
1613             }
1614 63         105 foreach $tag (@{$setMatches{$set}}) {
  63         180  
1615 1452         2103 my ($val, $noWarn);
1616 1452 50 33     3160 if ($$opts{SrcType} and $$opts{SrcType} ne $srcType) {
1617 0         0 $val = $srcExifTool->GetValue($tag, $$opts{SrcType});
1618             } else {
1619 1452         3347 $val = $$info{$tag};
1620             }
1621 1452         1532 my ($dstGrp, $dstTag) = @{$$set[2]};
  1452         3263  
1622 1452 100       2218 if ($dstGrp) {
1623 1370         2943 my @dstGrp = split /:/, $dstGrp;
1624             # destination group of '*' writes to same group as source tag
1625             # (family 1 unless otherwise specified)
1626 1370         2326 foreach (@dstGrp) {
1627 1372 100       5196 next unless /^(\d*)(all|\*)$/i;
1628 1086 50       4265 $_ = $1 . $srcExifTool->GetGroup($tag, length $1 ? $1 : 1);
1629 1086         1647 $noWarn = 1; # don't warn on wildcard destinations
1630             }
1631 1370         3400 $$opts{Group} = join ':', @dstGrp;
1632             } else {
1633 82         114 delete $$opts{Group};
1634             }
1635             # transfer maker note information if setting this tag
1636 1452 100       3878 if ($$srcExifTool{TAG_INFO}{$tag}{MakerNotes}) {
1637 7         39 $$opts{Fixup} = $$srcExifTool{TAG_EXTRA}{$tag}{Fixup};
1638 7         24 $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER};
1639             }
1640 1452 100       2477 if ($dstTag eq '*') {
1641 1419         1611 $dstTag = $tag;
1642 1419         1597 $noWarn = 1;
1643             }
1644 1452 100 100     3878 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     3128 $$srcExifTool{TAG_INFO}{$tag}{Binary};
1648             # don't copy to protected tags when using wildcards
1649 1389         2017 delete $$opts{Protected};
1650             # don't copy flattened tags if copying structures too when copying all
1651 1389 50       3004 $$opts{NoFlat} = $structOpt eq '2' ? 1 : 0;
1652             } else {
1653             # allow protected tags to be copied if specified explicitly
1654 37 50       223 $$opts{Protected} = 1 unless $dstTag =~ /[?*]/;
1655 37         78 delete $$opts{NoFlat};
1656             }
1657             # set value(s) for this tag
1658 1426         4620 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     4507 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         2613 delete $$opts{Fixup};
1670 1426 100       4268 $rtnInfo{$tag} = $val if $rtn; # tag was set successfully
1671             }
1672             }
1673 44         4155 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 7319     7319 1 9196 local $_;
1688 7319         9334 my $self = shift;
1689 7319         8592 my $tag = shift;
1690 7319         7818 my $nvHash;
1691 7319 100 100     28252 if ((ref $tag eq 'HASH' and $$tag{IsNVH}) or not defined $tag) {
      100        
1692 4243         5769 $nvHash = $tag;
1693             } else {
1694 3076         3519 my $newValueHashPt = shift;
1695 3076 100       5776 if ($$self{NEW_VALUE}) {
1696 2961         3509 my ($group, $tagInfo);
1697 2961 100 66     10785 if (ref $tag) {
    100          
1698 52         151 $nvHash = $self->GetNewValueHash($tag);
1699             } elsif (defined($tagInfo = $Image::ExifTool::Extra{$tag}) and
1700             $$tagInfo{Writable})
1701             {
1702 1551         3115 $nvHash = $self->GetNewValueHash($tagInfo);
1703             } else {
1704             # separate group from tag name
1705 1358         1519 my @groups;
1706 1358 100       3151 @groups = split ':', $1 if $tag =~ s/(.*)://;
1707 1358         2875 my @tagInfoList = FindTagInfo($tag);
1708             # decide which tag we want
1709 1358         1853 GNV_TagInfo: foreach $tagInfo (@tagInfoList) {
1710 1362 100       2296 my $nvh = $self->GetNewValueHash($tagInfo) or next;
1711             # select tag in specified group(s) if necessary
1712 4         9 foreach (@groups) {
1713 2 50       6 next if $_ eq $$nvh{WriteGroup};
1714 2         6 my @grps = $self->GetGroup($tagInfo);
1715 2 50       6 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       6 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         8 $nvHash = $nvh;
1728             # give priority to the one we are creating
1729 4 100       16 last if defined $$nvHash{IsCreating};
1730             }
1731             }
1732             }
1733             # return new value hash if requested
1734 3076 100       6228 $newValueHashPt and $$newValueHashPt = $nvHash;
1735             }
1736 7319 100 100     19423 unless ($nvHash and $$nvHash{Value}) {
1737 4962 100       12317 return () if wantarray; # return empty list
1738 3022         5312 return undef;
1739             }
1740 2357         3314 my $vals = $$nvHash{Value};
1741             # do inverse raw conversion if necessary
1742             # - must also check after doing a Shift
1743 2357 100 100     7591 if ($$nvHash{TagInfo}{RawConvInv} or $$nvHash{Shift}) {
1744 60         179 my @copyVals = @$vals; # modify a copy of the values
1745 60         109 $vals = \@copyVals;
1746 60         117 my $tagInfo = $$nvHash{TagInfo};
1747 60         105 my $conv = $$tagInfo{RawConvInv};
1748 60         106 my $table = $$tagInfo{Table};
1749 60         90 my ($val, $checkProc);
1750 60 100 66     216 $checkProc = $$table{CHECK_PROC} if $$nvHash{Shift} and $table;
1751 60         263 local $SIG{'__WARN__'} = \&SetWarning;
1752 60         124 undef $evalWarning;
1753 60         150 foreach $val (@$vals) {
1754             # must check value now if it was shifted
1755 60 100       115 if ($checkProc) {
1756 26         74 my $err = &$checkProc($self, $tagInfo, \$val);
1757 26 50 33     104 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       87 next unless $conv;
1764             } else {
1765 34 50       82 last unless $conv;
1766             }
1767             # do inverse raw conversion
1768 34 100       96 if (ref($conv) eq 'CODE') {
1769 2         10 $val = &$conv($val, $self);
1770             } else {
1771             #### eval RawConvInv ($self, $val, $tagInfo)
1772 32         3383 $val = eval $conv;
1773 32 50       161 $@ and $evalWarning = $@;
1774             }
1775 34 50       170 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 2357 100       3952 if (wantarray) {
1788             # remove duplicates if requested
1789 1168 50 66     2687 if (@$vals > 1 and $self->Options('NoDups')) {
1790 0         0 my %seen;
1791 0         0 @$vals = grep { !$seen{$_}++ } @$vals;
  0         0  
1792             }
1793 1168         3876 return @$vals;
1794             }
1795 1189         3328 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 477 my $self = shift;
1807 245         583 my $newVal = $$self{NEW_VALUE};
1808 245         626 my ($num, $pseudo) = (0, 0);
1809 245 100       614 if ($newVal) {
1810 226         523 $num = scalar keys %$newVal;
1811 226         329 my $nv;
1812 226         2270 foreach $nv (values %$newVal) {
1813 19807         29422 my $tagInfo = $$nv{TagInfo};
1814             # don't count tags that don't write anything
1815 19807 100       29308 $$tagInfo{WriteNothing} and --$num, next;
1816             # count the number of pseudo tags included
1817 19788 100       29815 $$tagInfo{WritePseudo} and ++$pseudo;
1818             }
1819             }
1820 245         431 $num += scalar keys %{$$self{DEL_GROUP}};
  245         1880  
1821 245 50       613 return $num unless wantarray;
1822 245         748 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 9 my $self = shift;
1833 1         2 my $newValues = $$self{NEW_VALUE};
1834 1         2 my $saveCount = ++$$self{SAVE_COUNT};
1835 1         3 my $key;
1836 1         34 foreach $key (keys %$newValues) {
1837 114         127 my $nvHash = $$newValues{$key};
1838 114         126 while ($nvHash) {
1839             # set Save count if not done already
1840 116 50       302 $$nvHash{Save} or $$nvHash{Save} = $saveCount;
1841 116         152 $nvHash = $$nvHash{Next};
1842             }
1843             }
1844             # initialize hash for saving overwritten new values
1845 1         11 $$self{SAVE_NEW_VALUE} = { };
1846             # make a copy of the delete group hash
1847 1         3 my %delGrp = %{$$self{DEL_GROUP}};
  1         9  
1848 1         3 $$self{SAVE_DEL_GROUP} = \%delGrp;
1849 1         3 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 9 my $self = shift;
1863 1         2 my $newValues = $$self{NEW_VALUE};
1864 1         2 my $savedValues = $$self{SAVE_NEW_VALUE};
1865 1         3 my $key;
1866             # 1) remove any new values which don't have the Save flag set
1867 1 50       6 if ($newValues) {
1868 1         262 my @keys = keys %$newValues;
1869 1         6 foreach $key (@keys) {
1870 579         542 my $lastHash;
1871 579         708 my $nvHash = $$newValues{$key};
1872 579         654 while ($nvHash) {
1873 581 100       951 if ($$nvHash{Save}) {
1874 27         26 $lastHash = $nvHash;
1875             } else {
1876             # remove this entry from the list
1877 554 50       709 if ($lastHash) {
    100          
1878 0         0 $$lastHash{Next} = $$nvHash{Next};
1879             } elsif ($$nvHash{Next}) {
1880 2         5 $$newValues{$key} = $$nvHash{Next};
1881             } else {
1882 552         528 delete $$newValues{$key};
1883             }
1884             }
1885 581         1879 $nvHash = $$nvHash{Next};
1886             }
1887             }
1888             }
1889             # 2) restore saved new values
1890 1 50       8 if ($savedValues) {
1891 1 50       4 $newValues or $newValues = $$self{NEW_VALUE} = { };
1892 1         21 foreach $key (keys %$savedValues) {
1893 89 100       98 if ($$newValues{$key}) {
1894             # add saved values to end of list
1895 2         6 my $nvHash = LastInList($$newValues{$key});
1896 2         5 $$nvHash{Next} = $$savedValues{$key};
1897             } else {
1898 87         114 $$newValues{$key} = $$savedValues{$key};
1899             }
1900             }
1901 1         6 $$self{SAVE_NEW_VALUE} = { }; # reset saved new values
1902             }
1903             # 3) restore delete groups
1904 1         2 my %delGrp = %{$$self{SAVE_DEL_GROUP}};
  1         4  
1905 1         11 $$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 48 my ($self, $g8, $file) = @_;
1916 6         14 $g8 = ucfirst lc $g8;
1917 6 50       60 return 0 unless $g8 =~ /^File\d+$/;
1918             # keep the same file if already initialized (possibly has metadata extracted)
1919 6 50 33     47 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         19 my $altExifTool = Image::ExifTool->new;
1925 6         17 $$altExifTool{ALT_FILE} = $file;
1926 6         24 $$self{ALT_EXIFTOOL}{$g8} = $altExifTool;
1927             }
1928 6         17 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 3 my ($self, $file, $newName, $opt, $usedFlag) = @_;
1991 1         3 my ($nvHash, $doName, $doDir);
1992              
1993 1 50       4 $opt or $opt = '';
1994             # determine the new file name
1995 1 50       3 unless (defined $newName) {
1996 1 50       3 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         3 my $filename = $self->GetNewValue('FileName', \$nvHash);
2007 1 50 33     6 $doName = 1 if defined $filename and $self->IsOverwriting($nvHash, $file);
2008 1         3 my $dir = $self->GetNewValue('Directory', \$nvHash);
2009 1 50 33     4 $doDir = 1 if defined $dir and $self->IsOverwriting($nvHash, $file);
2010 1 50 33     3 return 0 unless $doName or $doDir; # nothing to do
2011 1 50       4 if ($doName) {
2012 1         4 $newName = GetNewFileName($file, $filename);
2013 1 50       4 $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       5 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       3 length $newName or $self->Warn('New file name is empty'), return -1;
2048             # don't replace existing file
2049 1 0 0     7 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       4 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         5 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     9 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         24 $$self{NewName} = $newName; # remember new file name
2142 1         3 ++$$self{CHANGED};
2143 1         7 $self->VerboseValue('+ FileName', $newName);
2144 1         3 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 661 my ($self, $file) = @_;
2155 232         412 my $result = 0;
2156              
2157 232         909 my $perm = $self->GetNewValue('FilePermissions');
2158 232 50       723 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         528 my $uid = $self->GetNewValue('FileUserID');
2168 232         609 my $gid = $self->GetNewValue('FileGroupID');
2169 232 50 33     1084 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         447 my $tag;
2182 232         611 foreach $tag (@writableMacOSTags) {
2183 1624         1680 my $nvHash;
2184 1624         2709 my $val = $self->GetNewValue($tag, \$nvHash);
2185 1624 50       2975 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         948 my $zhash = $self->GetNewValueHash($Image::ExifTool::Extra{ZoneIdentifier});
2199 232 50       681 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         815 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 23588 local ($_, *EXIFTOOL_FILE2, *EXIFTOOL_OUTFILE);
2251 245         804 my ($self, $infile, $outfile, $outType) = @_;
2252 245         1266 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         795 my $oldRaf = $$self{RAF};
2256 245         471 my $rtnVal = 0;
2257              
2258             # initialize member variables
2259 245         1300 $self->Init();
2260 245         697 $$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         566 my ($nvHash, $nvHash2, $originalTime, $createTime);
2265 245         1346 my $setModDate = defined $self->GetNewValue('FileModifyDate', \$nvHash);
2266 245         1096 my $setCreateDate = defined $self->GetNewValue('FileCreateDate', \$nvHash2);
2267 245         489 my ($aTime, $mTime, $cTime);
2268 245 0 33     933 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     786 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         1039 my ($numNew, $numPseudo) = $self->CountNewValues();
2284 245 100 66     1018 if (not defined $outfile and defined $infile) {
2285 4         12 $hardLink = $self->GetNewValue('HardLink');
2286 4         12 $symLink = $self->GetNewValue('SymLink');
2287 4         15 $testName = $self->GetNewValue('TestName');
2288 4 50 33     20 undef $hardLink if defined $hardLink and not length $hardLink;
2289 4 50 33     17 undef $symLink if defined $symLink and not length $symLink;
2290 4 50 33     13 undef $testName if defined $testName and not length $testName;
2291 4         12 my $newFileName = $self->GetNewValue('FileName', \$nvHash);
2292 4         11 my $newDir = $self->GetNewValue('Directory');
2293 4 50 33     15 if (defined $newDir and length $newDir) {
2294 0 0       0 $newDir .= '/' unless $newDir =~ m{/$};
2295             } else {
2296 4         9 undef $newDir;
2297             }
2298 4 100 33     26 if ($numNew == $numPseudo) {
    50          
2299 1         2 $rtnVal = 2;
2300 1 50 33     7 if ((defined $newFileName or defined $newDir) and not ref $infile) {
      33        
2301 1         4 my $result = $self->SetFileName($infile);
2302 1 50       4 if ($result > 0) {
    0          
2303 1         2 $infile = $$self{NewName}; # file name changed
2304 1         3 $rtnVal = 1;
2305             } elsif ($result < 0) {
2306 0         0 return 0; # don't try to do anything else
2307             }
2308             }
2309 1 50 33     4 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     3 $self->SetFileModifyDate($infile, undef, 'FileCreateDate') > 0 and $rtnVal = 1 if $setCreateDate;
2312 1 50       5 $self->SetSystemTags($infile) > 0 and $rtnVal = 1;
2313             }
2314 1 50 33     9 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         6 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       9 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     1571 if (ref $infile) {
    100          
    50          
2344 5         11 $inRef = $infile;
2345 5 100 33     48 if (UNIVERSAL::isa($inRef,'GLOB')) {
    50 33        
    50          
2346 1         7 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       636 $outfile = $tmpfile = "${infile}_exiftool_tmp" unless defined $outfile;
2366 215 50       1374 if ($self->Open(\*EXIFTOOL_FILE2, $infile)) {
2367 215         1147 $fileExt = GetFileExtension($infile);
2368 215         802 $fileType = GetFileType($infile);
2369 215         648 @fileTypeList = GetFileType($infile);
2370 215         556 $tiffType = $$self{FILE_EXT} = GetFileExtension($infile);
2371 215         1395 $self->VPrint(0, "Rewriting $infile...\n");
2372 215         429 $inRef = \*EXIFTOOL_FILE2;
2373 215         457 $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     233 $outType = GetFileExtension($outfile) unless $outType or ref $outfile;
2384 24 50       99 if (CanCreate($outType)) {
    0          
2385 24 50       217 if ($$self{OPTIONS}{WriteMode} =~ /g/i) {
2386 24         55 $fileType = $tiffType = $outType; # use output file type if no input file
2387 24         60 $infile = "$fileType file"; # make bogus file name
2388 24         149 $self->VPrint(0, "Creating $infile...\n");
2389 24         53 $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       788 unless (@fileTypeList) {
2403 30 100       81 if ($fileType) {
2404 24         73 @fileTypeList = ( $fileType );
2405             } else {
2406 6         131 @fileTypeList = @fileTypes;
2407 6         14 $tiffType = 'TIFF';
2408             }
2409             }
2410             #
2411             # set up output file
2412             #
2413 244 100       1624 if (ref $outfile) {
    100          
    50          
    50          
2414 13         23 $outRef = $outfile;
2415 13 50       50 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       57 defined $$outRef or $$outRef = '';
2421 13         27 $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       3 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         3 $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         820 $outRef = \*EXIFTOOL_OUTFILE;
2437 230         416 $closeOut = 1; # we must close $outRef
2438 230         699 binmode($outRef);
2439 230         489 $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         1189 until ($$self{VALUE}{Error}) {
2448             # create random access file object (disable seek test in case of straight copy)
2449 244 50       2828 $raf or $raf = File::RandomAccess->new($inRef, 1);
2450 244         1169 $raf->BinMode();
2451 244 100 33     2374 if ($numNew == $numPseudo) {
    50 66        
2452 1         3 $rtnVal = 1;
2453             # just do a straight copy of the file (no "real" tags are being changed)
2454 1         2 my $buff;
2455 1         4 while ($raf->Read($buff, 65536)) {
2456 1 50       7 Write($outRef, $buff) or $rtnVal = -1, last;
2457             }
2458 1         3 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         911 $raf->SeekTest();
2464             }
2465             # $raf->Debug() and warn " RAF debugging enabled!\n";
2466 243         819 my $inPos = $raf->Tell();
2467 243         1397 $$self{RAF} = $raf;
2468 243         1007 my %dirInfo = (
2469             RAF => $raf,
2470             OutFile => $outRef,
2471             );
2472 243 100       1105 $raf->Read($hdr, 1024) or $hdr = '';
2473 243 50       942 $raf->Seek($inPos, 0) or $seekErr = 1;
2474 243         502 my $wrongType;
2475 243         865 until ($seekErr) {
2476 278         611 $type = shift @fileTypeList;
2477             # do quick test to see if this is the right file type
2478 278 100 66     6464 if ($magicNumber{$type} and length($hdr) and $hdr !~ /^$magicNumber{$type}/s) {
      100        
2479 35 50       78 next if @fileTypeList;
2480 0         0 $wrongType = 1;
2481 0         0 last;
2482             }
2483             # save file type in member variable
2484 243         1948 $dirInfo{Parent} = $$self{FILE_TYPE} = $$self{PATH}[0] = $type;
2485             # determine which directories we must write for this file type
2486 243         1315 $self->InitWriteDirs($type);
2487 243 100 100     1638 if ($type eq 'JPEG' or $type eq 'EXV') {
    100 33        
    100          
    50          
    50          
2488 110         595 $rtnVal = $self->WriteJPEG(\%dirInfo);
2489             } elsif ($type eq 'TIFF') {
2490             # disallow writing of some TIFF-based RAW images:
2491 13 50       44 if (grep /^$tiffType$/, @{$noWriteFile{TIFF}}) {
  13         227  
2492 0         0 $fileType = $tiffType;
2493 0         0 undef $rtnVal;
2494             } else {
2495 13 50       45 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         46 $dirInfo{Parent} = $tiffType;
2500 13         86 $rtnVal = $self->ProcessTIFF(\%dirInfo);
2501             }
2502 0         0 } elsif (exists $writableType{$type}) {
2503 118         229 my ($module, $func);
2504 118 100       424 if (ref $writableType{$type} eq 'ARRAY') {
2505 91   66     376 $module = $writableType{$type}[0] || $type;
2506 91         213 $func = $writableType{$type}[1];
2507             } else {
2508 27   66     112 $module = $writableType{$type} || $type;
2509             }
2510 118         1411 require "Image/ExifTool/$module.pm";
2511 118   66     546 $func = "Image::ExifTool::${module}::" . ($func || "Process$type");
2512 61     61   541 no strict 'refs';
  61         137  
  61         3493  
2513 118         1041 $rtnVal = &$func($self, \%dirInfo);
2514 61     61   262 use strict 'refs';
  61         117  
  61         130994  
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         10 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
2520 2         13 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
2521 2 50       7 if (defined $buff) {
2522 2 50       6 $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       919 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       659 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         884 last; # (didn't really want to loop)
2577             }
2578             # don't return success code if any error occurred
2579 244 50       772 if ($rtnVal > 0) {
2580 244 50 66     957 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     1365 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       1032 $rtnVal = 0 if $$self{VALUE}{Error};
2597             }
2598              
2599             # rewrite original file in place if required
2600 244 100       685 if (defined $outBuff) {
2601 1 50 33     9 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         3 my $len = length($outBuff);
2605 1         1 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     22 eval { truncate($inRef, $len) }); # shorten output file
      33        
      33        
      33        
      33        
2613             } else {
2614 0         0 $$inRef = $outBuff; # replace original data
2615             }
2616 1         5 $outBuff = ''; # free memory but leave $outBuff defined
2617             }
2618             # close input file if we opened it
2619 244 100       631 if ($closeIn) {
2620             # errors on input file are significant if we edited the file in place
2621 215 50 0     4129 $rtnVal and $rtnVal = -1 unless close($inRef) or not defined $outBuff;
      33        
2622 215 50       646 if ($rtnVal > 0) {
2623             # copy Mac OS resource fork if it exists
2624 215 50 33     1235 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     609 $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       622 if ($closeOut) {
2657             # close file and set $rtnVal to -1 if there was an error
2658 230 50 0     12570 $rtnVal and $rtnVal = -1 unless close($outRef);
2659             # erase the output file if we weren't successful
2660 230 50       1152 if ($rtnVal <= 0) {
    100          
2661 0         0 $self->Unlink($outfile);
2662             # else rename temporary file if necessary
2663             } elsif ($tmpfile) {
2664 2         16 $self->CopyFileAttrs($infile, $tmpfile); # copy attributes to new file
2665 2 50       12 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       12 $outfile = $infile if $rtnVal > 0;
2678             }
2679             }
2680             # set filesystem attributes if requested (and if possible!)
2681 244 50 100     1482 if ($rtnVal > 0 and ($closeOut or (defined $outBuff and ($closeIn or UNIVERSAL::isa($infile,'GLOB'))))) {
      66        
2682 231 100       753 my $target = $closeOut ? $outfile : $infile;
2683             # set file permissions if requested
2684 231 50       1348 ++$$self{CHANGED} if $self->SetSystemTags($target) > 0;
2685 231 100       697 if ($closeIn) { # (no use setting file times unless the input file is closed)
2686 206 50 33     788 ++$$self{CHANGED} if $setModDate and $self->SetFileModifyDate($target, $originalTime, undef, 1) > 0;
2687             # set FileCreateDate if requested (and if possible!)
2688 206 50 33     1062 ++$$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     788 ++$$self{CHANGED} if defined $hardLink and $self->SetFileName($target, $hardLink, 'HardLink');
2691 206 50 33     855 ++$$self{CHANGED} if defined $symLink and $self->SetFileName($target, $symLink, 'SymLink');
2692 206 50       695 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       1080 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       863 ++$rtnVal unless $$self{CHANGED};
2701             }
2702             # set things back to the way they were
2703 244         567 $$self{RAF} = $oldRaf;
2704              
2705 244         2696 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   474 no warnings; # (avoid "possible attempt to put comments in qw()")
  61         114  
  61         5162  
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   286 use warnings;
  61         252  
  61         241913  
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 305 local $_;
2896 2         9 my ($tableName, %addTags) = @_;
2897 2 50       9 my $table = GetTagTable($tableName) or return 0;
2898             # add tags to writer lookup
2899 2         14 Image::ExifTool::TagLookup::AddTags(\%addTags, $tableName);
2900 2         3 my $tagID;
2901 2         5 my $num = 0;
2902 2         5 foreach $tagID (keys %addTags) {
2903 2 50       7 next if $specialTags{$tagID};
2904 2         6 delete $$table{$tagID}; # delete old entry if it existed
2905 2         13 AddTagToTable($table, $tagID, $addTags{$tagID}, 1);
2906 2         4 ++$num;
2907             }
2908 2         7 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 9154 my ($self, $valPt) = @_;
2929             # de-reference SCALAR references
2930 5467 50       10487 $$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     18868 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       13890 if ($$self{OPTIONS}{Escape}) {
2945             # (XMP.pm and HTML.pm were require'd as necessary when option was set)
2946 92 50       270 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         271 $$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 30288     30288 0 62039 my ($self, $val, $tagInfo, $tag, $wgrp1, $convType, $wantGroup) = @_;
2966 30288         38231 my ($err, $type);
2967              
2968 30288 100 50     44972 $convType or $convType = $$self{ConvType} || 'PrintConv';
2969              
2970 30288         35387 Conv: for (;;) {
2971 77192 100       120919 if (not defined $type) {
    100          
2972             # split value into list if necessary
2973 30288 100       54344 if ($$tagInfo{List}) {
2974 600   100     2334 my $listSplit = $$tagInfo{AutoSplit} || $$self{OPTIONS}{ListSplit};
2975 600 50 100     1983 if (defined $listSplit and not $$tagInfo{Struct} and
      66        
      100        
2976             ($wantGroup or not defined $wantGroup))
2977             {
2978 75 50 66     357 $listSplit = ',?\s+' if $listSplit eq '1' and $$tagInfo{AutoSplit};
2979 75         1036 my @splitVal = split /$listSplit/, $val, -1;
2980 75 50       353 $val = @splitVal > 1 ? \@splitVal : @splitVal ? $splitVal[0] : '';
    100          
2981             }
2982             }
2983 30288         38437 $type = $convType;
2984             } elsif ($type eq 'PrintConv') {
2985 22727         27115 $type = 'ValueConv';
2986             } else {
2987             # split raw value if necessary
2988 24177 50 66     49948 if ($$tagInfo{RawJoin} and $$tagInfo{List} and not ref $val) {
      33        
2989 13         50 my @splitVal = split ' ', $val;
2990 13 50       49 $val = \@splitVal if @splitVal > 1;
2991             }
2992             # finally, do our value check
2993 24177         29560 my ($err2, $v);
2994 24177 100       43798 if ($$tagInfo{WriteCheck}) {
2995             #### eval WriteCheck ($self, $tagInfo, $val)
2996 296         20942 $err2 = eval $$tagInfo{WriteCheck};
2997 296 50       1143 $@ and warn($@), $err2 = 'Error evaluating WriteCheck';
2998             }
2999 24177 100       38480 unless (defined $err2) {
3000 24135         33938 my $table = $$tagInfo{Table};
3001 24135 100 100     110811 if ($table and $$table{CHECK_PROC} and not $$tagInfo{RawConvInv}) {
      100        
3002 23193         30434 my $checkProc = $$table{CHECK_PROC};
3003 23193 100       35724 if (ref $val eq 'ARRAY') {
3004             # loop through array values
3005 47         89 foreach $v (@$val) {
3006 139         325 $err2 = &$checkProc($self, $tagInfo, \$v, $convType);
3007 139 50       307 last if $err2;
3008             }
3009             } else {
3010 23146         66710 $err2 = &$checkProc($self, $tagInfo, \$val, $convType);
3011             }
3012             }
3013             }
3014 24177 100       44571 if (defined $err2) {
3015 3816 100       5876 if ($err2) {
3016 3808         5593 $err = "$err2 for $wgrp1:$tag";
3017 3808         13022 $self->VPrint(2, "$err\n");
3018 3808         5105 undef $val; # value was invalid
3019             } else {
3020 8         12 $err = $err2; # empty error (quietly don't write tag)
3021             }
3022             }
3023 24177         32431 last;
3024             }
3025 53015         70689 my $conv = $$tagInfo{$type};
3026 53015         78421 my $convInv = $$tagInfo{"${type}Inv"};
3027             # nothing to do at this level if no conversion defined
3028 53015 100 100     107646 next unless defined $conv or defined $convInv;
3029              
3030 23860         29914 my (@valList, $index, $convList, $convInvList);
3031 23860 100 66     72302 if (ref $val eq 'ARRAY') {
    100          
3032             # handle ValueConv of ListSplit and AutoSplit values
3033 12         41 @valList = @$val;
3034 12         28 $val = $valList[$index = 0];
3035             } elsif (ref $conv eq 'ARRAY' or ref $convInv eq 'ARRAY') {
3036             # handle conversion lists
3037 153         1251 @valList = split /$listSep{$type}/, $val;
3038 153         340 $val = $valList[$index = 0];
3039 153 50       418 if (ref $conv eq 'ARRAY') {
3040 153         201 $convList = $conv;
3041 153         424 $conv = $$conv[0];
3042             }
3043 153 100       371 if (ref $convInv eq 'ARRAY') {
3044 30         49 $convInvList = $convInv;
3045 30         56 $convInv = $$convInv[0];
3046             }
3047             }
3048             # loop through multiple values if necessary
3049 23860         25406 for (;;) {
3050 23912 100       39848 if ($convInv) {
    100          
3051             # capture eval warnings too
3052 13995         49829 local $SIG{'__WARN__'} = \&SetWarning;
3053 13995         19881 undef $evalWarning;
3054 13995 100       20767 if (ref($convInv) eq 'CODE') {
3055 196         665 $val = &$convInv($val, $self);
3056             } else {
3057             #### eval PrintConvInv/ValueConvInv ($val, $self, $wantGroup)
3058 13799         853503 $val = eval $convInv;
3059 13799 100       45432 $@ and $evalWarning = $@;
3060             }
3061 13995 100       45437 if ($evalWarning) {
    100          
3062             # an empty warning ("\n") ignores tag with no error
3063 227 100       491 if ($evalWarning eq "\n") {
3064 10 50       30 $err = '' unless defined $err;
3065             } else {
3066 217         559 $err = CleanWarning() . " in $wgrp1:$tag (${type}Inv)";
3067 217         843 $self->VPrint(2, "$err\n");
3068             }
3069 227         357 undef $val;
3070 227         814 last Conv;
3071             } elsif (not defined $val) {
3072 137         318 $err = "Error converting value for $wgrp1:$tag (${type}Inv)";
3073 137         641 $self->VPrint(2, "$err\n");
3074 137         576 last Conv;
3075             }
3076             } elsif ($conv) {
3077 9914 100 66     35852 if (ref $conv eq 'HASH' and (not exists $$tagInfo{"${type}Inv"} or $convInvList)) {
    100 66        
3078 9603         13714 my ($multi, $lc);
3079             # insert alternate language print conversions if required
3080 9603 0 33     20022 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 9603         13423 undef $evalWarning;
3100 9603 100       17346 if ($$conv{BITMASK}) {
3101 105         192 my $lookupBits = $$conv{BITMASK};
3102 105         254 my ($wbits, $tbits) = @$tagInfo{'BitsPerWord','BitsTotal'};
3103 105         375 my ($val2, $err2) = EncodeBits($val, $lookupBits, $wbits, $tbits);
3104 105 100       259 if ($err2) {
    100          
3105             # ok, try matching a straight value
3106 2         14 ($val, $multi) = ReverseLookup($val, $conv);
3107 2 50       7 unless (defined $val) {
3108 2         6 $err = "Can't encode $wgrp1:$tag ($err2)";
3109 2         11 $self->VPrint(2, "$err\n");
3110 2         5 last Conv;
3111             }
3112             } elsif (defined $val2) {
3113 72         127 $val = $val2;
3114             } else {
3115 31         65 delete $$conv{BITMASK};
3116 31         61 ($val, $multi) = ReverseLookup($val, $conv);
3117 31         71 $$conv{BITMASK} = $lookupBits;
3118             }
3119             } else {
3120 9498         18478 ($val, $multi) = ReverseLookup($val, $conv);
3121             }
3122 9601 100       18816 if (not defined $val) {
    50          
3123 5457 100       13001 my $prob = $evalWarning ? lcfirst CleanWarning() : ($multi ? 'matches more than one ' : 'not in ') . $type;
    50          
3124 5457         9638 $err = "Can't convert $wgrp1:$tag ($prob)";
3125 5457         18266 $self->VPrint(2, "$err\n");
3126 5457         11436 last Conv;
3127             } elsif ($evalWarning) {
3128 0         0 $self->VPrint(2, CleanWarning() . " for $wgrp1:$tag\n");
3129             }
3130             } elsif (not $$tagInfo{WriteAlso}) {
3131 288         497 $err = "Can't convert value for $wgrp1:$tag (no ${type}Inv)";
3132 288         1149 $self->VPrint(2, "$err\n");
3133 288         514 undef $val;
3134 288         588 last Conv;
3135             }
3136             }
3137 17801 100       39323 last unless @valList;
3138 124         224 $valList[$index] = $val;
3139 124 100       273 if (++$index >= @valList) {
3140             # leave AutoSplit lists in ARRAY form, or join conversion lists
3141 72 100       334 $val = $$tagInfo{List} ? \@valList : join ' ', @valList;
3142 72         165 last;
3143             }
3144 52 100       103 $conv = $$convList[$index] if $convList;
3145 52 100       89 $convInv = $$convInvList[$index] if $convInvList;
3146 52         74 $val = $valList[$index];
3147             }
3148             } # end ValueConv/PrintConv loop
3149              
3150 30288         67486 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 27 local $_;
3159 21         42 my ($self, $val, $list, $missing) = @_;
3160 21 100 66     193 if (ref $val eq 'ARRAY' and ref $$val[0] ne 'HASH') {
    50 33        
    50          
    100          
3161 1         24 $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         3 my $mval = $$self{OPTIONS}{MissingTagValue};
3173 1 50 33     9 push @$list, $mval if $missing and defined $mval;
3174             } else {
3175 19         53 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 37 local $_;
3203 10         32 my ($self, $line, $foundTags, $opt, $docGrp, $cache) = @_;
3204 10         25 my $rtnStr = '';
3205 10         14 my ($docNum, $tag);
3206              
3207 10 50       34 if ($docGrp) {
3208 0 0       0 $docNum = $docGrp =~ /(\d+(-\d+)*)$/ ? $1 : 0;
3209             } else {
3210 10         18 undef $cache; # no cache if no document groups
3211             }
3212 10 50 0     5038 $foundTags or $foundTags = $$self{FOUND_TAGS} || [];
3213 10         102 while ($line =~ s/(.*?)\$(\{\s*)?([-\w]*\w|\$|\/)//s) {
3214 14         59 my ($pre, $bra, $var) = ($1, $2, $3);
3215 14         25 my (@tags, $tg, $val, @val, $type, $expr, $didExpr, $level, $asList);
3216             # "$$" represents a "$" symbol, and "$/" is a newline
3217 14 50 33     83 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         71 while ($line =~ /^:([-\w]*\w)(.*)/s) {
3229 9         15 my $group = $var;
3230 9         20 ($var, $line) = ($1, $2);
3231 9         26 $var = "$group:$var";
3232             }
3233             # allow trailing '#' to indicate ValueConv value
3234 14 50       44 $type = 'ValueConv' if $line =~ s/^#//;
3235             # special advanced formatting '@' feature to evaluate list values separately
3236 14 100 100     79 if ($bra and $line =~ s/^\@(#)?//) {
3237 2         2 $asList = 1;
3238 2 50       7 $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     133 if ($bra and $line !~ s/^\s*\}// and $line =~ s/^\s*;\s*(.*?)\s*\}//s) {
      66        
3243 4         8 my $part = $1;
3244 4         10 $expr = '';
3245 4         7 for ($level=0; ; --$level) {
3246             # increase nesting level for each opening brace
3247 8         24 ++$level while $part =~ /\{/g;
3248 8         14 $expr .= $part;
3249 8 100 66     31 last unless $level and $line =~ s/^(.*?)\s*\}//s; # get next part
3250 4         8 $part = $1;
3251 4         5 $expr .= '}'; # this brace was part of the expression
3252             }
3253             # use default Windows filename filter if expression is empty
3254 4 50       14 $expr = 'tr(/\\\\?*:|"<>\\0)()d' unless length $expr;
3255             }
3256 14         28 push @tags, $var;
3257 14         65 ExpandShortcuts(\@tags);
3258 14 50       33 @tags or $rtnStr .= $pre, next;
3259             # save advanced formatting expression to allow access by user-defined ValueConv
3260 14         40 $$self{FMT_EXPR} = $expr;
3261              
3262 14         19 for (;;) {
3263             # temporarily reset ListJoin option if evaluating list values separately
3264 14         39 my $oldListJoin;
3265 14 100       44 $oldListJoin = $self->Options(ListJoin => undef) if $asList;
3266 14         28 $tag = shift @tags;
3267 14         35 my $lcTag = lc $tag;
3268 14 50 33     47 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     39 if ($docGrp and $lcTag !~ /\b(main|doc\d+):/) {
3300 0         0 $tag = $docGrp . ':' . $tag;
3301 0         0 $lcTag = lc $tag;
3302             }
3303 14         34 my ($et, $fileTags) = ($self, $foundTags);
3304 14 100       59 if ($tag =~ s/(\bfile\d+)://i) {
3305 3         15 $et = $$self{ALT_EXIFTOOL}{ucfirst lc $1};
3306 3 50       11 if ($et) {
3307 3         8 $fileTags = $$et{FoundTags};
3308             } else {
3309 0         0 $et = $self;
3310 0         0 $tag = 'no_alt_file';
3311             }
3312             }
3313 14 50       113 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         8 my ($group, @matches);
3319 4         11 ($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       25 if ($group =~ s/(^|:)(all|\*)(:|$)/$1 and $3/ei) {
  1 100       11  
    50          
3323 1 50       4 if (lc $tag eq 'all') {
3324 1 50       9 @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         8 $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         680 @matches = grep /^$tag(\s|$)/i, @$fileTags;
3336 3         17 @matches = $et->GroupMatches($group, \@matches);
3337 3         7 foreach $tg (@matches) {
3338 3 50 33     10 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         10 $val = $et->GetValue($tg, $type);
3344 3         9 $tag = $tg;
3345 3 100       17 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         43 $val = $et->GetValue($tag, $type);
3354 10 100       29 unless (defined $val) {
3355             # check for tag name with different case
3356 7         653 ($tg) = grep /^$tag$/i, @$fileTags;
3357 7 50       24 if (defined $tg) {
3358 7         25 $val = $et->GetValue($tg, $type);
3359 7         16 $tag = $tg;
3360             }
3361             }
3362             }
3363             }
3364 14 100       45 $self->Options(ListJoin => $oldListJoin) if $asList;
3365 14         66 $self->PushValue($val, \@val, $asList);
3366 14         21 undef $val;
3367 14 50       60 last unless @tags;
3368             }
3369 14 50       31 if (@val) {
    0          
3370 14 50       35 $self->PushValue($val, \@val) if defined $val;
3371 14         55 $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     61 if (defined $expr and defined $val) {
3377 4         25 local $SIG{'__WARN__'} = \&SetWarning;
3378 4         8 undef $evalWarning;
3379 4         6 $advFmtSelf = $self; # set variable for access to $self in helper functions
3380 4 100       13 if ($asList) {
3381 2         4 foreach (@val) {
3382             #### eval advanced formatting expression ($_, $self, @val, $tag, $advFmtSelf)
3383 7         538 eval $expr;
3384 7 50       23 $@ and $evalWarning = $@;
3385             }
3386             # join back together if any values are still defined
3387 2         10 @val = grep defined, @val;
3388 2 50       12 $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         149 eval $expr;
3393 2 50       8 $@ and $evalWarning = $@;
3394 2 50       7 $val = ref $_ eq 'ARRAY' ? join($$self{OPTIONS}{ListSep}, @$_): $_;
3395             }
3396 4 50       12 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         6 undef $advFmtSelf;
3408 4         15 $didExpr = 1; # set flag indicating an expression was evaluated
3409             }
3410 14 0 0     40 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   492 no strict 'refs';
  61         132  
  61         42388  
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       31 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         81 $rtnStr .= "$pre$val";
3437             }
3438             }
3439 10         28 $$self{FMT_EXPR} = undef;
3440 10         39 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   462 no strict 'refs';
  61         118  
  61         13859  
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   356 no strict 'refs';
  61         140  
  61         143363  
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 25 my $self = shift;
3548 13         108 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 8 my ($self, $src, $dst) = @_;
3558 2         51 my ($mode, $uid, $gid) = (stat($src))[2, 4, 5];
3559             # copy file attributes unless we already set them
3560 2 50 33     17 if (defined $mode and not defined $self->GetNewValue('FilePermissions')) {
3561 2         4 eval { chmod($mode & 07777, $dst) };
  2         74  
3562             }
3563 2         9 my $newUid = $self->GetNewValue('FileUserID');
3564 2         6 my $newGid = $self->GetNewValue('FileGroupID');
3565 2 50 33     22 if (defined $uid and defined $gid and (not defined $newUid or not defined $newGid)) {
      33        
      33        
3566 2 50       9 defined $newGid and $gid = $newGid;
3567 2 50       7 defined $newUid and $uid = $newUid;
3568 2         5 eval { chown($uid, $gid, $dst) };
  2         32  
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 2 my ($oldName, $newName) = @_;
3580 1         7 my ($dir, $name) = ($oldName =~ m{(.*/)(.*)});
3581 1 50       4 ($dir, $name) = ('', $oldName) unless defined $dir;
3582 1 50       5 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         3 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 9598     9598 0 15044 my ($val, $conv) = @_;
3612 9598 100       14504 return undef unless defined $val;
3613 9536         9954 my $multi;
3614 9536 100       14597 if ($val =~ /^Unknown\s*\((.*)\)$/i) {
3615 40         93 $val = $1; # was unknown
3616 40 50       76 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 9496         11735 my $qval = $val;
3623 9496         16085 $qval =~ s/\s+$//; # remove trailing whitespace
3624 9496         11926 $qval = quotemeta $qval;
3625 9496         26310 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 9496         11711 my ($pattern, $found, $matches);
3633 9496         11970 PAT: foreach $pattern (@patterns) {
3634 24799         426986 $matches = scalar grep /$pattern/, values(%$conv);
3635 24799 100       40278 next unless $matches;
3636             # multiple matches are bad unless they were exact
3637 7450 100 100     19288 if ($matches > 1 and $pattern !~ /\$$/) {
3638             # don't match entries that we should ignore
3639 3910         7760 foreach (keys %ignorePrintConv) {
3640 11730 100 100     20218 --$matches if defined $$conv{$_} and $$conv{$_} =~ /$pattern/;
3641             }
3642 3910 100       8082 last if $matches > 1;
3643             }
3644 3672         50783 foreach (sort keys %$conv) {
3645 10820 100 100     36483 next if $$conv{$_} !~ /$pattern/ or $ignorePrintConv{$_};
3646 3440         4445 $val = $_;
3647 3440         3944 $found = 1;
3648 3440         5402 last PAT;
3649             }
3650             }
3651 9496 100       19907 unless ($found) {
3652             # call OTHER conversion routine if available
3653 6056 100       11365 if ($$conv{OTHER}) {
3654 808         3244 local $SIG{'__WARN__'} = \&SetWarning;
3655 808         1125 undef $evalWarning;
3656 808         949 $val = &{$$conv{OTHER}}($val,1,$conv);
  808         2538  
3657             } else {
3658 5248         5903 $val = undef;
3659             }
3660 6056 100       12847 $multi = 1 if $matches > 1;
3661             }
3662             }
3663 9536 100       26740 return ($val, $multi) if wantarray;
3664 47         114 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 6725     6725 0 10543 my ($self, $nvHash, $val) = @_;
3679 6725 100       12099 return 0 unless $nvHash;
3680             # overwrite regardless if no DelValues specified
3681 6683 100       18646 return 1 unless $$nvHash{DelValue};
3682             # never overwrite if DelValue list exists but is empty
3683 117         205 my $shift = $$nvHash{Shift};
3684 117 100 100     141 return 0 unless @{$$nvHash{DelValue}} or defined $shift;
  117         374  
3685             # return "don't know" if we don't have a value to test
3686 104 100       309 return -1 unless defined $val;
3687             # apply raw conversion if necessary
3688 46         92 my $tagInfo = $$nvHash{TagInfo};
3689 46         107 my $conv = $$tagInfo{RawConv};
3690 46 100       90 if ($conv) {
3691 3         19 local $SIG{'__WARN__'} = \&SetWarning;
3692 3         6 undef $evalWarning;
3693 3 50       14 if (ref $conv eq 'CODE') {
3694 0         0 $val = &$conv($val, $self);
3695             } else {
3696 3         3 my ($priority, @grps);
3697 3         29 my $tag = $$tagInfo{Name};
3698             #### eval RawConv ($self, $val, $tag, $tagInfo, $priority, @grps)
3699 3         280 $val = eval $conv;
3700 3 50       17 $@ and $evalWarning = $@;
3701             }
3702 3 50       14 return -1 unless defined $val;
3703             }
3704             # do not overwrite if only creating
3705 46 100       121 return 0 if $$nvHash{CreateOnly};
3706             # apply time/number shift if necessary
3707 40 100       82 if (defined $shift) {
3708 13         40 my $shiftType = $$tagInfo{Shift};
3709 13 100 66     52 unless ($shiftType and $shiftType eq 'Time') {
3710 6 50       19 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         14 $shiftType = 'Number'; # allow any number to be shifted
3734             }
3735 13         87 require 'Image/ExifTool/Shift.pl';
3736 13         61 my $err = $self->ApplyShift($shiftType, $shift, $val, $nvHash);
3737 13 50       56 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         54 my $checkVal = $self->GetNewValue($nvHash);
3743 13 50       31 return 0 unless defined $checkVal;
3744             # don't bother overwriting if value is the same
3745 13 50       41 return 0 if $val eq $$nvHash{Value}[0];
3746 13         50 return 1;
3747             }
3748             # return 1 if value matches a DelValue
3749 27         34 my $delVal;
3750 27         37 foreach $delVal (@{$$nvHash{DelValue}}) {
  27         52  
3751 32 100       85 return 1 if $val eq $delVal;
3752             }
3753 17         39 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 34707     34707 0 61988 my ($self, $tagInfo, $writeGroup) = @_;
3772 34707 100       116913 return $writeGroup unless $writeGroup =~ /^(MakerNotes|XMP|Composite|QuickTime)$/;
3773 29029         76323 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 20 my ($self, $wantGroup, $writeGPS) = @_;
3784 5 100       33 my @grps = $wantGroup ? map lc, split(/:/, $wantGroup) : ();
3785 5         19 my %grps = map { $_ => $_ } @grps; # lookup for specified groups
  4         23  
3786 5 50 33     27 $grps{exif} and not $grps{gps} and $grps{gps} = 'gps', push(@grps, 'gps');
3787 5         138 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         11 my (@tags, $grp);
3800             # set specific City and GPS tags
3801 5         14 foreach $grp (@grps) {
3802 4 100       17 $tagGroups{$grp} and push @tags, map("$grp:$_", @{$tagGroups{$grp}});
  2         19  
3803             }
3804             # set default XMP City tags if necessary
3805 5 100       16 if (not $writeGPS) {
3806 4 50       13 push @tags, 'Keys:LocationName' if $grps{'keys'};
3807 4 100 66     48 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       14 $writeGPS = 1 unless defined $writeGPS; # (delete both City and GPS)
3812 5 50 66     24 push @tags, 'Keys:GPSCoordinates' if $writeGPS and $grps{'keys'};
3813             # set default QuickTime tag if necessary
3814 5         24 my $didQT = grep /GPSCoordinates$/, @tags;
3815 5 100 33     78 if (($grps{quicktime} and not $didQT) or ($writeGPS and not @tags and not $grps{xmp})) {
      100        
      66        
      66        
3816 1         3 push @tags, 'QuickTime:GPSCoordinates';
3817             }
3818             # set default GPS tags if necessary
3819 5 100       25 if ($writeGPS) {
3820 2 50 33     12 push @tags, qw(XMP:GPSLatitude XMP:GPSLongitude) if $grps{xmp} and not $grps{'xmp-exif'};
3821 2 100       20 push @tags, qw(GPSLatitude GPSLongitude GPSLatitudeRef GPSLongitudeRef) if not $wantGroup;
3822             }
3823 5         145 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 70016     70016 0 122531 my ($self, $tagInfo, $writeGroup, $opts) = @_;
3837 70016 100       96967 return undef unless $tagInfo;
3838 70012         122020 my $nvHash = $$self{NEW_VALUE}{$tagInfo};
3839              
3840 70012         73831 my %opts; # quick lookup for options
3841 70012 100       113341 $opts and $opts{$opts} = 1;
3842 70012 100       101605 $writeGroup = '' unless defined $writeGroup;
3843              
3844 70012 100       91491 if ($writeGroup) {
3845             # find the new value in the list with the specified write group
3846 47439   100     88926 while ($nvHash and $$nvHash{WriteGroup} ne $writeGroup) {
3847             # QuickTime and All are special cases because all group1 tags may be updated at once
3848 2001 100       4258 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 1965 100 33     4255 last if $writeGroup eq 'All' or $$nvHash{WriteGroup} eq 'EXIF' and $writeGroup =~ /IFD/;
      66        
3853 1962         3287 $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 70012 100 100     138375 if (defined $nvHash and ($opts{'delete'} or ($opts{'create'} and $$nvHash{Save}))) {
      100        
3859 2402   33     6363 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     7137 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         10094 $self->RemoveNewValueHash($nvHash, $tagInfo);
3871 2392         7853 undef $nvHash;
3872             } else {
3873             # save a copy of this new value hash
3874 10         114 my %copy = %$nvHash;
3875             # make copy of Value and DelValue lists
3876 10         17 my $key;
3877 10         28 foreach $key (keys %copy) {
3878 85 100       122 next unless ref $copy{$key} eq 'ARRAY';
3879 10         11 $copy{$key} = [ @{$copy{$key}} ];
  10         73  
3880             }
3881 10         20 my $saveHash = $$self{SAVE_NEW_VALUE};
3882             # add to linked list of saved new value hashes
3883 10         23 $copy{Next} = $$saveHash{$tagInfo};
3884 10         21 $$saveHash{$tagInfo} = \%copy;
3885 10         15 delete $$nvHash{Save}; # don't save it again
3886 10 0 33     25 $$nvHash{AddBefore} = scalar @{$$nvHash{Value}} if $protect and $$nvHash{Value};
  0         0  
3887             }
3888             }
3889 70012 100 100     153353 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 23923         96588 Order => $$self{NV_COUNT}++,
3896             };
3897             # add entry to our NEW_VALUE hash
3898 23923 100       44205 if ($$self{NEW_VALUE}{$tagInfo}) {
3899             # add to end of linked list
3900 36         136 my $lastHash = LastInList($$self{NEW_VALUE}{$tagInfo});
3901 36         95 $$lastHash{Next} = $nvHash;
3902             } else {
3903 23887         51192 $$self{NEW_VALUE}{$tagInfo} = $nvHash;
3904             }
3905             }
3906 70012         119377 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   462 no strict 'refs';
  61         486  
  61         172998  
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 4200 my ($self, $nvHash, $tagInfo) = @_;
3959 2665         4454 my $firstHash = $$self{NEW_VALUE}{$tagInfo};
3960 2665 50       5255 if ($nvHash eq $firstHash) {
3961             # remove first entry from linked list
3962 2665 50       4799 if ($$nvHash{Next}) {
3963 0         0 $$self{NEW_VALUE}{$tagInfo} = $$nvHash{Next};
3964             } else {
3965 2665         5535 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       6507 if ($$nvHash{Save}) {
3975 79         133 my $saveHash = $$self{SAVE_NEW_VALUE};
3976             # add to linked list of saved new value hashes
3977 79         190 $$nvHash{Next} = $$saveHash{$tagInfo};
3978 79         227 $$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 1116 my ($self, $group) = @_;
3988              
3989 955 100       1444 return unless $$self{NEW_VALUE};
3990              
3991             # make list of all groups we must remove
3992 11         27 my @groups = ( $group );
3993 11 100       41 push @groups, @{$removeGroups{$group}} if $removeGroups{$group};
  3         11  
3994              
3995 11         23 my ($out, @keys, $hashKey);
3996 11 50       41 $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose} > 1;
3997              
3998             # loop though all new values, and remove any in this group
3999 11         19 @keys = keys %{$$self{NEW_VALUE}};
  11         769  
4000 11         38 foreach $hashKey (@keys) {
4001 2009         3295 my $nvHash = $$self{NEW_VALUE}{$hashKey};
4002             # loop through each entry in linked list
4003 2009         2027 for (;;) {
4004 2015         2641 my $nextHash = $$nvHash{Next};
4005 2015         2732 my $tagInfo = $$nvHash{TagInfo};
4006 2015         3190 my ($grp0,$grp1) = $self->GetGroup($tagInfo);
4007 2015         3213 my $wgrp = $$nvHash{WriteGroup};
4008             # use group1 if write group is not specific
4009 2015 100       2893 $wgrp = $grp1 if $wgrp eq $grp0;
4010 2015 100 33     37113 if ($grp0 eq '*' or $wgrp eq '*' or grep /^($grp0|$wgrp)$/i, @groups) {
      66        
4011 273 50       515 $out and print $out "Removed new value for $wgrp:$$tagInfo{Name}\n";
4012             # remove from linked list
4013 273         582 $self->RemoveNewValueHash($nvHash, $tagInfo);
4014             }
4015 2015 100       5028 $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 2486 my ($self, $tagTablePtr) = @_;
4027 1257         1560 my @tagInfoList;
4028 1257         2413 my $nv = $$self{NEW_VALUE};
4029 1257 100       2538 if ($nv) {
4030 1236         1411 my $hashKey;
4031 1236         22144 foreach $hashKey (keys %$nv) {
4032 92030         135623 my $tagInfo = $$nv{$hashKey}{TagInfo};
4033 92030 100 100     193932 next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table};
4034 33567         39469 push @tagInfoList, $tagInfo;
4035             }
4036             }
4037 1257         14085 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 781 my $self = shift;
4048 585         767 my (%tagInfoHash, $hashKey);
4049 585         1031 my $nv = $$self{NEW_VALUE};
4050 585         1087 while ($nv) {
4051 1157   100     2137 my $tagTablePtr = shift || last;
4052 581         5012 foreach $hashKey (keys %$nv) {
4053 22328         29771 my $tagInfo = $$nv{$hashKey}{TagInfo};
4054 22328 100 66     55842 next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table};
4055 314         934 $tagInfoHash{$$tagInfo{TagID}} = $tagInfo;
4056             }
4057             }
4058 585         1484 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 1112 my ($self, $tagTablePtr, $parent) = @_;
4070 516 100       1154 $parent or $parent = $$tagTablePtr{GROUPS}{0};
4071 516         1246 my $tagID;
4072             my %addDirHash;
4073 516         0 my %editDirHash;
4074 516         963 my $addDirs = $$self{ADD_DIRS};
4075 516         980 my $editDirs = $$self{EDIT_DIRS};
4076 516         1768 foreach $tagID (TagTableKeys($tagTablePtr)) {
4077 181585         210081 my @infoArray = GetTagInfoList($tagTablePtr,$tagID);
4078 181585         165351 my $tagInfo;
4079 181585         176102 foreach $tagInfo (@infoArray) {
4080 219953 100       333670 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         49179 my $dirName = $$tagInfo{SubDirectory}{DirName};
4085 38658 100       44676 unless ($dirName) {
4086             # use tag name for directory name and save for next time
4087 4262         5511 $dirName = $$tagInfo{Name};
4088 4262         5288 $$tagInfo{SubDirectory}{DirName} = $dirName;
4089             }
4090             # save this directory information if we are writing it
4091 38658 100 100     62729 if ($$editDirs{$dirName} and $$editDirs{$dirName} eq $parent) {
4092 287         829 $editDirHash{$tagID} = $tagInfo;
4093 287 100       1053 $addDirHash{$tagID} = $tagInfo if $$addDirs{$dirName};
4094             }
4095             }
4096             }
4097 516 100       7924 return (\%addDirHash, \%editDirHash) if wantarray;
4098 438         1798 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 502 my ($tagInfo, $langCode) = @_;
4109             # make a new tagInfo hash for this locale
4110 335         533 my $table = $$tagInfo{Table};
4111 335         620 my $tagID = $$tagInfo{TagID} . '-' . $langCode;
4112 335         613 my $langInfo = $$table{$tagID};
4113 335 100       588 unless ($langInfo) {
4114             # make a new tagInfo entry for this locale
4115             $langInfo = {
4116             %$tagInfo,
4117             Name => $$tagInfo{Name} . '-' . $langCode,
4118 192         769 Description => Image::ExifTool::MakeDescription($$tagInfo{Name}) .
4119             " ($langCode)",
4120             LangCode => $langCode,
4121             SrcTagInfo => $tagInfo, # save reference to original tagInfo
4122             };
4123 192         498 AddTagToTable($table, $tagID, $langInfo);
4124             }
4125 335         632 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 862 my ($self, $fileType, $preferredGroup, $altGroup) = @_;
4140 332         1244 my $editDirs = $$self{EDIT_DIRS} = { };
4141 332         1244 my $addDirs = $$self{ADD_DIRS} = { };
4142 332         908 my $fileDirs = $dirMap{$fileType};
4143 332 100       968 unless ($fileDirs) {
4144 203 100       622 return unless ref $fileType eq 'HASH';
4145 85         145 $fileDirs = $fileType;
4146             }
4147 214         1009 my @tagInfoList = $self->GetNewTagInfoList();
4148 214         439 my ($tagInfo, $nvHash);
4149              
4150             # save the preferred group
4151 214         655 $$self{PreferredGroup} = $preferredGroup;
4152              
4153 214         419 foreach $tagInfo (@tagInfoList) {
4154             # cycle through all hashes in linked list
4155 13312         16763 for ($nvHash=$self->GetNewValueHash($tagInfo); $nvHash; $nvHash=$$nvHash{Next}) {
4156             # are we creating this tag? (otherwise just deleting or editing it)
4157 13340         16950 my $isCreating = $$nvHash{IsCreating};
4158 13340 100       16457 if ($preferredGroup) {
4159 3605         5202 my $g0 = $self->GetGroup($tagInfo, 0);
4160 3605 100       4531 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 805 100 100     2528 $$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 2800 50 100     7188 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 13340         18107 my $dirName = $$nvHash{WriteGroup};
4177             # remove MIE copy number(s) if they exist
4178 13340 100       20757 if ($dirName =~ /^MIE\d*(-[a-z]+)?\d*$/i) {
4179 398   50     1546 $dirName = 'MIE' . ($1 || '');
4180             }
4181 13340         12322 my @dirNames;
4182             # allow a group name of '*' to force writing EXIF/IPTC/XMP/PNG (ForceWrite tag)
4183 13340 50 33     23490 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         169 $dirName = $self->GetGroup($tagInfo, 1);
4197             }
4198 13340         16096 while ($dirName) {
4199 53850         56050 my $parent = $$fileDirs{$dirName};
4200 53850 100       61339 if (ref $parent) {
4201 6476         7964 push @dirNames, reverse @$parent;
4202 6476         6465 $parent = pop @dirNames;
4203             }
4204 53850         55714 $$editDirs{$dirName} = $parent;
4205 53850 100 100     70864 $$addDirs{$dirName} = $parent if $isCreating and $isCreating != 2;
4206 53850   100     95104 $dirName = $parent || shift @dirNames
4207             }
4208             }
4209             }
4210 214 100       376 if (%{$$self{DEL_GROUP}}) {
  214         877  
4211             # add delete groups to list of edited groups
4212 39         71 foreach (keys %{$$self{DEL_GROUP}}) {
  39         321  
4213 1036 100       1458 next if /^-/; # ignore excluded groups
4214 1034         987 my $dirName = $_;
4215             # translate necessary group 0 names
4216 1034 100       1376 $dirName = $translateWriteGroup{$dirName} if $translateWriteGroup{$dirName};
4217             # convert XMP group 1 names
4218 1034 100       1353 $dirName = 'XMP' if $dirName =~ /^XMP-/;
4219 1034         966 my @dirNames;
4220 1034         1245 while ($dirName) {
4221 1484         1597 my $parent = $$fileDirs{$dirName};
4222 1484 100       1774 if (ref $parent) {
4223 17         37 push @dirNames, reverse @$parent;
4224 17         24 $parent = pop @dirNames;
4225             }
4226 1484         1820 $$editDirs{$dirName} = $parent;
4227 1484   100     2986 $dirName = $parent || shift @dirNames
4228             }
4229             }
4230             }
4231             # special case to edit JFIF to get resolutions if editing EXIF information
4232 214 100 100     1158 if ($$editDirs{IFD0} and $$fileDirs{JFIF}) {
4233 88         340 $$editDirs{JFIF} = 'IFD1';
4234 88         274 $$editDirs{APP0} = undef;
4235             }
4236              
4237 214 100       2496 if ($$self{OPTIONS}{Verbose}) {
4238 2         5 my $out = $$self{OPTIONS}{TextOut};
4239 2         7 print $out " Editing tags in: ";
4240 2         11 foreach (sort keys %$editDirs) { print $out "$_ "; }
  13         18  
4241 2         5 print $out "\n";
4242 2 50       7 return unless $$self{OPTIONS}{Verbose} > 1;
4243 2         5 print $out " Creating tags in: ";
4244 2         6 foreach (sort keys %$addDirs) { print $out "$_ "; }
  8         12  
4245 2         8 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 5433 my ($self, $dirInfo, $tagTablePtr, $writeProc) = @_;
4257 1947         2744 my ($out, $nvHash, $delFlag);
4258              
4259 1947 50       3244 $tagTablePtr or return undef;
4260 1947 100       5516 $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose};
4261             # set directory name from default group0 name if not done already
4262 1947         3459 my $dirName = $$dirInfo{DirName};
4263 1947   100     4362 my $parent = $$dirInfo{Parent} || '';
4264 1947         2933 my $dataPt = $$dirInfo{DataPt};
4265 1947         4628 my $grp0 = $$tagTablePtr{GROUPS}{0};
4266 1947 100       4127 $dirName or $dirName = $$dirInfo{DirName} = $grp0;
4267 1947 100       2392 if (%{$$self{DEL_GROUP}}) {
  1947         4719  
4268 287         436 my $delGroup = $$self{DEL_GROUP};
4269             # delete entire directory if specified
4270 287         394 my $grp1 = $dirName;
4271 287   100     885 $delFlag = ($$delGroup{$grp0} or $$delGroup{$grp1});
4272 287 100 66     1351 if ($permanentDir{$grp0} and not ($$dirInfo{TagInfo} and $$dirInfo{TagInfo}{Deletable})) {
      66        
4273 186         234 undef $delFlag;
4274             }
4275 287 100       650 if ($delFlag) {
4276 42 50 100     536 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     109 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       104 if ($grp1) {
4306 42 100 66     177 if ($dataPt or $$dirInfo{RAF}) {
4307 32         60 ++$$self{CHANGED};
4308 32 100       76 $out and print $out " Deleting $grp1\n";
4309 32 100       88 $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       116 delete $$self{TIFF_END} if $dirName =~ /IFD/;
4312             }
4313             # don't add back into the wrong location
4314 42         98 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     151 $right = $$self{ADD_DIRS}{IFD0} if not $right and $grp1 eq 'EXIF';
4318 42 100 100     169 if ($delFlag == 2 and $right) {
4319             # also check grandparent because some routines create 2 levels in 1
4320 21   100     107 my $right2 = $$self{ADD_DIRS}{$right} || '';
4321 21 50 66     110 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         78  
4324 21 100       76 $$self{Recreated} or $$self{Recreated} = { };
4325 21 50       58 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         56 $$self{Recreated}{$path} = 1;
4331             # empty the directory
4332 21         56 my $data = '';
4333 21         48 $$dirInfo{DataPt} = \$data;
4334 21         35 $$dirInfo{DataLen} = 0;
4335 21         46 $$dirInfo{DirStart} = 0;
4336 21         48 $$dirInfo{DirLen} = 0;
4337 21         46 delete $$dirInfo{RAF};
4338 21         38 delete $$dirInfo{Base};
4339 21         54 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       118 return '' unless $$dirInfo{NoDelete};
4346             }
4347             }
4348             }
4349             }
4350             # use default proc from tag table if no proc specified
4351 1927 100 100     7382 $writeProc or $writeProc = $$tagTablePtr{WRITE_PROC} or return undef;
4352              
4353             # are we rewriting a pre-existing directory?
4354 1634   100     5418 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         2288 my $blockName = $dirName;
4358 1634 100       3304 $blockName = 'EXIF' if $blockName eq 'IFD0';
4359 1634   100     5518 my $tagInfo = $Image::ExifTool::Extra{$blockName} || $$dirInfo{TagInfo};
4360 1634   100     7669 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       38 if ($blockName eq 'EXIF') {
4365 1 50       5 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       6 last unless $writeProc eq \&Image::ExifTool::WriteTIFF;
4374             }
4375 11 100       45 last unless $self->IsOverwriting($nvHash, $dataPt ? $$dataPt : '');
    50          
4376 11         22 my $verb = 'Writing';
4377 11         48 my $newVal = $self->GetNewValue($nvHash);
4378 11 50 33     73 if (defined $newVal and length $newVal) {
4379             # hack to add back TIFF header when writing MakerNoteCanon to CMT3 in CR3 images
4380 11 50       46 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         35 $$dirInfo{BlockWrite} = 1; # set flag indicating we did a block write
4406 11 50       42 $out and print $out " $verb $blockName as a block\n";
4407 11         25 ++$$self{CHANGED};
4408 11         42 return $newVal;
4409             }
4410             # guard against writing the same directory twice
4411 1623 100 100     8442 if (defined $dataPt and defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos} and
      100        
      100        
4412             not $$dirInfo{NoRefTest})
4413             {
4414 693   100     2625 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     2366 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         2287 $$self{PROCESSED}{$addr} = $dirName;
4432             }
4433             }
4434 1623         2923 my $oldDir = $$self{DIR_NAME};
4435 1623         4240 my @save = @$self{'Compression','SubfileType'};
4436 1623         2040 my $name;
4437 1623 100       3037 if ($out) {
4438             $name = ($dirName eq 'MakerNotes' and $$dirInfo{TagInfo}) ?
4439 4 50 33     18 $$dirInfo{TagInfo}{Name} : $dirName;
4440 4 100 100     16 if (not defined $oldDir or $oldDir ne $name) {
4441 3 100       8 my $verb = $isRewriting ? 'Rewriting' : 'Creating';
4442 3         10 print $out " $verb $name\n";
4443             }
4444             }
4445 1623         3646 my $saveOrder = GetByteOrder();
4446 1623         2755 my $oldChanged = $$self{CHANGED};
4447 1623         2863 $$self{DIR_NAME} = $dirName;
4448 1623         1972 push @{$$self{PATH}}, $dirName;
  1623         3484  
4449 1623         2966 $$dirInfo{IsWriting} = 1;
4450 1623         1873 my $newData;
4451             {
4452 61     61   472 no strict 'refs';
  61         128  
  61         1200771  
  1623         1763  
4453 1623         9198 $newData = &$writeProc($self, $dirInfo, $tagTablePtr);
4454             }
4455 1623         2432 pop @{$$self{PATH}};
  1623         3212  
4456             # nothing changed if error occurred or nothing was created
4457 1623 100 100     5808 $$self{CHANGED} = $oldChanged unless defined $newData and (length($newData) or $isRewriting);
      100        
4458 1623         3105 $$self{DIR_NAME} = $oldDir;
4459 1623         3732 @$self{'Compression','SubfileType'} = @save;
4460 1623         4280 SetByteOrder($saveOrder);
4461 1623 100       3108 if ($out) {
4462 4 50 33     21 print $out " Deleting $name\n" if defined $newData and not length $newData;
4463 4 50 33     15 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         5832 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 18 my ($dataPt, $pos) = @_;
4476 12 50       54 my $pt = GetByteOrder() eq 'MM' ? 0 : 4; # get position of high word
4477 12         24 my $hi = Get32s($dataPt, $pos + $pt); # preserve sign bit of high word
4478 12         24 my $lo = Get32u($dataPt, $pos + 4 - $pt);
4479 12         23 return $hi * 4294967296 + $lo;
4480             }
4481             sub Get64u($$)
4482             {
4483 197     197 0 348 my ($dataPt, $pos) = @_;
4484 197 100       354 my $pt = GetByteOrder() eq 'MM' ? 0 : 4; # get position of high word
4485 197         421 my $hi = Get32u($dataPt, $pos + $pt); # (unsigned this time)
4486 197         423 my $lo = Get32u($dataPt, $pos + 4 - $pt);
4487 197         520 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 3 my ($dataPt, $pos) = @_;
4501 1 50       3 my $pt = GetByteOrder() eq 'MM' ? 0 : 2; # get position of exponent
4502 1         4 my $exp = Get16u($dataPt, $pos + $pt);
4503 1         3 my $sig = Get64u($dataPt, $pos + 2 - $pt); # get significand as int64u
4504 1 50       3 my $sign = $exp & 0x8000 ? -1 : 1;
4505 1         1 $exp = ($exp & 0x7fff) - 16383 - 63; # (-63 to fractionalize significand)
4506 1         22 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 243 my $dataPt = shift;
4524 169         209 my $len = shift;
4525 169         732 my %opts = @_;
4526 169   100     310 my $start = $opts{Start} || 0;
4527 169         229 my $addr = $opts{Addr};
4528 169   50     382 my $wid = $opts{Width} || 16;
4529 169   50     338 my $prefix = $opts{Prefix} || '';
4530 169   50     320 my $out = $opts{Out} || \*STDOUT;
4531 169         192 my $maxLen = $opts{MaxLen};
4532 169         210 my $datLen = length($$dataPt) - $start;
4533 169         215 my $more;
4534 169 50       278 $len = $opts{Len} if defined $opts{Len};
4535              
4536 169 100 50     451 $addr = $start + ($opts{DataPos} || 0) + ($opts{Base} || 0) unless defined $addr;
      50        
4537 169 100       260 $len = $datLen unless defined $len;
4538 169 100 66     471 if ($maxLen and $len > $maxLen) {
4539             # print one line less to allow for $more line below
4540 5         15 $maxLen = int(($maxLen - 1) / $wid) * $wid;
4541 5         6 $more = $len - $maxLen;
4542 5         10 $len = $maxLen;
4543             }
4544 169 50       252 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         343 my $format = sprintf("%%-%ds", $wid * 3);
4550 169         309 my $tmpl = 'H2' x $wid; # ('(H2)*' would have been nice, but older perl versions don't support it)
4551 169         208 my $i;
4552 169         295 for ($i=0; $i<$len; $i+=$wid) {
4553 228 100       403 $wid > $len-$i and $wid = $len-$i, $tmpl = 'H2' x $wid;
4554 228         603 printf $out "$prefix%8.4x: ", $addr+$i;
4555 228         396 my $dat = substr($$dataPt, $i+$start, $wid);
4556 228         837 my $s = join(' ', unpack($tmpl, $dat));
4557 228         696 printf $out $format, $s;
4558 228         332 $dat =~ tr /\x00-\x1f\x7f-\xff/./;
4559 228         466 print $out "[$dat]\n";
4560             }
4561 169 100       861 $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 2760 my ($self, $tagID, $tagInfo, %parms) = @_;
4584 617         1111 my $verbose = $$self{OPTIONS}{Verbose};
4585 617         954 my $out = $$self{OPTIONS}{TextOut};
4586 617         781 my ($tag, $line, $hexID);
4587              
4588             # generate hex number if tagID is numerical
4589 617 100       954 if (defined $tagID) {
4590 578 100       2703 $tagID =~ /^\d+$/ and $hexID = sprintf("0x%.4x", $tagID);
4591             } else {
4592 39         51 $tagID = 'Unknown';
4593             }
4594             # get tag name
4595 617 50 33     1963 if ($tagInfo and $$tagInfo{Name}) {
    0          
4596 617         988 $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         845 my $dataPt = $parms{DataPt};
4611 617         828 my $size = $parms{Size};
4612 617 50 66     1372 $size = length $$dataPt unless defined $size or not $dataPt;
4613 617         974 my $indent = $$self{INDENT};
4614              
4615             # Level 1: print tag/value information
4616 617         662 $line = $indent;
4617 617         792 my $index = $parms{Index};
4618 617 100       1055 if (defined $index) {
4619 365         495 $line .= $index . ') ';
4620 365 100       646 $line .= ' ' if length($index) < 2;
4621 365         448 $indent .= ' '; # indent everything else to align with tag name
4622             }
4623 617         887 $line .= $tag;
4624 617 100 66     1565 if ($tagInfo and $$tagInfo{SubDirectory}) {
4625 39         50 $line .= ' (SubDirectory) -->';
4626             } else {
4627 578         706 my $maxLen = 90 - length($line);
4628 578         834 my $val = $parms{Value};
4629 578 50       943 if (defined $val) {
    0          
4630 578 50       984 $val = '[' . join(',',@$val) . ']' if ref $val eq 'ARRAY';
4631 578         1411 $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         1440 print $out "$line\n";
4638              
4639             # Level 2: print detailed information about the tag
4640 617 50 66     2364 if ($verbose > 1 and ($parms{Extra} or $parms{Format} or
      66        
4641             $parms{DataPt} or defined $size or $tagID =~ /\//))
4642             {
4643 390         541 $line = $indent . '- Tag ';
4644 390 100       639 if ($hexID) {
4645 389         537 $line .= $hexID;
4646             } else {
4647 1         4 $tagID =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/ge;
  0         0  
4648 1         2 $line .= "'${tagID}'";
4649             }
4650 390 100       687 $line .= $parms{Extra} if defined $parms{Extra};
4651 390         575 my $format = $parms{Format};
4652 390 50 66     791 if ($format or defined $size) {
4653 390         421 $line .= ' (';
4654 390 50       639 if (defined $size) {
4655 390         524 $line .= "$size bytes";
4656 390 100       637 $line .= ', ' if $format;
4657             }
4658 390 100       595 if ($format) {
4659 352         497 $line .= $format;
4660 352 50       833 $line .= '['.$parms{Count}.']' if $parms{Count};
4661             }
4662 390         437 $line .= ')';
4663             }
4664 390 50 66     822 $line .= ':' if $verbose > 2 and $parms{DataPt};
4665 390         703 print $out "$line\n";
4666             }
4667              
4668             # Level 3: do hex dump of value
4669 617 100 100     2260 if ($verbose > 2 and $parms{DataPt} and (not $tagInfo or not $$tagInfo{ReadFromRAF})) {
      33        
      66        
4670 165         253 $parms{Out} = $out;
4671 165         282 $parms{Prefix} = $indent;
4672             # limit dump length if Verbose < 5
4673 165 50       385 $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5;
    50          
4674 165         548 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 2 my ($self, $dirInfo) = @_;
4685 1         4 my $raf = $$dirInfo{RAF};
4686 1         3 my $curPos = $raf->Tell();
4687 1   50     4 my $trailer = $$dirInfo{DirName} || 'Unknown';
4688 1         3 my $pos = $$dirInfo{DataPos};
4689 1         3 my $verbose = $$self{OPTIONS}{Verbose};
4690 1         3 my $htmlDump = $$self{HTML_DUMP};
4691 1         2 my ($buff, $buf2);
4692 1         3 my $size = $$dirInfo{DirLen};
4693 1 50       4 $pos = $curPos unless defined $pos;
4694              
4695             # get full trailer size if not specified
4696 1         2 for (;;) {
4697 1 50       3 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       4 $raf->Seek($pos, 0) or last;
4703 1 50       4 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         3 my $out = $$self{OPTIONS}{TextOut};
4711 1         8 printf $out "$trailer trailer (%d bytes at offset 0x%.4x):\n", $size, $pos;
4712 1 50       5 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         4 $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 49 my $element = shift;
4811 38         121 while ($$element{Next}) {
4812 1         4 $element = $$element{Next};
4813             }
4814 38         57 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 2331 return unless $_[0]{OPTIONS}{Verbose} > 1;
4824 11         27 my ($self, $str, $val, $xtra) = @_;
4825 11         22 my $out = $$self{OPTIONS}{TextOut};
4826 11 100       29 $xtra or $xtra = '';
4827 11         20 my $maxLen = 81 - length($str) - length($xtra);
4828 11         34 $val = $self->Printable($val, $maxLen);
4829 11         53 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 847 my @tm = localtime time;
4918 61 100 66     696 $guidCount = 0 unless defined $guidCount and ++$guidCount < 0x100;
4919 61         1554 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 189 my ($self, $tzFlag) = @_;
4985 61         100 my $timeNow;
4986 61 50       217 ref $self or $tzFlag = $self, $self = { };
4987 61 50       214 if ($$self{Now}) {
4988 0         0 $timeNow = $$self{Now}[0];
4989             } else {
4990 61         128 my $time = time();
4991 61         1890 my @tm = localtime $time;
4992 61         356 my $tz = TimeZoneString(\@tm, $time);
4993 61         412 $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         286 $$self{Now} = [ $timeNow, $tz ];
4997             }
4998 61 50 33     489 $timeNow .= $$self{Now}[1] if $tzFlag or not defined $tzFlag;
4999 61         246 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 1140 my ($self, $val, $tzFlag, $dateOnly) = @_;
5015 474         643 my ($rtnVal, $tz, $fs);
5016 474         1219 my $fmt = $$self{OPTIONS}{DateFormat};
5017             # strip off timezone first if it exists
5018 474 100 100     3604 if (not $fmt and $val =~ s/([-+])(\d{1,2}):?(\d{2})\s*(DST)?$//i) {
    50 66        
5019 6         57 $tz = sprintf("$1%.2d:$3", $2);
5020             } elsif (not $fmt and $val =~ s/Z$//i) {
5021 0         0 $tz = 'Z';
5022             } else {
5023 468         748 $tz = '';
5024             # allow special value of 'now'
5025 468 50       1225 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       853 if ($fmt) {
5029 1 50       6 unless (defined $strptimeLib) {
5030 1 50       3 if (eval { require POSIX::strptime }) {
  1 0       9  
5031 1         2 $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         3 ($fs, $tz) = ('', '');
5043 1 50       7 if ($fmt =~ /%(f|:?z)/) {
5044 1 50       9 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       6 if ($fmt =~ s/(.*[^%])%(:?)z/$1/) {
5048 1         3 my $colon = $2;
5049 1 50       53 $tz = "$2:$3" if $val =~ s/(.*)([-+]\d{2})$colon(\d{2})/$1/;
5050             }
5051             }
5052 1         5 my ($lib, $wrn, @a);
5053 1         3 TryLib: for ($lib=$strptimeLib; ; $lib='') {
5054             # handle %s format ourself (not supported in Fedora, see forum15032)
5055 1 50       3 if ($fmt eq '%s') {
5056 0         0 $val = ConvertUnixTime($val, 1);
5057 0         0 last;
5058             }
5059 1 50       6 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         55  
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     11 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     9 ++$a[4] if defined $a[4] and length $a[4]; # add 1 to month
5083 1         3 my $i;
5084 1         3 foreach $i (0..4) {
5085 5 50 33     21 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         7 $a[$i] = "0$a[$i]"; # pad to 2 digits if necessary
5094             }
5095             }
5096 1         7 $val = join(':', @a[5,4,3]) . ' ' . join(':', @a[2,1,0]) . $fs . $tz;
5097 1         4 last;
5098             }
5099             }
5100 474 100       1700 if ($val =~ /(\d{4})/g) { # get YYYY
5101 465         872 my $yr = $1;
5102 465         2178 my @a = ($val =~ /\d{1,2}/g); # get mm, dd, HH, and maybe MM, SS
5103 465   66     1968 length($_) < 2 and $_ = "0$_" foreach @a; # pad to 2 digits if necessary
5104 465 100       983 if (@a >= 3) {
    50          
5105 439         689 my $ss = $a[4]; # get SS
5106 439         848 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       812 unless ($fmt) {
5109 438 100 100     1125 $fs = (@a > 5 and $val =~ /(\.\d+)\s*$/) ? $1 : '';
5110             }
5111             # add/remove timezone if necessary
5112 439 100       1051 if ($tzFlag) {
    100          
5113 34 50       93 if (not $tz) {
5114 34 50       51 if (eval { require Time::Local }) {
  34         1158  
5115             # determine timezone offset for this time
5116 34         3084 my @args = ($a[4],$a[3],$a[2],$a[1],$a[0]-1,$yr);
5117 34         144 my $diff = Time::Local::timegm(@args) - TimeLocal(@args);
5118 34         121 $tz = TimeZoneString($diff / 60);
5119             } else {
5120 0         0 $tz = 'Z'; # don't know time zone
5121             }
5122             }
5123             } elsif (defined $tzFlag) {
5124 92         209 $tz = $fs = ''; # remove timezone and sub-seconds
5125             }
5126 439 100 66     1417 if (defined $ss and $ss < 60) {
    50          
5127 438         721 $ss = ":$ss";
5128             } elsif ($dateOnly) {
5129 1         2 $ss = '';
5130             } else {
5131 0         0 $ss = ':00';
5132             }
5133             # construct properly formatted date/time string
5134 439 50 33     1480 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     1335 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       894 $a[2] > 24 and warn("Hour '$a[2]' out of range 0..24\n"), return undef;
5143 439 50       799 $a[3] > 59 and warn("Minutes '$a[3]' out of range 0..59\n"), return undef;
5144 439         1225 $rtnVal = "$yr:$a[0]:$a[1] $a[2]:$a[3]$ss$fs$tz";
5145             } elsif ($dateOnly) {
5146 26         85 $rtnVal = join ':', $yr, @a;
5147             }
5148             }
5149 474 100       909 $rtnVal or warn "Invalid date/time (use YYYY:mm:dd HH:MM:SS[.ss][+/-HH:MM|Z])\n";
5150 474         3668 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 117 my ($self, $default) = @_;
5166             my $byteOrder = $self->Options('ByteOrder') ||
5167             $self->GetNewValue('ExifByteOrder') ||
5168 46   100     165 $default || $$self{MAKER_NOTE_BYTE_ORDER} || 'MM';
5169 46 50       168 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         139 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 4997 100   4997 0 8140 @_ < 3 and return @_;
5185 3523         4209 my ($num, $denom, $frac) = splice(@_, 0, 3);
5186 3523         4605 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 633     633 0 1054 my $val = shift;
5203 633 50       1354 return (1, 0) if $val eq 'inf';
5204 633 50       1152 return (0, 0) if $val eq 'undef';
5205 633 100       1435 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 617 100       1912 return (0, 1) if $val == 0;
5208 578 100       1156 my $sign = $val < 0 ? ($val = -$val, -1) : 1;
5209 578         808 my ($num, $denom, @fracs);
5210 578         745 my $frac = $val;
5211 578   100     1284 my $maxInt = shift || 0x7fffffff;
5212 578         686 for (;;) {
5213 1474         2955 my ($n, $d) = AssembleRational(int($frac + 0.5), 1, @fracs);
5214 1474 50 33     3619 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 1474         1985 ($num, $denom) = ($n, $d); # save last good values
5220 1474         2165 my $err = ($n/$d-$val) / $val; # get error of this rational
5221 1474 100       2496 last if abs($err) < 1e-8; # all done if error is small
5222 896         1012 my $int = int($frac);
5223 896         1163 unshift @fracs, $int;
5224 896 50       1303 last unless $frac -= $int;
5225 896         990 $frac = 1 / $frac;
5226             }
5227 578         1745 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 216 my $val = shift;
5237 188 100       304 $val < 0 and $val += 0x10000;
5238 188         292 return Set16u($val, @_);
5239             }
5240             sub Set32s(@)
5241             {
5242 69     69 0 112 my $val = shift;
5243 69 100       173 $val < 0 and $val += 0xffffffff, ++$val;
5244 69         157 return Set32u($val, @_);
5245             }
5246             sub Set64u(@)
5247             {
5248 28     28 0 33 my $val = $_[0];
5249 28         41 my $hi = int($val / 4294967296);
5250 28         49 my $lo = Set32u($val - $hi * 4294967296); # NOTE: subject to round-off errors!
5251 28         40 $hi = Set32u($hi);
5252 28 100       39 $val = GetByteOrder() eq 'MM' ? $hi . $lo : $lo . $hi;
5253 28 100       49 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  27         42  
5254 28         42 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 878 my ($numer,$denom) = Rationalize($_[0],0xffffffff);
5264 319         687 my $val = Set32u($numer) . Set32u($denom);
5265 319 50       625 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5266 319         777 return $val;
5267             }
5268             sub SetRational64s(@) {
5269 44     44 0 160 my ($numer,$denom) = Rationalize($_[0]);
5270 44         161 my $val = Set32s($numer) . Set32u($denom);
5271 44 50       104 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5272 44         116 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 17 my $val = shift;
5300 12 100       37 return Set32s(int($val * 0x10000 + ($val < 0 ? -0.5 : 0.5)), @_);
5301             }
5302             sub SetFloat(@) {
5303 64     64 0 439 my $val = SwapBytes(pack('f',$_[0]), 4);
5304 64 50       290 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5305 64         275 return $val;
5306             }
5307             sub SetDouble(@) {
5308             # swap 32-bit words (ARM quirk) and bytes if necessary
5309 66     66 0 394 my $val = SwapBytes(SwapWords(pack('d',$_[0])), 8);
5310 66 50       237 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5311 66         372 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 1405     1405 0 2767 my ($val, $format, $count, $dataPt, $offset) = @_;
5367 1405         2380 my $proc = $writeValueProc{$format};
5368 1405         1559 my $packed;
5369              
5370 1405 100 66     2661 if ($proc) {
    50          
5371 1064         2518 my @vals = split(' ',$val);
5372 1064 100       1637 if ($count) {
5373 571 100       1083 $count = @vals if $count < 0;
5374             } else {
5375 493         579 $count = 1; # assume 1 if count not specified
5376             }
5377 1064         1375 $packed = '';
5378 1064         1942 while ($count--) {
5379 1569         1980 $val = shift @vals;
5380 1569 50       2377 return undef unless defined $val;
5381             # validate numerical formats
5382 1569 100       4392 if ($format =~ /^int/) {
    100          
5383 1189 50 33     2415 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     112 return undef unless $format =~ /^rational/ and ($val eq 'inf' or
      33        
5391             $val eq 'undef' or IsRational($val));
5392             }
5393 1569         2804 $packed .= &$proc($val);
5394             }
5395             } elsif ($format eq 'string' or $format eq 'undef') {
5396 341 100       712 $format eq 'string' and $val .= "\0"; # null-terminate strings
5397 341 100 66     1254 if ($count and $count > 0) {
5398 61         116 my $diff = $count - length($val);
5399 61 100       166 if ($diff) {
5400             #warn "wrong string length!\n";
5401             # adjust length of string to match specified count
5402 33 100       56 if ($diff < 0) {
5403 26 50       67 if ($format eq 'string') {
5404 26 50       50 return undef unless $count;
5405 26         65 $val = substr($val, 0, $count - 1) . "\0";
5406             } else {
5407 0         0 $val = substr($val, 0, $count);
5408             }
5409             } else {
5410 7         19 $val .= "\0" x $diff;
5411             }
5412             }
5413             } else {
5414 280         384 $count = length($val);
5415             }
5416 341 100       568 $dataPt and substr($$dataPt, $offset, $count) = $val;
5417 341         872 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 1064 100       1947 $dataPt and substr($$dataPt, $offset, length($packed)) = $packed;
5423 1064         2201 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 299 my ($val, $lookup, $bits, $num) = @_;
5434 105 100       248 $bits or $bits = 32;
5435 105 100       184 $num or $num = $bits;
5436 105         299 my $words = int(($num + $bits - 1) / $bits);
5437 105         253 my @outVal = (0) x $words;
5438 105 100       282 if ($val ne '(none)') {
5439 86         311 my @vals = split /\s*,\s*/, $val;
5440 86         164 foreach $val (@vals) {
5441 42         50 my $bit;
5442 42 50       118 if ($lookup) {
5443 42         100 $bit = ReverseLookup($val, $lookup);
5444             # (Note: may get non-numerical $bit values from Unknown() tags)
5445 42 100       98 unless (defined $bit) {
5446 33 50       81 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     162 return undef unless @vals > 1 and wantarray;
5451 2         12 return (undef, "no match for '${val}'");
5452             }
5453             }
5454             } else {
5455 0         0 $bit = $val;
5456             }
5457 9 50 33     32 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         27 my $word = int($bit / $bits);
5462 9         33 $outVal[$word] |= (1 << ($bit - $word * $bits));
5463             }
5464             }
5465 72         321 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 543 my $outfile = shift;
5475 334 100       1041 if (UNIVERSAL::isa($outfile,'GLOB')) {
5476 305         1277 return tell($outfile);
5477             } else {
5478 29         116 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 5401 my $outfile = shift;
5489 4085 100       10233 if (UNIVERSAL::isa($outfile,'GLOB')) {
    50          
5490 2355         12992 return print $outfile @_;
5491             } elsif (ref $outfile eq 'SCALAR') {
5492 1730         6039 $$outfile .= join('', @_);
5493 1730         4423 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 41 my ($self, $trailInfo, $outfile) = @_;
5505 12 50       49 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         44 my $pos = Tell($outfile);
5511 12         40 my $trailPt = $$trailInfo{OutFile};
5512             # apply fixup if necessary (AFCP requires this)
5513 12 100       52 if ($$trailInfo{Fixup}) {
5514 8 50       26 if ($pos > 0) {
5515             # shift offsets to final AFCP location and write it out
5516 8         17 $$trailInfo{Fixup}{Shift} += $pos;
5517 8         51 $$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 345 my ($self, @types) = @_;
5534 133         410 my $trailPt;
5535 133 100       346 ref $types[0] and $trailPt = shift @types;
5536 133 100       370 $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       511 @types or @types = qw(CanonVRD CanonDR4);
5539             # add trailers as a block (if not done already)
5540 133         250 my $type;
5541 133         320 foreach $type (@types) {
5542 259 100       1053 next unless $$self{NEW_VALUE}{$Image::ExifTool::Extra{$type}};
5543 10 100       73 next if $$self{"Did$type"};
5544 9 100       47 my $val = $self->GetNewValue($type) or next;
5545             # DR4 record must be wrapped in VRD trailer package
5546 8 100       32 if ($type eq 'CanonDR4') {
5547 3 100       23 next if $$self{DidCanonVRD}; # (only allow one VRD trailer)
5548 2         23 require Image::ExifTool::CanonVRD;
5549 2         14 $val = Image::ExifTool::CanonVRD::WrapDR4($val);
5550 2         7 $$self{DidCanonVRD} = 1;
5551             }
5552 7 50       23 my $verb = $trailPt ? 'Writing' : 'Adding';
5553 7         46 $self->VPrint(0, " $verb $type as a block\n");
5554 7 50       17 if ($trailPt) {
5555 0         0 $$trailPt .= $val;
5556             } else {
5557 7         15 $trailPt = \$val;
5558             }
5559 7         24 $$self{"Did$type"} = 1;
5560 7         21 ++$$self{CHANGED};
5561             }
5562 133         1111 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 397 my ($outfile, $marker, $header, $dataPt, $type) = @_;
5574 114 100       366 $type or $type = '';
5575 114         200 my $len = length($$dataPt);
5576 114         315 my $hdr = "\xff" . chr($marker);
5577 114         193 my $count = 0;
5578 114         211 my $maxLen = $maxSegmentLen - length($header);
5579 114 100       298 $maxLen -= 2 if $type eq 'ICC'; # leave room for segment counters
5580 114         339 my $num = int(($len + $maxLen - 1) / $maxLen); # number of segments to write
5581 114         200 my $n = 0;
5582             # write data, splitting into multiple segments if necessary
5583             # (each segment gets its own header)
5584 114         175 for (;;) {
5585 114         155 ++$count;
5586 114         223 my $size = $len - $n;
5587 114 50       328 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         490 my $buff = substr($$dataPt,$n,$size);
5595 114         217 $n += $size;
5596 114         187 $size += length($header);
5597 114 100       281 if ($type eq 'ICC') {
5598 3         18 $buff = pack('CC', $count, $num) . $buff;
5599 3         6 $size += 2;
5600             }
5601             # write the new segment with appropriate header
5602 114         379 my $segHdr = $hdr . pack('n', $size + 2);
5603 114 50       326 Write($outfile, $segHdr, $header, $buff) or return 0;
5604 114 50       349 last if $n >= $len;
5605             }
5606 114         293 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 98 my ($self, $outfile, $dataPt, $extPt, $guid) = @_;
5617 36         51 my $success = 1;
5618              
5619             # write main XMP segment
5620 36         78 my $size = length($$dataPt) + length($xmpAPP1hdr);
5621 36 50       117 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         194 my $app1hdr = "\xff\xe1" . pack('n', $size + 2);
5626 36 50       151 Write($outfile, $app1hdr, $xmpAPP1hdr, $$dataPt) or $success = 0;
5627             # write extended XMP segment(s) if necessary
5628 36 50       139 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         197 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 278 my ($self, $dirInfo) = @_;
5653 114         285 my $outfile = $$dirInfo{OutFile};
5654 114         256 my $raf = $$dirInfo{RAF};
5655 114         253 my ($ch, $s, $length,$err, %doneDir, $isEXV, $creatingEXV);
5656 114         237 my $verbose = $$self{OPTIONS}{Verbose};
5657 114         335 my $out = $$self{OPTIONS}{TextOut};
5658 114         183 my $rtnVal = 0;
5659 114         296 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     486 unless ($raf->Read($s,2) == 2 and $s eq "\xff\xd8") {
5663 2 100 66     38 if (defined $s and length $s) {
5664 1 50 33     11 return 0 unless $s eq "\xff\x01" and $raf->Read($s,5) == 5 and $s eq 'Exiv2';
      33        
5665             } else {
5666 1 50       5 return 0 unless $$self{FILE_TYPE} eq 'EXV';
5667 1         2 $s = 'Exiv2';
5668 1         2 $creatingEXV = 1;
5669             }
5670 2 50       11 Write($outfile,"\xff\x01") or $err = 1;
5671 2         6 $isEXV = 1;
5672             }
5673              
5674 114         316 delete $$self{PREVIEW_INFO}; # reset preview information
5675 114         260 delete $$self{DEL_PREVIEW}; # reset flag to delete preview
5676              
5677 114 50       547 Write($outfile, $s) or $err = 1;
5678             # figure out what segments we need to write for the tags we have set
5679 114         313 my $addDirs = $$self{ADD_DIRS};
5680 114         261 my $editDirs = $$self{EDIT_DIRS};
5681 114         284 my $delGroup = $$self{DEL_GROUP};
5682 114         267 my $path = $$self{PATH};
5683 114         226 my $pn = scalar @$path;
5684              
5685             # set input record separator to 0xff (the JPEG marker) to make reading quicker
5686 114         645 local $/ = "\xff";
5687             #
5688             # pre-scan image to determine if any create-able segment already exists
5689             #
5690 114         358 my $pos = $raf->Tell();
5691 114         528 my ($marker, @dirOrder, %dirCount);
5692 114         209 Prescan: for (;;) {
5693             # read up to next marker (JPEG markers begin with 0xff)
5694 808 100       1487 $raf->ReadLine($s) or last;
5695             # JPEG markers can be padded with unlimited 0xff's
5696 807         987 for (;;) {
5697 807 50       1369 $raf->Read($ch, 1) or last Prescan;
5698 807         973 $marker = ord($ch);
5699 807 50       1316 last unless $marker == 0xff;
5700             }
5701 807         822 my $dirName;
5702             # stop pre-scan at SOS (end of meta information) or EOI (end of image)
5703 807 100 100     2149 if ($marker == 0xda or $marker == 0xd9) {
5704 113         260 $dirName = $jpegMarker{$marker};
5705 113         258 push(@dirOrder, $dirName);
5706 113         273 $dirCount{$dirName} = 1;
5707 113         295 last;
5708             }
5709             # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
5710 694 100 66     3765 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    50 100        
      33        
      66        
      33        
5711 112 50       263 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       996 last unless $raf->Read($s, 2) == 2;
5717 582         1209 my $len = unpack('n',$s); # get data length
5718 582 50 33     1572 last unless defined($len) and $len >= 2;
5719 582         726 $len -= 2; # subtract size of length word
5720 582 100       1196 if (($marker & 0xf0) == 0xe0) { # is this an APP segment?
5721 347 100       565 my $n = $len < 64 ? $len : 64;
5722 347 50       635 $raf->Read($s, $n) == $n or last;
5723 347         438 $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       1324 if ($marker == 0xe0) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
5727 45 100       150 $s =~ /^JFIF\0/ and $dirName = 'JFIF';
5728 45 100       129 $s =~ /^JFXX\0\x10/ and $dirName = 'JFXX';
5729 45 100       136 $s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF';
5730             } elsif ($marker == 0xe1) {
5731 84 100       641 if ($s =~ /^(.{0,4})Exif\0.(.{1,4})/is) {
5732 60         132 $dirName = 'IFD0';
5733 60         243 my ($junk, $bytes) = ($1, $2);
5734             # support multi-segment EXIF
5735 60 0 66     254 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       881 $s =~ /^$xmpAPP1hdr/ and $dirName = 'XMP';
5742 84 100       635 $s =~ /^$xmpExtAPP1hdr/ and $dirName = 'XMP';
5743             } elsif ($marker == 0xe2) {
5744 55 100       155 $s =~ /^ICC_PROFILE\0/ and $dirName = 'ICC_Profile';
5745 55 100       125 $s =~ /^FPXR\0/ and $dirName = 'FlashPix';
5746 55 100       125 $s =~ /^MPF\0/ and $dirName = 'MPF';
5747             } elsif ($marker == 0xe3) {
5748 9 50       71 $s =~ /^(Meta|META|Exif)\0\0/ and $dirName = 'Meta';
5749             } elsif ($marker == 0xe5) {
5750 9 50       44 $s =~ /^RMETA\0/ and $dirName = 'RMETA';
5751             } elsif ($marker == 0xea) {
5752 9 50       38 $s =~ /^AROT\0\0/ and $dirName = 'AROT';
5753             } elsif ($marker == 0xeb) {
5754 18 100       62 $s =~ /^JP/ and $dirName = 'JUMBF';
5755             } elsif ($marker == 0xec) {
5756 19 100       71 $s =~ /^Ducky/ and $dirName = 'Ducky';
5757             } elsif ($marker == 0xed) {
5758 29 100       271 $s =~ /^$psAPP13hdr/ and $dirName = 'Photoshop';
5759             } elsif ($marker == 0xee) {
5760 16 50       69 $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     1249 $doneDir{$dirName} = 0 if defined $dirName and not $$delGroup{$dirName};
5765             }
5766 582 50       1017 $raf->Seek($len, 1) or last;
5767             }
5768 694 100       1729 $dirName or $dirName = JpegMarkerName($marker);
5769 694   100     2176 $dirCount{$dirName} = ($dirCount{$dirName} || 0) + 1;
5770 694         1053 push @dirOrder, $dirName;
5771             }
5772 114 100 100     493 unless ($marker and $marker == 0xda) {
5773 2 50       8 $isEXV or $self->Error('Corrupted JPEG image'), return 1;
5774 2 50 66     15 $marker and $marker != 0xd9 and $self->Error('Corrupted EXV file'), return 1;
5775             }
5776 114 50       317 $raf->Seek($pos, 0) or $self->Error('Seek error'), return 1;
5777             #
5778             # re-write the image
5779             #
5780 114         386 my ($combinedSegData, $segPos, $firstSegPos, %extendedXMP);
5781 114         0 my (@iccChunk, $iccChunkCount, $iccChunksTotal);
5782             # read through each segment in the JPEG file
5783 114         174 Marker: for (;;) {
5784              
5785             # read up to next marker (JPEG markers begin with 0xff)
5786 808         1036 my $segJunk;
5787 808 100       2238 $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         1587 chomp($segJunk);
5791 808 100       1682 Write($outfile, $segJunk) if length $segJunk;
5792             # JPEG markers can be padded with unlimited 0xff's
5793 808         909 for (;;) {
5794 808 100       1781 if ($raf->Read($ch, 1)) {
    50          
5795 807         1069 $marker = ord($ch);
5796 807 50       1801 last unless $marker == 0xff;
5797             } elsif ($creatingEXV) {
5798             # create EXV from scratch
5799 1         3 $marker = 0xd9; # EOI
5800 1         4 push @dirOrder, 'EOI';
5801 1         5 $dirCount{EOI} = 1;
5802 1         2 last;
5803             } else {
5804 0         0 $self->Error('Format error');
5805 0         0 return 1;
5806             }
5807             }
5808             # read the segment data
5809 808         1103 my $segData;
5810             # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
5811 808 100 66     5826 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    100 100        
      33        
      66        
      66        
      66        
5812 112 50       259 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       1265 last unless $raf->Read($s, 2) == 2;
5820 694         1305 my $len = unpack('n',$s); # get data length
5821 694 50 33     2043 last unless defined($len) and $len >= 2;
5822 694         1673 $segPos = $raf->Tell();
5823 694         929 $len -= 2; # subtract size of length word
5824 694 50       1242 last unless $raf->Read($segData, $len) == $len;
5825             }
5826             # initialize variables for this segment
5827 808         1668 my $hdr = "\xff" . chr($marker); # segment header
5828 808         1764 my $markerName = JpegMarkerName($marker);
5829 808         1337 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         1667 while ($markerName ne 'SOI') {
5835 808 100 100     2125 if (exists $$addDirs{JFIF} and not defined $doneDir{JFIF}) {
5836 1         2 $doneDir{JFIF} = 1;
5837 1 50       5 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         9 $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     3794 last if $markerName eq 'APP0' or $dirCount{IFD0} or $dirCount{ExtendedEXIF};
      66        
5870             # EXIF information must come immediately after APP0
5871 703 100 100     2151 if (exists $$addDirs{IFD0} and not defined $doneDir{IFD0}) {
5872 33         90 $doneDir{IFD0} = 1;
5873 33 100       113 $verbose and print $out "Creating APP1:\n";
5874             # write new EXIF data
5875 33         107 $$self{TIFF_TYPE} = 'APP1';
5876 33         136 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
5877 33         197 my %dirInfo = (
5878             DirName => 'IFD0',
5879             Parent => 'APP1',
5880             );
5881 33         105 $$path[$pn] = 'APP1';
5882 33         232 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
5883 33 100 66     220 if (defined $buff and length $buff) {
5884 31 50       155 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     307 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         161 my $n = WriteMultiSegment($outfile, 0xe1, $exifAPP1hdr, \$buff, 'EXIF');
5905 31 50 33     198 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         149 ++$$self{CHANGED};
5912             }
5913             }
5914             # APP13 Photoshop segment next
5915 703 100       1493 last if $dirCount{Photoshop};
5916 521 100 100     1377 if (exists $$addDirs{Photoshop} and not defined $doneDir{Photoshop}) {
5917 21         48 $doneDir{Photoshop} = 1;
5918 21 50       95 $verbose and print $out "Creating APP13:\n";
5919             # write new APP13 Photoshop record to memory
5920 21         80 my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
5921 21         93 my %dirInfo = (
5922             Parent => 'APP13',
5923             );
5924 21         61 $$path[$pn] = 'APP13';
5925 21         121 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5926 21 50 33     95 if (defined $buff and length $buff) {
5927 21 50       79 WriteMultiSegment($outfile, 0xed, $psAPP13hdr, \$buff) or $err = 1;
5928 21         79 ++$$self{CHANGED};
5929             }
5930             }
5931             # then APP1 XMP segment
5932 521 100       1027 last if $dirCount{XMP};
5933 506 100 100     1311 if (exists $$addDirs{XMP} and not defined $doneDir{XMP}) {
5934 29         69 $doneDir{XMP} = 1;
5935 29 50       87 $verbose and print $out "Creating APP1:\n";
5936             # write new XMP data
5937 29         128 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
5938 29         155 my %dirInfo = (
5939             Parent => 'APP1',
5940             # specify MaxDataLen so XMP is split if required
5941             MaxDataLen => $maxXMPLen - length($xmpAPP1hdr),
5942             );
5943 29         79 $$path[$pn] = 'APP1';
5944 29         124 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5945 29 50 33     166 if (defined $buff and length $buff) {
5946             WriteMultiXMP($self, $outfile, \$buff, $dirInfo{ExtendedXMP},
5947 29 50       148 $dirInfo{ExtendedGUID}) or $err = 1;
5948             }
5949             }
5950             # then APP2 ICC_Profile segment
5951 506 100       1119 last if $dirCount{ICC_Profile};
5952 501 100 100     1127 if (exists $$addDirs{ICC_Profile} and not defined $doneDir{ICC_Profile}) {
5953 3         10 $doneDir{ICC_Profile} = 1;
5954 3 50 66     44 next if $$delGroup{ICC_Profile} and $$delGroup{ICC_Profile} != 2;
5955 3 50       10 $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         10 my %dirInfo = (
5959             Parent => 'APP2',
5960             );
5961 3         11 $$path[$pn] = 'APP2';
5962 3         13 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5963 3 50 33     15 if (defined $buff and length $buff) {
5964 3 50       16 WriteMultiSegment($outfile, 0xe2, "ICC_PROFILE\0", \$buff, 'ICC') or $err = 1;
5965 3         13 ++$$self{CHANGED};
5966             }
5967             }
5968             # then APP12 Ducky segment
5969 501 100       940 last if $dirCount{Ducky};
5970 500 100 100     1080 if (exists $$addDirs{Ducky} and not defined $doneDir{Ducky}) {
5971 2         7 $doneDir{Ducky} = 1;
5972 2 50       8 $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         7 my %dirInfo = (
5976             Parent => 'APP12',
5977             );
5978 2         5 $$path[$pn] = 'APP12';
5979 2         8 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5980 2 50 33     14 if (defined $buff and length $buff) {
5981 2         5 my $size = length($buff) + 5;
5982 2 50       7 if ($size <= $maxSegmentLen) {
5983             # write the new segment with appropriate header
5984 2         8 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       954 last if $dirCount{Adobe};
5993 475 50 33     1095 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       914 last if $dirCount{COM};
6011 455 100 100     1407 if (exists $$addDirs{COM} and not defined $doneDir{COM}) {
6012 5         9 $doneDir{COM} = 1;
6013 5 50 33     19 next if $$delGroup{File} and $$delGroup{File} != 2;
6014 5         19 my $newComment = $self->GetNewValue('Comment');
6015 5 50       18 if (defined $newComment) {
6016 5 50       12 if ($verbose) {
6017 0         0 print $out "Creating COM:\n";
6018 0         0 $self->VerboseValue('+ Comment', $newComment);
6019             }
6020 5 50       20 WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1;
6021 5         12 ++$$self{CHANGED};
6022             }
6023             }
6024 455         577 last; # didn't want to loop anyway
6025             }
6026 808         1339 $$path[$pn] = $markerName;
6027             # decrement counter for this directory since we are about to process it
6028 808         1449 --$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     5803 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    100 100        
    100 66        
    50 33        
      66        
      33        
6034 112 100       281 $verbose and print $out "JPEG $markerName:\n";
6035 112 50       312 Write($outfile, $hdr, $segData) or $err = 1;
6036 112         305 next;
6037             } elsif ($marker == 0xda) { # SOS
6038 112         238 pop @$path;
6039 112 100       325 $verbose and print $out "JPEG SOS\n";
6040             # write SOS segment
6041 112         316 $s = pack('n', length($segData) + 2);
6042 112 50       283 Write($outfile, $hdr, $s, $segData) or $err = 1;
6043 112         654 my ($buff, $endPos, $trailInfo);
6044 112         369 my $delPreview = $$self{DEL_PREVIEW};
6045 112 100       811 $trailInfo = $self->IdentifyTrailer($raf) unless $$delGroup{Trailer};
6046 112         561 my $nvTrail = $self->GetNewValueHash($Image::ExifTool::Extra{Trailer});
6047 112 50 33     1389 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         798 while ($raf->Read($buff, 65536)) {
6052 97 50       293 Write($outfile, $buff) or $err = 1, last;
6053             }
6054 97         171 $rtnVal = 1; # success unless we have a file write error
6055 97         219 last; # all done
6056             }
6057             # write the rest of the image (as quickly as possible) up to the EOI
6058 15         37 my $endedWithFF;
6059 15         29 for (;;) {
6060 15 50       54 my $n = $raf->Read($buff, 65536) or last Marker;
6061 15 50 33     167 if (($endedWithFF and $buff =~ m/^\xd9/sg) or
      33        
6062             $buff =~ m/\xff\xd9/sg)
6063             {
6064 15         29 $rtnVal = 1; # the JPEG is OK
6065             # write up to the EOI
6066 15         34 my $pos = pos($buff);
6067 15 50       63 Write($outfile, substr($buff, 0, $pos)) or $err = 1;
6068 15         80 $buff = substr($buff, $pos);
6069 15         36 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         52 $endPos = $$self{TrailerStart} = $raf->Tell() - length($buff);
6080             # write new trailer if specified
6081 15 50       50 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       58 if ($trailInfo) {
6095 11         25 my $tbuf = '';
6096 11         43 $raf->Seek(-length($buff), 1); # seek back to just after EOI
6097 11         32 $$trailInfo{OutFile} = \$tbuf; # rewrite the trailer
6098 11         31 $$trailInfo{ScanForTrailer} = 1;# scan if necessary
6099 11 50       53 $self->ProcessTrailers($trailInfo) or undef $trailInfo;
6100             }
6101 15 50       45 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       47 unless ($delPreview) {
6230 15         23 my $extra;
6231 15 100       42 if ($trailInfo) {
6232             # copy everything up to start of first processed trailer
6233 11 50       79 $extra = defined $$trailInfo{DataPos} ? ($$trailInfo{DataPos} - $endPos) : 0;
6234             } else {
6235             # copy everything up to end of file
6236 4 50       14 $raf->Seek(0, 2) or $err = 1;
6237 4         12 $extra = $raf->Tell() - $endPos;
6238             }
6239 15 100       45 if ($extra > 0) {
6240 3 100       12 if ($$delGroup{Trailer}) {
6241 2 50       5 $verbose and print $out " Deleting unknown trailer ($extra bytes)\n";
6242 2         23 ++$$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       5 $raf->Seek($endPos, 0) or $err = 1;
6247 1 50       6 CopyBlock($raf, $outfile, $extra) or $err = 1;
6248             }
6249             }
6250             }
6251             # write trailer if necessary
6252 15 100       73 if ($trailInfo) {
6253 11 50       57 $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1;
6254 11         80 undef $trailInfo;
6255             }
6256 15         57 last; # all done parsing file
6257              
6258             } elsif ($marker==0xd9 and $isEXV) {
6259             # write EXV EOI (any trailer will be lost)
6260 2 50       10 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         859 my $segDataPt = \$segData;
6276 582         846 $length = length($segData);
6277 582 100       1067 print $out "JPEG $markerName ($length bytes)\n" if $verbose;
6278             # group delete of APP segments
6279 582 100       1195 if ($$delGroup{$dirName}) {
6280 55 50       102 $verbose and print $out " Deleting $dirName segment\n";
6281 55 100       104 $self->Warn('ICC_Profile deleted. Image colors may be affected') if $dirName eq 'ICC_Profile';
6282 55         78 ++$$self{CHANGED};
6283 55         106 next Marker;
6284             }
6285 527         763 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     2076 while (exists $$editDirs{$markerName} or $$delGroup{'*'}) {
6289 131 100 33     869 if ($marker == 0xe0) { # APP0 (JFIF, CIFF)
    100          
    50          
    100          
    50          
    50          
    50          
    50          
    100          
    100          
    100          
    100          
6290 31 100       185 if ($$segDataPt =~ /^JFIF\0/) {
    100          
    100          
6291 11         25 $segType = 'JFIF';
6292 11 50       37 $$delGroup{JFIF} and $del = 1, last;
6293 11 50       35 last unless $$editDirs{JFIF};
6294 11         43 SetByteOrder('MM');
6295 11         39 my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
6296 11         97 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         60 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6305 11 50 33     64 if (defined $newData and length $newData) {
6306 11         66 $$segDataPt = "JFIF\0" . $newData;
6307             }
6308             } elsif ($$segDataPt =~ /^JFXX\0\x10/) {
6309 8         19 $segType = 'JFXX';
6310 8 100       29 $$delGroup{JFIF} and $del = 1;
6311             } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) {
6312 6         14 $segType = 'CIFF';
6313 6 50       19 $$delGroup{CIFF} and $del = 1, last;
6314 6 100       22 last unless $$editDirs{CIFF};
6315 4         8 my $newData = '';
6316 4         20 my %dirInfo = (
6317             RAF => File::RandomAccess->new($segDataPt),
6318             OutFile => \$newData,
6319             );
6320 4         33 require Image::ExifTool::CanonRaw;
6321 4 50       28 if (Image::ExifTool::CanonRaw::WriteCRW($self, \%dirInfo) > 0) {
6322 4 50       11 if (length $newData) {
6323 4         13 $$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     857 if ($$segDataPt =~ /^(.{0,4})Exif\0./is) {
    50          
    0          
6333 52         139 my $hdrLen = length $exifAPP1hdr;
6334 52 50       312 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         136 $segType = 'EXIF';
6341 52 100       187 last unless $$editDirs{IFD0};
6342             # add this data to the combined data if it exists
6343 51 50       300 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       165 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       160 $doneDir{IFD0} and $self->Warn('Multiple APP1 EXIF records');
6360 51         107 $doneDir{IFD0} = 1;
6361             # check del groups now so we can change byte order in one step
6362 51 100 66     420 if ($$delGroup{IFD0} or $$delGroup{EXIF}) {
6363 1         3 delete $doneDir{IFD0}; # delete so we will create a new one
6364 1         3 $del = 1;
6365 1         3 last;
6366             }
6367             # rewrite EXIF as if this were a TIFF file in memory
6368 50         442 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         221 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
6378 50         423 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
6379 50 50       182 if (defined $buff) {
6380 50         116 undef $$segDataPt; # free the old buffer
6381 50         111 $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       184 length $$segDataPt or $del = 1, last;
6387 46 50       181 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     428 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         228 my $n = WriteMultiSegment($outfile, $marker, $exifAPP1hdr, $segDataPt, 'EXIF');
6408 46 50 33     271 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         85 undef $combinedSegData;
6415 46         98 undef $$segDataPt;
6416 46         360 next Marker;
6417             # check for XMP data
6418             } elsif ($$segDataPt =~ /^($xmpAPP1hdr|$xmpExtAPP1hdr)/) {
6419 21         43 $segType = 'XMP';
6420 21 50       58 $$delGroup{XMP} and $del = 1, last;
6421 21   100     107 $doneDir{XMP} = ($doneDir{XMP} || 0) + 1;
6422 21 100       57 last unless $$editDirs{XMP};
6423 14 100       41 if ($doneDir{XMP} + $dirCount{XMP} > 1) {
6424             # must assemble all XMP segments before writing
6425 3         4 my ($guid, $extXMP);
6426 3 100       28 if ($$segDataPt =~ /^$xmpExtAPP1hdr/) {
6427             # save extended XMP data
6428 2 50       4 if (length $$segDataPt < 75) {
6429 0         0 $extendedXMP{Error} = 'Truncated data';
6430             } else {
6431 2         27 my ($size, $off) = unpack('x67N2', $$segDataPt);
6432 2         5 $guid = substr($$segDataPt, 35, 32);
6433 2 50       6 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         5 $extXMP = $extendedXMP{$guid};
6438 2 100       5 if ($extXMP) {
6439 1 50       6 $size == $$extXMP{Size} or $extendedXMP{Error} = 'Inconsistent size';
6440             } else {
6441 1         3 $extXMP = $extendedXMP{$guid} = { };
6442             }
6443 2         4 $$extXMP{Size} = $size;
6444 2         7 $$extXMP{$off} = substr($$segDataPt, 75);
6445             }
6446             }
6447             } else {
6448             # save all main XMP segments (should normally be only one)
6449 1 50       6 $extendedXMP{Main} = [] unless $extendedXMP{Main};
6450 1         1 push @{$extendedXMP{Main}}, substr($$segDataPt, length $xmpAPP1hdr);
  1         4  
6451             }
6452             # continue processing only if we have read all the segments
6453 3 100       10 next Marker if $dirCount{XMP};
6454             # reconstruct an XMP super-segment
6455 1         3 $$segDataPt = $xmpAPP1hdr;
6456 1         2 my $goodGuid = '';
6457 1         2 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     4 if ($goodGuid and $goodGuid ne $2) {
6463 0         0 $self->Warn('Multiple XMP segments specifying different extended XMP GUID');
6464             }
6465 1         3 $goodGuid = $2; # GUID for the standard extended XMP
6466             }
6467 1         3 $$segDataPt .= $_;
6468             }
6469             # GUID of the extended XMP that we want to read
6470 1   50     4 my $readGuid = $$self{OPTIONS}{ExtendedXMP} || 0;
6471 1 50       5 $readGuid = $goodGuid if $readGuid eq '1';
6472 1         5 foreach $guid (sort keys %extendedXMP) {
6473 2 100       5 next unless length $guid == 32; # ignore other (internal) keys
6474 1 50 33     5 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       4 if ($guid ne $goodGuid) {
6480 0         0 $self->Warn("Reading non-standard extended XMP (GUID $guid)");
6481             }
6482 1         3 $extXMP = $extendedXMP{$guid};
6483 1 50       4 next unless ref $extXMP eq 'HASH'; # (just to be safe)
6484 1         1 my $size = $$extXMP{Size};
6485 1         3 my (@offsets, $off);
6486 1         3 for ($off=0; $off<$size; ) {
6487 2 50       6 last unless defined $$extXMP{$off};
6488 2         3 push @offsets, $off;
6489 2         4 $off += length $$extXMP{$off};
6490             }
6491 1 50       4 if ($off == $size) {
6492             # add all XMP to super-segment
6493 1         6 $$segDataPt .= $$extXMP{$_} foreach @offsets;
6494             } else {
6495 0         0 $self->Error("Incomplete extended XMP (GUID $guid)", 1);
6496             }
6497             }
6498 1 50       5 $self->Error("$extendedXMP{Error} in extended XMP", 1) if $extendedXMP{Error};
6499             }
6500 12         21 my $start = length $xmpAPP1hdr;
6501 12         54 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
6502 12         78 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         58 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6510 12 100       41 if (defined $newData) {
6511 9         26 undef %extendedXMP;
6512 9 100       27 if (length $newData) {
6513             # write multi-segment XMP (XMP plus extended XMP if necessary)
6514             WriteMultiXMP($self, $outfile, \$newData, $dirInfo{ExtendedXMP},
6515 7 50       35 $dirInfo{ExtendedGUID}) or $err = 1;
6516 7         22 undef $$segDataPt; # free the old buffer
6517 7         38 next Marker;
6518             } else {
6519 2         4 $$segDataPt = ''; # delete the XMP
6520             }
6521             } else {
6522 3 50       11 $verbose and print $out " [XMP rewritten with no changes]\n";
6523 3 50       31 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       8 if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) {
6623 1         3 $segType = 'Kodak Meta';
6624 1 50       5 $$delGroup{Meta} and $del = 1, last;
6625 1 50       5 $doneDir{Meta} and $self->Warn('Multiple APP3 Meta segments');
6626 1         2 $doneDir{Meta} = 1;
6627 1 50       3 last unless $$editDirs{Meta};
6628             # rewrite Meta IFD as if this were a TIFF file in memory
6629 1         9 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         4 my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta');
6639 1         5 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
6640 1 50       4 if (defined $buff) {
6641             # update segment with new data
6642 1         3 $$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       6 $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       6 if ($$segDataPt =~ /^Ducky/) {
6671 1         3 $segType = 'Ducky';
6672 1 50       5 $$delGroup{Ducky} and $del = 1, last;
6673 1 50       4 $doneDir{Ducky} and $self->Warn('Multiple APP12 Ducky segments');
6674 1         2 $doneDir{Ducky} = 1;
6675 1 50       4 last unless $$editDirs{Ducky};
6676 1         4 my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
6677 1         7 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         6 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6686 1 50       4 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       7 $del = 1 unless length $$segDataPt;
6693             }
6694             } elsif ($marker == 0xed) { # APP13 (Photoshop)
6695 9 100       130 if ($$segDataPt =~ /^$psAPP13hdr/) {
6696 8         20 $segType = 'Photoshop';
6697             # add this data to the combined data if it exists
6698 8 50       26 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       24 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       32 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         39 my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
6718 8         49 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         38 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6727 8 50       26 if (defined $newData) {
6728 8         18 undef $$segDataPt; # free the old buffer
6729 8         25 $segDataPt = \$newData;
6730             }
6731 8 100       29 length $$segDataPt or $del = 1, last;
6732             # write as multi-segment
6733 6 50       27 WriteMultiSegment($outfile, $marker, $psAPP13hdr, $segDataPt) or $err = 1;
6734 6         10 undef $combinedSegData;
6735 6         14 undef $$segDataPt;
6736 6         31 next Marker;
6737             }
6738             } elsif ($marker == 0xee) { # APP14 (Adobe)
6739 4 50       23 if ($$segDataPt =~ /^Adobe/) {
6740 4         7 $segType = 'Adobe';
6741             # delete it and replace it later if editing
6742 4 50 33     25 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       16 unless ($doneDir{COM}) {
6750 4         9 $doneDir{COM} = 1;
6751 4 100 100     55 unless ($$delGroup{File} and $$delGroup{File} != 2) {
6752 3         9 my $tagInfo = $Image::ExifTool::Extra{Comment};
6753 3         10 my $nvHash = $self->GetNewValueHash($tagInfo);
6754 3         5 my $val = $segData;
6755 3         9 $val =~ s/\0+$//; # allow for stupid software that adds NULL terminator
6756 3 50 33     13 if ($self->IsOverwriting($nvHash, $val) or $$delGroup{File}) {
6757 3         10 $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         19 $self->VerboseValue('- Comment', $$segDataPt);
6765 4 100       12 if (defined $newComment) {
6766             # write out the comments
6767 2         9 $self->VerboseValue('+ Comment', $newComment);
6768 2 50       3884 WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1;
6769             } else {
6770 2 50       5 $verbose and print $out " Deleting COM segment\n";
6771             }
6772 4         10 ++$$self{CHANGED}; # increment the changed flag
6773 4         7 undef $segDataPt; # don't write existing comment
6774             }
6775 53         103 last; # didn't want to loop anyway
6776             }
6777              
6778             # delete necessary segments (including unknown segments if deleting all)
6779 466 100 100     1740 if ($del or ($$delGroup{'*'} and not $segType and $marker>=0xe0 and $marker<=0xef)) {
      100        
      100        
      100        
6780 13 100       42 $segType = 'unknown' unless $segType;
6781 13 50       32 $verbose and print $out " Deleting $markerName $segType segment\n";
6782 13         24 ++$$self{CHANGED};
6783 13         35 next Marker;
6784             }
6785             # write out this segment if $segDataPt is still defined
6786 453 100 66     1283 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         613 my $size = length($$segDataPt);
6790 449 50       691 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         991 $s = pack('n', length($$segDataPt) + 2);
6796 449 50       991 Write($outfile, $hdr, $s, $$segDataPt) or $err = 1;
6797             }
6798 449         751 undef $$segDataPt; # free the buffer
6799 449         685 undef $segDataPt;
6800             }
6801             }
6802             # make sure the ICC_Profile was complete
6803 114 50       306 $self->Error('Incomplete ICC_Profile record', 1) if defined $iccChunkCount;
6804 114 100       317 pop @$path if @$path > $pn;
6805             # if oldOutfile is still set, there was an error copying the JPEG
6806 114 50       291 $oldOutfile and return 0;
6807 114 50       263 if ($rtnVal) {
6808             # add any new trailers we are creating
6809 114         551 my $trailPt = $self->AddNewTrailers();
6810 114 100 33     369 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     523 $rtnVal = -1 if $rtnVal and $err;
6814 114 50 66     398 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         1178 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 372 my ($self, $valPtr) = @_;
6828 138 100 100     897 if (length($$valPtr) and $$valPtr!~/^\xff\xd8/ and not
      100        
6829             $self->Options('IgnoreMinorErrors'))
6830             {
6831 25         182 return '[Minor] Not a valid image';
6832             }
6833 113         843 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 20030     20030 0 41561 my ($valPtr, $format, $count) = @_;
6845 20030         25667 my (@vals, $val, $n);
6846              
6847 20030 100 100     61998 if ($format eq 'string' or $format eq 'undef') {
6848 2662 100 66     8392 return undef unless $count and $count > 0;
6849 365         661 my $len = length($$valPtr);
6850 365 100       659 if ($format eq 'string') {
6851 229 100       470 $len >= $count and return 'String too long';
6852             } else {
6853 136 50       344 $len > $count and return 'Data too long';
6854             }
6855 355 100       737 if ($len < $count) {
6856 269         812 $$valPtr .= "\0" x ($count - $len);
6857             }
6858 355         950 return undef;
6859             }
6860 17368 100 66     38167 if ($count and $count != 1) {
6861 2132         4593 @vals = split(' ',$$valPtr);
6862 2132 100 100     4264 $count < 0 and ($count = @vals or return undef);
6863             } else {
6864 15236         17917 $count = 1;
6865 15236         28534 @vals = ( $$valPtr );
6866             }
6867 17330 100       39083 if (@vals != $count) {
6868 1059 100       1684 my $str = @vals > $count ? 'Too many' : 'Not enough';
6869 1059         2656 return "$str values specified ($count required)";
6870             }
6871 16271         31527 for ($n=0; $n<$count; ++$n) {
6872 19314         27707 $val = shift @vals;
6873 19314 100 100     44186 if ($format =~ /^int/) {
    100 100        
6874             # make sure the value is integer
6875 17905 100       41821 unless (IsInt($val)) {
6876 3331 100       7738 if (IsHex($val)) {
6877 7         43 $val = $$valPtr = hex($val);
6878             } else {
6879             # round single floating point values to the nearest integer
6880 3324 100 100     5559 return 'Not an integer' unless IsFloat($val) and $count == 1;
6881 1264 100       4424 $val = $$valPtr = int($val + ($val < 0 ? -0.5 : 0.5));
6882             }
6883             }
6884 15845 50       37792 my $rng = $intRange{$format} or return "Bad int format: $format";
6885 15845 100       30948 return "Value below $format minimum" if $val < $$rng[0];
6886             # (allow 0xfeedfeed code as value for 16-bit pointers)
6887 15532 100 66     41242 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       3648 unless (IsFloat($val)) {
6891             # allow 'inf', 'undef' and fractional rational values
6892 273 100       763 if ($format =~ /^rational/) {
6893 235 100 66     854 next if $val eq 'inf' or $val eq 'undef';
6894 234 100       661 if ($val =~ m{^([-+]?\d+)/(\d+)$}) {
6895 70 50 66     390 next unless $1 < 0 and $format =~ /u$/;
6896 0         0 return 'Must be an unsigned rational';
6897             }
6898             }
6899 202         612 return 'Not a floating point number';
6900             }
6901 1133 50 66     6171 if ($format =~ /^rational\d+u$/ and $val < 0) {
6902 0         0 return 'Must be a positive number';
6903             }
6904             }
6905             }
6906 13690         29209 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 12532     12532 0 21899 my ($self, $tagInfo, $valPtr) = @_;
6916 12532         22979 my $format = $$tagInfo{Format};
6917 12532 100       19866 unless ($format) {
6918 4712         6473 my $table = $$tagInfo{Table};
6919 4712 100 66     14636 if ($table and $$table{FORMAT}) {
6920 3341         6218 $format = $$table{FORMAT};
6921             } else {
6922             # use default 'int8u' unless specified
6923 1371         2292 $format = 'int8u';
6924             }
6925             }
6926 12532         13925 my $count;
6927 12532 100       28283 if ($format =~ /(.*)\[(.*)\]/) {
6928 1888         4107 $format = $1;
6929 1888         2657 $count = $2;
6930             # can't evaluate $count now because we don't know $size yet
6931 1888 100       3044 $count = -1 if $count =~ /\$size/; # (-1 = any count allowed)
6932             }
6933 12532         27089 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 9 my ($self, $old, $new) = @_;
6943 3         8 my ($result, $try, $winUni);
6944              
6945 3 50       13 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         5 for (;;) {
6954 3 50       8 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         1550 $result = rename($old, $new);
6960             }
6961 3 50 33     27 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         20 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 4 my ($self, $raf, $size, $type, $noMsg) = @_;
7093 1 50       5 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 159 my ($raf, $outfile, $size) = @_;
7122 74         115 my $buff;
7123 74         103 for (;;) {
7124 127 100       311 last unless $size > 0;
7125 53 50       127 my $n = $size > 65536 ? 65536 : $size;
7126 53 50       124 $raf->Read($buff, $n) == $n or return 0;
7127 53 50       151 Write($outfile, $buff) or return undef;
7128 53         776 $size -= $n;
7129             }
7130 74         169 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 38 my ($self, $imageDataBlocks, $outfile) = @_;
7142 13         33 my $raf = $$self{RAF};
7143 13         24 my ($dataBlock, $err);
7144 13         24 my $num = @$imageDataBlocks;
7145 13 50       99 $self->VPrint(0, " Copying $num image data blocks\n") if $num;
7146 13         28 foreach $dataBlock (@$imageDataBlocks) {
7147 24         46 my ($pos, $size, $pad) = @$dataBlock;
7148 24 50       70 $raf->Seek($pos, 0) or $err = 'read', last;
7149 24         84 my $result = CopyBlock($raf, $outfile, $size);
7150 24 0       54 $result or $err = defined $result ? 'read' : 'writ';
    50          
7151             # pad if necessary
7152 24 100 33     82 Write($outfile, "\0" x $pad) or $err = 'writ' if $pad;
7153 24 50       56 last if $err;
7154             }
7155 13 50       36 if ($err) {
7156 0         0 $self->Error("Error ${err}ing image data");
7157 0         0 return 0;
7158             }
7159 13         42 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 16513     16513 0 21539 my ($self, $dirInfo, $tagTablePtr) = @_;
7169 16513 100       39401 $self or return 1; # allow dummy access to autoload this package
7170              
7171             # get default format ('int8u' unless specified)
7172 488 50       1223 my $dataPt = $$dirInfo{DataPt} or return undef;
7173 488         694 my $dataLen = length $$dataPt;
7174 488   100     1315 my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
7175 488         1121 my $increment = FormatSize($defaultFormat);
7176 488 50       957 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         697 my @varOffsets;
7182 488 100       1066 if ($$tagTablePtr{DATAMEMBER}) {
7183 220         559 $$dirInfo{DataMember} = $$tagTablePtr{DATAMEMBER};
7184 220         462 $$dirInfo{VarFormatData} = \@varOffsets;
7185 220         1229 $self->ProcessBinaryData($dirInfo, $tagTablePtr);
7186 220         467 delete $$dirInfo{DataMember};
7187 220         478 delete $$dirInfo{VarFormatData};
7188             }
7189 488   100     1429 my $dirStart = $$dirInfo{DirStart} || 0;
7190 488         822 my $dirLen = $$dirInfo{DirLen};
7191 488 100 66     1827 $dirLen = $dataLen - $dirStart if not defined $dirLen or $dirLen > $dataLen - $dirStart;
7192 488 50       1496 my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
7193 488         797 my $dirName = $$dirInfo{DirName};
7194 488         663 my $varSize = 0;
7195 488         791 my @varInfo = @varOffsets;
7196 488         548 my $tagInfo;
7197 488         681 $dataPt = \$newData;
7198 488         1438 foreach $tagInfo (sort { $$a{TagID} <=> $$b{TagID} } $self->GetNewTagInfoList($tagTablePtr)) {
  644         823  
7199 227         410 my $tagID = $$tagInfo{TagID};
7200             # evaluate conditional tags now if necessary
7201 227 100 100     874 if (ref $$tagTablePtr{$tagID} eq 'ARRAY' or $$tagInfo{Condition}) {
7202 22         62 my $writeInfo = $self->GetTagInfo($tagTablePtr, $tagID);
7203 22 100 100     109 next unless $writeInfo and $writeInfo eq $tagInfo;
7204             }
7205             # add offsets for variable-sized tags if necessary
7206 218   100     477 while (@varInfo and $varInfo[0][0] < $tagID) {
7207 10         14 $varSize = $varInfo[0][1]; # get accumulated variable size
7208 10         26 shift @varInfo;
7209             }
7210 218         287 my $count = 1;
7211 218         301 my $format = $$tagInfo{Format};
7212 218         317 my $entry = int($tagID) * $increment + $varSize; # relative offset of this entry
7213 218 100       312 if ($format) {
7214 87 100       307 if ($format =~ /(.*)\[(.*)\]/) {
    100          
7215 36         85 $format = $1;
7216 36         64 $count = $2;
7217 36         50 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         1416 $count = eval $count;
7221 36 50       118 $@ and warn($@), next;
7222             } elsif ($format eq 'string') {
7223             # string with no specified count runs to end of block
7224 1 50       17 $count = ($dirLen > $entry) ? $dirLen - $entry : 0;
7225             }
7226             } else {
7227 131         150 $format = $defaultFormat;
7228             }
7229             # read/write using variable format if changed in Hook
7230 218 100 66     449 $format = $varInfo[0][2] if @varInfo and $varInfo[0][0] == $tagID;
7231 218         498 my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen-$entry);
7232 218 100       395 next unless defined $val;
7233 215         710 my $nvHash = $self->GetNewValueHash($tagInfo, $$self{CUR_WRITE_GROUP});
7234 215 100       542 next unless $self->IsOverwriting($nvHash, $val) > 0;
7235 214         489 my $newVal = $self->GetNewValue($nvHash);
7236 214 100       352 next unless defined $newVal; # can't delete from a binary table
7237             # update DataMember with new value if necessary
7238 213 100       376 $$self{$$tagInfo{DataMember}} = $newVal if $$tagInfo{DataMember};
7239             # only write masked bits if specified
7240 213         316 my $mask = $$tagInfo{Mask};
7241 213 100       341 $newVal = (($newVal << $$tagInfo{BitShift}) & $mask) | ($val & ~$mask) if $mask;
7242             # set the size
7243 213 50 33     451 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         400 my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $entry);
7253 213 50       382 if (defined $rtnVal) {
7254 213         754 $self->VerboseValue("- $dirName:$$tagInfo{Name}", $val);
7255 213         478 $self->VerboseValue("+ $dirName:$$tagInfo{Name}", $newVal);
7256 213         463 ++$$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     1282 if ($$tagTablePtr{IS_OFFSET} and $$dirInfo{Fixup}) {
7263 1         3 $varSize = 0;
7264 1         1 @varInfo = @varOffsets;
7265 1         2 my $fixup = $$dirInfo{Fixup};
7266 1         6 my $tagID;
7267 1         2 foreach $tagID (@{$$tagTablePtr{IS_OFFSET}}) {
  1         3  
7268 1 50       3 $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID) or next;
7269 1   33     4 while (@varInfo and $varInfo[0][0] < $tagID) {
7270 0         0 $varSize = $varInfo[0][1];
7271 0         0 shift @varInfo;
7272             }
7273 1         2 my $entry = $tagID * $increment + $varSize; # (no offset to dirStart for new dir data)
7274 1 50       4 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       1056 if ($$tagTablePtr{IS_SUBDIR}) {
7319 12         29 $varSize = 0;
7320 12         29 @varInfo = @varOffsets;
7321 12         24 my $tagID;
7322 12         20 foreach $tagID (@{$$tagTablePtr{IS_SUBDIR}}) {
  12         38  
7323 13         78 my $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID);
7324 13 100       57 next unless defined $tagInfo;
7325 9   33     34 while (@varInfo and $varInfo[0][0] < $tagID) {
7326 0         0 $varSize = $varInfo[0][1];
7327 0         0 shift @varInfo;
7328             }
7329 9         21 my $entry = int($tagID) * $increment + $varSize;
7330 9 50       25 last if $entry >= $dirLen;
7331             # get value for Condition if necessary
7332 9 50       43 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       33 my $subdir = $$tagInfo{SubDirectory} or next;
7340 9         18 my $start = $$subdir{Start};
7341 9         14 my $len;
7342 9 50       23 if (not $start) {
    0          
7343 9         15 $start = $entry;
7344 9         15 $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         47 my %subdirInfo = (
7360             DataPt => \$newData,
7361             DirStart => $start,
7362             DirLen => $len,
7363             TagInfo => $tagInfo,
7364             );
7365 9         35 my $dat = $self->WriteDirectory(\%subdirInfo, GetTagTable($$subdir{TagTable}));
7366 9 50 33     73 substr($newData, $start, $len) = $dat if defined $dat and length $dat;
7367             }
7368             }
7369 488         1579 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 326 my ($self, $dirInfo, $tagTablePtr) = @_;
7379 113 50       345 $self or return 1; # allow dummy access
7380 113         304 my $buff = '';
7381 113         347 $$dirInfo{OutFile} = \$buff;
7382 113 50       606 return $buff if $self->ProcessTIFF($dirInfo, $tagTablePtr) > 0;
7383 0           return undef;
7384             }
7385              
7386             1; # end
7387              
7388             __END__