File Coverage

blib/lib/Image/ExifTool/Writer.pl
Criterion Covered Total %
statement 2582 3839 67.2
branch 1795 3174 56.5
condition 822 1552 52.9
subroutine 80 109 73.3
pod 15 95 15.7
total 5294 8769 60.3


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 58     58   472 use strict;
  58         129  
  58         2678  
16              
17 58     58   168468 use Image::ExifTool::TagLookup qw(FindTagInfo TagExists);
  58         9621  
  58         20825  
18 58     58   41261 use Image::ExifTool::Fixup;
  58         182  
  58         129955  
19              
20             sub AssembleRational($$@);
21             sub LastInList($);
22             sub CreateDirectory($$);
23             sub NextFreeTagKey($$);
24             sub RemoveNewValueHash($$$);
25             sub RemoveNewValuesForGroup($$);
26             sub GetWriteGroup1($$);
27             sub Sanitize($$);
28             sub ConvInv($$$$$;$$);
29              
30             my $loadedAllTables; # flag indicating we loaded all tables
31             my $advFmtSelf; # ExifTool object during evaluation of advanced formatting expr
32              
33             # the following is a road map of where we write each directory
34             # in the different types of files.
35             my %tiffMap = (
36             IFD0 => 'TIFF',
37             IFD1 => 'IFD0',
38             XMP => 'IFD0',
39             ICC_Profile => 'IFD0',
40             ExifIFD => 'IFD0',
41             GPS => 'IFD0',
42             SubIFD => 'IFD0',
43             GlobParamIFD => 'IFD0',
44             PrintIM => 'IFD0',
45             IPTC => 'IFD0',
46             Photoshop => '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             Ducky => 'APP12',
79             Photoshop => 'APP13',
80             Adobe => 'APP14',
81             IPTC => 'Photoshop',
82             MakerNotes => ['ExifIFD', 'CIFF'], # (first parent is the default)
83             CanonVRD => 'MakerNotes', # (so VRDOffset will get updated)
84             NikonCapture => 'MakerNotes', # (to allow delete by group)
85             Comment => 'COM',
86             );
87             my %dirMap = (
88             JPEG => \%jpegMap,
89             EXV => \%jpegMap,
90             TIFF => \%tiffMap,
91             ORF => \%tiffMap,
92             RAW => \%tiffMap,
93             EXIF => \%exifMap,
94             );
95              
96             # module names and write functions for each writable file type
97             # (defaults to "$type" and "Process$type" if not defined)
98             # - types that are handled specially will not appear in this list
99             my %writableType = (
100             CRW => [ 'CanonRaw', 'WriteCRW' ],
101             DR4 => 'CanonVRD',
102             EPS => [ 'PostScript', 'WritePS' ],
103             FLIF=> [ undef, 'WriteFLIF'],
104             GIF => undef,
105             ICC => [ 'ICC_Profile', 'WriteICC' ],
106             IND => 'InDesign',
107             JP2 => 'Jpeg2000',
108             JXL => 'Jpeg2000',
109             MIE => undef,
110             MOV => [ 'QuickTime', 'WriteMOV' ],
111             MRW => 'MinoltaRaw',
112             PDF => [ undef, 'WritePDF' ],
113             PNG => undef,
114             PPM => undef,
115             PS => [ 'PostScript', 'WritePS' ],
116             PSD => 'Photoshop',
117             RAF => [ 'FujiFilm', 'WriteRAF' ],
118             RIFF=> [ 'RIFF', 'WriteRIFF'],
119             VRD => 'CanonVRD',
120             WEBP=> [ 'RIFF', 'WriteRIFF'],
121             X3F => 'SigmaRaw',
122             XMP => [ undef, 'WriteXMP' ],
123             );
124              
125             # RAW file types
126             my %rawType = (
127             '3FR'=> 1, CR3 => 1, IIQ => 1, NEF => 1, RW2 => 1,
128             ARQ => 1, CRW => 1, K25 => 1, NRW => 1, RWL => 1,
129             ARW => 1, DCR => 1, KDC => 1, ORF => 1, SR2 => 1,
130             ARW => 1, ERF => 1, MEF => 1, PEF => 1, SRF => 1,
131             CR2 => 1, FFF => 1, MOS => 1, RAW => 1, SRW => 1,
132             );
133              
134             # groups we are allowed to delete
135             # Notes:
136             # 1) these names must either exist in %dirMap, or be translated in InitWriteDirs())
137             # 2) any dependencies must be added to %excludeGroups
138             my @delGroups = qw(
139             Adobe AFCP APP0 APP1 APP2 APP3 APP4 APP5 APP6 APP7 APP8 APP9 APP10 APP11
140             APP12 APP13 APP14 APP15 CanonVRD CIFF Ducky EXIF ExifIFD File FlashPix
141             FotoStation GlobParamIFD GPS ICC_Profile IFD0 IFD1 Insta360 InteropIFD IPTC
142             ItemList JFIF Jpeg2000 Keys MakerNotes Meta MetaIFD Microsoft MIE MPF
143             NikonApp NikonCapture PDF PDF-update PhotoMechanic Photoshop PNG PNG-pHYs
144             PrintIM QuickTime RMETA RSRC SubIFD Trailer UserData XML XML-* XMP XMP-*
145             );
146             # family 2 group names that we can delete
147             my @delGroup2 = qw(
148             Audio Author Camera Document ExifTool Image Location Other Preview Printing
149             Time Video
150             );
151             # Extra groups to delete when deleting another group
152             my %delMore = (
153             QuickTime => [ qw(ItemList UserData Keys) ],
154             XMP => [ 'XMP-*' ],
155             XML => [ 'XML-*' ],
156             );
157              
158             # family 0 groups where directories should never be deleted
159             my %permanentDir = ( QuickTime => 1, Jpeg2000 => 1 );
160              
161             # lookup for all valid family 2 groups (lower case)
162             my %family2groups = map { lc $_ => 1 } @delGroup2, 'Unknown';
163              
164             # groups we don't delete when deleting all information
165             my $protectedGroups = '(IFD1|SubIFD|InteropIFD|GlobParamIFD|PDF-update|Adobe)';
166              
167             # other group names of new tag values to remove when deleting an entire group
168             my %removeGroups = (
169             IFD0 => [ 'EXIF', 'MakerNotes' ],
170             EXIF => [ 'MakerNotes' ],
171             ExifIFD => [ 'MakerNotes', 'InteropIFD' ],
172             Trailer => [ 'CanonVRD' ], #(because we can add back CanonVRD as a block)
173             );
174             # related family 0/1 groups in @delGroups (and not already in %jpegMap)
175             # that must be removed from delete list when excluding a group
176             my %excludeGroups = (
177             EXIF => [ qw(IFD0 IFD1 ExifIFD GPS MakerNotes GlobParamIFD InteropIFD PrintIM SubIFD) ],
178             IFD0 => [ 'EXIF' ],
179             IFD1 => [ 'EXIF' ],
180             ExifIFD => [ 'EXIF' ],
181             GPS => [ 'EXIF' ],
182             MakerNotes => [ 'EXIF' ],
183             InteropIFD => [ 'EXIF' ],
184             GlobParamIFD => [ 'EXIF' ],
185             PrintIM => [ 'EXIF' ],
186             CIFF => [ 'MakerNotes' ],
187             # technically correct, but very uncommon and not a good reason to avoid deleting trailer
188             # IPTC => [ qw(AFCP FotoStation Trailer) ],
189             AFCP => [ 'Trailer' ],
190             FotoStation => [ 'Trailer' ],
191             CanonVRD => [ 'Trailer' ],
192             PhotoMechanic=> [ 'Trailer' ],
193             MIE => [ 'Trailer' ],
194             QuickTime => [ qw(ItemList UserData Keys) ],
195             );
196             # translate (lower case) wanted group when writing for tags where group name may change
197             my %translateWantGroup = (
198             ciff => 'canonraw',
199             );
200             # group names to translate for writing
201             my %translateWriteGroup = (
202             EXIF => 'ExifIFD',
203             Meta => 'MetaIFD',
204             File => 'Comment',
205             # any entry in this table causes the write group to be set from the
206             # tag information instead of whatever the user specified...
207             MIE => 'MIE',
208             APP14 => 'APP14',
209             );
210             # names of valid EXIF and Meta directories (lower case keys):
211             my %exifDirs = (
212             gps => 'GPS',
213             exififd => 'ExifIFD',
214             subifd => 'SubIFD',
215             globparamifd => 'GlobParamIFD',
216             interopifd => 'InteropIFD',
217             previewifd => 'PreviewIFD', # (in MakerNotes)
218             metaifd => 'MetaIFD', # Kodak APP3 Meta
219             makernotes => 'MakerNotes',
220             );
221             # valid family 0 groups when WriteGroup is set to "All"
222             my %allFam0 = (
223             exif => 1,
224             makernotes => 1,
225             );
226              
227             my @writableMacOSTags = qw(
228             FileCreateDate MDItemFinderComment MDItemFSCreationDate MDItemFSLabel MDItemUserTags
229             XAttrQuarantine
230             );
231              
232             # min/max values for integer formats
233             my %intRange = (
234             'int8u' => [0, 0xff],
235             'int8s' => [-0x80, 0x7f],
236             'int16u' => [0, 0xffff],
237             'int16uRev' => [0, 0xffff],
238             'int16s' => [-0x8000, 0x7fff],
239             'int32u' => [0, 0xffffffff],
240             'int32s' => [-0x80000000, 0x7fffffff],
241             'int64u' => [0, 18446744073709551615],
242             'int64s' => [-9223372036854775808, 9223372036854775807],
243             );
244             # lookup for file types with block-writable EXIF
245             my %blockExifTypes = map { $_ => 1 } qw(JPEG PNG JP2 MIE EXIF FLIF MOV MP4 RIFF);
246              
247             my $maxSegmentLen = 0xfffd; # maximum length of data in a JPEG segment
248             my $maxXMPLen = $maxSegmentLen; # maximum length of XMP data in JPEG
249              
250             # value separators when conversion list is used (in SetNewValue)
251             my %listSep = ( PrintConv => '; ?', ValueConv => ' ' );
252              
253             # printConv hash keys to ignore when doing reverse lookup
254             my %ignorePrintConv = map { $_ => 1 } qw(OTHER BITMASK Notes);
255              
256             #------------------------------------------------------------------------------
257             # Set tag value
258             # Inputs: 0) ExifTool object reference
259             # 1) tag key, tag name, or '*' (optionally prefixed by group name),
260             # or undef to reset all previous SetNewValue() calls
261             # 2) new value (scalar, scalar ref or list ref), or undef to delete tag
262             # 3-N) Options:
263             # Type => PrintConv, ValueConv or Raw - specifies value type
264             # AddValue => true to add to list of existing values instead of overwriting
265             # DelValue => true to delete this existing value value from a list, or
266             # or doing a conditional delete, or to shift a time value
267             # Group => family 0 or 1 group name (case insensitive)
268             # Replace => 0, 1 or 2 - overwrite previous new values (2=reset)
269             # Protected => bitmask to write tags with specified protections
270             # EditOnly => true to only edit existing tags (don't create new tag)
271             # EditGroup => true to only edit existing groups (don't create new group)
272             # Shift => undef, 0, +1 or -1 - shift value if possible
273             # NoFlat => treat flattened tags as 'unsafe'
274             # NoShortcut => true to prevent looking up shortcut tags
275             # ProtectSaved => protect existing new values with a save count greater than this
276             # IgnorePermanent => ignore attempts to delete a permanent tag
277             # CreateGroups => [internal use] createGroups hash ref from related tags
278             # ListOnly => [internal use] set only list or non-list tags
279             # SetTags => [internal use] hash ref to return tagInfo refs of set tags
280             # Sanitized => [internal use] set to avoid double-sanitizing the value
281             # Returns: number of tags set (plus error string in list context)
282             # Notes: For tag lists (like Keywords), call repeatedly with the same tag name for
283             # each value in the list. Internally, the new information is stored in
284             # the following members of the $$self{NEW_VALUE}{$tagInfo} hash:
285             # TagInfo - tag info ref
286             # DelValue - list ref for raw values to delete
287             # Value - list ref for raw values to add (not defined if deleting the tag)
288             # IsCreating - must be set for the tag to be added for the standard file types,
289             # otherwise just changed if it already exists. This may be
290             # overridden for file types with a PREFERRED metadata type.
291             # Set to 2 to create individual tags but not new groups
292             # EditOnly - flag set if tag should never be created (regardless of file type).
293             # If this is set, then IsCreating must be false
294             # CreateOnly - flag set if creating only (never edit existing tag)
295             # CreateGroups - hash of all family 0 group names where tag may be created
296             # WriteGroup - group name where information is being written (correct case)
297             # WantGroup - group name as specified in call to function (case insensitive)
298             # Next - pointer to next new value hash (if more than one)
299             # NoReplace - set if value was created with Replace=0
300             # AddBefore - number of list items added by a subsequent Replace=0 call
301             # IsNVH - Flag indicating this is a new value hash
302             # Shift - shift value
303             # Save - counter used by SaveNewValues()/RestoreNewValues()
304             # MAKER_NOTE_FIXUP - pointer to fixup if necessary for a maker note value
305             sub SetNewValue($;$$%)
306             {
307 5702     5702 1 47063 local $_;
308 5702         21102 my ($self, $tag, $value, %options) = @_;
309 5702         10451 my ($err, $tagInfo, $family);
310 5702         14543 my $verbose = $$self{OPTIONS}{Verbose};
311 5702         10914 my $out = $$self{OPTIONS}{TextOut};
312 5702   100     19569 my $protected = $options{Protected} || 0;
313 5702         10070 my $listOnly = $options{ListOnly};
314 5702         10123 my $setTags = $options{SetTags};
315 5702         9319 my $noFlat = $options{NoFlat};
316 5702         9217 my $numSet = 0;
317              
318 5702 100       12663 unless (defined $tag) {
319 40         960 delete $$self{NEW_VALUE};
320 40         121 $$self{SAVE_COUNT} = 0;
321 40         170 $$self{DEL_GROUP} = { };
322 40         189 return 1;
323             }
324             # allow value to be scalar or list reference
325 5662 100       13093 if (ref $value) {
326 218 100       1331 if (ref $value eq 'ARRAY') {
    100          
327             # value is an ARRAY so it may have more than one entry
328             # - set values both separately and as a combined string if there are more than one
329 78 100       315 if (@$value > 1) {
330             # set all list-type tags first
331 51         121 my $replace = $options{Replace};
332 51         104 my $noJoin;
333 51         154 foreach (@$value) {
334 153 100       398 $noJoin = 1 if ref $_;
335 153         811 my ($n, $e) = SetNewValue($self, $tag, $_, %options, ListOnly => 1);
336 153 100       450 $err = $e if $e;
337 153         285 $numSet += $n;
338 153         474 delete $options{Replace}; # don't replace earlier values in list
339             }
340 51 100       267 return $numSet if $noJoin; # don't join if list contains objects
341             # and now set only non-list tags
342 50         294 $value = join $$self{OPTIONS}{ListSep}, @$value;
343 50         864 $options{Replace} = $replace;
344 50         166 $listOnly = $options{ListOnly} = 0;
345             } else {
346 27         91 $value = $$value[0];
347 27 50       128 $value = $$value if ref $value eq 'SCALAR'; # (handle single scalar ref in a list)
348             }
349             } elsif (ref $value eq 'SCALAR') {
350 127         390 $value = $$value;
351             }
352             }
353             # un-escape as necessary and make sure the Perl UTF-8 flag is OFF for the value
354             # if perl is 5.6 or greater (otherwise our byte manipulations get corrupted!!)
355 5661 100 100     39640 $self->Sanitize(\$value) if defined $value and not ref $value and not $options{Sanitized};
      100        
356              
357             # set group name in options if specified
358 5661 100       19781 ($options{Group}, $tag) = ($1, $2) if $tag =~ /(.*):(.+)/;
359              
360             # allow trailing '#' for ValueConv value
361 5661 100       15783 $options{Type} = 'ValueConv' if $tag =~ s/#$//;
362 5661   66     24697 my $convType = $options{Type} || ($$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv');
363              
364             # filter value if necessary
365 5661 100 50     26096 $self->Filter($$self{OPTIONS}{FilterW}, \$value) or return 0 if $convType eq 'PrintConv';
366              
367 5661         10573 my (@wantGroup, $family2);
368 5661         10545 my $wantGroup = $options{Group};
369 5661 100       12445 if ($wantGroup) {
370 2446         8183 foreach (split /:/, $wantGroup) {
371 2472 50 33     16713 next unless length($_) and /^(\d+)?(.*)/; # separate family number and group name
372 2472         8571 my ($f, $g) = ($1, $2);
373 2472         5500 my $lcg = lc $g;
374             # save group/family unless '*' or 'all'
375 2472 100 66     12295 push @wantGroup, [ $f, $lcg ] unless $lcg eq '*' or $lcg eq 'all';
376 2472 100       9498 if ($g =~ s/^ID-//i) { # family 7 is a tag ID
    100          
377 1 50 33     10 return 0 if defined $f and $f ne 7;
378 1         6 $wantGroup[-1] = [ 7, $g ]; # group name with 'ID-' removed and case preserved
379             } elsif (defined $f) {
380 30 50       124 $f > 2 and return 0; # only allow family 0, 1 or 2
381 30 100       113 $family2 = 1 if $f == 2; # set flag indicating family 2 was used
382             } else {
383 2441 100       8606 $family2 = 1 if $family2groups{$lcg};
384             }
385             }
386 2446 100       6055 undef $wantGroup unless @wantGroup;
387             }
388              
389 5661         12075 $tag =~ s/ .*//; # convert from tag key to tag name if necessary
390 5661 100       13997 $tag = '*' if lc($tag) eq 'all'; # use '*' instead of 'all'
391             #
392             # handle group delete
393             #
394 5661   100     15938 while ($tag eq '*' and not defined $value and not $family2 and @wantGroup < 2) {
      100        
      66        
395             # set groups to delete
396 47         120 my (@del, $grp);
397 47   66     225 my $remove = ($options{Replace} and $options{Replace} > 1);
398 47 100       199 if ($wantGroup) {
399 34 50       2285 @del = grep /^$wantGroup$/i, @delGroups unless $wantGroup =~ /^XM[LP]-\*$/i;
400             # remove associated groups when excluding from mass delete
401 34 100 100     251 if (@del and $remove) {
402             # remove associated groups in other family
403 4 100       23 push @del, @{$excludeGroups{$del[0]}} if $excludeGroups{$del[0]};
  2         12  
404             # remove upstream groups according to JPEG map
405 4         13 my $dirName = $del[0];
406 4         9 my @dirNames;
407 4         9 for (;;) {
408 10         23 my $parent = $jpegMap{$dirName};
409 10 50       28 if (ref $parent) {
410 0         0 push @dirNames, @$parent;
411 0         0 $parent = pop @dirNames;
412             }
413 10 100 66     46 $dirName = $parent || shift @dirNames or last;
414 6         14 push @del, $dirName; # exclude this too
415             }
416             }
417             # allow MIE groups to be deleted by number,
418             # and allow any XMP family 1 group to be deleted
419 34 100       221 push @del, uc($wantGroup) if $wantGroup =~ /^(MIE\d+|XM[LP]-[-\w]*\w)$/i;
420             } else {
421             # push all groups plus '*', except the protected groups
422 13         1505 push @del, (grep !/^$protectedGroups$/, @delGroups), '*';
423             }
424 47 50       202 if (@del) {
    0          
425 47         110 ++$numSet;
426 47         112 my @donegrps;
427 47         139 my $delGroup = $$self{DEL_GROUP};
428 47         165 foreach $grp (@del) {
429 804 100       1306 if ($remove) {
430 23         38 my $didExcl;
431 23 100       73 if ($grp =~ /^(XM[LP])(-.*)?$/) {
432 4         15 my $x = $1;
433 4 100 33     38 if ($grp eq $x) {
    50          
434             # exclude all related family 1 groups too
435 1         15 foreach (keys %$delGroup) {
436 58 100       165 next unless /^(-?)$x-/;
437 1 50       6 push @donegrps, $_ unless $1;
438 1         5 delete $$delGroup{$_};
439             }
440             } elsif ($$delGroup{"$x-*"} and not $$delGroup{"-$grp"}) {
441             # must also exclude XMP or XML to prevent bulk delete
442 3 100       14 if ($$delGroup{$x}) {
443 2         6 push @donegrps, $x;
444 2         8 delete $$delGroup{$x};
445             }
446             # flag XMP/XML family 1 group for exclusion with leading '-'
447 3         9 $$delGroup{"-$grp"} = 1;
448 3         9 $didExcl = 1;
449             }
450             }
451 23 100       54 if (exists $$delGroup{$grp}) {
452 15         31 delete $$delGroup{$grp};
453             } else {
454 8 100       22 next unless $didExcl;
455             }
456             } else {
457 781         1672 $$delGroup{$grp} = 1;
458             # add extra groups to delete if necessary
459 781 100       1738 if ($delMore{$grp}) {
460 49         100 $$delGroup{$_} = 1, push @donegrps, $_ foreach @{$delMore{$grp}};
  49         286  
461             }
462             # remove all of this group from previous new values
463 781         1374 $self->RemoveNewValuesForGroup($grp);
464             }
465 799         1427 push @donegrps, $grp;
466             }
467 47 100 66     286 if ($verbose > 1 and @donegrps) {
468 1         5 @donegrps = sort @donegrps;
469 1 50       6 my $msg = $remove ? 'Excluding from deletion' : 'Deleting tags in';
470 1         11 print $out " $msg: @donegrps\n";
471             }
472             } elsif (grep /^$wantGroup$/i, @delGroup2) {
473 0         0 last; # allow tags to be deleted by group2 name
474             } else {
475 0         0 $err = "Not a deletable group: $wantGroup";
476             }
477             # all done
478 47 50       181 return ($numSet, $err) if wantarray;
479 47 50       166 $err and warn "$err\n";
480 47         307 return $numSet;
481             }
482              
483             # initialize write/create flags
484 5614         8481 my $createOnly;
485 5614         10051 my $editOnly = $options{EditOnly};
486 5614         9078 my $editGroup = $options{EditGroup};
487 5614         11708 my $writeMode = $$self{OPTIONS}{WriteMode};
488 5614 100       12631 if ($writeMode ne 'wcg') {
489 27 100       101 $createOnly = 1 if $writeMode !~ /w/i; # don't write existing tags
490 27 100       142 if ($writeMode !~ /c/i) {
    100          
491 2 50       8 return 0 if $createOnly; # nothing to do unless writing existing tags
492 2         15 $editOnly = 1; # don't create new tags
493             } elsif ($writeMode !~ /g/i) {
494 8         47 $editGroup = 1; # don't create new groups
495             }
496             }
497 5614         10333 my ($ifdName, $mieGroup, $movGroup, $fg);
498             # set family 1 group names
499 5614         11400 foreach $fg (@wantGroup) {
500 2338 100 100     6780 next if defined $$fg[0] and $$fg[0] != 1;
501 2319         4756 $_ = $$fg[1];
502             # set $ifdName if this group is a valid IFD or SubIFD name
503 2319         3363 my $grpName;
504 2319 100 100     19598 if (/^IFD(\d+)$/i) {
    50          
    50          
    100          
    100          
    100          
    100          
    100          
505 131         411 $grpName = $ifdName = "IFD$1";
506             } elsif (/^SubIFD(\d+)$/i) {
507 0         0 $grpName = $ifdName = "SubIFD$1";
508             } elsif (/^Version(\d+)$/i) {
509 0         0 $grpName = $ifdName = "Version$1"; # Sony IDC VersionIFD
510             } elsif ($exifDirs{$_}) {
511 318         790 $grpName = $exifDirs{$_};
512 318 50 33     956 $ifdName = $grpName unless $ifdName and $allFam0{$_};
513             } elsif ($allFam0{$_}) {
514 293         678 $grpName = $allFam0{$_};
515             } elsif (/^Track(\d+)$/i) {
516 1         6 $grpName = $movGroup = "Track$1"; # QuickTime track
517             } elsif (/^MIE(\d*-?)(\w+)$/i) {
518 2         12 $grpName = $mieGroup = "MIE$1" . ucfirst(lc($2));
519             } elsif (not $ifdName and /^XMP\b/i) {
520             # must load XMP table to set group1 names
521 500         1897 my $table = GetTagTable('Image::ExifTool::XMP::Main');
522 500         1761 my $writeProc = $$table{WRITE_PROC};
523 500 50       1288 if ($writeProc) {
524 58     58   567 no strict 'refs';
  58         141  
  58         87413  
525 500         1664 &$writeProc();
526             }
527             }
528             # fix case for known groups
529 2319 100 66     12462 $wantGroup =~ s/$grpName/$grpName/i if $grpName and $grpName ne $_;
530             }
531             #
532             # get list of tags we want to set
533             #
534 5614         9986 my $origTag = $tag;
535 5614         18615 my @matchingTags = FindTagInfo($tag);
536 5614         16094 until (@matchingTags) {
537 1416         2695 my $langCode;
538             # allow language suffix of form "-en_CA" or "-" on tag name
539 1416 100 100     8027 if ($tag =~ /^([?*\w]+)-([a-z]{2})(_[a-z]{2})$/i or # MIE
    50          
540             $tag =~ /^([?*\w]+)-([a-z]{2,3}|[xi])(-[a-z\d]{2,8}(-[a-z\d]{1,8})*)?$/i) # XMP/PNG/QuickTime
541             {
542 51         175 $tag = $1;
543             # normalize case of language codes
544 51         148 $langCode = lc($2);
545 51 100       265 $langCode .= (length($3) == 3 ? uc($3) : lc($3)) if $3;
    100          
546 51         151 my @newMatches = FindTagInfo($tag);
547 51         133 foreach $tagInfo (@newMatches) {
548             # only allow language codes in tables which support them
549 238 50       712 next unless $$tagInfo{Table};
550 238 100       620 my $langInfoProc = $$tagInfo{Table}{LANG_INFO} or next;
551 186         630 my $langInfo = &$langInfoProc($tagInfo, $langCode);
552 186 100       545 push @matchingTags, $langInfo if $langInfo;
553             }
554 51 100       225 last if @matchingTags;
555             } elsif (not $options{NoShortcut}) {
556             # look for a shortcut or alias
557 1365         10585 require Image::ExifTool::Shortcuts;
558 1365         32729 my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main;
559 1365         4115 undef $err;
560 1365 100       3747 if ($match) {
561 1         5 $options{NoShortcut} = $options{Sanitized} = 1;
562 1         2 foreach $tag (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  1         4  
563 3         69 my ($n, $e) = $self->SetNewValue($tag, $value, %options);
564 3         8 $numSet += $n;
565 3 50       13 $e and $err = $e;
566             }
567 1 50       4 undef $err if $numSet; # no error if any set successfully
568 1 50       5 return ($numSet, $err) if wantarray;
569 1 50       3 $err and warn "$err\n";
570 1         10 return $numSet;
571             }
572             }
573 1366 50       3217 unless ($listOnly) {
574 1366 100       4269 if (not TagExists($tag)) {
    50          
    100          
575 49 50       243 if ($tag =~ /^[-\w*?]+$/) {
576 49 100       151 my $pre = $wantGroup ? $wantGroup . ':' : '';
577 49         135 $err = "Tag '$pre${origTag}' is not defined";
578 49 100       152 $err .= ' or has a bad language code' if $origTag =~ /-/;
579 49 50 66     168 if (not $pre and uc($origTag) eq 'TAG') {
580 0         0 $err .= " (specify a writable tag name, not '${origTag}' literally)"
581             }
582             } else {
583 0         0 $err = "Invalid tag name '${tag}'";
584 0 0       0 $err .= " (remove the leading '\$')" if $tag =~ /^\$/;
585             }
586             } elsif ($langCode) {
587 0         0 $err = "Tag '${tag}' does not support alternate languages";
588             } elsif ($wantGroup) {
589 507         1488 $err = "Sorry, $wantGroup:$origTag doesn't exist or isn't writable";
590             } else {
591 810         2383 $err = "Sorry, $origTag is not writable";
592             }
593 1366 50       3483 $verbose > 2 and print $out "$err\n";
594             }
595             # all done
596 1366 50       7954 return ($numSet, $err) if wantarray;
597 0 0       0 $err and warn "$err\n";
598 0         0 return $numSet;
599             }
600             # get group name that we're looking for
601 4247         7743 my $foundMatch = 0;
602             #
603             # determine the groups for all tags found, and the tag with
604             # the highest priority group
605             #
606 4247         11354 my (@tagInfoList, @writeAlsoList, %writeGroup, %preferred, %tagPriority);
607 4247         0 my (%avoid, $wasProtected, $noCreate, %highestPriority, %highestQT);
608              
609 4247         9022 TAG: foreach $tagInfo (@matchingTags) {
610 69370         258461 $tag = $$tagInfo{Name}; # get tag name for warnings
611 69370         113129 my $lcTag = lc $tag; # get lower-case tag name for use in variables
612             # initialize highest priority if we are starting a new tag
613 69370 100       186215 $highestPriority{$lcTag} = -999 unless defined $highestPriority{$lcTag};
614 69370         102325 my ($priority, $writeGroup);
615 69370 100       206580 my $prfTag = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED};
616 69370 100       127313 if ($wantGroup) {
617             # a WriteGroup of All is special
618 49375   100     101221 my $wgAll = ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All');
619 49375         123503 my @grp = $self->GetGroup($tagInfo);
620 49375         86564 my $hiPri = 1000;
621 49375         83937 foreach $fg (@wantGroup) {
622 49413         92465 my ($fam, $lcWant) = @$fg;
623 49413 100       103946 $lcWant = $translateWantGroup{$lcWant} if $translateWantGroup{$lcWant};
624             # only set tag in specified group
625             # bump priority of preferred tag
626 49413 100       88511 $hiPri += $prfTag if $prfTag;
627 49413 100 66     90313 if (not defined $fam) {
    100          
    100          
628 49171 100       100386 if ($lcWant eq lc $grp[0]) {
629             # don't go to more general write group of "All"
630             # if something more specific was wanted
631 2166 100 100     5094 $writeGroup = $grp[0] if $wgAll and not $writeGroup;
632 2166         4200 next;
633             }
634 47005 100       89455 next if $lcWant eq lc $grp[2];
635             } elsif ($fam == 7) {
636 2 100       7 next if IsSameID($$tagInfo{TagID}, $lcWant);
637             } elsif ($fam != 1 and not $$tagInfo{AllowGroup}) {
638 132 100       335 next if $lcWant eq lc $grp[$fam];
639 110 100 100     345 if ($wgAll and not $fam and $allFam0{$lcWant}) {
      100        
640 5 100       24 $writeGroup or $writeGroup = $allFam0{$lcWant};
641 5         13 next;
642             }
643 105         275 next TAG; # wrong group
644             }
645             # handle family 1 groups specially
646 36670 100 66     206180 if ($grp[0] eq 'EXIF' or $grp[0] eq 'SonyIDC' or $wgAll) {
    100 100        
    100 100        
    100          
647 1597 100 100     5509 unless ($ifdName and $lcWant eq lc $ifdName) {
648 1119 100 100     4421 next TAG unless $wgAll and not $fam and $allFam0{$lcWant};
      100        
649 7 100       33 $writeGroup = $allFam0{$lcWant} unless $writeGroup;
650 7         19 next;
651             }
652 478 100 100     1325 next TAG if $wgAll and $allFam0{$lcWant} and $fam;
      100        
653             # can't yet write PreviewIFD tags (except for image)
654 476 50       1009 $lcWant eq 'PreviewIFD' and ++$foundMatch, next TAG;
655 476         1081 $writeGroup = $ifdName; # write to the specified IFD
656             } elsif ($grp[0] eq 'QuickTime') {
657 1552 100       3790 if ($grp[1] eq 'Track#') {
658 16 100 66     91 next TAG unless $movGroup and $lcWant eq lc($movGroup);
659 1         12 $writeGroup = $movGroup;
660             } else {
661 1536         3743 my $grp = $$tagInfo{Table}{WRITE_GROUP};
662 1536 100 100     7068 next TAG unless $grp and $lcWant eq lc $grp;
663 28         74 $writeGroup = $grp;
664             }
665             } elsif ($grp[0] eq 'MIE') {
666 768 100 66     3654 next TAG unless $mieGroup and $lcWant eq lc($mieGroup);
667 2         6 $writeGroup = $mieGroup; # write to specific MIE group
668             # set specific write group with document number if specified
669 2 0 33     19 if ($writeGroup =~ /^MIE\d+$/ and $$tagInfo{Table}{WRITE_GROUP}) {
670 0         0 $writeGroup = $$tagInfo{Table}{WRITE_GROUP};
671 0         0 $writeGroup =~ s/^MIE/$mieGroup/;
672             }
673             } elsif (not $$tagInfo{AllowGroup} or $lcWant !~ /^$$tagInfo{AllowGroup}$/i) {
674             # allow group1 name to be specified
675 32752 100       100109 next TAG unless $lcWant eq lc $grp[1];
676             }
677             }
678 13635 100 66     67019 $writeGroup or $writeGroup = ($$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP} || $grp[0]);
679 13635         26362 $priority = $hiPri; # highest priority since group was specified
680             }
681 33630         46913 ++$foundMatch;
682             # must do a dummy call to the write proc to autoload write package
683             # before checking Writable flag
684 33630         52305 my $table = $$tagInfo{Table};
685 33630         64333 my $writeProc = $$table{WRITE_PROC};
686             # load source table if this was a user-defined table
687 33630 100       72032 if ($$table{SRC_TABLE}) {
688 9         43 my $src = GetTagTable($$table{SRC_TABLE});
689 9 50       23 $writeProc = $$src{WRITE_PROC} unless $writeProc;
690             }
691             {
692 58     58   508 no strict 'refs';
  58         158  
  58         705401  
  33630         47712  
693 33630 100 66     107806 next unless $writeProc and &$writeProc();
694             }
695             # must still check writable flags in case of UserDefined tags
696 33630         71142 my $writable = $$tagInfo{Writable};
697             next unless $writable or ($$table{WRITABLE} and
698 33630 50 66     143305 not defined $writable and not $$tagInfo{SubDirectory});
      66        
      66        
699             # set specific write group (if we didn't already)
700 33629 100 66     89095 if (not $writeGroup or ($translateWriteGroup{$writeGroup} and
      66        
      66        
701             (not $$tagInfo{WriteGroup} or $$tagInfo{WriteGroup} ne 'All')))
702             {
703             # use default write group
704 20071   100     61903 $writeGroup = $$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP};
705             # use group 0 name if no WriteGroup specified
706 20071         53966 my $group0 = $self->GetGroup($tagInfo, 0);
707 20071 100       46176 $writeGroup or $writeGroup = $group0;
708             # get priority for this group
709 20071 100       36648 unless ($priority) {
710 19994         43859 $priority = $$self{WRITE_PRIORITY}{lc($writeGroup)};
711 19994 100       37728 unless ($priority) {
712 3502   100     11295 $priority = $$self{WRITE_PRIORITY}{lc($group0)} || 0;
713             }
714             }
715             # adjust priority based on Preferred level for this tag
716 20071 100       40320 $priority += $prfTag if $prfTag;
717             }
718             # don't write tag if protected
719 33629         54686 my $prot = $$tagInfo{Protected};
720 33629 100 100     72888 $prot = 1 if $noFlat and defined $$tagInfo{Flat};
721 33629 100       60858 if ($prot) {
722 2237         4591 $prot &= ~$protected;
723 2237 100       4671 if ($prot) {
724 1200         4419 my %lkup = ( 1=>'unsafe', 2=>'protected', 3=>'unsafe and protected');
725 1200         2430 $wasProtected = $lkup{$prot};
726 1200 100       2683 if ($verbose > 1) {
727 1         7 my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup);
728 1         9 print $out "Sorry, $wgrp1:$tag is $wasProtected for writing\n";
729             }
730 1200         3567 next;
731             }
732             }
733             # set priority for this tag
734 32429         107535 $tagPriority{$tagInfo} = $priority;
735             # keep track of highest priority QuickTime tag
736             $highestQT{$lcTag} = $priority if $$table{GROUPS}{0} eq 'QuickTime' and
737 32429 100 100     99590 (not defined $highestQT{$lcTag} or $highestQT{$lcTag} < $priority);
      100        
738 32429 100       87225 if ($priority > $highestPriority{$lcTag}) {
    100          
739 10286         17699 $highestPriority{$lcTag} = $priority;
740 10286         36588 $preferred{$lcTag} = { $tagInfo => 1 };
741 10286 100       30802 $avoid{$lcTag} = $$tagInfo{Avoid} ? 1 : 0;
742             } elsif ($priority == $highestPriority{$lcTag}) {
743             # create all tags with highest priority
744 13312         32544 $preferred{$lcTag}{$tagInfo} = 1;
745 13312 100       32304 ++$avoid{$lcTag} if $$tagInfo{Avoid};
746             }
747 32429 100       64068 if ($$tagInfo{WriteAlso}) {
748             # store WriteAlso tags separately so we can set them first
749 108         329 push @writeAlsoList, $tagInfo;
750             } else {
751 32321         58759 push @tagInfoList, $tagInfo;
752             }
753             # special case to allow override of XMP WriteGroup
754 32429 100       64166 if ($writeGroup eq 'XMP') {
755 5452   33     17980 my $wg = $$tagInfo{WriteGroup} || $$table{WRITE_GROUP};
756 5452 50       12111 $writeGroup = $wg if $wg;
757             }
758 32429         101733 $writeGroup{$tagInfo} = $writeGroup;
759             }
760             # sort tag info list in reverse order of priority (highest number last)
761             # so we get the highest priority error message in the end
762 4247         14446 @tagInfoList = sort { $tagPriority{$a} <=> $tagPriority{$b} } @tagInfoList;
  54114         108374  
763             # must write any tags which also write other tags first
764 4247 100       11189 unshift @tagInfoList, @writeAlsoList if @writeAlsoList;
765              
766             # check priorities for each set of tags we are writing
767 4247         7448 my $lcTag;
768 4247         14684 foreach $lcTag (keys %preferred) {
769             # don't create tags with priority 0 if group priorities are set
770 9495 100 66     44299 if ($preferred{$lcTag} and $highestPriority{$lcTag} == 0 and
      66        
771 9         50 %{$$self{WRITE_PRIORITY}})
772             {
773 9         31 delete $preferred{$lcTag}
774             }
775             # avoid creating tags with 'Avoid' flag set if there are other alternatives
776 9495 50 66     24670 if ($avoid{$lcTag} and $preferred{$lcTag}) {
777 1362 100       2505 if ($avoid{$lcTag} < scalar(keys %{$preferred{$lcTag}})) {
  1362 100       8489  
778             # just remove the 'Avoid' tags since there are other preferred tags
779 1226         3276 foreach $tagInfo (@tagInfoList) {
780 4482573 100       8785437 next unless $lcTag eq lc $$tagInfo{Name};
781 5774 100       17149 delete $preferred{$lcTag}{$tagInfo} if $$tagInfo{Avoid};
782             }
783             } elsif ($highestPriority{$lcTag} < 1000) {
784             # look for another priority tag to create instead
785 51         168 my $nextHighest = 0;
786 51         106 my @nextBestTags;
787 51         143 foreach $tagInfo (@tagInfoList) {
788 10868 100       21679 next unless $lcTag eq lc $$tagInfo{Name};
789 212 100       540 my $priority = $tagPriority{$tagInfo} or next;
790 211 100       521 next if $priority == $highestPriority{$lcTag};
791 159 50       336 next if $priority < $nextHighest;
792 159         251 my $permanent = $$tagInfo{Permanent};
793 159 50       430 $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent;
794 159 100 66     553 next if $$tagInfo{Avoid} or $permanent;
795 133 100       360 next if $writeGroup{$tagInfo} eq 'MakerNotes';
796 89 100       238 if ($nextHighest < $priority) {
797 84         140 $nextHighest = $priority;
798 84         171 undef @nextBestTags;
799             }
800 89         219 push @nextBestTags, $tagInfo;
801             }
802 51 100       211 if (@nextBestTags) {
803             # change our preferred tags to the next best tags
804 35         102 delete $preferred{$lcTag};
805 35         94 foreach $tagInfo (@nextBestTags) {
806 36         223 $preferred{$lcTag}{$tagInfo} = 1;
807             }
808             }
809             }
810             }
811             }
812             #
813             # generate new value hash for each tag
814             #
815 4247         8742 my ($prioritySet, $createGroups, %alsoWrote);
816              
817 4247         8721 delete $$self{CHECK_WARN}; # reset CHECK_PROC warnings
818              
819             # loop through all valid tags to find the one(s) to write
820 4247         8318 foreach $tagInfo (@tagInfoList) {
821 32417 100       90823 next if $alsoWrote{$tagInfo}; # don't rewrite tags we already wrote
822             # only process List or non-List tags if specified
823 32388 100 100     79656 next if defined $listOnly and ($listOnly xor $$tagInfo{List});
      100        
824 32167         49200 my $noConv;
825 32167         86587 my $writeGroup = $writeGroup{$tagInfo};
826 32167         63324 my $permanent = $$tagInfo{Permanent};
827 32167 100       100607 $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent;
828 32167 100 100     96111 $writeGroup eq 'MakerNotes' and $permanent = 1 unless defined $permanent;
829 32167         87417 my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup);
830 32167         67996 $tag = $$tagInfo{Name}; # get tag name for warnings
831 32167         60004 my $lcTag = lc $tag;
832 32167   100     87467 my $pref = $preferred{$lcTag} || { };
833 32167         57778 my $shift = $options{Shift};
834 32167         52908 my $addValue = $options{AddValue};
835 32167 100       68305 if (defined $shift) {
836             # (can't currently shift list-type tags)
837 164         288 my $shiftable;
838 164 50       360 if ($$tagInfo{List}) {
839 0         0 $shiftable = ''; # can add/delete but not shift
840             } else {
841 164         300 $shiftable = $$tagInfo{Shift};
842 164 100       354 unless ($shift) {
843             # set shift according to AddValue/DelValue
844 24 50       60 $shift = 1 if $addValue;
845             # can shift a date/time with -=, but this is
846             # a conditional delete operation for other tags
847 24 0 33     75 $shift = -1 if $options{DelValue} and defined $shiftable and $shiftable eq 'Time';
      33        
848             }
849 164 50 33     900 if ($shift and (not defined $value or not length $value)) {
      33        
850             # (now allow -= to be used for shiftable tag - v8.05)
851             #$err = "No value for time shift of $wgrp1:$tag";
852             #$verbose > 2 and print $out "$err\n";
853             #next;
854 0         0 undef $shift;
855             }
856             }
857             # can't shift List-type tag
858 164 0 66     532 if ((defined $shiftable and not $shiftable) and
      0        
      33        
859             # and don't try to conditionally delete if Shift is "0"
860             ($shift or ($shiftable eq '0' and $options{DelValue})))
861             {
862 0         0 $err = "$wgrp1:$tag is not shiftable";
863 0 0       0 $verbose and print $out "$err\n";
864 0         0 next;
865             }
866             }
867 32167         50910 my $val = $value;
868 32167 100 33     76037 if (defined $val) {
    100          
    50          
869             # check to make sure this is a List or Shift tag if adding
870 21460 100 100     51664 if ($addValue and not ($shift or $$tagInfo{List})) {
      100        
871 9 50       41 if ($addValue eq '2') {
872 0         0 undef $addValue; # quietly reset this option
873             } else {
874 9         33 $err = "Can't add $wgrp1:$tag (not a List type)";
875 9 50       27 $verbose > 2 and print $out "$err\n";
876 9         30 next;
877             }
878             }
879 21451 100 66     101376 if ($shift) {
    100 100        
    100          
880 164 100 66     706 if ($$tagInfo{Shift} and $$tagInfo{Shift} eq 'Time') {
    100          
881             # add '+' or '-' prefix to indicate shift direction
882 47 100       146 $val = ($shift > 0 ? '+' : '-') . $val;
883             # check the shift for validity
884 47         2231 require 'Image/ExifTool/Shift.pl';
885 47         175 my $err2 = CheckShift($$tagInfo{Shift}, $val);
886 47 50       111 if ($err2) {
887 0         0 $err = "$err2 for $wgrp1:$tag";
888 0 0       0 $verbose > 2 and print $out "$err\n";
889 0         0 next;
890             }
891             } elsif (IsFloat($val)) {
892 113         343 $val *= $shift;
893             } else {
894 4         21 $err = "Shift value for $wgrp1:$tag is not a number";
895 4 50       13 $verbose > 2 and print $out "$err\n";
896 4         16 next;
897             }
898 160         313 $noConv = 1; # no conversions if shifting tag
899             } elsif (not length $val and $options{DelValue}) {
900 35         58 $noConv = 1; # no conversions for deleting empty value
901             } elsif (ref $val eq 'HASH' and not $$tagInfo{Struct}) {
902 2         11 $err = "Can't write a structure to $wgrp1:$tag";
903 2 50       9 $verbose > 2 and print $out "$err\n";
904 2         7 next;
905             }
906             } elsif ($permanent) {
907 6674 100       14975 return 0 if $options{IgnorePermanent};
908             # can't delete permanent tags, so set them to DelValue or empty string instead
909 6670 100       14954 if (defined $$tagInfo{DelValue}) {
910 33         135 $val = $$tagInfo{DelValue};
911 33         105 $noConv = 1; # DelValue is the raw value, so no conversion necessary
912             } else {
913 6637         10407 $val = '';
914             }
915             } elsif ($addValue or $options{DelValue}) {
916 0         0 $err = "No value to add or delete in $wgrp1:$tag";
917 0 0       0 $verbose > 2 and print $out "$err\n";
918 0         0 next;
919             } else {
920 4033 100       10899 if ($$tagInfo{DelCheck}) {
921             #### eval DelCheck ($self, $tagInfo, $wantGroup)
922 6         604 my $err2 = eval $$tagInfo{DelCheck};
923 6 50       50 $@ and warn($@), $err2 = 'Error evaluating DelCheck';
924 6 50       27 if (defined $err2) {
925             # (allow other tags to be set using DelCheck as a hook)
926 6 100       110 $err2 or goto WriteAlso; # GOTO!
927 3 50       20 $err2 .= ' for' unless $err2 =~ /delete$/;
928 3         15 $err = "$err2 $wgrp1:$tag";
929 3 50       13 $verbose > 2 and print $out "$err\n";
930 3         13 next;
931             }
932             }
933             # set group delete flag if this tag represents an entire group
934 4027 100 66     10089 if ($$tagInfo{DelGroup} and not $options{DelValue}) {
935 3         21 my @del = ( $tag );
936 3         12 $$self{DEL_GROUP}{$tag} = 1;
937             # delete extra groups if necessary
938 3 50       13 if ($delMore{$tag}) {
939 0         0 $$self{DEL_GROUP}{$_} = 1, push(@del,$_) foreach @{$delMore{$tag}};
  0         0  
940             }
941             # remove all of this group from previous new values
942 3         19 $self->RemoveNewValuesForGroup($tag);
943 3 50       10 $verbose and print $out " Deleting tags in: @del\n";
944 3         7 ++$numSet;
945 3         12 next;
946             }
947 4024         5902 $noConv = 1; # value is not defined, so don't do conversion
948             }
949             # apply inverse PrintConv and ValueConv conversions
950             # save ValueConv setting for use in ConvInv()
951 32139 100       64460 unless ($noConv) {
952             # set default conversion type used by ConvInv() and CHECK_PROC routines
953 27887         56482 $$self{ConvType} = $convType;
954 27887         42314 my $e;
955 27887         78437 ($val,$e) = $self->ConvInv($val,$tagInfo,$tag,$wgrp1,$$self{ConvType},$wantGroup);
956 27887 100       68405 if (defined $e) {
957             # empty error string causes error to be ignored without setting the value
958 8332 100       20235 $e or goto WriteAlso; # GOTO!
959 8315         15210 $err = $e;
960             }
961             }
962 32122 100 100     91054 if (not defined $val and defined $value) {
963             # if value conversion failed, we must still add a NEW_VALUE
964             # entry for this tag it it was a DelValue
965 2778 50       10817 next unless $options{DelValue};
966 0         0 $val = 'xxx never delete xxx';
967             }
968 29344 100       78655 $$self{NEW_VALUE} or $$self{NEW_VALUE} = { };
969 29344 100       76412 if ($options{Replace}) {
970             # delete the previous new value
971 14183         61614 $self->GetNewValueHash($tagInfo, $writeGroup, 'delete', $options{ProtectSaved});
972             # also delete related tag previous new values
973 14183 100       40126 if ($$tagInfo{WriteAlso}) {
974 27         108 my ($wgrp, $wtag);
975 27 100 66     215 if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) {
      66        
976 6         20 $wgrp = $writeGroup . ':';
977             } else {
978 21         84 $wgrp = '';
979             }
980 27         68 foreach $wtag (sort keys %{$$tagInfo{WriteAlso}}) {
  27         201  
981 95         500 my ($n,$e) = $self->SetNewValue($wgrp . $wtag, undef, Replace=>2);
982 95         263 $numSet += $n;
983             }
984             }
985 14183 100       33639 $options{Replace} == 2 and ++$numSet, next;
986             }
987              
988 29064 100 33     69214 if (defined $val) {
    100          
    50          
989             # we are editing this tag, so create a NEW_VALUE hash entry
990             my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create',
991 19713   66     75940 $options{ProtectSaved}, ($options{DelValue} and not $shift));
992             # ignore new values protected with ProtectSaved
993 19713 50       50115 $nvHash or ++$numSet, next; # (increment $numSet to avoid warning)
994 19713 100 100     51603 $$nvHash{NoReplace} = 1 if $$tagInfo{List} and not $options{Replace};
995 19713         39994 $$nvHash{WantGroup} = $wantGroup;
996 19713 100       40036 $$nvHash{EditOnly} = 1 if $editOnly;
997             # save maker note information if writing maker notes
998 19713 100       45506 if ($$tagInfo{MakerNotes}) {
999 22         111 $$nvHash{MAKER_NOTE_FIXUP} = $$self{MAKER_NOTE_FIXUP};
1000             }
1001 19713 100 100     99298 if ($createOnly) { # create only (never edit)
    100 100        
1002             # empty item in DelValue list to never edit existing value
1003 46         126 $$nvHash{DelValue} = [ '' ];
1004 46         168 $$nvHash{CreateOnly} = 1;
1005             } elsif ($options{DelValue} or $addValue or $shift) {
1006             # flag any AddValue or DelValue by creating the DelValue list
1007 227 100       742 $$nvHash{DelValue} or $$nvHash{DelValue} = [ ];
1008 227 100       530 if ($shift) {
    100          
1009             # add shift value to list
1010 160         376 $$nvHash{Shift} = $val;
1011             } elsif ($options{DelValue}) {
1012             # don't create if we are replacing a specific value
1013 54 100 100     228 $$nvHash{IsCreating} = 0 unless $val eq '' or $$tagInfo{List};
1014             # add delete value to list
1015 54 100       92 push @{$$nvHash{DelValue}}, ref $val eq 'ARRAY' ? @$val : $val;
  54         208  
1016 54 50       174 if ($verbose > 1) {
1017 0 0       0 my $verb = $permanent ? 'Replacing' : 'Deleting';
1018 0 0       0 my $fromList = $$tagInfo{List} ? ' from list' : '';
1019 0 0       0 my @vals = (ref $val eq 'ARRAY' ? @$val : $val);
1020 0         0 foreach (@vals) {
1021 0 0       0 if (ref $_ eq 'HASH') {
1022 0         0 require 'Image/ExifTool/XMPStruct.pl';
1023 0         0 $_ = Image::ExifTool::XMP::SerializeStruct($_);
1024             }
1025 0         0 print $out "$verb $wgrp1:$tag$fromList if value is '${_}'\n";
1026             }
1027             }
1028             }
1029             }
1030             # set priority flag to add only the high priority info
1031             # (will only create the priority tag if it doesn't exist,
1032             # others get changed only if they already exist)
1033 19713 100       53374 my $prf = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED};
1034             # hack to prefer only a single tag in the QuickTime group
1035 19713 100       56122 if ($$tagInfo{Table}{GROUPS}{0} eq 'QuickTime') {
1036 657 100       2791 $prf = 0 if $tagPriority{$tagInfo} < $highestQT{$lcTag};
1037             }
1038 19713 100 100     67703 if ($$pref{$tagInfo} or $prf) {
1039 9130 100 100     43628 if ($permanent or $shift) {
    100 100        
      66        
      100        
      100        
1040             # don't create permanent or Shift-ed tag but define IsCreating
1041             # so we know that it is the preferred tag
1042 5374         12439 $$nvHash{IsCreating} = 0;
1043             } elsif (($$tagInfo{List} and not $options{DelValue}) or
1044             not ($$nvHash{DelValue} and @{$$nvHash{DelValue}}) or
1045             # also create tag if any DelValue value is empty ('')
1046 58         398 grep(/^$/,@{$$nvHash{DelValue}}))
1047             {
1048 3742 100       12083 $$nvHash{IsCreating} = $editOnly ? 0 : ($editGroup ? 2 : 1);
    100          
1049             # add to hash of groups where this tag is being created
1050 3742 100 100     13727 $createGroups or $createGroups = $options{CreateGroups} || { };
1051 3742         14647 $$createGroups{$self->GetGroup($tagInfo, 0)} = 1;
1052 3742         10315 $$nvHash{CreateGroups} = $createGroups;
1053             }
1054             }
1055 19713 100       53590 if ($$nvHash{IsCreating}) {
    100          
1056 3732 100       5668 if (%{$$self{DEL_GROUP}}) {
  3732         11656  
1057 227         460 my ($grp, @grps);
1058 227         382 foreach $grp (keys %{$$self{DEL_GROUP}}) {
  227         3013  
1059 12589 100       23756 next if $$self{DEL_GROUP}{$grp} == 2;
1060             # set flag indicating tags were written after this group was deleted
1061 354         490 $$self{DEL_GROUP}{$grp} = 2;
1062 354         579 push @grps, $grp;
1063             }
1064 227 100 66     1161 if ($verbose > 1 and @grps) {
1065 1         5 @grps = sort @grps;
1066 1         11 print $out " Writing new tags after deleting groups: @grps\n";
1067             }
1068             }
1069             } elsif ($createOnly) {
1070 19 100       80 $noCreate = $permanent ? 'permanent' : ($$tagInfo{Avoid} ? 'avoided' : '');
    100          
1071 19 50       58 $noCreate or $noCreate = $shift ? 'shifting' : 'not preferred';
    100          
1072 19 50       54 $verbose > 2 and print $out "Not creating $wgrp1:$tag ($noCreate)\n";
1073 19         67 next; # nothing to do (not creating and not editing)
1074             }
1075 19694 100 100     68441 if ($shift or not $options{DelValue}) {
1076 19640 100       60878 $$nvHash{Value} or $$nvHash{Value} = [ ];
1077 19640 100 33     45007 if (not $$tagInfo{List}) {
    50          
1078             # not a List tag -- overwrite existing value
1079 19135         44951 $$nvHash{Value}[0] = $val;
1080 0         0 } elsif (defined $$nvHash{AddBefore} and @{$$nvHash{Value}} >= $$nvHash{AddBefore}) {
1081             # values from a later argument have been added (ie. Replace=0)
1082             # to this list, so the new values should come before these
1083 0 0       0 splice @{$$nvHash{Value}}, -$$nvHash{AddBefore}, 0, ref $val eq 'ARRAY' ? @$val : $val;
  0         0  
1084             } else {
1085             # add at end of existing list
1086 505 100       872 push @{$$nvHash{Value}}, ref $val eq 'ARRAY' ? @$val : $val;
  505         1900  
1087             }
1088 19640 100       48490 if ($verbose > 1) {
1089             my $ifExists = $$nvHash{IsCreating} ? ( $createOnly ?
1090             ($$nvHash{IsCreating} == 2 ?
1091             " if $writeGroup exists and tag doesn't" :
1092             " if tag doesn't exist") :
1093             ($$nvHash{IsCreating} == 2 ? " if $writeGroup exists" : '')) :
1094 26 0 33     110 (($$nvHash{DelValue} and @{$$nvHash{DelValue}}) ?
    50          
    50          
    50          
    100          
1095             ' if tag was deleted' : ' if tag exists');
1096 26 50       68 my $verb = ($shift ? 'Shifting' : ($addValue ? 'Adding' : 'Writing'));
    50          
1097 26         119 print $out "$verb $wgrp1:$tag$ifExists\n";
1098             }
1099             }
1100             } elsif ($permanent) {
1101 5439         12290 $err = "Can't delete Permanent tag $wgrp1:$tag";
1102 5439 50       10830 $verbose > 1 and print $out "$err\n";
1103 5439         15979 next;
1104             } elsif ($addValue or $options{DelValue}) {
1105 0 0       0 $verbose > 1 and print $out "Adding/Deleting nothing does nothing\n";
1106 0         0 next;
1107             } else {
1108             # create empty new value hash entry to delete this tag
1109 3912         11522 $self->GetNewValueHash($tagInfo, $writeGroup, 'delete');
1110 3912         8701 my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create');
1111 3912         8876 $$nvHash{WantGroup} = $wantGroup;
1112 3912 50       10610 $verbose > 1 and print $out "Deleting $wgrp1:$tag\n";
1113             }
1114 23606 100       46788 $$setTags{$tagInfo} = 1 if $setTags;
1115 23606 100       61633 $prioritySet = 1 if $$pref{$tagInfo};
1116 23626         37111 WriteAlso:
1117             ++$numSet;
1118             # also write related tags
1119 23626         40555 my $writeAlso = $$tagInfo{WriteAlso};
1120 23626 100       73090 if ($writeAlso) {
1121 93         298 my ($wgrp, $wtag, $n);
1122 93 100 66     805 if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) {
      66        
1123 43         143 $wgrp = $writeGroup . ':';
1124             } else {
1125 50         120 $wgrp = '';
1126             }
1127 93         592 local $SIG{'__WARN__'} = \&SetWarning;
1128 93         697 foreach $wtag (sort keys %$writeAlso) {
1129             my %opts = (
1130             Type => 'ValueConv',
1131             Protected => $protected | 0x02,
1132             AddValue => $addValue,
1133             DelValue => $options{DelValue},
1134             Shift => $options{Shift},
1135             Replace => $options{Replace}, # handle lists properly
1136 274         2249 CreateGroups=> $createGroups,
1137             SetTags => \%alsoWrote, # remember tags already written
1138             );
1139 274         591 undef $evalWarning;
1140             #### eval WriteAlso ($val)
1141 274         21414 my $v = eval $$writeAlso{$wtag};
1142             # we wanted to do the eval in case there are side effect, but we
1143             # don't want to write a value for a tag that is being deleted:
1144 274 100       1456 undef $v unless defined $val;
1145 274 50       820 $@ and $evalWarning = $@;
1146 274 50       746 unless ($evalWarning) {
1147 274         2198 ($n,$evalWarning) = $self->SetNewValue($wgrp . $wtag, $v, %opts);
1148 274         871 $numSet += $n;
1149             # count this as being set if any related tag is set
1150 274 100 100     1529 $prioritySet = 1 if $n and $$pref{$tagInfo};
1151             }
1152 274 100 66     1592 if ($evalWarning and (not $err or $verbose > 2)) {
      66        
1153 9         44 my $str = CleanWarning();
1154 9 50       34 if ($str) {
1155 9 50       75 $str .= " for $wtag" unless $str =~ / for [-\w:]+$/;
1156 9         45 $str .= " in $wgrp1:$tag (WriteAlso)";
1157 9 50       43 $err or $err = $str;
1158 9 50       93 print $out "$str\n" if $verbose > 2;
1159             }
1160             }
1161             }
1162             }
1163             }
1164             # print warning if we couldn't set our priority tag
1165 4243 100 100     24800 if (defined $err and not $prioritySet) {
    100 66        
    50          
    100          
1166 85 50 33     568 warn "$err\n" if $err and not wantarray;
1167             } elsif (not $numSet) {
1168 619 100       2199 my $pre = $wantGroup ? $wantGroup . ':' : '';
1169 619 100       1834 if ($wasProtected) {
    100          
1170 372         684 $verbose = 0; # we already printed this verbose message
1171 372 100 100     1962 unless ($options{Replace} and $options{Replace} == 2) {
1172 360         1370 $err = "Sorry, $pre$tag is $wasProtected for writing";
1173             }
1174             } elsif (not $listOnly) {
1175 240 50 33     1574 if ($origTag =~ /[?*]/) {
    50          
    50          
    50          
1176 0 0       0 if ($noCreate) {
    0          
1177 0         0 $err = "No tags matching 'pre${origTag}' will be created";
1178 0         0 $verbose = 0; # (already printed)
1179             } elsif ($foundMatch) {
1180 0         0 $err = "Sorry, no writable tags matching '$pre${origTag}'";
1181             } else {
1182 0         0 $err = "No matching tags for '$pre${origTag}'";
1183             }
1184             } elsif ($noCreate) {
1185 0         0 $err = "Not creating $pre$tag";
1186 0         0 $verbose = 0; # (already printed)
1187             } elsif ($foundMatch) {
1188 0         0 $err = "Sorry, $pre$tag is not writable";
1189             } elsif ($wantGroup and @matchingTags) {
1190 240         768 $err = "Sorry, $pre$tag doesn't exist or isn't writable";
1191             } else {
1192 0         0 $err = "Tag '$pre${tag}' is not defined";
1193             }
1194             }
1195 619 100       1607 if ($err) {
1196 600 50       1438 $verbose > 2 and print $out "$err\n";
1197 600 50       1613 warn "$err\n" unless wantarray;
1198             }
1199             } elsif ($$self{CHECK_WARN}) {
1200 0         0 $err = $$self{CHECK_WARN};
1201 0 0       0 $verbose > 2 and print $out "$err\n";
1202             } elsif ($err and not $verbose) {
1203 434         1055 undef $err;
1204             }
1205 4243 100       42012 return ($numSet, $err) if wantarray;
1206 419         31137 return $numSet;
1207             }
1208              
1209             #------------------------------------------------------------------------------
1210             # set new values from information in specified file
1211             # Inputs: 0) ExifTool object reference, 1) source file name or reference, etc
1212             # 2-N) List of tags to set (or all if none specified), or reference(s) to
1213             # hash for options to pass to SetNewValue. The Replace option defaults
1214             # to 1 for SetNewValuesFromFile -- set this to 0 to allow multiple tags
1215             # to be copied to a list
1216             # Returns: Hash of information set successfully (includes Warning or Error messages)
1217             # Notes: Tag names may contain a group prefix, a leading '-' to exclude from copy,
1218             # and/or a trailing '#' to copy the ValueConv value. The tag name '*' may
1219             # be used to represent all tags in a group. An optional destination tag
1220             # may be specified with '>DSTTAG' ('DSTTAG
1221             # case the source tag may also be an expression involving tag names).
1222             sub SetNewValuesFromFile($$;@)
1223             {
1224 58     58 1 1234 local $_;
1225 58         266 my ($self, $srcFile, @setTags) = @_;
1226 58         176 my ($key, $tag, @exclude, @reqTags);
1227              
1228             # get initial SetNewValuesFromFile options
1229 58         269 my %opts = ( Replace => 1 ); # replace existing list items by default
1230 58         384 while (ref $setTags[0] eq 'HASH') {
1231 1         5 $_ = shift @setTags;
1232 1         9 foreach $key (keys %$_) {
1233 1         18 $opts{$key} = $$_{$key};
1234             }
1235             }
1236             # expand shortcuts
1237 58 100       439 @setTags and ExpandShortcuts(\@setTags);
1238 58         384 my $srcExifTool = new Image::ExifTool;
1239             # set flag to indicate we are being called from inside SetNewValuesFromFile()
1240 58         291 $$srcExifTool{TAGS_FROM_FILE} = 1;
1241             # synchronize and increment the file sequence number
1242 58         301 $$srcExifTool{FILE_SEQUENCE} = $$self{FILE_SEQUENCE}++;
1243             # set options for our extraction tool
1244 58         185 my $options = $$self{OPTIONS};
1245             # copy both structured and flattened tags by default (but flattened tags are "unsafe")
1246 58 50       283 my $structOpt = defined $$options{Struct} ? $$options{Struct} : 2;
1247             # copy structures only if no tags specified (since flattened tags are "unsafe")
1248 58 100 66     498 $structOpt = 1 if $structOpt eq '2' and not @setTags;
1249             # +------------------------------------------+
1250             # ! DON'T FORGET!! Must consider each new !
1251             # ! option to decide how it is handled here. !
1252             # +------------------------------------------+
1253             $srcExifTool->Options(
1254             Binary => 1,
1255             Charset => $$options{Charset},
1256             CharsetEXIF => $$options{CharsetEXIF},
1257             CharsetFileName => $$options{CharsetFileName},
1258             CharsetID3 => $$options{CharsetID3},
1259             CharsetIPTC => $$options{CharsetIPTC},
1260             CharsetPhotoshop=> $$options{CharsetPhotoshop},
1261             Composite => $$options{Composite},
1262             CoordFormat => $$options{CoordFormat} || '%d %d %.8f', # copy coordinates at high resolution unless otherwise specified
1263             DateFormat => $$options{DateFormat},
1264             Duplicates => 1,
1265             Escape => $$options{Escape},
1266             # Exclude (set below)
1267             ExtendedXMP => $$options{ExtendedXMP},
1268             ExtractEmbedded => $$options{ExtractEmbedded},
1269             FastScan => $$options{FastScan},
1270             Filter => $$options{Filter},
1271             FixBase => $$options{FixBase},
1272             GlobalTimeShift => $$options{GlobalTimeShift},
1273             HexTagIDs => $$options{HexTagIDs},
1274             IgnoreMinorErrors=>$$options{IgnoreMinorErrors},
1275             IgnoreTags => $$options{IgnoreTags},
1276             Lang => $$options{Lang},
1277             LargeFileSupport=> $$options{LargeFileSupport},
1278             List => 1,
1279             ListItem => $$options{ListItem},
1280             ListSep => $$options{ListSep},
1281             MakerNotes => $$options{FastScan} && $$options{FastScan} > 1 ? undef : 1,
1282             MDItemTags => $$options{MDItemTags},
1283             MissingTagValue => $$options{MissingTagValue},
1284             NoPDFList => $$options{NoPDFList},
1285             Password => $$options{Password},
1286             PrintConv => $$options{PrintConv},
1287             QuickTimeUTC => $$options{QuickTimeUTC},
1288             RequestAll => $$options{RequestAll} || 1, # (is this still necessary now that RequestTags are being set?)
1289             RequestTags => $$options{RequestTags},
1290             SaveFormat => $$options{SaveFormat},
1291             SavePath => $$options{SavePath},
1292             ScanForXMP => $$options{ScanForXMP},
1293             StrictDate => defined $$options{StrictDate} ? $$options{StrictDate} : 1,
1294             Struct => $structOpt,
1295             SystemTags => $$options{SystemTags},
1296             TimeZone => $$options{TimeZone},
1297             Unknown => $$options{Unknown},
1298             UserParam => $$options{UserParam},
1299             Validate => $$options{Validate},
1300             XAttrTags => $$options{XAttrTags},
1301             XMPAutoConv => $$options{XMPAutoConv},
1302 58 50 50     2517 );
    50 33        
      50        
1303 58         282 $$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
1304 58         253 foreach $tag (@setTags) {
1305 62 100       275 next if ref $tag;
1306 61 100       269 if ($tag =~ /^-(.*)/) {
1307             # avoid extracting tags that are excluded
1308 7         24 push @exclude, $1;
1309 7         56 next;
1310             }
1311             # add specified tags to list of requested tags
1312 54         180 $_ = $tag;
1313 54 100       610 if (/(.+?)\s*(>|<)\s*(.+)/) {
1314 23 100       113 if ($2 eq '>') {
1315 10         34 $_ = $1;
1316             } else {
1317 13         50 $_ = $3;
1318 13 100       99 /\$/ and push(@reqTags, /\$\{?(?:[-\w]+:)*([-\w?*]+)/g), next;
1319             }
1320             }
1321 49 50       506 push @reqTags, $2 if /(^|:)([-\w?*]+)#?$/;
1322             }
1323 58 100       273 if (@exclude) {
1324 6         36 ExpandShortcuts(\@exclude, 1);
1325 6         41 $srcExifTool->Options(Exclude => \@exclude);
1326             }
1327 58 100       411 $srcExifTool->Options(RequestTags => \@reqTags) if @reqTags;
1328 58         233 my $printConv = $$options{PrintConv};
1329 58 50       316 if ($opts{Type}) {
1330             # save source type separately because it may be different than dst Type
1331 0         0 $opts{SrcType} = $opts{Type};
1332             # override PrintConv option with initial Type if given
1333 0 0       0 $printConv = ($opts{Type} eq 'PrintConv' ? 1 : 0);
1334 0         0 $srcExifTool->Options(PrintConv => $printConv);
1335             }
1336 58 100       275 my $srcType = $printConv ? 'PrintConv' : 'ValueConv';
1337              
1338             # get all tags from source file (including MakerNotes block)
1339 58         308 my $info = $srcExifTool->ImageInfo($srcFile);
1340 58 50 33     433 return $info if $$info{Error} and $$info{Error} eq 'Error opening file';
1341 58         222 delete $$srcExifTool{VALUE}{Error}; # delete so we can check this later
1342              
1343             # sort tags in reverse order so we get priority tag last
1344 58         5655 my @tags = reverse sort keys %$info;
1345             #
1346             # simply transfer all tags from source image if no tags specified
1347             #
1348 58 100       644 unless (@setTags) {
1349             # transfer maker note information to this object
1350 15         83 $$self{MAKER_NOTE_FIXUP} = $$srcExifTool{MAKER_NOTE_FIXUP};
1351 15         61 $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER};
1352 15         62 foreach $tag (@tags) {
1353             # don't try to set errors or warnings
1354 2649 100       10845 next if $tag =~ /^(Error|Warning)\b/;
1355             # get appropriate value type if necessary
1356 2645 50 33     7848 if ($opts{SrcType} and $opts{SrcType} ne $srcType) {
1357 0         0 $$info{$tag} = $srcExifTool->GetValue($tag, $opts{SrcType});
1358             }
1359             # set value for this tag
1360 2645         12124 my ($n, $e) = $self->SetNewValue($tag, $$info{$tag}, %opts);
1361             # delete this tag if we couldn't set it
1362 2645 100       10281 $n or delete $$info{$tag};
1363             }
1364 15         1409 return $info;
1365             }
1366             #
1367             # transfer specified tags in the proper order
1368             #
1369             # 1) loop through input list of tags to set, and build @setList
1370 43         181 my (@setList, $set, %setMatches, $t);
1371 43         162 foreach $t (@setTags) {
1372 62 100       316 if (ref $t eq 'HASH') {
1373             # update current options
1374 1         7 foreach $key (keys %$t) {
1375 1         5 $opts{$key} = $$t{$key};
1376             }
1377 1         3 next;
1378             }
1379             # make a copy of the current options for this setTag
1380             # (also use this hash to store expression and wildcard flags, EXPR and WILD)
1381 61         291 my $opts = { %opts };
1382 61         252 $tag = lc $t; # change tag/group names to all lower case
1383 61         178 my (@fg, $grp, $dst, $dstGrp, $dstTag, $isExclude);
1384             # handle redirection to another tag
1385 61 100       804 if ($tag =~ /(.+?)\s*(>|<)\s*(.+)/) {
1386 23         63 $dstGrp = '';
1387 23         48 my $opt;
1388 23 100       110 if ($2 eq '>') {
1389 10         50 ($tag, $dstTag) = ($1, $3);
1390             # flag add and delete (eg. '+<' and '-<') redirections
1391 10 50 33     100 $opt = $1 if $tag =~ s/\s*([-+])$// or $dstTag =~ s/^([-+])\s*//;
1392             } else {
1393 13         76 ($tag, $dstTag) = ($3, $1);
1394 13 50       76 $opt = $1 if $dstTag =~ s/\s*([-+])$//;
1395             # handle expressions
1396 13 100       62 if ($tag =~ /\$/) {
1397 5         18 $tag = $t; # restore original case
1398             # recover leading whitespace (except for initial single space)
1399 5         45 $tag =~ s/(.+?)\s*(>|<) ?//;
1400 5         26 $$opts{EXPR} = 1; # flag this expression
1401             } else {
1402 8 50       40 $opt = $1 if $tag =~ s/^([-+])\s*//;
1403             }
1404             }
1405             # validate tag name(s)
1406 23 50 66     178 unless ($$opts{EXPR} or ValidTagName($tag)) {
1407 0         0 $self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value");
1408 0         0 next;
1409             }
1410 23 50       110 ValidTagName($dstTag) or $self->Warn("Invalid tag name '${dstTag}'"), next;
1411             # translate '+' and '-' to appropriate SetNewValue option
1412 23 50       106 if ($opt) {
1413 0         0 $$opts{{ '+' => 'AddValue', '-' => 'DelValue' }->{$opt}} = 1;
1414 0         0 $$opts{Shift} = 0; # shift if shiftable
1415             }
1416 23 100       161 ($dstGrp, $dstTag) = ($1, $2) if $dstTag =~ /(.*):(.+)/;
1417             # ValueConv may be specified separately on the destination with '#'
1418 23 50       111 $$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//;
1419             # replace tag name of 'all' with '*'
1420 23 100       95 $dstTag = '*' if $dstTag eq 'all';
1421             }
1422 61 100       311 unless ($$opts{EXPR}) {
1423 56         235 $isExclude = ($tag =~ s/^-//);
1424 56 100       303 if ($tag =~ /(.*):(.+)/) {
1425 31         152 ($grp, $tag) = ($1, $2);
1426 31         160 foreach (split /:/, $grp) {
1427             # save family/groups in list (ignoring 'all' and '*')
1428 32 50 33     320 next unless length($_) and /^(\d+)?(.*)/;
1429 32         131 my ($f, $g) = ($1, $2);
1430 32 50       160 $f = 7 if $g =~ s/^ID-//i;
1431 32 100 100     301 push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all';
1432             }
1433             }
1434             # allow ValueConv to be specified by a '#' on the tag name
1435 56 50       248 if ($tag =~ s/#$//) {
1436 0         0 $$opts{SrcType} = 'ValueConv';
1437 0 0       0 $$opts{Type} = 'ValueConv' unless $dstTag;
1438             }
1439             # replace 'all' with '*' in tag and group names
1440 56 100       209 $tag = '*' if $tag eq 'all';
1441             # allow wildcards in tag names (handle differently from all tags: '*')
1442 56 100 100     424 if ($tag =~ /[?*]/ and $tag ne '*') {
1443 2         9 $$opts{WILD} = 1; # set flag indicating wildcards were used in source tag
1444 2         7 $tag =~ s/\*/[-\\w]*/g;
1445 2         13 $tag =~ s/\?/[-\\w]/g;
1446             }
1447             }
1448             # redirect, exclude or set this tag (Note: @fg is empty if we don't care about the group)
1449 61 100       270 if ($dstTag) {
    100          
1450             # redirect this tag
1451 23 50       93 $isExclude and return { Error => "Can't redirect excluded tag" };
1452             # set destination group the same as source if necessary
1453             # (removed in 7.72 so '-*:*
1454             # $dstGrp = $grp if $dstGrp eq '*' and $grp;
1455             # write to specified destination group/tag
1456 23         88 $dst = [ $dstGrp, $dstTag ];
1457             } elsif ($isExclude) {
1458             # implicitly assume '*' if first entry is an exclusion
1459 7 100       40 unshift @setList, [ [ ], '*', [ '', '*' ], $opts ] unless @setList;
1460             # exclude this tag by leaving $dst undefined
1461             } else {
1462 31 100 100     270 $dst = [ $grp || '', $$opts{WILD} ? '*' : $tag ]; # use same group name for dest
1463             }
1464             # save in reverse order so we don't set tags before an exclude
1465 61         350 unshift @setList, [ \@fg, $tag, $dst, $opts ];
1466             }
1467             # 2) initialize lists of matching tags for each setTag
1468 43         162 foreach $set (@setList) {
1469 62 100       399 $$set[2] and $setMatches{$set} = [ ];
1470             }
1471             # 3) loop through all tags in source image and save tags matching each setTag
1472 43         122 my %rtnInfo;
1473 43         148 foreach $tag (@tags) {
1474             # don't try to set errors or warnings
1475 6295 100       15357 if ($tag =~ /^(Error|Warning)( |$)/) {
1476 13         45 $rtnInfo{$tag} = $$info{$tag};
1477 13         31 next;
1478             }
1479             # only set specified tags
1480 6282         13031 my $lcTag = lc(GetTagName($tag));
1481 6282         10484 my (@grp, %grp);
1482 6282         10183 SET: foreach $set (@setList) {
1483             # check first for matching tag
1484 8868 100 100     27453 unless ($$set[1] eq $lcTag or $$set[1] eq '*') {
1485             # handle wildcards
1486 6252 100 100     17319 next unless $$set[3]{WILD} and $lcTag =~ /^$$set[1]$/;
1487             }
1488             # then check for matching group
1489 2630 100       3804 if (@{$$set[0]}) {
  2630         5393  
1490             # get lower case group names if not done already
1491 1468 100       2913 unless (@grp) {
1492 1365         3344 @grp = map(lc, $srcExifTool->GetGroup($tag));
1493 1365         8249 $grp{$_} = 1 foreach @grp;
1494             }
1495 1468         2367 foreach (@{$$set[0]}) {
  1468         3018  
1496 1510         2982 my ($f, $g) = @$_;
1497 1510 50       2946 if (not defined $f) {
    0          
1498 1510 100       5119 next SET unless $grp{$g};
1499             } elsif ($f == 7) {
1500 0 0       0 next SET unless IsSameID($srcExifTool->GetTagID($tag), $g);
1501             } else {
1502 0 0 0     0 next SET unless defined $grp[$f] and $g eq $grp[$f];
1503             }
1504             }
1505             }
1506 1616 100       3762 last unless $$set[2]; # all done if we hit an exclude
1507             # add to the list of tags matching this setTag
1508 1442         1986 push @{$setMatches{$set}}, $tag;
  1442         5125  
1509             }
1510             }
1511             # 4) loop through each setTag in original order, setting new tag values
1512 43         222 foreach $set (reverse @setList) {
1513             # get options for SetNewValue
1514 62         189 my $opts = $$set[3];
1515             # handle expressions
1516 62 100       253 if ($$opts{EXPR}) {
1517 5         43 my $val = $srcExifTool->InsertTagValues(\@tags, $$set[1], 'Error');
1518 5 50       29 if ($$srcExifTool{VALUE}{Error}) {
1519             # pass on any error as a warning
1520 0         0 $tag = NextFreeTagKey(\%rtnInfo, 'Warning');
1521 0         0 $rtnInfo{$tag} = $$srcExifTool{VALUE}{Error};
1522 0         0 delete $$srcExifTool{VALUE}{Error};
1523 0 0       0 next unless defined $val;
1524             }
1525 5         14 my ($dstGrp, $dstTag) = @{$$set[2]};
  5         27  
1526 5 50 33     46 $$opts{Protected} = 1 unless $dstTag =~ /[?*]/ and $dstTag ne '*';
1527 5 50       24 $$opts{Group} = $dstGrp if $dstGrp;
1528 5         48 my @rtnVals = $self->SetNewValue($dstTag, $val, %$opts);
1529 5 50       32 $rtnInfo{$dstTag} = $val if $rtnVals[0]; # tag was set successfully
1530 5         20 next;
1531             }
1532 57         138 foreach $tag (@{$setMatches{$set}}) {
  57         244  
1533 1442         2440 my ($val, $noWarn);
1534 1442 50 33     4596 if ($$opts{SrcType} and $$opts{SrcType} ne $srcType) {
1535 0         0 $val = $srcExifTool->GetValue($tag, $$opts{SrcType});
1536             } else {
1537 1442         4721 $val = $$info{$tag};
1538             }
1539 1442         2303 my ($dstGrp, $dstTag) = @{$$set[2]};
  1442         3806  
1540 1442 100       3003 if ($dstGrp) {
1541 1364         3883 my @dstGrp = split /:/, $dstGrp;
1542             # destination group of '*' writes to same group as source tag
1543             # (family 1 unless otherwise specified)
1544 1364         2965 foreach (@dstGrp) {
1545 1366 100       6739 next unless /^(\d*)(all|\*)$/i;
1546 1082 50       5428 $_ = $1 . $srcExifTool->GetGroup($tag, length $1 ? $1 : 1);
1547 1082         2495 $noWarn = 1; # don't warn on wildcard destinations
1548             }
1549 1364         4416 $$opts{Group} = join ':', @dstGrp;
1550             } else {
1551 78         174 delete $$opts{Group};
1552             }
1553             # transfer maker note information if setting this tag
1554 1442 100       5030 if ($$srcExifTool{TAG_INFO}{$tag}{MakerNotes}) {
1555 7         50 $$self{MAKER_NOTE_FIXUP} = $$srcExifTool{MAKER_NOTE_FIXUP};
1556 7         30 $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER};
1557             }
1558 1442 100       3620 if ($dstTag eq '*') {
1559 1415         2306 $dstTag = $tag;
1560 1415         2163 $noWarn = 1;
1561             }
1562 1442 100 100     4290 if ($$set[1] eq '*' or $$set[3]{WILD}) {
1563             # don't copy from protected binary tags when using wildcards
1564             next if $$srcExifTool{TAG_INFO}{$tag}{Protected} and
1565 1409 100 100     4334 $$srcExifTool{TAG_INFO}{$tag}{Binary};
1566             # don't copy to protected tags when using wildcards
1567 1383         2303 delete $$opts{Protected};
1568             # don't copy flattened tags if copying structures too when copying all
1569 1383 50       3447 $$opts{NoFlat} = $structOpt eq '2' ? 1 : 0;
1570             } else {
1571             # allow protected tags to be copied if specified explicitly
1572 33 50       185 $$opts{Protected} = 1 unless $dstTag =~ /[?*]/;
1573 33         93 delete $$opts{NoFlat};
1574             }
1575             # set value(s) for this tag
1576 1416         6038 my ($rtn, $wrn) = $self->SetNewValue($dstTag, $val, %$opts);
1577             # this was added in version 9.14, and allowed actions like "-subject
1578             # write values of multiple tags into a list, but it had the side effect of
1579             # duplicating items if there were multiple list tags with the same name
1580             # (eg. -use mwg "-creator
1581             # $$opts{Replace} = 0; # accumulate values from tags matching a single argument
1582 1416 50 66     5559 if ($wrn and not $noWarn) {
1583             # return this warning
1584 0         0 $rtnInfo{NextFreeTagKey(\%rtnInfo, 'Warning')} = $wrn;
1585 0         0 $noWarn = 1;
1586             }
1587 1416 100       5471 $rtnInfo{$tag} = $val if $rtn; # tag was set successfully
1588             }
1589             }
1590 43         3208 return \%rtnInfo; # return information that we set
1591             }
1592              
1593             #------------------------------------------------------------------------------
1594             # Get new value(s) for tag
1595             # Inputs: 0) ExifTool object reference, 1) tag name (or tagInfo or nvHash ref, not public)
1596             # 2) optional pointer to return new value hash reference (not part of public API)
1597             # Returns: List of new Raw values (list may be empty if tag is being deleted)
1598             # Notes: 1) Preferentially returns new value from Extra table if writable Extra tag exists
1599             # 2) Must call AFTER IsOverwriting() returns 1 to get proper value for shifted times
1600             # 3) Tag name is case sensitive and may be prefixed by family 0 or 1 group name
1601             # 4) Value may have been modified by CHECK_PROC routine after ValueConv
1602             sub GetNewValue($$;$)
1603             {
1604 6705     6705 1 11867 local $_;
1605 6705         11307 my $self = shift;
1606 6705         11159 my $tag = shift;
1607 6705         10752 my $nvHash;
1608 6705 100 100     32449 if ((ref $tag eq 'HASH' and $$tag{IsNVH}) or not defined $tag) {
      100        
1609 3990         7316 $nvHash = $tag;
1610             } else {
1611 2715         4427 my $newValueHashPt = shift;
1612 2715 100       6784 if ($$self{NEW_VALUE}) {
1613 2598         4602 my ($group, $tagInfo);
1614 2598 100 66     13542 if (ref $tag) {
    100          
1615 49         178 $nvHash = $self->GetNewValueHash($tag);
1616             } elsif (defined($tagInfo = $Image::ExifTool::Extra{$tag}) and
1617             $$tagInfo{Writable})
1618             {
1619 1471         3596 $nvHash = $self->GetNewValueHash($tagInfo);
1620             } else {
1621             # separate group from tag name
1622 1078         1873 my @groups;
1623 1078 100       3327 @groups = split ':', $1 if $tag =~ s/(.*)://;
1624 1078         3906 my @tagInfoList = FindTagInfo($tag);
1625             # decide which tag we want
1626 1078         2359 GNV_TagInfo: foreach $tagInfo (@tagInfoList) {
1627 1082 100       2733 my $nvh = $self->GetNewValueHash($tagInfo) or next;
1628             # select tag in specified group(s) if necessary
1629 4         10 foreach (@groups) {
1630 2 50       14 next if $_ eq $$nvh{WriteGroup};
1631 2         39 my @grps = $self->GetGroup($tagInfo);
1632 2 50       9 if ($grps[0] eq $$nvh{WriteGroup}) {
1633             # check family 1 group only if WriteGroup is not specific
1634 0 0       0 next if $_ eq $grps[1];
1635             } else {
1636             # otherwise check family 0 group
1637 2 50       9 next if $_ eq $grps[0];
1638             }
1639             # also check family 7
1640 0 0 0     0 next if /^ID-(.*)/i and IsSameID($$tagInfo{TagID}, $1);
1641             # step to next entry in list
1642 0 0       0 $nvh = $$nvh{Next} or next GNV_TagInfo;
1643             }
1644 4         10 $nvHash = $nvh;
1645             # give priority to the one we are creating
1646 4 100       25 last if defined $$nvHash{IsCreating};
1647             }
1648             }
1649             }
1650             # return new value hash if requested
1651 2715 100       7424 $newValueHashPt and $$newValueHashPt = $nvHash;
1652             }
1653 6705 100 100     22853 unless ($nvHash and $$nvHash{Value}) {
1654 4435 100       17000 return () if wantarray; # return empty list
1655 2662         7304 return undef;
1656             }
1657 2270         4368 my $vals = $$nvHash{Value};
1658             # do inverse raw conversion if necessary
1659             # - must also check after doing a Shift
1660 2270 100 100     9403 if ($$nvHash{TagInfo}{RawConvInv} or $$nvHash{Shift}) {
1661 60         254 my @copyVals = @$vals; # modify a copy of the values
1662 60         166 $vals = \@copyVals;
1663 60         187 my $tagInfo = $$nvHash{TagInfo};
1664 60         149 my $conv = $$tagInfo{RawConvInv};
1665 60         142 my $table = $$tagInfo{Table};
1666 60         134 my ($val, $checkProc);
1667 60 100 66     296 $checkProc = $$table{CHECK_PROC} if $$nvHash{Shift} and $table;
1668 60         340 local $SIG{'__WARN__'} = \&SetWarning;
1669 60         182 undef $evalWarning;
1670 60         170 foreach $val (@$vals) {
1671             # must check value now if it was shifted
1672 60 100       203 if ($checkProc) {
1673 26         102 my $err = &$checkProc($self, $tagInfo, \$val);
1674 26 50 33     133 if ($err or not defined $val) {
1675 0 0       0 $err or $err = 'Error generating raw value';
1676 0         0 $self->WarnOnce("$err for $$tagInfo{Name}");
1677 0         0 @$vals = ();
1678 0         0 last;
1679             }
1680 26 50       136 next unless $conv;
1681             } else {
1682 34 50       120 last unless $conv;
1683             }
1684             # do inverse raw conversion
1685 34 100       133 if (ref($conv) eq 'CODE') {
1686 2         9 $val = &$conv($val, $self);
1687             } else {
1688             #### eval RawConvInv ($self, $val, $tagInfo)
1689 32         3777 $val = eval $conv;
1690 32 50       215 $@ and $evalWarning = $@;
1691             }
1692 34 50       231 if ($evalWarning) {
1693             # an empty warning ("\n") ignores tag with no error
1694 0 0       0 if ($evalWarning ne "\n") {
1695 0         0 my $err = CleanWarning() . " in $$tagInfo{Name} (RawConvInv)";
1696 0         0 $self->WarnOnce($err);
1697             }
1698 0         0 @$vals = ();
1699 0         0 last;
1700             }
1701             }
1702             }
1703             # return our value(s)
1704 2270 100       9185 return @$vals if wantarray;
1705 1160         4905 return $$vals[0];
1706             }
1707              
1708             #------------------------------------------------------------------------------
1709             # Return the total number of new values set
1710             # Inputs: 0) ExifTool object reference
1711             # Returns: Scalar context) Number of new values that have been set (incl pseudo)
1712             # List context) Number of new values (incl pseudo), number of "pseudo" values
1713             # ("pseudo" values are those which don't require rewriting the file to change)
1714             sub CountNewValues($)
1715             {
1716 235     235 1 629 my $self = shift;
1717 235         694 my $newVal = $$self{NEW_VALUE};
1718 235         772 my ($num, $pseudo) = (0, 0);
1719 235 100       871 if ($newVal) {
1720 216         1003 $num = scalar keys %$newVal;
1721 216         484 my $nv;
1722 216         2750 foreach $nv (values %$newVal) {
1723 19092         43822 my $tagInfo = $$nv{TagInfo};
1724             # don't count tags that don't write anything
1725 19092 100       41705 $$tagInfo{WriteNothing} and --$num, next;
1726             # count the number of pseudo tags included
1727 19077 100       42901 $$tagInfo{WritePseudo} and ++$pseudo;
1728             }
1729             }
1730 235         722 $num += scalar keys %{$$self{DEL_GROUP}};
  235         1033  
1731 235 50       1056 return $num unless wantarray;
1732 235         925 return ($num, $pseudo);
1733             }
1734              
1735             #------------------------------------------------------------------------------
1736             # Save new values for subsequent restore
1737             # Inputs: 0) ExifTool object reference
1738             # Returns: Number of times new values have been saved
1739             # Notes: increments SAVE_COUNT flag each time routine is called
1740             sub SaveNewValues($)
1741             {
1742 1     1 1 12 my $self = shift;
1743 1         5 my $newValues = $$self{NEW_VALUE};
1744 1         4 my $saveCount = ++$$self{SAVE_COUNT};
1745 1         3 my $key;
1746 1         28 foreach $key (keys %$newValues) {
1747 112         170 my $nvHash = $$newValues{$key};
1748 112         194 while ($nvHash) {
1749             # set Save count if not done already
1750 113 50       284 $$nvHash{Save} or $$nvHash{Save} = $saveCount;
1751 113         222 $nvHash = $$nvHash{Next};
1752             }
1753             }
1754             # initialize hash for saving overwritten new values
1755 1         11 $$self{SAVE_NEW_VALUE} = { };
1756             # make a copy of the delete group hash
1757 1         3 my %delGrp = %{$$self{DEL_GROUP}};
  1         4  
1758 1         6 $$self{SAVE_DEL_GROUP} = \%delGrp;
1759 1         4 return $saveCount;
1760             }
1761              
1762             #------------------------------------------------------------------------------
1763             # Restore new values to last saved state
1764             # Inputs: 0) ExifTool object reference
1765             # Notes: Restores saved new values, but currently doesn't restore them in the
1766             # original order, so there may be some minor side-effects when restoring tags
1767             # with overlapping groups. eg) XMP:Identifier, XMP-dc:Identifier
1768             # Also, this doesn't do the right thing for list-type tags which accumulate
1769             # values across a save point
1770             sub RestoreNewValues($)
1771             {
1772 1     1 1 12 my $self = shift;
1773 1         3 my $newValues = $$self{NEW_VALUE};
1774 1         5 my $savedValues = $$self{SAVE_NEW_VALUE};
1775 1         3 my $key;
1776             # 1) remove any new values which don't have the Save flag set
1777 1 50       6 if ($newValues) {
1778 1         193 my @keys = keys %$newValues;
1779 1         8 foreach $key (@keys) {
1780 572         717 my $lastHash;
1781 572         1025 my $nvHash = $$newValues{$key};
1782 572         900 while ($nvHash) {
1783 573 100       1188 if ($$nvHash{Save}) {
1784 24         39 $lastHash = $nvHash;
1785             } else {
1786             # remove this entry from the list
1787 549 50       1050 if ($lastHash) {
    100          
1788 0         0 $$lastHash{Next} = $$nvHash{Next};
1789             } elsif ($$nvHash{Next}) {
1790 1         5 $$newValues{$key} = $$nvHash{Next};
1791             } else {
1792 548         785 delete $$newValues{$key};
1793             }
1794             }
1795 573         2552 $nvHash = $$nvHash{Next};
1796             }
1797             }
1798             }
1799             # 2) restore saved new values
1800 1 50       7 if ($savedValues) {
1801 1 50       5 $newValues or $newValues = $$self{NEW_VALUE} = { };
1802 1         204 foreach $key (keys %$savedValues) {
1803 89 100       154 if ($$newValues{$key}) {
1804             # add saved values to end of list
1805 1         10 my $nvHash = LastInList($$newValues{$key});
1806 1         5 $$nvHash{Next} = $$savedValues{$key};
1807             } else {
1808 88         183 $$newValues{$key} = $$savedValues{$key};
1809             }
1810             }
1811 1         10 $$self{SAVE_NEW_VALUE} = { }; # reset saved new values
1812             }
1813             # 3) restore delete groups
1814 1         6 my %delGrp = %{$$self{SAVE_DEL_GROUP}};
  1         8  
1815 1         14 $$self{DEL_GROUP} = \%delGrp;
1816             }
1817              
1818             #------------------------------------------------------------------------------
1819             # Set filesystem time from from FileModifyDate or FileCreateDate tag
1820             # Inputs: 0) ExifTool object reference, 1) file name or file ref
1821             # 2) time (-M or -C) of original file (used for shift; obtained from file if not given)
1822             # 3) tag name to write (undef for 'FileModifyDate')
1823             # 4) flag set if argument 2 has already been converted to Unix seconds
1824             # Returns: 1=time changed OK, 0=nothing done, -1=error setting time
1825             # (increments CHANGED flag and sets corresponding WRITTEN tag)
1826             sub SetFileModifyDate($$;$$$)
1827             {
1828 0     0 1 0 my ($self, $file, $originalTime, $tag, $isUnixTime) = @_;
1829 0         0 my $nvHash;
1830 0 0       0 $tag = 'FileModifyDate' unless defined $tag;
1831 0         0 my $val = $self->GetNewValue($tag, \$nvHash);
1832 0 0       0 return 0 unless defined $val;
1833 0         0 my $isOverwriting = $self->IsOverwriting($nvHash);
1834 0 0       0 return 0 unless $isOverwriting;
1835             # can currently only set creation date on Windows systems
1836             # (and Mac now too, but that is handled with the MacOS tags)
1837 0 0 0     0 return 0 if $tag eq 'FileCreateDate' and $^O ne 'MSWin32';
1838 0 0       0 if ($isOverwriting < 0) { # are we shifting time?
1839             # use original time of this file if not specified
1840 0 0       0 unless (defined $originalTime) {
1841 0         0 my ($aTime, $mTime, $cTime) = $self->GetFileTime($file);
1842 0 0       0 $originalTime = ($tag eq 'FileCreateDate') ? $cTime : $mTime;
1843 0 0       0 return 0 unless defined $originalTime;
1844 0         0 $isUnixTime = 1;
1845             }
1846 0 0       0 $originalTime = int($^T - $originalTime*(24*3600) + 0.5) unless $isUnixTime;
1847 0 0       0 return 0 unless $self->IsOverwriting($nvHash, $originalTime);
1848 0         0 $val = $$nvHash{Value}[0]; # get shifted value
1849             }
1850 0         0 my ($aTime, $mTime, $cTime);
1851 0 0       0 if ($tag eq 'FileCreateDate') {
1852 0 0       0 eval { require Win32::API } or $self->WarnOnce("Install Win32::API to set $tag"), return -1;
  0         0  
1853 0 0       0 eval { require Win32API::File } or $self->WarnOnce("Install Win32API::File to set $tag"), return -1;
  0         0  
1854 0         0 $cTime = $val;
1855             } else {
1856 0         0 $aTime = $mTime = $val;
1857             }
1858 0 0       0 $self->SetFileTime($file, $aTime, $mTime, $cTime, 1) or $self->Warn("Error setting $tag"), return -1;
1859 0         0 ++$$self{CHANGED};
1860 0         0 $$self{WRITTEN}{$tag} = $val; # remember that we wrote this tag
1861 0         0 $self->VerboseValue("+ $tag", $val);
1862 0         0 return 1;
1863             }
1864              
1865             #------------------------------------------------------------------------------
1866             # Change file name and/or directory from FileName and Directory tags
1867             # Inputs: 0) ExifTool object reference, 1) current file name (including path)
1868             # 2) new name (or undef to build from FileName and Directory tags)
1869             # 3) option: 'HardLink'/'SymLink' to create hard/symbolic link instead of renaming
1870             # 'Test' to only print new file name
1871             # 4) 0 to indicate that a file will no longer exist (used for 'Test' only)
1872             # Returns: 1=name changed OK, 0=nothing changed, -1=error changing name
1873             # (and increments CHANGED flag if filename changed)
1874             # Notes: Will not overwrite existing file. Creates directories as necessary.
1875             sub SetFileName($$;$$$)
1876             {
1877 1     1 1 10 my ($self, $file, $newName, $opt, $usedFlag) = @_;
1878 1         3 my ($nvHash, $doName, $doDir);
1879              
1880 1 50       7 $opt or $opt = '';
1881             # determine the new file name
1882 1 50       6 unless (defined $newName) {
1883 1 50       4 if ($opt) {
1884 0 0 0     0 if ($opt eq 'HardLink' or $opt eq 'Link') {
    0          
    0          
1885 0         0 $newName = $self->GetNewValue('HardLink');
1886             } elsif ($opt eq 'SymLink') {
1887 0         0 $newName = $self->GetNewValue('SymLink');
1888             } elsif ($opt eq 'Test') {
1889 0         0 $newName = $self->GetNewValue('TestName');
1890             }
1891 0 0       0 return 0 unless defined $newName;
1892             } else {
1893 1         7 my $filename = $self->GetNewValue('FileName', \$nvHash);
1894 1 50 33     8 $doName = 1 if defined $filename and $self->IsOverwriting($nvHash, $file);
1895 1         5 my $dir = $self->GetNewValue('Directory', \$nvHash);
1896 1 50 33     10 $doDir = 1 if defined $dir and $self->IsOverwriting($nvHash, $file);
1897 1 50 33     6 return 0 unless $doName or $doDir; # nothing to do
1898 1 50       4 if ($doName) {
1899 1         5 $newName = GetNewFileName($file, $filename);
1900 1 50       6 $newName = GetNewFileName($newName, $dir) if $doDir;
1901             } else {
1902 0         0 $newName = GetNewFileName($file, $dir);
1903             }
1904             }
1905             }
1906             # validate new file name in Windows
1907 1 50       9 if ($^O eq 'MSWin32') {
1908 0 0       0 if ($newName =~ /[\0-\x1f<>"|*]/) {
1909 0         0 $self->Warn('New file name not allowed in Windows (contains reserved characters)');
1910 0         0 return -1;
1911             }
1912 0 0 0     0 if ($newName =~ /:/ and $newName !~ /^[A-Z]:[^:]*$/i) {
1913 0         0 $self->Warn("New file name not allowed in Windows (contains ':')");
1914 0         0 return -1;
1915             }
1916 0 0 0     0 if ($newName =~ /\?/ and $newName !~ m{^[\\/]{2}\?[\\/][^?]*$}) {
1917 0         0 $self->Warn("New file name not allowed in Windows (contains '?')");
1918 0         0 return -1;
1919             }
1920 0 0       0 if ($newName =~ m{(^|[\\/])(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.[^.]*)?$}i) {
1921 0         0 $self->Warn('New file name not allowed in Windows (reserved device name)');
1922 0         0 return -1;
1923             }
1924 0 0       0 if ($newName =~ /([. ])$/) {
1925 0 0       0 $self->Warn("New file name not recommended for Windows (ends with '${1}')", 2) and return -1;
1926             }
1927 0 0 0     0 if (length $newName > 259 and $newName !~ /\?/) {
1928 0 0       0 $self->Warn('New file name not recommended for Windows (exceeds 260 chars)', 2) and return -1;
1929             }
1930             } else {
1931 1         7 $newName =~ tr/\0//d; # make sure name doesn't contain nulls
1932             }
1933             # protect against empty file name
1934 1 50       5 length $newName or $self->Warn('New file name is empty'), return -1;
1935             # don't replace existing file
1936 1 0 0     9 if ($self->Exists($newName) and (not defined $usedFlag or $usedFlag)) {
      33        
1937 0 0 0     0 if ($file ne $newName or $opt =~ /Link$/) {
1938             # allow for case-insensitive filesystem
1939 0 0 0     0 if ($opt =~ /Link$/ or not $self->IsSameFile($file, $newName)) {
1940 0         0 $self->Warn("File '${newName}' already exists");
1941 0         0 return -1;
1942             }
1943             } else {
1944 0         0 $self->Warn('File name is unchanged');
1945 0         0 return 0;
1946             }
1947             }
1948 1 50       8 if ($opt eq 'Test') {
1949 0         0 my $out = $$self{OPTIONS}{TextOut};
1950 0         0 print $out "'${file}' --> '${newName}'\n";
1951 0         0 return 1;
1952             }
1953             # create directory for new file if necessary
1954 1         5 my $result;
1955 1 50       10 if (($result = $self->CreateDirectory($newName)) != 0) {
1956 0 0       0 if ($result < 0) {
1957 0         0 $self->Warn("Error creating directory for '${newName}'");
1958 0         0 return -1;
1959             }
1960 0         0 $self->VPrint(0, "Created directory for '${newName}'\n");
1961             }
1962 1 50 33     14 if ($opt eq 'HardLink' or $opt eq 'Link') {
    50          
1963 0 0       0 unless (link $file, $newName) {
1964 0         0 $self->Warn("Error creating hard link '${newName}'");
1965 0         0 return -1;
1966             }
1967 0         0 ++$$self{CHANGED};
1968 0         0 $self->VerboseValue('+ HardLink', $newName);
1969 0         0 return 1;
1970             } elsif ($opt eq 'SymLink') {
1971 0 0       0 $^O eq 'MSWin32' and $self->Warn('SymLink not supported in Windows'), return -1;
1972 0         0 $newName =~ s(^\./)(); # remove leading "./" from link name if it exists
1973             # path to linked file must be relative to the $newName directory, but $file
1974             # is relative to the current directory, so convert it to an absolute path
1975             # if using a relative directory and $newName isn't in the current directory
1976 0 0 0     0 if ($file !~ m(^/) and $newName =~ m(/)) {
1977 0 0       0 unless (eval { require Cwd }) {
  0         0  
1978 0         0 $self->Warn('Install Cwd to make symlinks to other directories');
1979 0         0 return -1;
1980             }
1981 0         0 $file = eval { Cwd::abs_path($file) };
  0         0  
1982 0 0       0 unless (defined $file) {
1983 0         0 $self->Warn('Error in Cwd::abs_path when creating symlink');
1984 0         0 return -1;
1985             }
1986             }
1987 0 0       0 unless (eval { symlink $file, $newName } ) {
  0         0  
1988 0         0 $self->Warn("Error creating symbolic link '${newName}'");
1989 0         0 return -1;
1990             }
1991 0         0 ++$$self{CHANGED};
1992 0         0 $self->VerboseValue('+ SymLink', $newName);
1993 0         0 return 1;
1994             }
1995             # attempt to rename the file
1996 1 50       10 unless ($self->Rename($file, $newName)) {
1997 0         0 local (*EXIFTOOL_SFN_IN, *EXIFTOOL_SFN_OUT);
1998             # renaming didn't work, so copy the file instead
1999 0 0       0 unless ($self->Open(\*EXIFTOOL_SFN_IN, $file)) {
2000 0         0 $self->Error("Error opening '${file}'");
2001 0         0 return -1;
2002             }
2003 0 0       0 unless ($self->Open(\*EXIFTOOL_SFN_OUT, $newName, '>')) {
2004 0         0 close EXIFTOOL_SFN_IN;
2005 0         0 $self->Error("Error creating '${newName}'");
2006 0         0 return -1;
2007             }
2008 0         0 binmode EXIFTOOL_SFN_IN;
2009 0         0 binmode EXIFTOOL_SFN_OUT;
2010 0         0 my ($buff, $err);
2011 0         0 while (read EXIFTOOL_SFN_IN, $buff, 65536) {
2012 0 0       0 print EXIFTOOL_SFN_OUT $buff or $err = 1;
2013             }
2014 0 0       0 close EXIFTOOL_SFN_OUT or $err = 1;
2015 0         0 close EXIFTOOL_SFN_IN;
2016 0 0       0 if ($err) {
2017 0         0 $self->Unlink($newName); # erase bad output file
2018 0         0 $self->Error("Error writing '${newName}'");
2019 0         0 return -1;
2020             }
2021             # preserve modification time
2022 0         0 my ($aTime, $mTime, $cTime) = $self->GetFileTime($file);
2023 0         0 $self->SetFileTime($newName, $aTime, $mTime, $cTime);
2024             # remove the original file
2025 0 0       0 $self->Unlink($file) or $self->Warn('Error removing old file');
2026             }
2027 1         7 $$self{NewName} = $newName; # remember new file name
2028 1         4 ++$$self{CHANGED};
2029 1         7 $self->VerboseValue('+ FileName', $newName);
2030 1         4 return 1;
2031             }
2032              
2033             #------------------------------------------------------------------------------
2034             # Set file permissions, group/user id and various MDItem tags from new tag values
2035             # Inputs: 0) ExifTool ref, 1) file name or glob (must be a name for MDItem tags)
2036             # Returns: 1=something was set OK, 0=didn't try, -1=error (and warning set)
2037             # Notes: There may be errors even if 1 is returned
2038             sub SetSystemTags($$)
2039             {
2040 222     222 0 898 my ($self, $file) = @_;
2041 222         642 my $result = 0;
2042              
2043 222         932 my $perm = $self->GetNewValue('FilePermissions');
2044 222 50       1030 if (defined $perm) {
2045 0 0       0 if (eval { chmod($perm & 07777, $file) }) {
  0         0  
2046 0         0 $self->VerboseValue('+ FilePermissions', $perm);
2047 0         0 $result = 1;
2048             } else {
2049 0         0 $self->WarnOnce('Error setting FilePermissions');
2050 0         0 $result = -1;
2051             }
2052             }
2053 222         901 my $uid = $self->GetNewValue('FileUserID');
2054 222         1073 my $gid = $self->GetNewValue('FileGroupID');
2055 222 50 33     1933 if (defined $uid or defined $gid) {
2056 0 0       0 defined $uid or $uid = -1;
2057 0 0       0 defined $gid or $gid = -1;
2058 0 0       0 if (eval { chown($uid, $gid, $file) }) {
  0         0  
2059 0 0       0 $self->VerboseValue('+ FileUserID', $uid) if $uid >= 0;
2060 0 0       0 $self->VerboseValue('+ FileGroupID', $gid) if $gid >= 0;
2061 0         0 $result = 1;
2062             } else {
2063 0         0 $self->WarnOnce('Error setting FileGroup/UserID');
2064 0 0       0 $result = -1 unless $result;
2065             }
2066             }
2067 222         668 my $tag;
2068 222         834 foreach $tag (@writableMacOSTags) {
2069 1332         1984 my $nvHash;
2070 1332         3263 my $val = $self->GetNewValue($tag, \$nvHash);
2071 1332 50       4702 next unless $nvHash;
2072 0 0       0 if ($^O eq 'darwin') {
    0          
2073 0 0       0 ref $file and $self->Warn('Setting MDItem tags requires a file name'), last;
2074 0         0 require Image::ExifTool::MacOS;
2075 0         0 my $res = Image::ExifTool::MacOS::SetMacOSTags($self, $file, \@writableMacOSTags);
2076 0 0 0     0 $result = $res if $res == 1 or not $result;
2077 0         0 last;
2078             } elsif ($tag ne 'FileCreateDate') {
2079 0         0 $self->WarnOnce('Can only set MDItem tags on OS X');
2080 0         0 last;
2081             }
2082             }
2083             # delete Windows Zone.Identifier if specified
2084 222         1386 my $zhash = $self->GetNewValueHash($Image::ExifTool::Extra{ZoneIdentifier});
2085 222 50       1294 if ($zhash) {
2086 0         0 my $res = -1;
2087 0 0       0 if ($^O ne 'MSWin32') {
    0          
    0          
    0          
2088 0         0 $self->Warn('ZoneIdentifer is a Windows-only tag');
2089             } elsif (ref $file) {
2090 0         0 $self->Warn('Writing ZoneIdentifer requires a file name');
2091             } elsif (defined $self->GetNewValue('ZoneIdentifier', \$zhash)) {
2092 0         0 $self->Warn('ZoneIndentifier may only be delted');
2093 0         0 } elsif (not eval { require Win32API::File }) {
2094 0         0 $self->Warn('Install Win32API::File to write ZoneIdentifier');
2095             } else {
2096 0         0 my ($wattr, $wide);
2097 0         0 my $zfile = "${file}:Zone.Identifier";
2098 0 0       0 if ($self->EncodeFileName($zfile)) {
2099 0         0 $wide = 1;
2100 0         0 $wattr = eval { Win32API::File::GetFileAttributesW($zfile) };
  0         0  
2101             } else {
2102 0         0 $wattr = eval { Win32API::File::GetFileAttributes($zfile) };
  0         0  
2103             }
2104 0 0       0 if ($wattr == Win32API::File::INVALID_FILE_ATTRIBUTES()) {
    0          
2105 0         0 $res = 0; # file doesn't exist, nothing to do
2106             } elsif ($wattr & Win32API::File::FILE_ATTRIBUTE_READONLY()) {
2107 0         0 $self->Warn('Zone.Identifier stream is read-only');
2108             } else {
2109 0 0       0 if ($wide) {
2110 0 0       0 $res = 1 if eval { Win32API::File::DeleteFileW($zfile) };
  0         0  
2111             } else {
2112 0 0       0 $res = 1 if eval { Win32API::File::DeleteFile($zfile) };
  0         0  
2113             }
2114 0 0       0 if ($res > 0) {
2115 0         0 $self->VPrint(0, " Deleting Zone.Identifier stream\n");
2116             } else {
2117 0         0 $self->Warn('Error deleting Zone.Identifier stream');
2118             }
2119             }
2120             }
2121 0 0 0     0 $result = $res if $res == 1 or not $result;
2122             }
2123 222         1148 return $result;
2124             }
2125              
2126             #------------------------------------------------------------------------------
2127             # Write information back to file
2128             # Inputs: 0) ExifTool object reference,
2129             # 1) input filename, file ref, RAF ref, or scalar ref (or '' or undef to create from scratch)
2130             # 2) output filename, file ref, or scalar ref (or undef to overwrite)
2131             # 3) optional output file type (required only if input file is not specified
2132             # and output file is a reference)
2133             # Returns: 1=file written OK, 2=file written but no changes made, 0=file write error
2134             sub WriteInfo($$;$$)
2135             {
2136 235     235 1 20793 local ($_, *EXIFTOOL_FILE2, *EXIFTOOL_OUTFILE);
2137 235         1090 my ($self, $infile, $outfile, $outType) = @_;
2138 235         1558 my (@fileTypeList, $fileType, $tiffType, $hdr, $seekErr, $type, $tmpfile);
2139 235         0 my ($inRef, $outRef, $closeIn, $closeOut, $outPos, $outBuff, $eraseIn, $raf, $fileExt);
2140 235         0 my ($hardLink, $symLink, $testName);
2141 235         811 my $oldRaf = $$self{RAF};
2142 235         605 my $rtnVal = 0;
2143              
2144             # initialize member variables
2145 235         1488 $self->Init();
2146 235         986 $$self{IsWriting} = 1;
2147              
2148             # first, save original file modify date if necessary
2149             # (do this now in case we are modifying file in place and shifting date)
2150 235         676 my ($nvHash, $nvHash2, $originalTime, $createTime);
2151 235         1309 my $setModDate = defined $self->GetNewValue('FileModifyDate', \$nvHash);
2152 235         1068 my $setCreateDate = defined $self->GetNewValue('FileCreateDate', \$nvHash2);
2153 235         897 my ($aTime, $mTime, $cTime);
2154 235 0 33     1284 if ($setModDate and $self->IsOverwriting($nvHash) < 0 and
      33        
      0        
2155             defined $infile and ref $infile ne 'SCALAR')
2156             {
2157 0         0 ($aTime, $mTime, $cTime) = $self->GetFileTime($infile);
2158 0         0 $originalTime = $mTime;
2159             }
2160 235 0 33     1114 if ($setCreateDate and $self->IsOverwriting($nvHash2) < 0 and
      33        
      0        
2161             defined $infile and ref $infile ne 'SCALAR')
2162             {
2163 0 0       0 ($aTime, $mTime, $cTime) = $self->GetFileTime($infile) unless defined $cTime;
2164 0         0 $createTime = $cTime;
2165             }
2166             #
2167             # do quick in-place change of file dir/name or date if that is all we are doing
2168             #
2169 235         1220 my ($numNew, $numPseudo) = $self->CountNewValues();
2170 235 100 66     1252 if (not defined $outfile and defined $infile) {
2171 4         16 $hardLink = $self->GetNewValue('HardLink');
2172 4         27 $symLink = $self->GetNewValue('SymLink');
2173 4         33 $testName = $self->GetNewValue('TestName');
2174 4 50 33     31 undef $hardLink if defined $hardLink and not length $hardLink;
2175 4 50 33     19 undef $symLink if defined $symLink and not length $symLink;
2176 4 50 33     22 undef $testName if defined $testName and not length $testName;
2177 4         13 my $newFileName = $self->GetNewValue('FileName', \$nvHash);
2178 4         22 my $newDir = $self->GetNewValue('Directory');
2179 4 50 33     30 if (defined $newDir and length $newDir) {
2180 0 0       0 $newDir .= '/' unless $newDir =~ m{/$};
2181             } else {
2182 4         10 undef $newDir;
2183             }
2184 4 100 33     37 if ($numNew == $numPseudo) {
    50          
2185 1         3 $rtnVal = 2;
2186 1 50 33     11 if ((defined $newFileName or defined $newDir) and not ref $infile) {
      33        
2187 1         7 my $result = $self->SetFileName($infile);
2188 1 50       4 if ($result > 0) {
    0          
2189 1         5 $infile = $$self{NewName}; # file name changed
2190 1         3 $rtnVal = 1;
2191             } elsif ($result < 0) {
2192 0         0 return 0; # don't try to do anything else
2193             }
2194             }
2195 1 50 33     6 if (not ref $infile or UNIVERSAL::isa($infile,'GLOB')) {
2196 1 50 0     3 $self->SetFileModifyDate($infile) > 0 and $rtnVal = 1 if $setModDate;
2197 1 50 0     3 $self->SetFileModifyDate($infile, undef, 'FileCreateDate') > 0 and $rtnVal = 1 if $setCreateDate;
2198 1 50       5 $self->SetSystemTags($infile) > 0 and $rtnVal = 1;
2199             }
2200 1 50 33     12 if (defined $hardLink or defined $symLink or defined $testName) {
      33        
2201 0 0 0     0 $hardLink and $self->SetFileName($infile, $hardLink, 'HardLink') and $rtnVal = 1;
2202 0 0 0     0 $symLink and $self->SetFileName($infile, $symLink, 'SymLink') and $rtnVal = 1;
2203 0 0 0     0 $testName and $self->SetFileName($infile, $testName, 'Test') and $rtnVal = 1;
2204             }
2205 1         8 return $rtnVal;
2206             } elsif (defined $newFileName and length $newFileName) {
2207             # can't simply rename file, so just set the output name if new FileName
2208             # --> in this case, must erase original copy
2209 0 0       0 if (ref $infile) {
    0          
2210 0         0 $outfile = $newFileName;
2211             # can't delete original
2212             } elsif ($self->IsOverwriting($nvHash, $infile)) {
2213 0         0 $outfile = GetNewFileName($infile, $newFileName);
2214 0         0 $eraseIn = 1; # delete original
2215             }
2216             }
2217             # set new directory if specified
2218 3 50       15 if (defined $newDir) {
2219 0 0 0     0 $outfile = $infile unless defined $outfile or ref $infile;
2220 0 0       0 if (defined $outfile) {
2221 0         0 $outfile = GetNewFileName($outfile, $newDir);
2222 0 0       0 $eraseIn = 1 unless ref $infile;
2223             }
2224             }
2225             }
2226             #
2227             # set up input file
2228             #
2229 234 100 66     5342 if (ref $infile) {
    100          
    50          
2230 5         14 $inRef = $infile;
2231 5 100 33     61 if (UNIVERSAL::isa($inRef,'GLOB')) {
    50 33        
    50          
2232 1         12 seek($inRef, 0, 0); # make sure we are at the start of the file
2233             } elsif (UNIVERSAL::isa($inRef,'File::RandomAccess')) {
2234 0         0 $inRef->Seek(0);
2235 0         0 $raf = $inRef;
2236             } elsif ($] >= 5.006 and (eval { require Encode; Encode::is_utf8($$inRef) } or $@)) {
2237             # convert image data from UTF-8 to character stream if necessary
2238 0 0       0 my $buff = $@ ? pack('C*',unpack($] < 5.010000 ? 'U0C*' : 'C0C*',$$inRef)) : Encode::encode('utf8',$$inRef);
    0          
2239 0 0       0 if (defined $outfile) {
2240 0         0 $inRef = \$buff;
2241             } else {
2242 0         0 $$inRef = $buff;
2243             }
2244             }
2245             } elsif (defined $infile and $infile ne '') {
2246             # write to a temporary file if no output file given
2247 206 100       843 $outfile = $tmpfile = "${infile}_exiftool_tmp" unless defined $outfile;
2248 206 50       1576 if ($self->Open(\*EXIFTOOL_FILE2, $infile)) {
2249 206         1671 $fileExt = GetFileExtension($infile);
2250 206         1232 $fileType = GetFileType($infile);
2251 206         831 @fileTypeList = GetFileType($infile);
2252 206         900 $tiffType = $$self{FILE_EXT} = GetFileExtension($infile);
2253 206         1864 $self->VPrint(0, "Rewriting $infile...\n");
2254 206         640 $inRef = \*EXIFTOOL_FILE2;
2255 206         658 $closeIn = 1; # we must close the file since we opened it
2256             } else {
2257 0         0 $self->Error('Error opening file');
2258 0         0 return 0;
2259             }
2260             } elsif (not defined $outfile) {
2261 0         0 $self->Error("WriteInfo(): Must specify infile or outfile\n");
2262 0         0 return 0;
2263             } else {
2264             # create file from scratch
2265 23 100 66     252 $outType = GetFileExtension($outfile) unless $outType or ref $outfile;
2266 23 50       142 if (CanCreate($outType)) {
    0          
2267 23 50       186 if ($$self{OPTIONS}{WriteMode} =~ /g/i) {
2268 23         79 $fileType = $tiffType = $outType; # use output file type if no input file
2269 23         83 $infile = "$fileType file"; # make bogus file name
2270 23         179 $self->VPrint(0, "Creating $infile...\n");
2271 23         98 $inRef = \ ''; # set $inRef to reference to empty data
2272             } else {
2273 0         0 $self->Error("Not creating new $outType file (disallowed by WriteMode)");
2274 0         0 return 0;
2275             }
2276             } elsif ($outType) {
2277 0         0 $self->Error("Can't create $outType files");
2278 0         0 return 0;
2279             } else {
2280 0         0 $self->Error("Can't create file (unknown type)");
2281 0         0 return 0;
2282             }
2283             }
2284 234 100       1077 unless (@fileTypeList) {
2285 29 100       104 if ($fileType) {
2286 23         84 @fileTypeList = ( $fileType );
2287             } else {
2288 6         121 @fileTypeList = @fileTypes;
2289 6         18 $tiffType = 'TIFF';
2290             }
2291             }
2292             #
2293             # set up output file
2294             #
2295 234 100       2272 if (ref $outfile) {
    100          
    50          
    50          
2296 13         49 $outRef = $outfile;
2297 13 50       79 if (UNIVERSAL::isa($outRef,'GLOB')) {
2298 0         0 binmode($outRef);
2299 0         0 $outPos = tell($outRef);
2300             } else {
2301             # initialize our output buffer if necessary
2302 13 50       61 defined $$outRef or $$outRef = '';
2303 13         38 $outPos = length($$outRef);
2304             }
2305             } elsif (not defined $outfile) {
2306             # editing in place, so write to memory first
2307             # (only when infile is a file ref or scalar ref)
2308 1 50       5 if ($raf) {
2309 0         0 $self->Error("Can't edit File::RandomAccess object in place");
2310 0         0 return 0;
2311             }
2312 1         7 $outBuff = '';
2313 1         4 $outRef = \$outBuff;
2314 1         3 $outPos = 0;
2315             } elsif ($self->Exists($outfile)) {
2316 0         0 $self->Error("File already exists: $outfile");
2317             } elsif ($self->Open(\*EXIFTOOL_OUTFILE, $outfile, '>')) {
2318 220         1306 $outRef = \*EXIFTOOL_OUTFILE;
2319 220         761 $closeOut = 1; # we must close $outRef
2320 220         1014 binmode($outRef);
2321 220         640 $outPos = 0;
2322             } else {
2323 0 0       0 my $tmp = $tmpfile ? ' temporary' : '';
2324 0         0 $self->Error("Error creating$tmp file: $outfile");
2325             }
2326             #
2327             # write the file
2328             #
2329 234         1443 until ($$self{VALUE}{Error}) {
2330             # create random access file object (disable seek test in case of straight copy)
2331 234 50       3050 $raf or $raf = new File::RandomAccess($inRef, 1);
2332 234         1360 $raf->BinMode();
2333 234 100 33     3123 if ($numNew == $numPseudo) {
    50 66        
2334 1         3 $rtnVal = 1;
2335             # just do a straight copy of the file (no "real" tags are being changed)
2336 1         2 my $buff;
2337 1         8 while ($raf->Read($buff, 65536)) {
2338 1 50       6 Write($outRef, $buff) or $rtnVal = -1, last;
2339             }
2340 1         4 last;
2341             } elsif (not ref $infile and ($infile eq '-' or $infile =~ /\|$/)) {
2342             # patch for Windows command shell pipe
2343 0         0 $$raf{TESTED} = -1; # force buffering
2344             } else {
2345 233         1168 $raf->SeekTest();
2346             }
2347             # $raf->Debug() and warn " RAF debugging enabled!\n";
2348 233         1244 my $inPos = $raf->Tell();
2349 233         868 $$self{RAF} = $raf;
2350 233         1280 my %dirInfo = (
2351             RAF => $raf,
2352             OutFile => $outRef,
2353             );
2354 233 100       1204 $raf->Read($hdr, 1024) or $hdr = '';
2355 233 50       1535 $raf->Seek($inPos, 0) or $seekErr = 1;
2356 233         876 my $wrongType;
2357 233         1036 until ($seekErr) {
2358 268         775 $type = shift @fileTypeList;
2359             # do quick test to see if this is the right file type
2360 268 100 66     7164 if ($magicNumber{$type} and length($hdr) and $hdr !~ /^$magicNumber{$type}/s) {
      100        
2361 35 50       126 next if @fileTypeList;
2362 0         0 $wrongType = 1;
2363 0         0 last;
2364             }
2365             # save file type in member variable
2366 233         1930 $dirInfo{Parent} = $$self{FILE_TYPE} = $$self{PATH}[0] = $type;
2367             # determine which directories we must write for this file type
2368 233         1732 $self->InitWriteDirs($type);
2369 233 100 100     1992 if ($type eq 'JPEG' or $type eq 'EXV') {
    100 33        
    100          
    50          
    50          
2370 106         688 $rtnVal = $self->WriteJPEG(\%dirInfo);
2371             } elsif ($type eq 'TIFF') {
2372             # disallow writing of some TIFF-based RAW images:
2373 13 50       33 if (grep /^$tiffType$/, @{$noWriteFile{TIFF}}) {
  13         297  
2374 0         0 $fileType = $tiffType;
2375 0         0 undef $rtnVal;
2376             } else {
2377 13 50       66 if ($tiffType eq 'FFF') {
2378             # (see https://exiftool.org/forum/index.php?topic=10848.0)
2379 0         0 $self->Error('Phocus may not properly update previews of edited FFF images', 1);
2380             }
2381 13         47 $dirInfo{Parent} = $tiffType;
2382 13         94 $rtnVal = $self->ProcessTIFF(\%dirInfo);
2383             }
2384 0         0 } elsif (exists $writableType{$type}) {
2385 112         320 my ($module, $func);
2386 112 100       520 if (ref $writableType{$type} eq 'ARRAY') {
2387 85   66     492 $module = $writableType{$type}[0] || $type;
2388 85         323 $func = $writableType{$type}[1];
2389             } else {
2390 27   66     146 $module = $writableType{$type} || $type;
2391             }
2392 112         1636 require "Image/ExifTool/$module.pm";
2393 112   66     718 $func = "Image::ExifTool::${module}::" . ($func || "Process$type");
2394 58     58   645 no strict 'refs';
  58         153  
  58         3299  
2395 112         1104 $rtnVal = &$func($self, \%dirInfo);
2396 58     58   417 use strict 'refs';
  58         320  
  58         368356  
2397             } elsif ($type eq 'ORF' or $type eq 'RAW') {
2398 0         0 $rtnVal = $self->ProcessTIFF(\%dirInfo);
2399             } elsif ($type eq 'EXIF') {
2400             # go through WriteDirectory so block writes, etc are handled
2401 2         12 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
2402 2         21 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
2403 2 50       13 if (defined $buff) {
2404 2 50       10 $rtnVal = Write($outRef, $buff) ? 1 : -1;
2405             } else {
2406 0         0 $rtnVal = 0;
2407             }
2408             } else {
2409 0         0 undef $rtnVal; # flag that we don't write this type of file
2410             }
2411             # all done unless we got the wrong type
2412 233 50       1255 last if $rtnVal;
2413 0 0       0 last unless @fileTypeList;
2414             # seek back to original position in files for next try
2415 0 0       0 $raf->Seek($inPos, 0) or $seekErr = 1, last;
2416 0 0       0 if (UNIVERSAL::isa($outRef,'GLOB')) {
2417 0         0 seek($outRef, 0, $outPos);
2418             } else {
2419 0         0 $$outRef = substr($$outRef, 0, $outPos);
2420             }
2421             }
2422             # print file format errors
2423 233 50       941 unless ($rtnVal) {
2424 0         0 my $err;
2425 0 0 0     0 if ($seekErr) {
    0          
    0          
2426 0         0 $err = 'Error seeking in file';
2427             } elsif ($fileType and defined $rtnVal) {
2428 0 0       0 if ($$self{VALUE}{Error}) {
    0          
2429             # existing error message will do
2430             } elsif ($fileType eq 'RAW') {
2431 0         0 $err = 'Writing this type of RAW file is not supported';
2432             } else {
2433 0 0       0 if ($wrongType) {
2434 0   0     0 my $type = $fileExt || ($fileType eq 'TIFF' ? $tiffType : $fileType);
2435 0         0 $err = "Not a valid $type";
2436             # do a quick check to see what this file looks like
2437 0         0 foreach $type (@fileTypes) {
2438 0 0       0 next unless $magicNumber{$type};
2439 0 0       0 next unless $hdr =~ /^$magicNumber{$type}/s;
2440 0         0 $err .= " (looks more like a $type)";
2441 0         0 last;
2442             }
2443             } else {
2444 0         0 $err = 'Format error in file';
2445             }
2446             }
2447             } elsif ($fileType) {
2448             # get specific type of file from extension
2449 0 0 0     0 $fileType = GetFileExtension($infile) if $infile and GetFileType($infile);
2450 0         0 $err = "Writing of $fileType files is not yet supported";
2451             } else {
2452 0         0 $err = 'Writing of this type of file is not supported';
2453             }
2454 0 0       0 $self->Error($err) if $err;
2455 0         0 $rtnVal = 0; # (in case it was undef)
2456             }
2457             # $raf->Close(); # only used to force debug output
2458 233         919 last; # (didn't really want to loop)
2459             }
2460             # don't return success code if any error occurred
2461 234 50       966 if ($rtnVal > 0) {
2462 234 50 66     1422 if ($outType and $type and $outType ne $type) {
      66        
2463 0         0 my @types = GetFileType($outType);
2464 0 0       0 unless (grep /^$type$/, @types) {
2465 0         0 $self->Error("Can't create $outType file from $type");
2466 0         0 $rtnVal = 0;
2467             }
2468             }
2469 234 50 33     1664 if ($rtnVal > 0 and not Tell($outRef) and not $$self{VALUE}{Error}) {
      33        
2470             # don't write a file with zero length
2471 0 0 0     0 if (defined $hdr and length $hdr) {
2472 0 0       0 $type = '' unless defined $type;
2473 0         0 $self->Error("Can't delete all meta information from $type file");
2474             } else {
2475 0         0 $self->Error('Nothing to write');
2476             }
2477             }
2478 234 50       1331 $rtnVal = 0 if $$self{VALUE}{Error};
2479             }
2480              
2481             # rewrite original file in place if required
2482 234 100       950 if (defined $outBuff) {
2483 1 50 33     12 if ($rtnVal <= 0 or not $$self{CHANGED}) {
    50          
2484             # nothing changed, so no need to write $outBuff
2485             } elsif (UNIVERSAL::isa($inRef,'GLOB')) {
2486 1         3 my $len = length($outBuff);
2487 1         3 my $size;
2488             $rtnVal = -1 unless
2489             seek($inRef, 0, 2) and # seek to the end of file
2490             ($size = tell $inRef) >= 0 and # get the file size
2491             seek($inRef, 0, 0) and # seek back to the start
2492             print $inRef $outBuff and # write the new data
2493             ($len >= $size or # if necessary:
2494 1 50 33     38 eval { truncate($inRef, $len) }); # shorten output file
      33        
      33        
      33        
      33        
2495             } else {
2496 0         0 $$inRef = $outBuff; # replace original data
2497             }
2498 1         6 $outBuff = ''; # free memory but leave $outBuff defined
2499             }
2500             # close input file if we opened it
2501 234 100       810 if ($closeIn) {
2502             # errors on input file are significant if we edited the file in place
2503 206 50 0     4681 $rtnVal and $rtnVal = -1 unless close($inRef) or not defined $outBuff;
      33        
2504 206 50       1091 if ($rtnVal > 0) {
2505             # copy Mac OS resource fork if it exists
2506 206 50 33     1365 if ($^O eq 'darwin' and -s "$infile/..namedfork/rsrc") {
2507 0 0       0 if ($$self{DEL_GROUP}{RSRC}) {
2508 0         0 $self->VPrint(0,"Deleting Mac OS resource fork\n");
2509 0         0 ++$$self{CHANGED};
2510             } else {
2511 0         0 $self->VPrint(0,"Copying Mac OS resource fork\n");
2512 0         0 my ($buf, $err);
2513 0         0 local (*SRC, *DST);
2514 0 0       0 if ($self->Open(\*SRC, "$infile/..namedfork/rsrc")) {
2515 0 0       0 if ($self->Open(\*DST, "$outfile/..namedfork/rsrc", '>')) {
2516 0         0 binmode SRC; # (not necessary for Darwin, but let's be thorough)
2517 0         0 binmode DST;
2518 0         0 while (read SRC, $buf, 65536) {
2519 0 0       0 print DST $buf or $err = 'copying', last;
2520             }
2521 0 0 0     0 close DST or $err or $err = 'closing';
2522             } else {
2523             # (this is normal if the destination filesystem isn't Mac OS)
2524 0         0 $self->Warn('Error creating Mac OS resource fork');
2525             }
2526 0         0 close SRC;
2527             } else {
2528 0         0 $err = 'opening';
2529             }
2530 0 0 0     0 $rtnVal = 0 if $err and $self->Error("Error $err Mac OS resource fork", 2);
2531             }
2532             }
2533             # erase input file if renaming while editing information in place
2534 206 50 0     790 $self->Unlink($infile) or $self->Warn('Error erasing original file') if $eraseIn;
2535             }
2536             }
2537             # close output file if we created it
2538 234 100       774 if ($closeOut) {
2539             # close file and set $rtnVal to -1 if there was an error
2540 220 50 0     13679 $rtnVal and $rtnVal = -1 unless close($outRef);
2541             # erase the output file if we weren't successful
2542 220 50       1758 if ($rtnVal <= 0) {
    100          
2543 0         0 $self->Unlink($outfile);
2544             # else rename temporary file if necessary
2545             } elsif ($tmpfile) {
2546 2         18 $self->CopyFileAttrs($infile, $tmpfile); # copy attributes to new file
2547 2 50       16 unless ($self->Rename($tmpfile, $infile)) {
2548             # some filesystems won't overwrite with 'rename', so try erasing original
2549 0 0       0 if (not $self->Unlink($infile)) {
    0          
2550 0         0 $self->Unlink($tmpfile);
2551 0         0 $self->Error('Error renaming temporary file');
2552 0         0 $rtnVal = 0;
2553             } elsif (not $self->Rename($tmpfile, $infile)) {
2554 0         0 $self->Error('Error renaming temporary file after deleting original');
2555 0         0 $rtnVal = 0;
2556             }
2557             }
2558             # the output file should now have the name of the original infile
2559 2 50       12 $outfile = $infile if $rtnVal > 0;
2560             }
2561             }
2562             # set filesystem attributes if requested (and if possible!)
2563 234 50 100     1868 if ($rtnVal > 0 and ($closeOut or (defined $outBuff and ($closeIn or UNIVERSAL::isa($infile,'GLOB'))))) {
      66        
2564 221 100       938 my $target = $closeOut ? $outfile : $infile;
2565             # set file permissions if requested
2566 221 50       1460 ++$$self{CHANGED} if $self->SetSystemTags($target) > 0;
2567 221 100       865 if ($closeIn) { # (no use setting file times unless the input file is closed)
2568 197 50 33     926 ++$$self{CHANGED} if $setModDate and $self->SetFileModifyDate($target, $originalTime, undef, 1) > 0;
2569             # set FileCreateDate if requested (and if possible!)
2570 197 50 33     911 ++$$self{CHANGED} if $setCreateDate and $self->SetFileModifyDate($target, $createTime, 'FileCreateDate', 1) > 0;
2571             # create hard link if requested and no output filename specified (and if possible!)
2572 197 50 33     925 ++$$self{CHANGED} if defined $hardLink and $self->SetFileName($target, $hardLink, 'HardLink');
2573 197 50 33     904 ++$$self{CHANGED} if defined $symLink and $self->SetFileName($target, $symLink, 'SymLink');
2574 197 50       730 defined $testName and $self->SetFileName($target, $testName, 'Test');
2575             }
2576             }
2577             # check for write error and set appropriate error message and return value
2578 234 50       1316 if ($rtnVal < 0) {
    50          
2579 0 0       0 $self->Error('Error writing output file') unless $$self{VALUE}{Error};
2580 0         0 $rtnVal = 0; # return 0 on failure
2581             } elsif ($rtnVal > 0) {
2582 234 100       991 ++$rtnVal unless $$self{CHANGED};
2583             }
2584             # set things back to the way they were
2585 234         728 $$self{RAF} = $oldRaf;
2586              
2587 234         2655 return $rtnVal;
2588             }
2589              
2590             #------------------------------------------------------------------------------
2591             # Get list of all available tags for specified group
2592             # Inputs: 0) optional group name (or string of names separated by colons)
2593             # Returns: tag list (sorted alphabetically)
2594             # Notes: Can't get tags for specific IFD
2595             sub GetAllTags(;$)
2596             {
2597 0     0 1 0 local $_;
2598 0         0 my $group = shift;
2599 0         0 my (%allTags, @groups);
2600 0 0       0 @groups = split ':', $group if $group;
2601              
2602 0         0 my $et = new Image::ExifTool;
2603 0         0 LoadAllTables(); # first load all our tables
2604 0         0 my @tableNames = keys %allTables;
2605              
2606             # loop through all tables and save tag names to %allTags hash
2607 0         0 while (@tableNames) {
2608 0         0 my $table = GetTagTable(pop @tableNames);
2609             # generate flattened tag names for structure fields if this is an XMP table
2610 0 0 0     0 if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') {
2611 0         0 Image::ExifTool::XMP::AddFlattenedTags($table);
2612             }
2613 0         0 my $tagID;
2614 0         0 foreach $tagID (TagTableKeys($table)) {
2615 0         0 my @infoArray = GetTagInfoList($table,$tagID);
2616 0         0 my $tagInfo;
2617 0         0 GATInfo: foreach $tagInfo (@infoArray) {
2618 0         0 my $tag = $$tagInfo{Name};
2619 0 0       0 $tag or warn("no name for tag!\n"), next;
2620             # don't list subdirectories unless they are writable
2621 0 0 0     0 next if $$tagInfo{SubDirectory} and not $$tagInfo{Writable};
2622 0 0       0 next if $$tagInfo{Hidden}; # ignore hidden tags
2623 0 0       0 if (@groups) {
2624 0         0 my @tg = $et->GetGroup($tagInfo);
2625 0         0 foreach $group (@groups) {
2626 0 0       0 next GATInfo unless grep /^$group$/i, @tg;
2627             }
2628             }
2629 0         0 $allTags{$tag} = 1;
2630             }
2631             }
2632             }
2633 0         0 return sort keys %allTags;
2634             }
2635              
2636             #------------------------------------------------------------------------------
2637             # Get list of all writable tags
2638             # Inputs: 0) optional group name (or names separated by colons)
2639             # Returns: tag list (sorted alphabetically)
2640             sub GetWritableTags(;$)
2641             {
2642 0     0 1 0 local $_;
2643 0         0 my $group = shift;
2644 0         0 my (%writableTags, @groups);
2645 0 0       0 @groups = split ':', $group if $group;
2646              
2647 0         0 my $et = new Image::ExifTool;
2648 0         0 LoadAllTables();
2649 0         0 my @tableNames = keys %allTables;
2650              
2651 0         0 while (@tableNames) {
2652 0         0 my $tableName = pop @tableNames;
2653 0         0 my $table = GetTagTable($tableName);
2654             # generate flattened tag names for structure fields if this is an XMP table
2655 0 0 0     0 if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') {
2656 0         0 Image::ExifTool::XMP::AddFlattenedTags($table);
2657             }
2658             # attempt to load Write tables if autoloaded
2659 0         0 my @parts = split(/::/,$tableName);
2660 0 0       0 if (@parts > 3) {
2661 0         0 my $i = $#parts - 1;
2662 0         0 $parts[$i] = "Write$parts[$i]"; # add 'Write' before class name
2663 0         0 my $module = join('::',@parts[0..$i]);
2664 0         0 eval { require $module }; # (fails silently if nothing loaded)
  0         0  
2665             }
2666 0         0 my $tagID;
2667 0         0 foreach $tagID (TagTableKeys($table)) {
2668 0         0 my @infoArray = GetTagInfoList($table,$tagID);
2669 0         0 my $tagInfo;
2670 0         0 GWTInfo: foreach $tagInfo (@infoArray) {
2671 0         0 my $tag = $$tagInfo{Name};
2672 0 0       0 $tag or warn("no name for tag!\n"), next;
2673 0         0 my $writable = $$tagInfo{Writable};
2674             next unless $writable or ($$table{WRITABLE} and
2675 0 0 0     0 not defined $writable and not $$tagInfo{SubDirectory});
      0        
      0        
2676 0 0       0 next if $$tagInfo{Hidden}; # ignore hidden tags
2677 0 0       0 if (@groups) {
2678 0         0 my @tg = $et->GetGroup($tagInfo);
2679 0         0 foreach $group (@groups) {
2680 0 0       0 next GWTInfo unless grep /^$group$/i, @tg;
2681             }
2682             }
2683 0         0 $writableTags{$tag} = 1;
2684             }
2685             }
2686             }
2687 0         0 return sort keys %writableTags;
2688             }
2689              
2690             #------------------------------------------------------------------------------
2691             # Get list of all group names
2692             # Inputs: 0) [optional] ExifTool ref, 1) Group family number
2693             # Returns: List of group names (sorted alphabetically)
2694             sub GetAllGroups($;$)
2695             {
2696 0     0 1 0 local $_;
2697 0   0     0 my $family = shift || 0;
2698 0         0 my $self;
2699 0 0 0     0 ref $family and $self = $family, $family = shift || 0;
2700              
2701 0 0       0 $family == 3 and return('Doc#', 'Main');
2702 0 0       0 $family == 4 and return('Copy#');
2703 0 0       0 $family == 5 and return('[too many possibilities to list]');
2704 0 0       0 $family == 6 and return(@Image::ExifTool::Exif::formatName[1..$#Image::ExifTool::Exif::formatName]);
2705              
2706 0         0 LoadAllTables(); # first load all our tables
2707              
2708 0         0 my @tableNames = keys %allTables;
2709              
2710             # loop through all tag tables and get all group names
2711 0         0 my %allGroups;
2712 0         0 while (@tableNames) {
2713 0         0 my $table = GetTagTable(pop @tableNames);
2714 0         0 my ($grps, $grp, $tag, $tagInfo);
2715 0 0 0     0 $allGroups{$grp} = 1 if ($grps = $$table{GROUPS}) and ($grp = $$grps{$family});
2716 0         0 foreach $tag (TagTableKeys($table)) {
2717 0         0 my @infoArray = GetTagInfoList($table, $tag);
2718 0 0       0 if ($family == 7) {
2719 0         0 foreach $tagInfo (@infoArray) {
2720 0         0 my $id = $$tagInfo{TagID};
2721 0 0       0 if (not defined $id) {
    0          
2722 0         0 $id = ''; # (just to be safe)
2723             } elsif ($id =~ /^\d+$/) {
2724 0 0 0     0 $id = sprintf('0x%x', $id) if $self and $$self{OPTIONS}{HexTagIDs};
2725             } else {
2726 0         0 $id =~ s/([^-_A-Za-z0-9])/sprintf('%.2x',ord $1)/ge;
  0         0  
2727             }
2728 0         0 $allGroups{'ID-' . $id} = 1;
2729             }
2730             } else {
2731 0         0 foreach $tagInfo (@infoArray) {
2732 0 0 0     0 next unless ($grps = $$tagInfo{Groups}) and ($grp = $$grps{$family});
2733 0         0 $allGroups{$grp} = 1;
2734             }
2735             }
2736             }
2737             }
2738 0         0 delete $allGroups{'*'}; # (not a real group)
2739 0         0 return sort keys %allGroups;
2740             }
2741              
2742             #------------------------------------------------------------------------------
2743             # get priority group list for new values
2744             # Inputs: 0) ExifTool object reference
2745             # Returns: List of group names
2746             sub GetNewGroups($)
2747             {
2748 0     0 1 0 my $self = shift;
2749 0         0 return @{$$self{WRITE_GROUPS}};
  0         0  
2750             }
2751              
2752             #------------------------------------------------------------------------------
2753             # Get list of all deletable group names
2754             # Returns: List of group names (sorted alphabetically)
2755             sub GetDeleteGroups()
2756             {
2757 0     0 1 0 return sort @delGroups, @delGroup2;
2758             }
2759              
2760             #------------------------------------------------------------------------------
2761             # Add user-defined tags at run time
2762             # Inputs: 0) destination table name, 1) tagID/tagInfo pairs for tags to add
2763             # Returns: number of tags added
2764             # Notes: will replace existing tags
2765             sub AddUserDefinedTags($%)
2766             {
2767 1     1 1 239 local $_;
2768 1         8 my ($tableName, %addTags) = @_;
2769 1 50       5 my $table = GetTagTable($tableName) or return 0;
2770             # add tags to writer lookup
2771 1         9 Image::ExifTool::TagLookup::AddTags(\%addTags, $tableName);
2772 1         3 my $tagID;
2773 1         7 my $num = 0;
2774 1         3 foreach $tagID (keys %addTags) {
2775 1 50       8 next if $specialTags{$tagID};
2776 1         4 delete $$table{$tagID}; # delete old entry if it existed
2777 1         13 AddTagToTable($table, $tagID, $addTags{$tagID}, 1);
2778 1         4 ++$num;
2779             }
2780 1         10 return $num;
2781             }
2782              
2783             #==============================================================================
2784             # Functions below this are not part of the public API
2785              
2786             #------------------------------------------------------------------------------
2787             # Maintain backward compatibility for old GetNewValues function name
2788             sub GetNewValues($$;$)
2789             {
2790 0     0 0 0 my ($self, $tag, $nvHashPt) = @_;
2791 0         0 return $self->GetNewValue($tag, $nvHashPt);
2792             }
2793              
2794             #------------------------------------------------------------------------------
2795             # Un-escape string according to options settings and clear UTF-8 flag
2796             # Inputs: 0) ExifTool ref, 1) string ref or string ref ref
2797             # Notes: also de-references SCALAR values
2798             sub Sanitize($$)
2799             {
2800 5409     5409 0 12284 my ($self, $valPt) = @_;
2801             # de-reference SCALAR references
2802 5409 50       13399 $$valPt = $$$valPt if ref $$valPt eq 'SCALAR';
2803             # make sure the Perl UTF-8 flag is OFF for the value if perl 5.6 or greater
2804             # (otherwise our byte manipulations get corrupted!!)
2805 5409 50 33     15232 if ($] >= 5.006 and (eval { require Encode; Encode::is_utf8($$valPt) } or $@)) {
      33        
2806 0         0 local $SIG{'__WARN__'} = \&SetWarning;
2807             # repack by hand if Encode isn't available
2808 0 0       0 $$valPt = $@ ? pack('C*',unpack($] < 5.010000 ? 'U0C*' : 'C0C*',$$valPt)) : Encode::encode('utf8',$$valPt);
    0          
2809             }
2810             # un-escape value if necessary
2811 5409 100       19766 if ($$self{OPTIONS}{Escape}) {
2812             # (XMP.pm and HTML.pm were require'd as necessary when option was set)
2813 92 50       325 if ($$self{OPTIONS}{Escape} eq 'XML') {
    50          
2814 0         0 $$valPt = Image::ExifTool::XMP::UnescapeXML($$valPt);
2815             } elsif ($$self{OPTIONS}{Escape} eq 'HTML') {
2816 92         297 $$valPt = Image::ExifTool::HTML::UnescapeHTML($$valPt, $$self{OPTIONS}{Charset});
2817             }
2818             }
2819             }
2820              
2821             #------------------------------------------------------------------------------
2822             # Apply inverse conversions
2823             # Inputs: 0) ExifTool ref, 1) value, 2) tagInfo (or Struct item) ref,
2824             # 3) tag name, 4) group 1 name, 5) conversion type (or undef),
2825             # 6) [optional] want group ("" for structure field)
2826             # Returns: 0) converted value, 1) error string (or undef on success)
2827             # Notes:
2828             # - uses ExifTool "ConvType" member when conversion type is undef
2829             # - conversion types other than 'ValueConv' and 'PrintConv' are treated as 'Raw'
2830             sub ConvInv($$$$$;$$)
2831             {
2832 27960     27960 0 71000 my ($self, $val, $tagInfo, $tag, $wgrp1, $convType, $wantGroup) = @_;
2833 27960         43381 my ($err, $type);
2834              
2835 27960 100 50     63506 $convType or $convType = $$self{ConvType} || 'PrintConv';
2836              
2837 27960         46561 Conv: for (;;) {
2838 72507 100       168952 if (not defined $type) {
    100          
2839             # split value into list if necessary
2840 27960 100       64919 if ($$tagInfo{List}) {
2841 569   100     2883 my $listSplit = $$tagInfo{AutoSplit} || $$self{OPTIONS}{ListSplit};
2842 569 50 100     2330 if (defined $listSplit and not $$tagInfo{Struct} and
      66        
      100        
2843             ($wantGroup or not defined $wantGroup))
2844             {
2845 74 50 66     575 $listSplit = ',?\s+' if $listSplit eq '1' and $$tagInfo{AutoSplit};
2846 74         844 my @splitVal = split /$listSplit/, $val, -1;
2847 74 50       467 $val = @splitVal > 1 ? \@splitVal : @splitVal ? $splitVal[0] : '';
    100          
2848             }
2849             }
2850 27960         45817 $type = $convType;
2851             } elsif ($type eq 'PrintConv') {
2852 21604         40282 $type = 'ValueConv';
2853             } else {
2854             # split raw value if necessary
2855 22943 50 66     55425 if ($$tagInfo{RawJoin} and $$tagInfo{List} and not ref $val) {
      33        
2856 13         84 my @splitVal = split ' ', $val;
2857 13 50       82 $val = \@splitVal if @splitVal > 1;
2858             }
2859             # finally, do our value check
2860 22943         38203 my ($err2, $v);
2861 22943 100       51855 if ($$tagInfo{WriteCheck}) {
2862             #### eval WriteCheck ($self, $tagInfo, $val)
2863 275         21795 $err2 = eval $$tagInfo{WriteCheck};
2864 275 50       1428 $@ and warn($@), $err2 = 'Error evaluating WriteCheck';
2865             }
2866 22943 100       46382 unless ($err2) {
2867 22909         42900 my $table = $$tagInfo{Table};
2868 22909 100 100     130381 if ($table and $$table{CHECK_PROC} and not $$tagInfo{RawConvInv}) {
      100        
2869 22040         38494 my $checkProc = $$table{CHECK_PROC};
2870 22040 100       44264 if (ref $val eq 'ARRAY') {
2871             # loop through array values
2872 46         167 foreach $v (@$val) {
2873 136         404 $err2 = &$checkProc($self, $tagInfo, \$v, $convType);
2874 136 50       414 last if $err2;
2875             }
2876             } else {
2877 21994         67718 $err2 = &$checkProc($self, $tagInfo, \$val, $convType);
2878             }
2879             }
2880             }
2881 22943 100       60280 if (defined $err2) {
2882 3315 100       7179 if ($err2) {
2883 3307         8556 $err = "$err2 for $wgrp1:$tag";
2884 3307         13974 $self->VPrint(2, "$err\n");
2885 3307         7252 undef $val; # value was invalid
2886             } else {
2887 8         18 $err = $err2; # empty error (quietly don't write tag)
2888             }
2889             }
2890 22943         42954 last;
2891             }
2892 49564         95463 my $conv = $$tagInfo{$type};
2893 49564         114084 my $convInv = $$tagInfo{"${type}Inv"};
2894             # nothing to do at this level if no conversion defined
2895 49564 100 100     140621 next unless defined $conv or defined $convInv;
2896              
2897 22410         40426 my (@valList, $index, $convList, $convInvList);
2898 22410 100 66     92957 if (ref $val eq 'ARRAY') {
    100          
2899             # handle ValueConv of ListSplit and AutoSplit values
2900 12         62 @valList = @$val;
2901 12         60 $val = $valList[$index = 0];
2902             } elsif (ref $conv eq 'ARRAY' or ref $convInv eq 'ARRAY') {
2903             # handle conversion lists
2904 153         1454 @valList = split /$listSep{$type}/, $val;
2905 153         436 $val = $valList[$index = 0];
2906 153 50       579 if (ref $conv eq 'ARRAY') {
2907 153         345 $convList = $conv;
2908 153         480 $conv = $$conv[0];
2909             }
2910 153 100       531 if (ref $convInv eq 'ARRAY') {
2911 29         61 $convInvList = $convInv;
2912 29         73 $convInv = $$convInv[0];
2913             }
2914             }
2915             # loop through multiple values if necessary
2916 22410         32834 for (;;) {
2917 22462 100       49613 if ($convInv) {
    100          
2918             # capture eval warnings too
2919 13651         61516 local $SIG{'__WARN__'} = \&SetWarning;
2920 13651         28234 undef $evalWarning;
2921 13651 100       27577 if (ref($convInv) eq 'CODE') {
2922 131         657 $val = &$convInv($val, $self);
2923             } else {
2924             #### eval PrintConvInv/ValueConvInv ($val, $self, $wantGroup)
2925 13520         875125 $val = eval $convInv;
2926 13520 100       57876 $@ and $evalWarning = $@;
2927             }
2928 13651 100       60334 if ($evalWarning) {
    100          
2929             # an empty warning ("\n") ignores tag with no error
2930 223 100       681 if ($evalWarning eq "\n") {
2931 9 50       40 $err = '' unless defined $err;
2932             } else {
2933 214         785 $err = CleanWarning() . " in $wgrp1:$tag (${type}Inv)";
2934 214         1013 $self->VPrint(2, "$err\n");
2935             }
2936 223         511 undef $val;
2937 223         1155 last Conv;
2938             } elsif (not defined $val) {
2939 124         609 $err = "Error converting value for $wgrp1:$tag (${type}Inv)";
2940 124         705 $self->VPrint(2, "$err\n");
2941 124         584 last Conv;
2942             }
2943             } elsif ($conv) {
2944 8808 100 66     44912 if (ref $conv eq 'HASH' and (not exists $$tagInfo{"${type}Inv"} or $convInvList)) {
    100 66        
2945 8620         16922 my ($multi, $lc);
2946             # insert alternate language print conversions if required
2947 8620 0 33     24350 if ($$self{CUR_LANG} and $type eq 'PrintConv' and
      33        
      0        
2948             ref($lc = $$self{CUR_LANG}{$tag}) eq 'HASH' and
2949             ($lc = $$lc{PrintConv}))
2950             {
2951 0         0 my %newConv;
2952 0         0 foreach (keys %$conv) {
2953 0         0 my $val = $$conv{$_};
2954 0 0       0 defined $$lc{$val} or $newConv{$_} = $val, next;
2955 0         0 $newConv{$_} = $self->Decode($$lc{$val}, 'UTF8');
2956             }
2957 0 0       0 if ($$conv{BITMASK}) {
2958 0         0 foreach (keys %{$$conv{BITMASK}}) {
  0         0  
2959 0         0 my $val = $$conv{BITMASK}{$_};
2960 0 0       0 defined $$lc{$val} or $newConv{BITMASK}{$_} = $val, next;
2961 0         0 $newConv{BITMASK}{$_} = $self->Decode($$lc{$val}, 'UTF8');
2962             }
2963             }
2964 0         0 $conv = \%newConv;
2965             }
2966 8620         13759 undef $evalWarning;
2967 8620 100       22801 if ($$conv{BITMASK}) {
2968 100         315 my $lookupBits = $$conv{BITMASK};
2969 100         347 my ($wbits, $tbits) = @$tagInfo{'BitsPerWord','BitsTotal'};
2970 100         378 my ($val2, $err2) = EncodeBits($val, $lookupBits, $wbits, $tbits);
2971 100 100       386 if ($err2) {
    100          
2972             # ok, try matching a straight value
2973 2         10 ($val, $multi) = ReverseLookup($val, $conv);
2974 2 50       18 unless (defined $val) {
2975 2         13 $err = "Can't encode $wgrp1:$tag ($err2)";
2976 2         18 $self->VPrint(2, "$err\n");
2977 2         10 last Conv;
2978             }
2979             } elsif (defined $val2) {
2980 67         162 $val = $val2;
2981             } else {
2982 31         83 delete $$conv{BITMASK};
2983 31         97 ($val, $multi) = ReverseLookup($val, $conv);
2984 31         108 $$conv{BITMASK} = $lookupBits;
2985             }
2986             } else {
2987 8520         19001 ($val, $multi) = ReverseLookup($val, $conv);
2988             }
2989 8618 100       23945 if (not defined $val) {
    50          
2990 4518 100       14388 my $prob = $evalWarning ? lcfirst CleanWarning() : ($multi ? 'matches more than one ' : 'not in ') . $type;
    50          
2991 4518         12539 $err = "Can't convert $wgrp1:$tag ($prob)";
2992 4518         19978 $self->VPrint(2, "$err\n");
2993 4518         11626 last Conv;
2994             } elsif ($evalWarning) {
2995 0         0 $self->VPrint(2, CleanWarning() . " for $wgrp1:$tag\n");
2996             }
2997             } elsif (not $$tagInfo{WriteAlso}) {
2998 150         669 $err = "Can't convert value for $wgrp1:$tag (no ${type}Inv)";
2999 150         806 $self->VPrint(2, "$err\n");
3000 150         365 undef $val;
3001 150         385 last Conv;
3002             }
3003             }
3004 17445 100       55271 last unless @valList;
3005 125         381 $valList[$index] = $val;
3006 125 100       415 if (++$index >= @valList) {
3007             # leave AutoSplit lists in ARRAY form, or join conversion lists
3008 73 100       482 $val = $$tagInfo{List} ? \@valList : join ' ', @valList;
3009 73         257 last;
3010             }
3011 52 100       165 $conv = $$convList[$index] if $convList;
3012 52 100       131 $convInv = $$convInvList[$index] if $convInvList;
3013 52         120 $val = $valList[$index];
3014             }
3015             } # end ValueConv/PrintConv loop
3016              
3017 27960         80324 return($val, $err);
3018             }
3019              
3020             #------------------------------------------------------------------------------
3021             # Convert tag names to values or variables in a string
3022             # (eg. '${EXIF:ISO}x $$' --> '100x $' without hash ref, or "$info{'EXIF:ISO'}x $" with)
3023             # Inputs: 0) ExifTool object ref, 1) reference to list of found tags
3024             # 2) string with embedded tag names, 3) Options:
3025             # undef - set missing tags to ''
3026             # 'Error' - issue minor error on missing tag (and return undef)
3027             # 'Warn' - issue minor warning on missing tag (and return undef)
3028             # 'Silent' - just return undef on missing tag (no errors/warnings)
3029             # Hash ref - defined to interpolate as variables in string instead of values
3030             # --> receives tag/value pairs for interpolation of the variables
3031             # 4) document group name if extracting from a specific document
3032             # 5) hash ref to cache tag keys for subsequent calls in document loop
3033             # Returns: string with embedded tag values (or '$info{TAGNAME}' entries with Hash ref option)
3034             # Notes:
3035             # - tag names are not case sensitive and may end with '#' for ValueConv value
3036             # - uses MissingTagValue option if set
3037             # - '$GROUP:all' evaluates to 1 if any tag from GROUP exists, or 0 otherwise
3038             # - advanced feature allows Perl expressions inside braces (eg. '${model;tr/ //d}')
3039             # - an error/warning in an advanced expression ("${TAG;EXPR}") generates an error
3040             # if option set to 'Error', or a warning otherwise
3041             sub InsertTagValues($$$;$$$)
3042             {
3043 6     6 0 28 local $_;
3044 6         30 my ($self, $foundTags, $line, $opt, $docGrp, $cache) = @_;
3045 6         22 my $rtnStr = '';
3046 6         15 my ($docNum, $tag);
3047 6 50       29 if ($docGrp) {
3048 0 0       0 $docNum = $docGrp =~ /(\d+)$/ ? $1 : 0;
3049             } else {
3050 6         18 undef $cache; # no cache if no document groups
3051             }
3052 6         76 while ($line =~ s/(.*?)\$(\{\s*)?([-\w]*\w|\$|\/)//s) {
3053 9         69 my ($pre, $bra, $var) = ($1, $2, $3);
3054 9         32 my (@tags, $val, $tg, @val, $type, $expr, $didExpr, $level, $asList);
3055             # "$$" represents a "$" symbol, and "$/" is a newline
3056 9 50 33     63 if ($var eq '$' or $var eq '/') {
3057 0 0       0 $line =~ s/^\s*\}// if $bra;
3058 0 0 0     0 if ($var eq '/') {
    0          
3059 0         0 $var = "\n";
3060             } elsif ($line =~ /^self\b/ and not $rtnStr =~ /\$$/) {
3061 0         0 $var = '$$'; # ("$$self{var}" in string)
3062             }
3063 0         0 $rtnStr .= "$pre$var";
3064 0         0 next;
3065             }
3066             # allow multiple group names
3067 9         87 while ($line =~ /^:([-\w]*\w)(.*)/s) {
3068 4         12 my $group = $var;
3069 4         21 ($var, $line) = ($1, $2);
3070 4         22 $var = "$group:$var";
3071             }
3072             # allow trailing '#' to indicate ValueConv value
3073 9 50       37 $type = 'ValueConv' if $line =~ s/^#//;
3074             # special advanced formatting '@' feature to evaluate list values separately
3075 9 100 100     81 if ($bra and $line =~ s/^\@(#)?//) {
3076 1         5 $asList = 1;
3077 1 50       6 $type = 'ValueConv' if $1;
3078             }
3079             # remove trailing bracket if there was a leading one
3080             # and extract Perl expression from inside brackets if it exists
3081 9 100 100     184 if ($bra and $line !~ s/^\s*\}// and $line =~ s/^\s*;\s*(.*?)\s*\}//s) {
      66        
3082 3         13 my $part = $1;
3083 3         11 $expr = '';
3084 3         10 for ($level=0; ; --$level) {
3085             # increase nesting level for each opening brace
3086 7         33 ++$level while $part =~ /\{/g;
3087 7         15 $expr .= $part;
3088 7 100 66     42 last unless $level and $line =~ s/^(.*?)\s*\}//s; # get next part
3089 4         10 $part = $1;
3090 4         10 $expr .= '}'; # this brace was part of the expression
3091             }
3092             # use default Windows filename filter if expression is empty
3093 3 50       16 $expr = 'tr(/\\\\?*:|"<>\\0)()d' unless length $expr;
3094             }
3095 9         34 push @tags, $var;
3096 9         58 ExpandShortcuts(\@tags);
3097 9 50       36 @tags or $rtnStr .= $pre, next;
3098             # save advanced formatting expression to allow access by user-defined ValueConv
3099 9         36 $$self{FMT_EXPR} = $expr;
3100              
3101 9         23 for (;;) {
3102             # temporarily reset ListJoin option if evaluating list values separately
3103 9         17 my $oldListJoin;
3104 9 100       33 $oldListJoin = $self->Options(ListJoin => undef) if $asList;
3105 9         28 $tag = shift @tags;
3106 9         30 my $lcTag = lc $tag;
3107 9 50 33     59 if ($cache and $lcTag !~ /(^|:)all$/) {
3108             # remove group from tag name (but not lower-case version)
3109 0         0 my $group;
3110 0 0       0 $tag =~ s/^(.*):// and $group = $1;
3111             # cache tag keys to speed processing for a large number of sub-documents
3112             # (similar to code in BuildCompositeTags(), but this is case-insensitive)
3113 0         0 my $cacheTag = $$cache{$lcTag};
3114 0 0       0 unless ($cacheTag) {
3115 0         0 $cacheTag = $$cache{$lcTag} = [ ];
3116             # find all matching keys, organize into groups, and store in cache
3117 0         0 my $ex = $$self{TAG_EXTRA};
3118 0         0 my @matches = grep /^$tag(\s|$)/i, @$foundTags;
3119 0 0       0 @matches = $self->GroupMatches($group, \@matches) if defined $group;
3120 0         0 foreach (@matches) {
3121 0 0 0     0 my $doc = $$ex{$_} ? $$ex{$_}{G3} || 0 : 0;
3122 0 0       0 if (defined $$cacheTag[$doc]) {
3123 0 0       0 next unless $$cacheTag[$doc] =~ / \((\d+)\)$/;
3124 0         0 my $cur = $1;
3125             # keep the most recently extracted tag
3126 0 0 0     0 next if / \((\d+)\)$/ and $1 < $cur;
3127             }
3128 0         0 $$cacheTag[$doc] = $_;
3129             }
3130             }
3131 0 0 0     0 my $doc = $lcTag =~ /\b(main|doc(\d+)):/ ? ($2 || 0) : $docNum;
3132 0 0       0 if ($$cacheTag[$doc]) {
3133 0         0 $tag = $$cacheTag[$doc];
3134 0         0 $val = $self->GetValue($tag, $type);
3135             }
3136             } else {
3137             # add document number to tag if specified and it doesn't already exist
3138 9 50 33     45 if ($docGrp and $lcTag !~ /\b(main|doc\d+):/) {
3139 0         0 $tag = $docGrp . ':' . $tag;
3140 0         0 $lcTag = lc $tag;
3141             }
3142 9 50       92 if ($lcTag eq 'all') {
    50          
    100          
    50          
3143 0         0 $val = 1; # always some tag available
3144             } elsif (defined $$self{OPTIONS}{UserParam}{$lcTag}) {
3145 0         0 $val = $$self{OPTIONS}{UserParam}{$lcTag};
3146             } elsif ($tag =~ /(.*):(.+)/) {
3147 3         9 my $group;
3148 3         10 ($group, $tag) = ($1, $2);
3149 3 50       15 if (lc $tag eq 'all') {
3150             # see if any tag from the specified group exists
3151 0         0 my $match = $self->GroupMatches($group, $foundTags);
3152 0 0       0 $val = $match ? 1 : 0;
3153             } else {
3154             # find the specified tag
3155 3         663 my @matches = grep /^$tag(\s|$)/i, @$foundTags;
3156 3         20 @matches = $self->GroupMatches($group, \@matches);
3157 3         13 foreach $tg (@matches) {
3158 3 50 33     14 if (defined $val and $tg =~ / \((\d+)\)$/) {
3159             # take the most recently extracted tag
3160 0         0 my $tagNum = $1;
3161 0 0 0     0 next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum;
3162             }
3163 3         15 $val = $self->GetValue($tg, $type);
3164 3         9 $tag = $tg;
3165 3 100       23 last unless $tag =~ / /; # all done if we got our best match
3166             }
3167             }
3168             } elsif ($tag eq 'self') {
3169 0         0 $val = $self; # ("$self{var}" or "$self->{var}" in string)
3170             } else {
3171             # get the tag value
3172 6         33 $val = $self->GetValue($tag, $type);
3173 6 100       34 unless (defined $val) {
3174             # check for tag name with different case
3175 3         300 ($tg) = grep /^$tag$/i, @$foundTags;
3176 3 50       27 if (defined $tg) {
3177 3         14 $val = $self->GetValue($tg, $type);
3178 3         13 $tag = $tg;
3179             }
3180             }
3181             }
3182             }
3183 9 100       38 $self->Options(ListJoin => $oldListJoin) if $asList;
3184 9 100       75 if (ref $val eq 'ARRAY') {
    50          
    50          
    50          
3185 1         6 push @val, @$val;
3186 1         3 undef $val;
3187 1 50       7 last unless @tags;
3188             } elsif (ref $val eq 'SCALAR') {
3189 0 0 0     0 if ($$self{OPTIONS}{Binary} or $$val =~ /^Binary data/) {
3190 0         0 $val = $$val;
3191             } else {
3192 0         0 $val = 'Binary data ' . length($$val) . ' bytes';
3193             }
3194             } elsif (ref $val eq 'HASH') {
3195 0         0 require 'Image/ExifTool/XMPStruct.pl';
3196 0         0 $val = Image::ExifTool::XMP::SerializeStruct($val);
3197             } elsif (not defined $val) {
3198 0 0       0 $val = $$self{OPTIONS}{MissingTagValue} if $asList;
3199             }
3200 8 50       32 last unless @tags;
3201 0 0       0 push @val, $val if defined $val;
3202 0         0 undef $val;
3203             }
3204 9 100       35 if (@val) {
3205 1 50       6 push @val, $val if defined $val;
3206 1         9 $val = join $$self{OPTIONS}{ListSep}, @val;
3207             } else {
3208 8 50       31 push @val, $val if defined $val; # (so the eval has access to @val if required)
3209             }
3210             # evaluate advanced formatting expression if given (eg. "${TAG;EXPR}")
3211 9 100 66     57 if (defined $expr and defined $val) {
3212 3         24 local $SIG{'__WARN__'} = \&SetWarning;
3213 3         10 undef $evalWarning;
3214 3         10 $advFmtSelf = $self;
3215 3 100       14 if ($asList) {
3216 1         4 foreach (@val) {
3217             #### eval advanced formatting expression ($_, $self, @val, $advFmtSelf)
3218 3         261 eval $expr;
3219 3 50       16 $@ and $evalWarning = $@;
3220             }
3221             # join back together if any values are still defined
3222 1         11 @val = grep defined, @val;
3223 1 50       14 $val = @val ? join $$self{OPTIONS}{ListSep}, @val : undef;
3224             } else {
3225 2         6 $_ = $val;
3226             #### eval advanced formatting expression ($_, $self, @val, $advFmtSelf)
3227 2         156 eval $expr;
3228 2 50       14 $@ and $evalWarning = $@;
3229 2 50       12 $val = ref $_ eq 'ARRAY' ? join($$self{OPTIONS}{ListSep}, @$_): $_;
3230             }
3231 3 50       15 if ($evalWarning) {
3232 0 0 0     0 my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : '';
3233 0         0 my $str = CleanWarning() . " for '$g3${var}'";
3234 0 0       0 if ($opt) {
3235 0 0       0 if ($opt eq 'Error') {
    0          
3236 0         0 $self->Error($str);
3237             } elsif ($opt ne 'Silent') {
3238 0         0 $self->Warn($str);
3239             }
3240             }
3241             }
3242 3         10 undef $advFmtSelf;
3243 3         13 $didExpr = 1; # set flag indicating an expression was evaluated
3244             }
3245 9 50 33     48 unless (defined $val or ref $opt) {
3246 0         0 $val = $$self{OPTIONS}{MissingTagValue};
3247 0 0       0 unless (defined $val) {
3248 0 0 0     0 my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : '';
3249 0 0       0 my $msg = $didExpr ? "Advanced formatting expression returned undef for '$g3${var}'" :
3250             "Tag '$g3${var}' not defined";
3251 58     58   673 no strict 'refs';
  58         213  
  58         40970  
3252 0 0 0     0 $opt and ($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef;
      0        
3253 0         0 $val = '';
3254             }
3255             }
3256 9 50       34 if (ref $opt eq 'HASH') {
3257 0 0       0 $var .= '#' if $type;
3258 0 0       0 if (defined $expr) {
3259             # generate unique variable name for this modified tag value
3260 0         0 my $i = 1;
3261 0         0 ++$i while exists $$opt{"$var.expr$i"};
3262 0         0 $var .= '.expr' . $i;
3263             }
3264 0         0 $rtnStr .= "$pre\$info{'${var}'}";
3265 0         0 $$opt{$var} = $val;
3266             } else {
3267 9         62 $rtnStr .= "$pre$val";
3268             }
3269             }
3270 6         27 $$self{FMT_EXPR} = undef;
3271 6         32 return $rtnStr . $line;
3272             }
3273              
3274             #------------------------------------------------------------------------------
3275             # Reformat date/time value in $_ based on specified format string
3276             # Inputs: 0) date/time format string
3277             sub DateFmt($)
3278             {
3279 0     0 0 0 my $et = bless { OPTIONS => { DateFormat => shift, StrictDate => 1 } };
3280 0         0 my $shift;
3281 0 0 0     0 if ($advFmtSelf and defined($shift = $$advFmtSelf{OPTIONS}{GlobalTimeShift})) {
3282 0         0 $$et{OPTIONS}{GlobalTimeShift} = $shift;
3283 0         0 $$et{GLOBAL_TIME_OFFSET} = $$advFmtSelf{GLOBAL_TIME_OFFSET};
3284             }
3285 0         0 $_ = $et->ConvertDateTime($_);
3286 0 0       0 defined $_ or warn "Error converting date/time\n";
3287 0 0       0 $$advFmtSelf{GLOBAL_TIME_OFFSET} = $$et{GLOBAL_TIME_OFFSET} if $shift;
3288             }
3289              
3290             #------------------------------------------------------------------------------
3291             # Utility routine to remove duplicate items from default input string
3292             # Inputs: 0) true to set $_ to undef if not changed
3293             # Notes: - for use only in advanced formatting expressions
3294             sub NoDups
3295             {
3296 0     0 0 0 my %seen;
3297 0 0       0 my $sep = $advFmtSelf ? $$advFmtSelf{OPTIONS}{ListSep} : ', ';
3298 0         0 my $new = join $sep, grep { !$seen{$_}++ } split /\Q$sep\E/, $_;
  0         0  
3299 0 0 0     0 $_ = ($_[0] and $new eq $_) ? undef : $new;
3300             }
3301              
3302             #------------------------------------------------------------------------------
3303             # Is specified tag writable
3304             # Inputs: 0) tag name, case insensitive (optional group name currently ignored)
3305             # Returns: 0=exists but not writable, 1=writable, undef=doesn't exist
3306             sub IsWritable($)
3307             {
3308 0     0 0 0 my $tag = shift;
3309 0         0 $tag =~ s/^(.*)://; # ignore group name
3310 0         0 my @tagInfo = FindTagInfo($tag);
3311 0 0       0 unless (@tagInfo) {
3312 0 0       0 return 0 if TagExists($tag);
3313 0         0 return undef;
3314             }
3315 0         0 my $tagInfo;
3316 0         0 foreach $tagInfo (@tagInfo) {
3317 0 0       0 return $$tagInfo{Writable} ? 1 : 0 if defined $$tagInfo{Writable};
    0          
3318 0 0       0 return 1 if $$tagInfo{Table}{WRITABLE};
3319             # must call WRITE_PROC to autoload writer because this may set the writable tag
3320 0         0 my $writeProc = $$tagInfo{Table}{WRITE_PROC};
3321 0 0       0 if ($writeProc) {
3322 58     58   518 no strict 'refs';
  58         175  
  58         17919  
3323 0         0 &$writeProc(); # dummy call to autoload writer
3324 0 0       0 return 1 if $$tagInfo{Writable};
3325             }
3326             }
3327 0         0 return 0;
3328             }
3329              
3330             #------------------------------------------------------------------------------
3331             # Check to see if these are the same file
3332             # Inputs: 0) ExifTool ref, 1) first file name, 2) second file name
3333             # Returns: true if file names reference the same file
3334             sub IsSameFile($$$)
3335             {
3336 0     0 0 0 my ($self, $file, $file2) = @_;
3337 0 0       0 return 0 unless lc $file eq lc $file2; # (only looking for differences in case)
3338 0         0 my ($isSame, $interrupted);
3339 0         0 my $tmp1 = "${file}_ExifTool_tmp_$$";
3340 0         0 my $tmp2 = "${file2}_ExifTool_tmp_$$";
3341             {
3342 0         0 local *TMP1;
  0         0  
3343 0     0   0 local $SIG{INT} = sub { $interrupted = 1 };
  0         0  
3344 0 0       0 if ($self->Open(\*TMP1, $tmp1, '>')) {
3345 0         0 close TMP1;
3346 0 0       0 $isSame = 1 if $self->Exists($tmp2);
3347 0         0 $self->Unlink($tmp1);
3348             }
3349             }
3350 0 0 0     0 if ($interrupted and $SIG{INT}) {
3351 58     58   500 no strict 'refs';
  58         204  
  58         146064  
3352 0         0 &{$SIG{INT}}();
  0         0  
3353             }
3354 0         0 return $isSame;
3355             }
3356              
3357             #------------------------------------------------------------------------------
3358             # Is this a raw file type?
3359             # Inputs: 0) ExifTool ref
3360             # Returns: true if FileType is a type of RAW image
3361             sub IsRawType($)
3362             {
3363 12     12 0 37 my $self = shift;
3364 12         114 return $rawType{$$self{FileType}};
3365             }
3366              
3367             #------------------------------------------------------------------------------
3368             # Create directory for specified file
3369             # Inputs: 0) ExifTool ref, 1) complete file name including path
3370             # Returns: 1 = directory created, 0 = nothing done, -1 = error
3371             my $k32CreateDir;
3372             sub CreateDirectory($$)
3373             {
3374 1     1 0 2 local $_;
3375 1         4 my ($self, $file) = @_;
3376 1         5 my $rtnVal = 0;
3377 1         8 my $enc = $$self{OPTIONS}{CharsetFileName};
3378 1         2 my $dir;
3379 1         9 ($dir = $file) =~ s/[^\/]*$//; # remove filename from path specification
3380             # recode as UTF-8 if necessary
3381 1 50 33     12 if ($dir and not $self->IsDirectory($dir)) {
3382 0         0 my @parts = split /\//, $dir;
3383 0         0 $dir = '';
3384 0         0 foreach (@parts) {
3385 0         0 $dir .= $_;
3386 0 0 0     0 if (length $dir and not $self->IsDirectory($dir)) {
3387             # create directory since it doesn't exist
3388 0         0 my $d2 = $dir; # (must make a copy in case EncodeFileName recodes it)
3389 0 0       0 if ($self->EncodeFileName($d2)) {
3390             # handle Windows Unicode directory names
3391 0 0       0 unless (eval { require Win32::API }) {
  0         0  
3392 0         0 $self->Warn('Install Win32::API to create directories with Unicode names');
3393 0         0 return -1;
3394             }
3395 0 0       0 unless ($k32CreateDir) {
3396 0 0       0 return -1 if defined $k32CreateDir;
3397 0         0 $k32CreateDir = new Win32::API('KERNEL32', 'CreateDirectoryW', 'PP', 'I');
3398 0 0       0 unless ($k32CreateDir) {
3399 0         0 $self->Warn('Error calling Win32::API::CreateDirectoryW');
3400 0         0 $k32CreateDir = 0;
3401 0         0 return -1;
3402             }
3403             }
3404 0 0       0 $k32CreateDir->Call($d2, 0) or return -1;
3405             } else {
3406 0 0       0 mkdir($d2, 0777) or return -1;
3407             }
3408 0         0 $rtnVal = 1;
3409             }
3410 0         0 $dir .= '/';
3411             }
3412             }
3413 1         13 return $rtnVal;
3414             }
3415              
3416             #------------------------------------------------------------------------------
3417             # Copy file attributes from one file to another
3418             # Inputs: 0) ExifTool ref, 1) source file name, 2) destination file name
3419             # Notes: eventually add support for extended attributes?
3420             sub CopyFileAttrs($$$)
3421             {
3422 2     2 0 10 my ($self, $src, $dst) = @_;
3423 2         51 my ($mode, $uid, $gid) = (stat($src))[2, 4, 5];
3424             # copy file attributes unless we already set them
3425 2 50 33     22 if (defined $mode and not defined $self->GetNewValue('FilePermissions')) {
3426 2         8 eval { chmod($mode & 07777, $dst) };
  2         59  
3427             }
3428 2         16 my $newUid = $self->GetNewValue('FileUserID');
3429 2         8 my $newGid = $self->GetNewValue('FileGroupID');
3430 2 50 33     25 if (defined $uid and defined $gid and (not defined $newUid or not defined $newGid)) {
      33        
      33        
3431 2 50       11 defined $newGid and $gid = $newGid;
3432 2 50       10 defined $newUid and $uid = $newUid;
3433 2         5 eval { chown($uid, $gid, $dst) };
  2         51  
3434             }
3435             }
3436              
3437             #------------------------------------------------------------------------------
3438             # Get new file path name
3439             # Inputs: 0) existing name (may contain directory),
3440             # 1) new file name, new directory, or new path (dir+name)
3441             # Returns: new file path name
3442             sub GetNewFileName($$)
3443             {
3444 1     1 0 4 my ($oldName, $newName) = @_;
3445 1         9 my ($dir, $name) = ($oldName =~ m{(.*/)(.*)});
3446 1 50       4 ($dir, $name) = ('', $oldName) unless defined $dir;
3447 1 50       11 if ($newName =~ m{/$}) {
    50          
3448 0         0 $newName = "$newName$name"; # change dir only
3449             } elsif ($newName !~ m{/}) {
3450 1         4 $newName = "$dir$newName"; # change name only if newname doesn't specify dir
3451             } # else change dir and name
3452 1         4 return $newName;
3453             }
3454              
3455             #------------------------------------------------------------------------------
3456             # Get next available tag key
3457             # Inputs: 0) hash reference (keys are tag keys), 1) tag name
3458             # Returns: next available tag key
3459             sub NextFreeTagKey($$)
3460             {
3461 0     0 0 0 my ($info, $tag) = @_;
3462 0 0       0 return $tag unless exists $$info{$tag};
3463 0         0 my $i;
3464 0         0 for ($i=1; ; ++$i) {
3465 0         0 my $key = "$tag ($i)";
3466 0 0       0 return $key unless exists $$info{$key};
3467             }
3468             }
3469              
3470             #------------------------------------------------------------------------------
3471             # Reverse hash lookup
3472             # Inputs: 0) value, 1) hash reference
3473             # Returns: Hash key or undef if not found (plus flag for multiple matches in list context)
3474             sub ReverseLookup($$)
3475             {
3476 8620     8620 0 18214 my ($val, $conv) = @_;
3477 8620 100       18109 return undef unless defined $val;
3478 8559         12612 my $multi;
3479 8559 100       18042 if ($val =~ /^Unknown\s*\((.*)\)$/i) {
3480 40         122 $val = $1; # was unknown
3481 40 50       116 if ($val =~ /^0x([\da-fA-F]+)$/) {
3482             # disable "Hexadecimal number > 0xffffffff non-portable" warning
3483 0     0   0 local $SIG{'__WARN__'} = sub { };
3484 0         0 $val = hex($val); # convert hex value
3485             }
3486             } else {
3487 8519         13969 my $qval = $val;
3488 8519         19985 $qval =~ s/\s+$//; # remove trailing whitespace
3489 8519         14405 $qval = quotemeta $qval;
3490 8519         34426 my @patterns = (
3491             "^$qval\$", # exact match
3492             "^(?i)$qval\$", # case-insensitive
3493             "^(?i)$qval", # beginning of string
3494             "(?i)$qval", # substring
3495             );
3496             # hash entries to ignore in reverse lookup
3497 8519         17238 my ($pattern, $found, $matches);
3498 8519         18729 PAT: foreach $pattern (@patterns) {
3499 21760         380012 $matches = scalar grep /$pattern/, values(%$conv);
3500 21760 100       59970 next unless $matches;
3501             # multiple matches are bad unless they were exact
3502 6450 100 100     23039 if ($matches > 1 and $pattern !~ /\$$/) {
3503             # don't match entries that we should ignore
3504 3128         9171 foreach (keys %ignorePrintConv) {
3505 9384 100 100     23559 --$matches if defined $$conv{$_} and $$conv{$_} =~ /$pattern/;
3506             }
3507 3128 100       9455 last if $matches > 1;
3508             }
3509 3452         52237 foreach (sort keys %$conv) {
3510 10767 100 100     43669 next if $$conv{$_} !~ /$pattern/ or $ignorePrintConv{$_};
3511 3411         7026 $val = $_;
3512 3411         5579 $found = 1;
3513 3411         7283 last PAT;
3514             }
3515             }
3516 8519 100       24652 unless ($found) {
3517             # call OTHER conversion routine if available
3518 5108 100       11835 if ($$conv{OTHER}) {
3519 792         3813 local $SIG{'__WARN__'} = \&SetWarning;
3520 792         1567 undef $evalWarning;
3521 792         1237 $val = &{$$conv{OTHER}}($val,1,$conv);
  792         3122  
3522             } else {
3523 4316         7622 $val = undef;
3524             }
3525 5108 100       20034 $multi = 1 if $matches > 1;
3526             }
3527             }
3528 8559 100       32817 return ($val, $multi) if wantarray;
3529 47         144 return $val;
3530             }
3531              
3532             #------------------------------------------------------------------------------
3533             # Return true if we are deleting or overwriting the specified tag
3534             # Inputs: 0) ExifTool object ref, 1) new value hash reference
3535             # 2) optional tag value (before RawConv) if deleting specific values
3536             # Returns: >0 - tag should be overwritten
3537             # =0 - the tag should be preserved
3538             # <0 - not sure, we need the old value to tell (if there is no old value
3539             # then the tag should be written if $$nvHash{IsCreating} is true)
3540             # Notes: $$nvHash{Value} is updated with the new value when shifting a value
3541             sub IsOverwriting($$;$)
3542             {
3543 6205     6205 0 13499 my ($self, $nvHash, $val) = @_;
3544 6205 100       16382 return 0 unless $nvHash;
3545             # overwrite regardless if no DelValues specified
3546 6164 100       25270 return 1 unless $$nvHash{DelValue};
3547             # never overwrite if DelValue list exists but is empty
3548 117         323 my $shift = $$nvHash{Shift};
3549 117 100 100     192 return 0 unless @{$$nvHash{DelValue}} or defined $shift;
  117         472  
3550             # return "don't know" if we don't have a value to test
3551 104 100       395 return -1 unless defined $val;
3552             # apply raw conversion if necessary
3553 46         127 my $tagInfo = $$nvHash{TagInfo};
3554 46         108 my $conv = $$tagInfo{RawConv};
3555 46 100       127 if ($conv) {
3556 3         24 local $SIG{'__WARN__'} = \&SetWarning;
3557 3         10 undef $evalWarning;
3558 3 50       17 if (ref $conv eq 'CODE') {
3559 0         0 $val = &$conv($val, $self);
3560             } else {
3561 3         8 my ($priority, @grps);
3562 3         11 my $tag = $$tagInfo{Name};
3563             #### eval RawConv ($self, $val, $tag, $tagInfo, $priority, @grps)
3564 3         307 $val = eval $conv;
3565 3 50       29 $@ and $evalWarning = $@;
3566             }
3567 3 50       23 return -1 unless defined $val;
3568             }
3569             # do not overwrite if only creating
3570 46 100       162 return 0 if $$nvHash{CreateOnly};
3571             # apply time/number shift if necessary
3572 40 100       134 if (defined $shift) {
3573 13         41 my $shiftType = $$tagInfo{Shift};
3574 13 100 66     67 unless ($shiftType and $shiftType eq 'Time') {
3575 6 50       24 unless (IsFloat($val)) {
3576             # do the ValueConv to try to get a number
3577 0         0 my $conv = $$tagInfo{ValueConv};
3578 0 0       0 if (defined $conv) {
3579 0         0 local $SIG{'__WARN__'} = \&SetWarning;
3580 0         0 undef $evalWarning;
3581 0 0       0 if (ref $conv eq 'CODE') {
    0          
3582 0         0 $val = &$conv($val, $self);
3583             } elsif (not ref $conv) {
3584             #### eval ValueConv ($val, $self)
3585 0         0 $val = eval $conv;
3586 0 0       0 $@ and $evalWarning = $@;
3587             }
3588 0 0       0 if ($evalWarning) {
3589 0         0 $self->Warn("ValueConv $$tagInfo{Name}: " . CleanWarning());
3590 0         0 return 0;
3591             }
3592             }
3593 0 0 0     0 unless (defined $val and IsFloat($val)) {
3594 0         0 $self->Warn("Can't shift $$tagInfo{Name} (not a number)");
3595 0         0 return 0;
3596             }
3597             }
3598 6         20 $shiftType = 'Number'; # allow any number to be shifted
3599             }
3600 13         96 require 'Image/ExifTool/Shift.pl';
3601 13         78 my $err = $self->ApplyShift($shiftType, $shift, $val, $nvHash);
3602 13 50       44 if ($err) {
3603 0         0 $self->Warn("$err when shifting $$tagInfo{Name}");
3604 0         0 return 0;
3605             }
3606             # ensure that the shifted value is valid and reformat if necessary
3607 13         61 my $checkVal = $self->GetNewValue($nvHash);
3608 13 50       39 return 0 unless defined $checkVal;
3609             # don't bother overwriting if value is the same
3610 13 50       59 return 0 if $val eq $$nvHash{Value}[0];
3611 13         74 return 1;
3612             }
3613             # return 1 if value matches a DelValue
3614 27         53 my $delVal;
3615 27         49 foreach $delVal (@{$$nvHash{DelValue}}) {
  27         76  
3616 32 100       127 return 1 if $val eq $delVal;
3617             }
3618 17         60 return 0;
3619             }
3620              
3621             #------------------------------------------------------------------------------
3622             # Get write group for specified tag
3623             # Inputs: 0) new value hash reference
3624             # Returns: Write group name
3625             sub GetWriteGroup($)
3626             {
3627 0     0 0 0 return $_[0]{WriteGroup};
3628             }
3629              
3630             #------------------------------------------------------------------------------
3631             # Get name of write group or family 1 group
3632             # Inputs: 0) ExifTool ref, 1) tagInfo ref, 2) write group name
3633             # Returns: Name of group for verbose message
3634             sub GetWriteGroup1($$)
3635             {
3636 32168     32168 0 69295 my ($self, $tagInfo, $writeGroup) = @_;
3637 32168 100       144652 return $writeGroup unless $writeGroup =~ /^(MakerNotes|XMP|Composite|QuickTime)$/;
3638 26832         94335 return $self->GetGroup($tagInfo, 1);
3639             }
3640              
3641             #------------------------------------------------------------------------------
3642             # Get new value hash for specified tagInfo/writeGroup
3643             # Inputs: 0) ExifTool object reference, 1) reference to tag info hash
3644             # 2) Write group name, 3) Options: 'delete' or 'create' new value hash
3645             # 4) optional ProtectSaved value, 5) true if we are deleting a value
3646             # Returns: new value hash reference for specified write group
3647             # (or first new value hash in linked list if write group not specified)
3648             # Notes: May return undef when 'create' is used with ProtectSaved
3649             sub GetNewValueHash($$;$$$$)
3650             {
3651 67445     67445 0 162930 my ($self, $tagInfo, $writeGroup, $opts) = @_;
3652 67445 100       131732 return undef unless $tagInfo;
3653 67444         173180 my $nvHash = $$self{NEW_VALUE}{$tagInfo};
3654              
3655 67444         95704 my %opts; # quick lookup for options
3656 67444 100       148565 $opts and $opts{$opts} = 1;
3657 67444 100       136412 $writeGroup = '' unless defined $writeGroup;
3658              
3659 67444 100       121987 if ($writeGroup) {
3660             # find the new value in the list with the specified write group
3661 46315   100     114357 while ($nvHash and $$nvHash{WriteGroup} ne $writeGroup) {
3662             # QuickTime and All are special cases because all group1 tags may be updated at once
3663 2003 100       6719 last if $$nvHash{WriteGroup} =~ /^(QuickTime|All)$/;
3664             # replace existing entry if WriteGroup is 'All' (avoids confusion of forum10349)
3665 1967 100 100     5225 last if $$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All';
3666 1955         4514 $nvHash = $$nvHash{Next};
3667             }
3668             }
3669             # remove this entry if deleting, or if creating a new entry and
3670             # this entry is marked with "Save" flag
3671 67444 100 100     177272 if (defined $nvHash and ($opts{'delete'} or ($opts{'create'} and $$nvHash{Save}))) {
      100        
3672 2403   33     7463 my $protect = (defined $_[4] and defined $$nvHash{Save} and $$nvHash{Save} > $_[4]);
3673             # this is a bit tricky: we want to add to a protected nvHash only if we
3674             # are adding a conditional delete ($_[5] true or DelValue with no Shift)
3675             # or accumulating List items (NoReplace true)
3676 2403 50 0     8038 if ($protect and not ($opts{create} and ($$nvHash{NoReplace} or $_[5] or
    100 33        
3677             ($$nvHash{DelValue} and not defined $$nvHash{Shift}))))
3678             {
3679 0         0 return undef; # honour ProtectSaved value by not writing this tag
3680             } elsif ($opts{'delete'}) {
3681 2394         7541 $self->RemoveNewValueHash($nvHash, $tagInfo);
3682 2394         7877 undef $nvHash;
3683             } else {
3684             # save a copy of this new value hash
3685 9         86 my %copy = %$nvHash;
3686             # make copy of Value and DelValue lists
3687 9         25 my $key;
3688 9         31 foreach $key (keys %copy) {
3689 67 100       143 next unless ref $copy{$key} eq 'ARRAY';
3690 9         12 $copy{$key} = [ @{$copy{$key}} ];
  9         45  
3691             }
3692 9         20 my $saveHash = $$self{SAVE_NEW_VALUE};
3693             # add to linked list of saved new value hashes
3694 9         40 $copy{Next} = $$saveHash{$tagInfo};
3695 9         27 $$saveHash{$tagInfo} = \%copy;
3696 9         20 delete $$nvHash{Save}; # don't save it again
3697 9 0 33     27 $$nvHash{AddBefore} = scalar @{$$nvHash{Value}} if $protect and $$nvHash{Value};
  0         0  
3698             }
3699             }
3700 67444 100 100     192211 if (not defined $nvHash and $opts{'create'}) {
3701             # create a new entry
3702 23023         97129 $nvHash = {
3703             TagInfo => $tagInfo,
3704             WriteGroup => $writeGroup,
3705             IsNVH => 1, # set flag so we can recognize a new value hash
3706             };
3707             # add entry to our NEW_VALUE hash
3708 23023 100       58671 if ($$self{NEW_VALUE}{$tagInfo}) {
3709             # add to end of linked list
3710 32         173 my $lastHash = LastInList($$self{NEW_VALUE}{$tagInfo});
3711 32         144 $$lastHash{Next} = $nvHash;
3712             } else {
3713 22991         64907 $$self{NEW_VALUE}{$tagInfo} = $nvHash;
3714             }
3715             }
3716 67444         169658 return $nvHash;
3717             }
3718              
3719             #------------------------------------------------------------------------------
3720             # Load all tag tables
3721             sub LoadAllTables()
3722             {
3723 0 0   0 0 0 return if $loadedAllTables;
3724              
3725             # load all of our non-referenced tables (first our modules)
3726 0         0 my $table;
3727 0         0 foreach $table (@loadAllTables) {
3728 0         0 my $tableName = "Image::ExifTool::$table";
3729 0 0       0 $tableName .= '::Main' unless $table =~ /:/;
3730 0         0 GetTagTable($tableName);
3731             }
3732             # (then our special tables)
3733 0         0 GetTagTable('Image::ExifTool::Extra');
3734 0         0 GetTagTable('Image::ExifTool::Composite');
3735             # recursively load all tables referenced by the current tables
3736 0         0 my @tableNames = keys %allTables;
3737 0         0 my %pushedTables;
3738 0         0 while (@tableNames) {
3739 0         0 $table = GetTagTable(shift @tableNames);
3740             # call write proc if it exists in case it adds tags to the table
3741 0         0 my $writeProc = $$table{WRITE_PROC};
3742 0 0       0 if ($writeProc) {
3743 58     58   596 no strict 'refs';
  58         183  
  58         179295  
3744 0         0 &$writeProc();
3745             }
3746             # recursively scan through tables in subdirectories
3747 0         0 foreach (TagTableKeys($table)) {
3748 0         0 my @infoArray = GetTagInfoList($table,$_);
3749 0         0 my $tagInfo;
3750 0         0 foreach $tagInfo (@infoArray) {
3751 0 0       0 my $subdir = $$tagInfo{SubDirectory} or next;
3752 0 0       0 my $tableName = $$subdir{TagTable} or next;
3753             # next if table already loaded or queued for loading
3754 0 0 0     0 next if $allTables{$tableName} or $pushedTables{$tableName};
3755 0         0 push @tableNames, $tableName; # must scan this one too
3756 0         0 $pushedTables{$tableName} = 1;
3757             }
3758             }
3759             }
3760 0         0 $loadedAllTables = 1;
3761             }
3762              
3763             #------------------------------------------------------------------------------
3764             # Remove new value hash from linked list (and save if necessary)
3765             # Inputs: 0) ExifTool object reference, 1) new value hash ref, 2) tagInfo ref
3766             sub RemoveNewValueHash($$$)
3767             {
3768 2664     2664 0 5254 my ($self, $nvHash, $tagInfo) = @_;
3769 2664         5656 my $firstHash = $$self{NEW_VALUE}{$tagInfo};
3770 2664 50       6562 if ($nvHash eq $firstHash) {
3771             # remove first entry from linked list
3772 2664 50       5582 if ($$nvHash{Next}) {
3773 0         0 $$self{NEW_VALUE}{$tagInfo} = $$nvHash{Next};
3774             } else {
3775 2664         7311 delete $$self{NEW_VALUE}{$tagInfo};
3776             }
3777             } else {
3778             # find the list element pointing to this hash
3779 0         0 $firstHash = $$firstHash{Next} while $$firstHash{Next} ne $nvHash;
3780             # remove from linked list
3781 0         0 $$firstHash{Next} = $$nvHash{Next};
3782             }
3783             # save the existing entry if necessary
3784 2664 100       7731 if ($$nvHash{Save}) {
3785 80         161 my $saveHash = $$self{SAVE_NEW_VALUE};
3786             # add to linked list of saved new value hashes
3787 80         221 $$nvHash{Next} = $$saveHash{$tagInfo};
3788 80         306 $$saveHash{$tagInfo} = $nvHash;
3789             }
3790             }
3791              
3792             #------------------------------------------------------------------------------
3793             # Remove all new value entries for specified group
3794             # Inputs: 0) ExifTool object reference, 1) group name
3795             sub RemoveNewValuesForGroup($$)
3796             {
3797 784     784 0 1345 my ($self, $group) = @_;
3798              
3799 784 100       1605 return unless $$self{NEW_VALUE};
3800              
3801             # make list of all groups we must remove
3802 7         27 my @groups = ( $group );
3803 7 100       42 push @groups, @{$removeGroups{$group}} if $removeGroups{$group};
  2         8  
3804              
3805 7         20 my ($out, @keys, $hashKey);
3806 7 50       36 $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose} > 1;
3807              
3808             # loop though all new values, and remove any in this group
3809 7         19 @keys = keys %{$$self{NEW_VALUE}};
  7         737  
3810 7         36 foreach $hashKey (@keys) {
3811 1979         5154 my $nvHash = $$self{NEW_VALUE}{$hashKey};
3812             # loop through each entry in linked list
3813 1979         2805 for (;;) {
3814 1985         4469 my $nextHash = $$nvHash{Next};
3815 1985         4342 my $tagInfo = $$nvHash{TagInfo};
3816 1985         5385 my ($grp0,$grp1) = $self->GetGroup($tagInfo);
3817 1985         5330 my $wgrp = $$nvHash{WriteGroup};
3818             # use group1 if write group is not specific
3819 1985 100       4394 $wgrp = $grp1 if $wgrp eq $grp0;
3820 1985 100       44906 if (grep /^($grp0|$wgrp)$/i, @groups) {
3821 270 50       762 $out and print $out "Removed new value for $wgrp:$$tagInfo{Name}\n";
3822             # remove from linked list
3823 270         709 $self->RemoveNewValueHash($nvHash, $tagInfo);
3824             }
3825 1985 100       7983 $nvHash = $nextHash or last;
3826             }
3827             }
3828             }
3829              
3830             #------------------------------------------------------------------------------
3831             # Get list of tagInfo hashes for all new data
3832             # Inputs: 0) ExifTool object reference, 1) optional tag table pointer
3833             # Returns: list of tagInfo hashes
3834             sub GetNewTagInfoList($;$)
3835             {
3836 1194     1194 0 3019 my ($self, $tagTablePtr) = @_;
3837 1194         2067 my @tagInfoList;
3838 1194         2683 my $nv = $$self{NEW_VALUE};
3839 1194 100       3209 if ($nv) {
3840 1170         1858 my $hashKey;
3841 1170         22908 foreach $hashKey (keys %$nv) {
3842 88840         171115 my $tagInfo = $$nv{$hashKey}{TagInfo};
3843 88840 100 100     265786 next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table};
3844 32433         56571 push @tagInfoList, $tagInfo;
3845             }
3846             }
3847 1194         14747 return @tagInfoList;
3848             }
3849              
3850             #------------------------------------------------------------------------------
3851             # Get hash of tagInfo references keyed on tagID for a specific table
3852             # Inputs: 0) ExifTool object reference, 1-N) tag table pointers
3853             # Returns: hash reference
3854             # Notes: returns only one tagInfo ref for each conditional list
3855             sub GetNewTagInfoHash($@)
3856             {
3857 473     473 0 891 my $self = shift;
3858 473         879 my (%tagInfoHash, $hashKey);
3859 473         993 my $nv = $$self{NEW_VALUE};
3860 473         1208 while ($nv) {
3861 921   100     2213 my $tagTablePtr = shift || last;
3862 463         5015 foreach $hashKey (keys %$nv) {
3863 21399         37788 my $tagInfo = $$nv{$hashKey}{TagInfo};
3864 21399 100 66     75047 next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table};
3865 287         1062 $tagInfoHash{$$tagInfo{TagID}} = $tagInfo;
3866             }
3867             }
3868 473         1578 return \%tagInfoHash;
3869             }
3870              
3871             #------------------------------------------------------------------------------
3872             # Get a tagInfo/tagID hash for subdirectories we need to add
3873             # Inputs: 0) ExifTool object reference, 1) parent tag table reference
3874             # 2) parent directory name (taken from GROUP0 of tag table if not defined)
3875             # Returns: Reference to Hash of subdirectory tagInfo references keyed by tagID
3876             # (plus Reference to edit directory hash in list context)
3877             sub GetAddDirHash($$;$)
3878             {
3879 459     459 0 1392 my ($self, $tagTablePtr, $parent) = @_;
3880 459 100       1423 $parent or $parent = $$tagTablePtr{GROUPS}{0};
3881 459         1801 my $tagID;
3882             my %addDirHash;
3883 459         0 my %editDirHash;
3884 459         1191 my $addDirs = $$self{ADD_DIRS};
3885 459         1087 my $editDirs = $$self{EDIT_DIRS};
3886 459         1743 foreach $tagID (TagTableKeys($tagTablePtr)) {
3887 150141         267010 my @infoArray = GetTagInfoList($tagTablePtr,$tagID);
3888 150141         195924 my $tagInfo;
3889 150141         219244 foreach $tagInfo (@infoArray) {
3890 185743 100       453884 next unless $$tagInfo{SubDirectory};
3891             # get name for this sub directory
3892             # (take directory name from SubDirectory DirName if it exists,
3893             # otherwise Group0 name of SubDirectory TagTable or tag Group1 name)
3894 34424         61318 my $dirName = $$tagInfo{SubDirectory}{DirName};
3895 34424 100       55127 unless ($dirName) {
3896             # use tag name for directory name and save for next time
3897 3794         7286 $dirName = $$tagInfo{Name};
3898 3794         6389 $$tagInfo{SubDirectory}{DirName} = $dirName;
3899             }
3900             # save this directory information if we are writing it
3901 34424 100 100     77580 if ($$editDirs{$dirName} and $$editDirs{$dirName} eq $parent) {
3902 252         877 $editDirHash{$tagID} = $tagInfo;
3903 252 100       1208 $addDirHash{$tagID} = $tagInfo if $$addDirs{$dirName};
3904             }
3905             }
3906             }
3907 459 100       7188 return (\%addDirHash, \%editDirHash) if wantarray;
3908 384         2051 return \%addDirHash;
3909             }
3910              
3911             #------------------------------------------------------------------------------
3912             # Get localized version of tagInfo hash (used by MIE, XMP, PNG and QuickTime)
3913             # Inputs: 0) tagInfo hash ref, 1) locale code (eg. "en_CA" for MIE)
3914             # Returns: new tagInfo hash ref, or undef if invalid
3915             # - sets LangCode member in new tagInfo
3916             sub GetLangInfo($$)
3917             {
3918 298     298 0 650 my ($tagInfo, $langCode) = @_;
3919             # make a new tagInfo hash for this locale
3920 298         617 my $table = $$tagInfo{Table};
3921 298         829 my $tagID = $$tagInfo{TagID} . '-' . $langCode;
3922 298         654 my $langInfo = $$table{$tagID};
3923 298 100       734 unless ($langInfo) {
3924             # make a new tagInfo entry for this locale
3925             $langInfo = {
3926             %$tagInfo,
3927             Name => $$tagInfo{Name} . '-' . $langCode,
3928 182         1343 Description => Image::ExifTool::MakeDescription($$tagInfo{Name}) .
3929             " ($langCode)",
3930             LangCode => $langCode,
3931             SrcTagInfo => $tagInfo, # save reference to original tagInfo
3932             };
3933 182         633 AddTagToTable($table, $tagID, $langInfo);
3934             }
3935 298         777 return $langInfo;
3936             }
3937              
3938             #------------------------------------------------------------------------------
3939             # initialize ADD_DIRS and EDIT_DIRS hashes for all directories that need
3940             # to be created or will have tags changed in them
3941             # Inputs: 0) ExifTool object reference, 1) file type string (or map hash ref)
3942             # 2) preferred family 0 group for creating tags, 3) alternate preferred group
3943             # Notes:
3944             # - the ADD_DIRS and EDIT_DIRS keys are the directory names, and the values
3945             # are the names of the parent directories (undefined for a top-level directory)
3946             # - also initializes FORCE_WRITE lookup
3947             sub InitWriteDirs($$;$$)
3948             {
3949 317     317 0 1235 my ($self, $fileType, $preferredGroup, $altGroup) = @_;
3950 317         1460 my $editDirs = $$self{EDIT_DIRS} = { };
3951 317         1194 my $addDirs = $$self{ADD_DIRS} = { };
3952 317         1164 my $fileDirs = $dirMap{$fileType};
3953 317 100       1118 unless ($fileDirs) {
3954 192 100       782 return unless ref $fileType eq 'HASH';
3955 80         270 $fileDirs = $fileType;
3956             }
3957 205         1204 my @tagInfoList = $self->GetNewTagInfoList();
3958 205         688 my ($tagInfo, $nvHash);
3959              
3960             # save the preferred group
3961 205         803 $$self{PreferredGroup} = $preferredGroup;
3962              
3963 205         626 foreach $tagInfo (@tagInfoList) {
3964             # cycle through all hashes in linked list
3965 12929         25154 for ($nvHash=$self->GetNewValueHash($tagInfo); $nvHash; $nvHash=$$nvHash{Next}) {
3966             # are we creating this tag? (otherwise just deleting or editing it)
3967 12955         25936 my $isCreating = $$nvHash{IsCreating};
3968 12955 100       22481 if ($preferredGroup) {
3969 3524         8237 my $g0 = $self->GetGroup($tagInfo, 0);
3970 3524 100       6824 if ($isCreating) {
3971             # if another group is taking priority, only create
3972             # directory if specifically adding tags to this group
3973             # or if this tag isn't being added to the priority group
3974             $isCreating = 0 if $preferredGroup ne $g0 and
3975 825 100 100     3871 $$nvHash{CreateGroups}{$preferredGroup} and
      100        
      100        
3976             (not $altGroup or $altGroup ne $g0);
3977             } else {
3978             # create this directory if any tag is preferred and has a value
3979             # (unless group creation is disabled via the WriteMode option)
3980             $isCreating = 1 if $$nvHash{Value} and $preferredGroup eq $g0 and
3981 2699 50 100     10902 not $$nvHash{EditOnly} and $$self{OPTIONS}{WriteMode} =~ /g/;
      66        
      66        
3982             }
3983             }
3984             # tag belongs to directory specified by WriteGroup, or by
3985             # the Group0 name if WriteGroup not defined
3986 12955         28389 my $dirName = $$nvHash{WriteGroup};
3987             # remove MIE copy number(s) if they exist
3988 12955 100       29765 if ($dirName =~ /^MIE\d*(-[a-z]+)?\d*$/i) {
3989 387   50     1762 $dirName = 'MIE' . ($1 || '');
3990             }
3991 12955         17568 my @dirNames;
3992             # allow a group name of '*' to force writing EXIF/IPTC/XMP/PNG (ForceWrite tag)
3993 12955 50 33     32870 if ($dirName eq '*' and $$nvHash{Value}) {
    100          
3994 0         0 my $val = $$nvHash{Value}[0];
3995 0 0       0 if ($val) {
3996 0         0 foreach (qw(EXIF IPTC XMP PNG FixBase)) {
3997 0 0       0 next unless $val =~ /\b($_|All)\b/i;
3998 0         0 push @dirNames, $_;
3999 0 0       0 push @dirNames, 'EXIF' if $_ eq 'FixBase';
4000 0         0 $$self{FORCE_WRITE}{$_} = 1;
4001             }
4002             }
4003 0         0 $dirName = shift @dirNames;
4004             } elsif ($dirName eq 'QuickTime') {
4005             # write to specific QuickTime group
4006 46         239 $dirName = $self->GetGroup($tagInfo, 1);
4007             }
4008 12955         22373 while ($dirName) {
4009 52584         78871 my $parent = $$fileDirs{$dirName};
4010 52584 100       89505 if (ref $parent) {
4011 6351         12475 push @dirNames, reverse @$parent;
4012 6351         10200 $parent = pop @dirNames;
4013             }
4014 52584         77878 $$editDirs{$dirName} = $parent;
4015 52584 100 100     98928 $$addDirs{$dirName} = $parent if $isCreating and $isCreating != 2;
4016 52584   100     141919 $dirName = $parent || shift @dirNames
4017             }
4018             }
4019             }
4020 205 100       618 if (%{$$self{DEL_GROUP}}) {
  205         1101  
4021             # add delete groups to list of edited groups
4022 37         86 foreach (keys %{$$self{DEL_GROUP}}) {
  37         313  
4023 843 100       1581 next if /^-/; # ignore excluded groups
4024 841         1188 my $dirName = $_;
4025             # translate necessary group 0 names
4026 841 100       1630 $dirName = $translateWriteGroup{$dirName} if $translateWriteGroup{$dirName};
4027             # convert XMP group 1 names
4028 841 100       1549 $dirName = 'XMP' if $dirName =~ /^XMP-/;
4029 841         1083 my @dirNames;
4030 841         1416 while ($dirName) {
4031 1199         1825 my $parent = $$fileDirs{$dirName};
4032 1199 100       2126 if (ref $parent) {
4033 13         39 push @dirNames, reverse @$parent;
4034 13         26 $parent = pop @dirNames;
4035             }
4036 1199         1982 $$editDirs{$dirName} = $parent;
4037 1199   100     3358 $dirName = $parent || shift @dirNames
4038             }
4039             }
4040             }
4041             # special case to edit JFIF to get resolutions if editing EXIF information
4042 205 100 100     1651 if ($$editDirs{IFD0} and $$fileDirs{JFIF}) {
4043 86         401 $$editDirs{JFIF} = 'IFD1';
4044 86         680 $$editDirs{APP0} = undef;
4045             }
4046              
4047 205 100       2657 if ($$self{OPTIONS}{Verbose}) {
4048 2         9 my $out = $$self{OPTIONS}{TextOut};
4049 2         9 print $out " Editing tags in: ";
4050 2         18 foreach (sort keys %$editDirs) { print $out "$_ "; }
  11         22  
4051 2         17 print $out "\n";
4052 2 50       13 return unless $$self{OPTIONS}{Verbose} > 1;
4053 2         5 print $out " Creating tags in: ";
4054 2         10 foreach (sort keys %$addDirs) { print $out "$_ "; }
  7         18  
4055 2         12 print $out "\n";
4056             }
4057             }
4058              
4059             #------------------------------------------------------------------------------
4060             # Write an image directory
4061             # Inputs: 0) ExifTool object reference, 1) source directory information reference
4062             # 2) tag table reference, 3) optional reference to writing procedure
4063             # Returns: New directory data or undefined on error (or empty string to delete directory)
4064             sub WriteDirectory($$$;$)
4065             {
4066 1715     1715 0 5800 my ($self, $dirInfo, $tagTablePtr, $writeProc) = @_;
4067 1715         3894 my ($out, $nvHash, $delFlag);
4068              
4069 1715 50       4150 $tagTablePtr or return undef;
4070 1715 100       5578 $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose};
4071             # set directory name from default group0 name if not done already
4072 1715         3554 my $dirName = $$dirInfo{DirName};
4073 1715         2977 my $dataPt = $$dirInfo{DataPt};
4074 1715         6160 my $grp0 = $$tagTablePtr{GROUPS}{0};
4075 1715 100       4522 $dirName or $dirName = $$dirInfo{DirName} = $grp0;
4076 1715 100       2849 if (%{$$self{DEL_GROUP}}) {
  1715         4938  
4077 207         412 my $delGroup = $$self{DEL_GROUP};
4078             # delete entire directory if specified
4079 207         447 my $grp1 = $dirName;
4080 207 100 100     874 $delFlag = ($$delGroup{$grp0} or $$delGroup{$grp1}) unless $permanentDir{$grp0};
4081             # (never delete an entire QuickTime group)
4082 207 100       570 if ($delFlag) {
4083 40 50 100     496 if (($grp0 =~ /^(MakerNotes)$/ or $grp1 =~ /^(IFD0|ExifIFD|MakerNotes)$/) and
    100 66        
      0        
      33        
4084             $self->IsRawType() and
4085             # allow non-permanent MakerNote directories to be deleted (ie. NikonCapture)
4086             (not $$dirInfo{TagInfo} or not defined $$dirInfo{TagInfo}{Permanent} or
4087             $$dirInfo{TagInfo}{Permanent}))
4088             {
4089 0         0 $self->WarnOnce("Can't delete $1 from $$self{FileType}",1);
4090 0         0 undef $grp1;
4091             } elsif (not $blockExifTypes{$$self{FILE_TYPE}}) {
4092             # restrict delete logic to prevent entire tiff image from being killed
4093             # (don't allow IFD0 to be deleted, and delete only ExifIFD if EXIF specified)
4094 10 50 33     146 if ($$self{FILE_TYPE} eq 'PSD') {
    50          
    50          
    50          
4095             # don't delete Photoshop directories from PSD image
4096 0 0       0 undef $grp1 if $grp0 eq 'Photoshop';
4097             } elsif ($$self{FILE_TYPE} =~ /^(EPS|PS)$/) {
4098             # allow anything to be deleted from PostScript files
4099             } elsif ($grp1 eq 'IFD0') {
4100 0   0     0 my $type = $$self{TIFF_TYPE} || $$self{FILE_TYPE};
4101 0 0       0 $$delGroup{IFD0} and $self->Warn("Can't delete IFD0 from $type",1);
4102 0         0 undef $grp1;
4103             } elsif ($grp0 eq 'EXIF' and $$delGroup{$grp0}) {
4104 0 0 0     0 undef $grp1 unless $$delGroup{$grp1} or $grp1 eq 'ExifIFD';
4105             }
4106             }
4107 40 50       135 if ($grp1) {
4108 40 100 66     183 if ($dataPt or $$dirInfo{RAF}) {
4109 30         73 ++$$self{CHANGED};
4110 30 100       92 $out and print $out " Deleting $grp1\n";
4111 30 100       135 $self->Warn('ICC_Profile deleted. Image colors may be affected') if $grp1 eq 'ICC_Profile';
4112             # can no longer validate TIFF_END if deleting an entire IFD
4113 30 100       143 delete $$self{TIFF_END} if $dirName =~ /IFD/;
4114             }
4115             # don't add back into the wrong location
4116 40         122 my $right = $$self{ADD_DIRS}{$grp1};
4117             # (take care because EXIF directory name may be either EXIF or IFD0,
4118             # but IFD0 will be the one that appears in the directory map)
4119 40 100 100     191 $right = $$self{ADD_DIRS}{IFD0} if not $right and $grp1 eq 'EXIF';
4120 40 100 100     190 if ($delFlag == 2 and $right) {
4121             # also check grandparent because some routines create 2 levels in 1
4122 21   100     99 my $right2 = $$self{ADD_DIRS}{$right} || '';
4123 21         49 my $parent = $$dirInfo{Parent};
4124 21 50 66     132 if (not $parent or $parent eq $right or $parent eq $right2) {
      33        
4125             # prevent duplicate directories from being recreated at the same path
4126 21         43 my $path = join '-', @{$$self{PATH}}, $dirName;
  21         90  
4127 21 100       98 $$self{Recreated} or $$self{Recreated} = { };
4128 21 50       80 if ($$self{Recreated}{$path}) {
4129 0 0       0 my $p = $parent ? " in $parent" : '';
4130 0         0 $self->Warn("Not recreating duplicate $grp1$p",1);
4131 0         0 return '';
4132             }
4133 21         81 $$self{Recreated}{$path} = 1;
4134             # empty the directory
4135 21         51 my $data = '';
4136 21         59 $$dirInfo{DataPt} = \$data;
4137 21         53 $$dirInfo{DataLen} = 0;
4138 21         53 $$dirInfo{DirStart} = 0;
4139 21         48 $$dirInfo{DirLen} = 0;
4140 21         56 delete $$dirInfo{RAF};
4141 21         44 delete $$dirInfo{Base};
4142 21         71 delete $$dirInfo{DataPos};
4143             } else {
4144 0         0 $self->Warn("Not recreating $grp1 in $parent (should be in $right)",1);
4145 0         0 return '';
4146             }
4147             } else {
4148 19 100       128 return '' unless $$dirInfo{NoDelete};
4149             }
4150             }
4151             }
4152             }
4153             # use default proc from tag table if no proc specified
4154 1697 100 100     8469 $writeProc or $writeProc = $$tagTablePtr{WRITE_PROC} or return undef;
4155              
4156             # are we rewriting a pre-existing directory?
4157 1466   100     6209 my $isRewriting = ($$dirInfo{DirLen} or (defined $dataPt and length $$dataPt) or $$dirInfo{RAF});
4158              
4159             # copy or delete new directory as a block if specified
4160 1466         2636 my $blockName = $dirName;
4161 1466 100       3744 $blockName = 'EXIF' if $blockName eq 'IFD0';
4162 1466   100     6026 my $tagInfo = $Image::ExifTool::Extra{$blockName} || $$dirInfo{TagInfo};
4163 1466   100     7534 while ($tagInfo and ($nvHash = $$self{NEW_VALUE}{$tagInfo}) and
      66        
      33        
      66        
4164             $self->IsOverwriting($nvHash) and not ($$nvHash{CreateOnly} and $isRewriting))
4165             {
4166             # protect against writing EXIF to wrong file types, etc
4167 13 100       72 if ($blockName eq 'EXIF') {
4168 1 50       6 unless ($blockExifTypes{$$self{FILE_TYPE}}) {
4169 0         0 $self->Warn("Can't write EXIF as a block to $$self{FILE_TYPE} file");
4170 0         0 last;
4171             }
4172             # this can happen if we call WriteDirectory for an EXIF directory without going
4173             # through WriteTIFF as the WriteProc (which happens if conditionally replacing
4174             # the EXIF block and the condition fails), but we never want to do a block write
4175             # in this case because the EXIF block would end up with two TIFF headers
4176 1 50       6 last unless $writeProc eq \&Image::ExifTool::WriteTIFF;
4177             }
4178 13 100       69 last unless $self->IsOverwriting($nvHash, $dataPt ? $$dataPt : '');
    50          
4179 13         41 my $verb = 'Writing';
4180 13         57 my $newVal = $self->GetNewValue($nvHash);
4181 13 50 33     118 unless (defined $newVal and length $newVal) {
4182 0 0 0     0 return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed
4183             # don't allow MakerNotes to be removed from RAW files
4184 0 0 0     0 if ($blockName eq 'MakerNotes' and $rawType{$$self{FileType}}) {
4185 0         0 $self->Warn("Can't delete MakerNotes from $$self{VALUE}{FileType}",1);
4186 0         0 return undef;
4187             }
4188 0         0 $verb = 'Deleting';
4189 0         0 $newVal = '';
4190             }
4191 13         55 $$dirInfo{BlockWrite} = 1; # set flag indicating we did a block write
4192 13 50       67 $out and print $out " $verb $blockName as a block\n";
4193 13         38 ++$$self{CHANGED};
4194 13         59 return $newVal;
4195             }
4196             # guard against writing the same directory twice
4197 1453 100 100     10527 if (defined $dataPt and defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos} and
      100        
      100        
4198             not $$dirInfo{NoRefTest})
4199             {
4200 680   100     3205 my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0) + $$self{BASE};
4201             # (Phase One P25 IIQ files have ICC_Profile duplicated in IFD0 and IFD1)
4202 680 50 0     2895 if ($$self{PROCESSED}{$addr} and ($dirName ne 'ICC_Profile' or $$self{TIFF_TYPE} ne 'IIQ')) {
      33        
4203 0 0 0     0 if (defined $$dirInfo{DirLen} and not $$dirInfo{DirLen} and $dirName ne $$self{PROCESSED}{$addr}) {
    0 0        
4204             # it is hypothetically possible to have 2 different directories
4205             # with the same address if one has a length of zero
4206             } elsif ($self->Error("$dirName pointer references previous $$self{PROCESSED}{$addr} directory", 2)) {
4207 0         0 return undef;
4208             } else {
4209 0         0 $self->Warn("Deleting duplicate $dirName directory");
4210 0 0       0 $out and print $out " Deleting $dirName\n";
4211             # delete the duplicate directory (don't recreate it when writing new
4212             # tags to prevent propagating a duplicate IFD in cases like when the
4213             # same ExifIFD exists in both IFD0 and IFD1)
4214 0         0 return '';
4215             }
4216             } else {
4217 680         2259 $$self{PROCESSED}{$addr} = $dirName;
4218             }
4219             }
4220 1453         3303 my $oldDir = $$self{DIR_NAME};
4221 1453         4419 my @save = @$self{'Compression','SubfileType'};
4222 1453         2701 my $name;
4223 1453 100       3470 if ($out) {
4224             $name = ($dirName eq 'MakerNotes' and $$dirInfo{TagInfo}) ?
4225 4 50 33     19 $$dirInfo{TagInfo}{Name} : $dirName;
4226 4 100 100     36 if (not defined $oldDir or $oldDir ne $name) {
4227 3 100       13 my $verb = $isRewriting ? 'Rewriting' : 'Creating';
4228 3         17 print $out " $verb $name\n";
4229             }
4230             }
4231 1453         4250 my $saveOrder = GetByteOrder();
4232 1453         3333 my $oldChanged = $$self{CHANGED};
4233 1453         2891 $$self{DIR_NAME} = $dirName;
4234 1453         2382 push @{$$self{PATH}}, $dirName;
  1453         3634  
4235 1453         3200 $$dirInfo{IsWriting} = 1;
4236 1453         2434 my $newData;
4237             {
4238 58     58   558 no strict 'refs';
  58         196  
  58         1227369  
  1453         2197  
4239 1453         11321 $newData = &$writeProc($self, $dirInfo, $tagTablePtr);
4240             }
4241 1453         2876 pop @{$$self{PATH}};
  1453         3587  
4242             # nothing changed if error occurred or nothing was created
4243 1453 100 100     6688 $$self{CHANGED} = $oldChanged unless defined $newData and (length($newData) or $isRewriting);
      100        
4244 1453         3429 $$self{DIR_NAME} = $oldDir;
4245 1453         4097 @$self{'Compression','SubfileType'} = @save;
4246 1453         5198 SetByteOrder($saveOrder);
4247 1453 100       3761 if ($out) {
4248 4 50 33     25 print $out " Deleting $name\n" if defined $newData and not length $newData;
4249 4 50 33     19 if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) {
4250 0         0 print $out "$$self{INDENT} [nothing changed in $dirName]\n";
4251             }
4252             }
4253 1453         6662 return $newData;
4254             }
4255              
4256             #------------------------------------------------------------------------------
4257             # Uncommon utility routines to for reading binary data values
4258             # Inputs: 0) data reference, 1) offset into data
4259             sub Get64s($$)
4260             {
4261 12     12 0 27 my ($dataPt, $pos) = @_;
4262 12 50       26 my $pt = GetByteOrder() eq 'MM' ? 0 : 4; # get position of high word
4263 12         35 my $hi = Get32s($dataPt, $pos + $pt); # preserve sign bit of high word
4264 12         37 my $lo = Get32u($dataPt, $pos + 4 - $pt);
4265 12         35 return $hi * 4294967296 + $lo;
4266             }
4267             sub Get64u($$)
4268             {
4269 183     183 0 508 my ($dataPt, $pos) = @_;
4270 183 100       470 my $pt = GetByteOrder() eq 'MM' ? 0 : 4; # get position of high word
4271 183         599 my $hi = Get32u($dataPt, $pos + $pt); # (unsigned this time)
4272 183         601 my $lo = Get32u($dataPt, $pos + 4 - $pt);
4273 183         745 return $hi * 4294967296 + $lo;
4274             }
4275             sub GetFixed64s($$)
4276             {
4277 0     0 0 0 my ($dataPt, $pos) = @_;
4278 0         0 my $val = Get64s($dataPt, $pos) / 4294967296;
4279             # remove insignificant digits
4280 0 0       0 return int($val * 1e10 + ($val>0 ? 0.5 : -0.5)) / 1e10;
4281             }
4282             # Decode extended 80-bit float used by Apple SANE and Intel 8087
4283             # (note: different than the IEEE standard 80-bit float)
4284             sub GetExtended($$)
4285             {
4286 1     1 0 4 my ($dataPt, $pos) = @_;
4287 1 50       4 my $pt = GetByteOrder() eq 'MM' ? 0 : 2; # get position of exponent
4288 1         7 my $exp = Get16u($dataPt, $pos + $pt);
4289 1         8 my $sig = Get64u($dataPt, $pos + 2 - $pt); # get significand as int64u
4290 1 50       7 my $sign = $exp & 0x8000 ? -1 : 1;
4291 1         3 $exp = ($exp & 0x7fff) - 16383 - 63; # (-63 to fractionalize significand)
4292 1         14 return $sign * $sig * 2 ** $exp;
4293             }
4294              
4295             #------------------------------------------------------------------------------
4296             # Dump data in hex and ASCII to console
4297             # Inputs: 0) data reference, 1) length or undef, 2-N) Options:
4298             # Options: Start => offset to start of data (default=0)
4299             # Addr => address to print for data start (default=DataPos+Base+Start)
4300             # DataPos => position of data within block (relative to Base)
4301             # Base => base offset for pointers from start of file
4302             # Width => width of printout (bytes, default=16)
4303             # Prefix => prefix to print at start of line (default='')
4304             # MaxLen => maximum length to dump
4305             # Out => output file reference
4306             # Len => data length
4307             sub HexDump($;$%)
4308             {
4309 169     169 0 304 my $dataPt = shift;
4310 169         296 my $len = shift;
4311 169         823 my %opts = @_;
4312 169   100     454 my $start = $opts{Start} || 0;
4313 169         249 my $addr = $opts{Addr};
4314 169   50     505 my $wid = $opts{Width} || 16;
4315 169   100     353 my $prefix = $opts{Prefix} || '';
4316 169   50     366 my $out = $opts{Out} || \*STDOUT;
4317 169         266 my $maxLen = $opts{MaxLen};
4318 169         294 my $datLen = length($$dataPt) - $start;
4319 169         245 my $more;
4320 169 50       354 $len = $opts{Len} if defined $opts{Len};
4321              
4322 169 100 50     493 $addr = $start + ($opts{DataPos} || 0) + ($opts{Base} || 0) unless defined $addr;
      50        
4323 169 100       310 $len = $datLen unless defined $len;
4324 169 100 66     549 if ($maxLen and $len > $maxLen) {
4325             # print one line less to allow for $more line below
4326 5         20 $maxLen = int(($maxLen - 1) / $wid) * $wid;
4327 5         8 $more = $len - $maxLen;
4328 5         9 $len = $maxLen;
4329             }
4330 169 50       371 if ($len > $datLen) {
4331 0         0 print $out "$prefix Warning: Attempted dump outside data\n";
4332 0         0 print $out "$prefix ($len bytes specified, but only $datLen available)\n";
4333 0         0 $len = $datLen;
4334             }
4335 169         531 my $format = sprintf("%%-%ds", $wid * 3);
4336 169         398 my $tmpl = 'H2' x $wid; # ('(H2)*' would have been nice, but older perl versions don't support it)
4337 169         241 my $i;
4338 169         440 for ($i=0; $i<$len; $i+=$wid) {
4339 228 100       572 $wid > $len-$i and $wid = $len-$i, $tmpl = 'H2' x $wid;
4340 228         833 printf $out "$prefix%8.4x: ", $addr+$i;
4341 228         566 my $dat = substr($$dataPt, $i+$start, $wid);
4342 228         1022 my $s = join(' ', unpack($tmpl, $dat));
4343 228         748 printf $out $format, $s;
4344 228         433 $dat =~ tr /\x00-\x1f\x7f-\xff/./;
4345 228         691 print $out "[$dat]\n";
4346             }
4347 169 100       1214 $more and print $out "$prefix [snip $more bytes]\n";
4348             }
4349              
4350             #------------------------------------------------------------------------------
4351             # Print verbose tag information
4352             # Inputs: 0) ExifTool object reference, 1) tag ID
4353             # 2) tag info reference (or undef)
4354             # 3-N) extra parms:
4355             # Parms: Index => Index of tag in menu (starting at 0)
4356             # Value => Tag value
4357             # DataPt => reference to value data block
4358             # DataPos => location of data block in file
4359             # Base => base added to all offsets
4360             # Size => length of value data within block
4361             # Format => value format string
4362             # Count => number of values
4363             # Extra => Extra Verbose=2 information to put after tag number
4364             # Table => Reference to tag table
4365             # --> plus any of these HexDump() options: Start, Addr, Width
4366             sub VerboseInfo($$$%)
4367             {
4368 617     617 0 3312 my ($self, $tagID, $tagInfo, %parms) = @_;
4369 617         1438 my $verbose = $$self{OPTIONS}{Verbose};
4370 617         1115 my $out = $$self{OPTIONS}{TextOut};
4371 617         998 my ($tag, $line, $hexID);
4372              
4373             # generate hex number if tagID is numerical
4374 617 100       1232 if (defined $tagID) {
4375 578 100       3892 $tagID =~ /^\d+$/ and $hexID = sprintf("0x%.4x", $tagID);
4376             } else {
4377 39         109 $tagID = 'Unknown';
4378             }
4379             # get tag name
4380 617 50 33     2633 if ($tagInfo and $$tagInfo{Name}) {
4381 617         1246 $tag = $$tagInfo{Name};
4382             } else {
4383 0         0 my $prefix;
4384 0 0       0 $prefix = $parms{Table}{TAG_PREFIX} if $parms{Table};
4385 0 0 0     0 if ($prefix or $hexID) {
4386 0 0       0 $prefix = 'Unknown' unless $prefix;
4387 0 0       0 $tag = $prefix . '_' . ($hexID ? $hexID : $tagID);
4388             } else {
4389 0         0 $tag = $tagID;
4390             }
4391             }
4392 617         1009 my $dataPt = $parms{DataPt};
4393 617         1015 my $size = $parms{Size};
4394 617 50 66     1655 $size = length $$dataPt unless defined $size or not $dataPt;
4395 617         1223 my $indent = $$self{INDENT};
4396              
4397             # Level 1: print tag/value information
4398 617         1000 $line = $indent;
4399 617         1006 my $index = $parms{Index};
4400 617 100       1249 if (defined $index) {
4401 365         686 $line .= $index . ') ';
4402 365 100       821 $line .= ' ' if length($index) < 2;
4403 365         566 $indent .= ' '; # indent everything else to align with tag name
4404             }
4405 617         1010 $line .= $tag;
4406 617 100 66     2131 if ($tagInfo and $$tagInfo{SubDirectory}) {
4407 39         77 $line .= ' (SubDirectory) -->';
4408             } else {
4409 578         1062 my $maxLen = 90 - length($line);
4410 578         964 my $val = $parms{Value};
4411 578 50       1100 if (defined $val) {
    0          
4412 578 50       1244 $val = '[' . join(',',@$val) . ']' if ref $val eq 'ARRAY';
4413 578         1815 $line .= ' = ' . $self->Printable($val, $maxLen);
4414             } elsif ($dataPt) {
4415 0   0     0 my $start = $parms{Start} || 0;
4416 0         0 $line .= ' = ' . $self->Printable(substr($$dataPt,$start,$size), $maxLen);
4417             }
4418             }
4419 617         1991 print $out "$line\n";
4420              
4421             # Level 2: print detailed information about the tag
4422 617 50 66     2906 if ($verbose > 1 and ($parms{Extra} or $parms{Format} or
      66        
4423             $parms{DataPt} or defined $size or $tagID =~ /\//))
4424             {
4425 390         730 $line = $indent . '- Tag ';
4426 390 100       690 if ($hexID) {
4427 389         551 $line .= $hexID;
4428             } else {
4429 1         4 $tagID =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/ge;
  0         0  
4430 1         3 $line .= "'${tagID}'";
4431             }
4432 390 50       797 $line .= $parms{Extra} if defined $parms{Extra};
4433 390         657 my $format = $parms{Format};
4434 390 50 66     972 if ($format or defined $size) {
4435 390         595 $line .= ' (';
4436 390 50       752 if (defined $size) {
4437 390         709 $line .= "$size bytes";
4438 390 100       770 $line .= ', ' if $format;
4439             }
4440 390 100       736 if ($format) {
4441 352         531 $line .= $format;
4442 352 50       952 $line .= '['.$parms{Count}.']' if $parms{Count};
4443             }
4444 390         629 $line .= ')';
4445             }
4446 390 50 66     1047 $line .= ':' if $verbose > 2 and $parms{DataPt};
4447 390         1047 print $out "$line\n";
4448             }
4449              
4450             # Level 3: do hex dump of value
4451 617 100 100     2930 if ($verbose > 2 and $parms{DataPt} and (not $tagInfo or not $$tagInfo{ReadFromRAF})) {
      33        
      66        
4452 165         361 $parms{Out} = $out;
4453 165         306 $parms{Prefix} = $indent;
4454             # limit dump length if Verbose < 5
4455 165 50       526 $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5;
    50          
4456 165         725 HexDump($dataPt, $size, %parms);
4457             }
4458             }
4459              
4460             #------------------------------------------------------------------------------
4461             # Dump trailer information
4462             # Inputs: 0) ExifTool object ref, 1) dirInfo hash (RAF, DirName, DataPos, DirLen)
4463             # Notes: Restores current file position before returning
4464             sub DumpTrailer($$)
4465             {
4466 1     1 0 5 my ($self, $dirInfo) = @_;
4467 1         4 my $raf = $$dirInfo{RAF};
4468 1         4 my $curPos = $raf->Tell();
4469 1   50     6 my $trailer = $$dirInfo{DirName} || 'Unknown';
4470 1         5 my $pos = $$dirInfo{DataPos};
4471 1         3 my $verbose = $$self{OPTIONS}{Verbose};
4472 1         3 my $htmlDump = $$self{HTML_DUMP};
4473 1         2 my ($buff, $buf2);
4474 1         2 my $size = $$dirInfo{DirLen};
4475 1 50       4 $pos = $curPos unless defined $pos;
4476              
4477             # get full trailer size if not specified
4478 1         2 for (;;) {
4479 1 50       4 unless ($size) {
4480 0 0       0 $raf->Seek(0, 2) or last;
4481 0         0 $size = $raf->Tell() - $pos;
4482 0 0       0 last unless $size;
4483             }
4484 1 50       5 $raf->Seek($pos, 0) or last;
4485 1 50       5 if ($htmlDump) {
4486 0 0       0 my $num = $raf->Read($buff, $size) or return;
4487 0         0 my $desc = "$trailer trailer";
4488 0 0       0 $desc = "[$desc]" if $trailer eq 'Unknown';
4489 0         0 $self->HDump($pos, $num, $desc, undef, 0x08);
4490 0         0 last;
4491             }
4492 1         3 my $out = $$self{OPTIONS}{TextOut};
4493 1         8 printf $out "$trailer trailer (%d bytes at offset 0x%.4x):\n", $size, $pos;
4494 1 50       6 last unless $verbose > 2;
4495 0         0 my $num = $size; # number of bytes to read
4496             # limit size if not very verbose
4497 0 0       0 if ($verbose < 5) {
4498 0 0       0 my $limit = $verbose < 4 ? 96 : 512;
4499 0 0       0 $num = $limit if $num > $limit;
4500             }
4501 0 0       0 $raf->Read($buff, $num) == $num or return;
4502             # read the end of the trailer too if not done already
4503 0 0       0 if ($size > 2 * $num) {
    0          
4504 0         0 $raf->Seek($pos + $size - $num, 0);
4505 0         0 $raf->Read($buf2, $num);
4506             } elsif ($size > $num) {
4507 0         0 $raf->Seek($pos + $num, 0);
4508 0         0 $raf->Read($buf2, $size - $num);
4509 0         0 $buff .= $buf2;
4510 0         0 undef $buf2;
4511             }
4512 0         0 HexDump(\$buff, undef, Addr => $pos, Out => $out);
4513 0 0       0 if (defined $buf2) {
4514 0         0 print $out " [snip ", $size - $num * 2, " bytes]\n";
4515 0         0 HexDump(\$buf2, undef, Addr => $pos + $size - $num, Out => $out);
4516             }
4517 0         0 last;
4518             }
4519 1         5 $raf->Seek($curPos, 0);
4520             }
4521              
4522             #------------------------------------------------------------------------------
4523             # Dump unknown trailer information
4524             # Inputs: 0) ExifTool ref, 1) dirInfo ref (with RAF, DataPos and DirLen defined)
4525             # Notes: changes dirInfo elements
4526             sub DumpUnknownTrailer($$)
4527             {
4528 0     0 0 0 my ($self, $dirInfo) = @_;
4529 0         0 my $pos = $$dirInfo{DataPos};
4530 0         0 my $endPos = $pos + $$dirInfo{DirLen};
4531             # account for preview/MPF image trailer
4532 0   0     0 my $prePos = $$self{VALUE}{PreviewImageStart} || $$self{PreviewImageStart};
4533 0   0     0 my $preLen = $$self{VALUE}{PreviewImageLength} || $$self{PreviewImageLength};
4534 0         0 my $tag = 'PreviewImage';
4535 0         0 my $mpImageNum = 0;
4536 0         0 my (%image, $lastOne);
4537 0         0 for (;;) {
4538             # add to Preview block list if valid and in the trailer
4539 0 0 0     0 $image{$prePos} = [$tag, $preLen] if $prePos and $preLen and $prePos+$preLen > $pos;
      0        
4540 0 0       0 last if $lastOne; # checked all images
4541             # look for MPF images (in the proper order)
4542 0         0 ++$mpImageNum;
4543 0         0 $prePos = $$self{VALUE}{"MPImageStart ($mpImageNum)"};
4544 0 0       0 if (defined $prePos) {
4545 0         0 $preLen = $$self{VALUE}{"MPImageLength ($mpImageNum)"};
4546             } else {
4547 0         0 $prePos = $$self{VALUE}{'MPImageStart'};
4548 0         0 $preLen = $$self{VALUE}{'MPImageLength'};
4549 0         0 $lastOne = 1;
4550             }
4551 0         0 $tag = "MPImage$mpImageNum";
4552             }
4553             # dump trailer sections in order
4554 0         0 $image{$endPos} = [ '', 0 ]; # add terminator "image"
4555 0         0 foreach $prePos (sort { $a <=> $b } keys %image) {
  0         0  
4556 0 0       0 if ($pos < $prePos) {
4557             # dump unknown trailer data
4558 0         0 $$dirInfo{DirName} = 'Unknown';
4559 0         0 $$dirInfo{DataPos} = $pos;
4560 0         0 $$dirInfo{DirLen} = $prePos - $pos;
4561 0         0 $self->DumpTrailer($dirInfo);
4562             }
4563 0         0 ($tag, $preLen) = @{$image{$prePos}};
  0         0  
4564 0 0       0 last unless $preLen;
4565             # dump image if verbose (it is htmlDump'd by ExtractImage)
4566 0 0       0 if ($$self{OPTIONS}{Verbose}) {
4567 0         0 $$dirInfo{DirName} = $tag;
4568 0         0 $$dirInfo{DataPos} = $prePos;
4569 0         0 $$dirInfo{DirLen} = $preLen;
4570 0         0 $self->DumpTrailer($dirInfo);
4571             }
4572 0         0 $pos = $prePos + $preLen;
4573             }
4574             }
4575              
4576             #------------------------------------------------------------------------------
4577             # Find last element in linked list
4578             # Inputs: 0) element in list
4579             # Returns: Last element in list
4580             sub LastInList($)
4581             {
4582 33     33 0 72 my $element = shift;
4583 33         156 while ($$element{Next}) {
4584 0         0 $element = $$element{Next};
4585             }
4586 33         75 return $element;
4587             }
4588              
4589             #------------------------------------------------------------------------------
4590             # Print verbose value while writing
4591             # Inputs: 0) ExifTool object ref, 1) heading "eg. '+ IPTC:Keywords',
4592             # 2) value, 3) [optional] extra text after value
4593             sub VerboseValue($$$;$)
4594             {
4595 1058 100   1058 0 3236 return unless $_[0]{OPTIONS}{Verbose} > 1;
4596 14         35 my ($self, $str, $val, $xtra) = @_;
4597 14         28 my $out = $$self{OPTIONS}{TextOut};
4598 14 100       35 $xtra or $xtra = '';
4599 14         30 my $maxLen = 81 - length($str) - length($xtra);
4600 14         42 $val = $self->Printable($val, $maxLen);
4601 14         67 print $out " $str = '${val}'$xtra\n";
4602             }
4603              
4604             #------------------------------------------------------------------------------
4605             # Pack Unicode numbers into UTF8 string
4606             # Inputs: 0-N) list of Unicode numbers
4607             # Returns: Packed UTF-8 string
4608             sub PackUTF8(@)
4609             {
4610 0     0 0 0 my @out;
4611 0         0 while (@_) {
4612 0         0 my $ch = pop;
4613 0 0       0 unshift(@out, $ch), next if $ch < 0x80;
4614 0         0 unshift(@out, 0x80 | ($ch & 0x3f));
4615 0         0 $ch >>= 6;
4616 0 0       0 unshift(@out, 0xc0 | $ch), next if $ch < 0x20;
4617 0         0 unshift(@out, 0x80 | ($ch & 0x3f));
4618 0         0 $ch >>= 6;
4619 0 0       0 unshift(@out, 0xe0 | $ch), next if $ch < 0x10;
4620 0         0 unshift(@out, 0x80 | ($ch & 0x3f));
4621 0         0 $ch >>= 6;
4622 0         0 unshift(@out, 0xf0 | ($ch & 0x07));
4623             }
4624 0         0 return pack('C*', @out);
4625             }
4626              
4627             #------------------------------------------------------------------------------
4628             # Unpack numbers from UTF8 string
4629             # Inputs: 0) UTF-8 string
4630             # Returns: List of Unicode numbers (sets $evalWarning on error)
4631             sub UnpackUTF8($)
4632             {
4633 0     0 0 0 my (@out, $pos);
4634 0         0 pos($_[0]) = $pos = 0; # start at beginning of string
4635 0         0 for (;;) {
4636 0         0 my ($ch, $newPos, $val, $byte);
4637 0 0       0 if ($_[0] =~ /([\x80-\xff])/g) {
4638 0         0 $ch = ord($1);
4639 0         0 $newPos = pos($_[0]) - 1;
4640             } else {
4641 0         0 $newPos = length $_[0];
4642             }
4643             # unpack 7-bit characters
4644 0         0 my $len = $newPos - $pos;
4645 0 0       0 push @out, unpack("x${pos}C$len",$_[0]) if $len;
4646 0 0       0 last unless defined $ch;
4647 0         0 $pos = $newPos + 1;
4648             # minimum lead byte for 2-byte sequence is 0xc2 (overlong sequences
4649             # not allowed), 0xf8-0xfd are restricted by RFC 3629 (no 5 or 6 byte
4650             # sequences), and 0xfe and 0xff are not valid in UTF-8 strings
4651 0 0 0     0 if ($ch < 0xc2 or $ch >= 0xf8) {
4652 0         0 push @out, ord('?'); # invalid UTF-8
4653 0         0 $evalWarning = 'Bad UTF-8';
4654 0         0 next;
4655             }
4656             # decode 2, 3 and 4-byte sequences
4657 0         0 my $n = 1;
4658 0 0       0 if ($ch < 0xe0) {
    0          
4659 0         0 $val = $ch & 0x1f; # 2-byte sequence
4660             } elsif ($ch < 0xf0) {
4661 0         0 $val = $ch & 0x0f; # 3-byte sequence
4662 0         0 ++$n;
4663             } else {
4664 0         0 $val = $ch & 0x07; # 4-byte sequence
4665 0         0 $n += 2;
4666             }
4667 0 0       0 unless ($_[0] =~ /\G([\x80-\xbf]{$n})/g) {
4668 0         0 pos($_[0]) = $pos; # restore position
4669 0         0 push @out, ord('?'); # invalid UTF-8
4670 0         0 $evalWarning = 'Bad UTF-8';
4671 0         0 next;
4672             }
4673 0         0 foreach $byte (unpack 'C*', $1) {
4674 0         0 $val = ($val << 6) | ($byte & 0x3f);
4675             }
4676 0         0 push @out, $val; # save Unicode character value
4677 0         0 $pos += $n; # position at end of UTF-8 character
4678             }
4679 0         0 return @out;
4680             }
4681              
4682             #------------------------------------------------------------------------------
4683             # Generate a new, random GUID
4684             # Inputs:
4685             # Returns: GUID string
4686             my $guidCount;
4687             sub NewGUID()
4688             {
4689 58     58 0 1435 my @tm = localtime time;
4690 58 100 66     635 $guidCount = 0 unless defined $guidCount and ++$guidCount < 0x100;
4691 58         1749 return sprintf('%.4d%.2d%.2d%.2d%.2d%.2d%.2X%.4X%.4X%.4X%.4X',
4692             $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $guidCount,
4693             $$ & 0xffff, rand(0x10000), rand(0x10000), rand(0x10000));
4694             }
4695              
4696             #------------------------------------------------------------------------------
4697             # Make TIFF header for raw data
4698             # Inputs: 0) width, 1) height, 2) num colour components, 3) bits, 4) resolution
4699             # 5) color-map data for palette-color image (8 or 16 bit)
4700             # Returns: TIFF header
4701             # Notes: Multi-byte data must be little-endian
4702             sub MakeTiffHeader($$$$;$$)
4703             {
4704 0     0 0 0 my ($w, $h, $cols, $bits, $res, $cmap) = @_;
4705 0 0       0 $res or $res = 72;
4706 0         0 my $saveOrder = GetByteOrder();
4707 0         0 SetByteOrder('II');
4708 0 0       0 if (not $cmap) {
    0          
    0          
4709 0         0 $cmap = '';
4710             } elsif (length $cmap == 3 * 2**$bits) {
4711             # convert to short
4712 0         0 $cmap = pack 'v*', map { $_ | ($_<<8) } unpack 'C*', $cmap;
  0         0  
4713             } elsif (length $cmap != 6 * 2**$bits) {
4714 0         0 $cmap = '';
4715             }
4716 0 0       0 my $cmo = $cmap ? 12 : 0; # offset due to ColorMap IFD entry
4717 0 0       0 my $hdr =
    0          
    0          
    0          
4718             "\x49\x49\x2a\0\x08\0\0\0\x0e\0" . # 0x00 14 menu entries:
4719             "\xfe\x00\x04\0\x01\0\0\0\x00\0\0\0" . # 0x0a SubfileType = 0
4720             "\x00\x01\x04\0\x01\0\0\0" . Set32u($w) . # 0x16 ImageWidth
4721             "\x01\x01\x04\0\x01\0\0\0" . Set32u($h) . # 0x22 ImageHeight
4722             "\x02\x01\x03\0" . Set32u($cols) . # 0x2e BitsPerSample
4723             Set32u($cols == 1 ? $bits : 0xb6 + $cmo) .
4724             "\x03\x01\x03\0\x01\0\0\0\x01\0\0\0" . # 0x3a Compression = 1
4725             "\x06\x01\x03\0\x01\0\0\0" . # 0x46 PhotometricInterpretation
4726             Set32u($cmap ? 3 : $cols == 1 ? 1 : 2) .
4727             "\x11\x01\x04\0\x01\0\0\0" . # 0x52 StripOffsets
4728             Set32u(0xcc + $cmo + length($cmap)) .
4729             "\x15\x01\x03\0\x01\0\0\0" . Set32u($cols) . # 0x5e SamplesPerPixel
4730             "\x16\x01\x04\0\x01\0\0\0" . Set32u($h) . # 0x6a RowsPerStrip
4731             "\x17\x01\x04\0\x01\0\0\0" . # 0x76 StripByteCounts
4732             Set32u($w * $h * $cols * int(($bits+7)/8)) .
4733             "\x1a\x01\x05\0\x01\0\0\0" . Set32u(0xbc + $cmo) . # 0x82 XResolution
4734             "\x1b\x01\x05\0\x01\0\0\0" . Set32u(0xc4 + $cmo) . # 0x8e YResolution
4735             "\x1c\x01\x03\0\x01\0\0\0\x01\0\0\0" . # 0x9a PlanarConfiguration = 1
4736             "\x28\x01\x03\0\x01\0\0\0\x02\0\0\0" . # 0xa6 ResolutionUnit = 2
4737             ($cmap ? # 0xb2 ColorMap [optional]
4738             "\x40\x01\x03\0" . Set32u(3 * 2**$bits) . "\xd8\0\0\0" : '') .
4739             "\0\0\0\0" . # 0xb2+$cmo (no IFD1)
4740             (Set16u($bits) x 3) . # 0xb6+$cmo BitsPerSample value
4741             Set32u($res) . "\x01\0\0\0" . # 0xbc+$cmo XResolution = 72
4742             Set32u($res) . "\x01\0\0\0" . # 0xc4+$cmo YResolution = 72
4743             $cmap; # 0xcc or 0xd8 (cmap and data go here)
4744 0         0 SetByteOrder($saveOrder);
4745 0         0 return $hdr;
4746             }
4747              
4748             #------------------------------------------------------------------------------
4749             # Return current time in EXIF format
4750             # Inputs: 0) [optional] ExifTool ref, 1) flag to include timezone (0 to disable,
4751             # undef or 1 to include)
4752             # Returns: time string
4753             # - a consistent value is returned for each processed file
4754             sub TimeNow(;$$)
4755             {
4756 58     58 0 251 my ($self, $tzFlag) = @_;
4757 58         353 my $timeNow;
4758 58 50       284 ref $self or $tzFlag = $self, $self = { };
4759 58 50       248 if ($$self{Now}) {
4760 0         0 $timeNow = $$self{Now}[0];
4761             } else {
4762 58         316 my $time = time();
4763 58         2456 my @tm = localtime $time;
4764 58         461 my $tz = TimeZoneString(\@tm, $time);
4765 58         482 $timeNow = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d",
4766             $tm[5]+1900, $tm[4]+1, $tm[3],
4767             $tm[2], $tm[1], $tm[0]);
4768 58         329 $$self{Now} = [ $timeNow, $tz ];
4769             }
4770 58 50 33     604 $timeNow .= $$self{Now}[1] if $tzFlag or not defined $tzFlag;
4771 58         369 return $timeNow;
4772             }
4773              
4774             #------------------------------------------------------------------------------
4775             # Inverse date/time print conversion (reformat to YYYY:mm:dd HH:MM:SS[.ss][+-HH:MM|Z])
4776             # Inputs: 0) ExifTool object ref, 1) Date/Time string, 2) timezone flag:
4777             # 0 - remove timezone and sub-seconds if they exist
4778             # 1 - add timezone if it doesn't exist
4779             # undef - leave timezone alone
4780             # 3) flag to allow date-only (YYYY, YYYY:mm or YYYY:mm:dd) or time without seconds
4781             # Returns: formatted date/time string (or undef and issues warning on error)
4782             # Notes: currently accepts different separators, but doesn't use DateFormat yet
4783             my $strptimeLib; # strptime library name if available
4784             sub InverseDateTime($$;$$)
4785             {
4786 437     437 0 1291 my ($self, $val, $tzFlag, $dateOnly) = @_;
4787 437         852 my ($rtnVal, $tz);
4788 437         1181 my $fmt = $$self{OPTIONS}{DateFormat};
4789             # strip off timezone first if it exists
4790 437 100 66     3877 if (not $fmt and $val =~ s/([-+])(\d{1,2}):?(\d{2})\s*(DST)?$//i) {
    50 33        
4791 6         52 $tz = sprintf("$1%.2d:$3", $2);
4792             } elsif (not $fmt and $val =~ s/Z$//i) {
4793 0         0 $tz = 'Z';
4794             } else {
4795 431         889 $tz = '';
4796             # allow special value of 'now'
4797 431 50       1300 return $self->TimeNow($tzFlag) if lc($val) eq 'now';
4798             }
4799             # only convert date if a format was specified and the date is recognizable
4800 437 50       1061 if ($fmt) {
4801 0 0       0 unless (defined $strptimeLib) {
4802 0 0       0 if (eval { require POSIX::strptime }) {
  0 0       0  
4803 0         0 $strptimeLib = 'POSIX::strptime';
4804 0         0 } elsif (eval { require Time::Piece }) {
4805 0         0 $strptimeLib = 'Time::Piece';
4806             # (call use_locale() to convert localized date/time,
4807             # only available in Time::Piece 1.32 and later)
4808 0         0 eval { Time::Piece->use_locale() };
  0         0  
4809             } else {
4810 0         0 $strptimeLib = '';
4811             }
4812             }
4813             # handle factional seconds (%f), but only at the end of the string
4814 0 0 0     0 my $fs = ($fmt =~ s/%f$// and $val =~ s/(\.\d+)\s*$//) ? $1 : '';
4815 0         0 my ($lib, $wrn, @a);
4816 0         0 TryLib: for ($lib=$strptimeLib; ; $lib='') {
4817 0 0       0 if (not $lib) {
    0          
4818 0 0       0 last unless $$self{OPTIONS}{StrictDate};
4819 0   0     0 warn $wrn || "Install POSIX::strptime or Time::Piece for inverse date/time conversions\n";
4820 0         0 return undef;
4821             } elsif ($lib eq 'POSIX::strptime') {
4822 0         0 @a = eval { POSIX::strptime($val, $fmt) };
  0         0  
4823             } else {
4824             # protect against a negative epoch time, it can cause a hard crash in Windows
4825 0 0 0     0 if ($^O eq 'MSWin32' and $fmt =~ /%s/ and $val =~ /-\d/) {
      0        
4826 0         0 warn "Can't convert negative epoch time\n";
4827 0         0 return undef;
4828             }
4829 0         0 @a = eval {
4830 0         0 my $t = Time::Piece->strptime($val, $fmt);
4831 0         0 return ($t->sec, $t->min, $t->hour, $t->mday, $t->_mon, $t->_year);
4832             };
4833             }
4834 0 0 0     0 if (defined $a[5] and length $a[5]) {
4835 0         0 $a[5] += 1900; # add 1900 to year
4836             } else {
4837 0         0 $wrn = "Invalid date/time (no year) using $lib\n";
4838 0         0 next;
4839             }
4840 0 0 0     0 ++$a[4] if defined $a[4] and length $a[4]; # add 1 to month
4841 0         0 my $i;
4842 0         0 foreach $i (0..4) {
4843 0 0 0     0 if (not defined $a[$i] or not length $a[$i]) {
    0          
4844 0 0 0     0 if ($i < 2 or $dateOnly) { # (allow missing minutes/seconds)
4845 0         0 $a[$i] = ' ';
4846             } else {
4847 0         0 $wrn = "Incomplete date/time specification using $lib\n";
4848 0         0 next TryLib;
4849             }
4850             } elsif (length($a[$i]) < 2) {
4851 0         0 $a[$i] = "0$a[$i]"; # pad to 2 digits if necessary
4852             }
4853             }
4854 0         0 $val = join(':', @a[5,4,3]) . ' ' . join(':', @a[2,1,0]) . $fs;
4855 0         0 last;
4856             }
4857             }
4858 437 100       2020 if ($val =~ /(\d{4})/g) { # get YYYY
4859 430         1067 my $yr = $1;
4860 430         2578 my @a = ($val =~ /\d{1,2}/g); # get mm, dd, HH, and maybe MM, SS
4861 430   66     2570 length($_) < 2 and $_ = "0$_" foreach @a; # pad to 2 digits if necessary
4862 430 100       1142 if (@a >= 3) {
    50          
4863 404         748 my $ss = $a[4]; # get SS
4864 404         1023 push @a, '00' while @a < 5; # add MM, SS if not given
4865             # get sub-seconds if they exist (must be after SS, and have leading ".")
4866 404 100 100     1395 my $fs = (@a > 5 and $val =~ /(\.\d+)\s*$/) ? $1 : '';
4867             # add/remove timezone if necessary
4868 404 100       1355 if ($tzFlag) {
    100          
4869 34 50       124 if (not $tz) {
4870 34 50       75 if (eval { require Time::Local }) {
  34         985  
4871             # determine timezone offset for this time
4872 34         2715 my @args = ($a[4],$a[3],$a[2],$a[1],$a[0]-1,$yr);
4873 34         168 my $diff = Time::Local::timegm(@args) - TimeLocal(@args);
4874 34         154 $tz = TimeZoneString($diff / 60);
4875             } else {
4876 0         0 $tz = 'Z'; # don't know time zone
4877             }
4878             }
4879             } elsif (defined $tzFlag) {
4880 92         268 $tz = $fs = ''; # remove timezone and sub-seconds
4881             }
4882 404 100 66     1820 if (defined $ss and $ss < 60) {
    50          
4883 403         1109 $ss = ":$ss";
4884             } elsif ($dateOnly) {
4885 1         4 $ss = '';
4886             } else {
4887 0         0 $ss = ':00';
4888             }
4889             # construct properly formatted date/time string
4890 404 50 33     1936 if ($a[0] < 1 or $a[0] > 12) {
4891 0         0 warn "Month '$a[0]' out of range 1..12\n";
4892 0         0 return undef;
4893             }
4894 404 50 33     1665 if ($a[1] < 1 or $a[1] > 31) {
4895 0         0 warn "Day '$a[1]' out of range 1..31\n";
4896 0         0 return undef;
4897             }
4898 404 50       1029 $a[2] > 24 and warn("Hour '$a[2]' out of range 0..24\n"), return undef;
4899 404 50       912 $a[3] > 59 and warn("Minutes '$a[3]' out of range 0..59\n"), return undef;
4900 404         1656 $rtnVal = "$yr:$a[0]:$a[1] $a[2]:$a[3]$ss$fs$tz";
4901             } elsif ($dateOnly) {
4902 26         103 $rtnVal = join ':', $yr, @a;
4903             }
4904             }
4905 437 100       1140 $rtnVal or warn "Invalid date/time (use YYYY:mm:dd HH:MM:SS[.ss][+/-HH:MM|Z])\n";
4906 437         3940 return $rtnVal;
4907             }
4908              
4909             #------------------------------------------------------------------------------
4910             # Set byte order according to our current preferences
4911             # Inputs: 0) ExifTool object ref, 1) default byte order
4912             # Returns: new byte order ('II' or 'MM') and sets current byte order
4913             # Notes: takes the first of the following that is valid:
4914             # 1) ByteOrder option
4915             # 2) new value for ExifByteOrder
4916             # 3) default byte order passed to this routine
4917             # 4) makenote byte order from last file read
4918             # 5) big endian
4919             sub SetPreferredByteOrder($;$)
4920             {
4921 44     44 0 172 my ($self, $default) = @_;
4922             my $byteOrder = $self->Options('ByteOrder') ||
4923             $self->GetNewValue('ExifByteOrder') ||
4924 44   100     197 $default || $$self{MAKER_NOTE_BYTE_ORDER} || 'MM';
4925 44 50       262 unless (SetByteOrder($byteOrder)) {
4926 0 0       0 warn "Invalid byte order '${byteOrder}'\n" if $self->Options('Verbose');
4927 0   0     0 $byteOrder = $$self{MAKER_NOTE_BYTE_ORDER} || 'MM';
4928 0         0 SetByteOrder($byteOrder);
4929             }
4930 44         265 return GetByteOrder();
4931             }
4932              
4933             #------------------------------------------------------------------------------
4934             # Assemble a continuing fraction into a rational value
4935             # Inputs: 0) numerator, 1) denominator
4936             # 2-N) list of fraction denominators, deepest first
4937             # Returns: numerator, denominator (in list context)
4938             sub AssembleRational($$@)
4939             {
4940 4967 100   4967 0 10978 @_ < 3 and return @_;
4941 3417         6028 my ($num, $denom, $frac) = splice(@_, 0, 3);
4942 3417         6421 return AssembleRational($frac*$num+$denom, $num, @_);
4943             }
4944              
4945             #------------------------------------------------------------------------------
4946             # Convert a floating point number (or 'inf' or 'undef' or a fraction) into a rational
4947             # Inputs: 0) floating point number, 1) optional maximum value (defaults to 0x7fffffff)
4948             # Returns: numerator, denominator (in list context)
4949             # Notes:
4950             # - the returned rational will be accurate to at least 8 significant figures if possible
4951             # - eg. an input of 3.14159265358979 returns a rational of 104348/33215,
4952             # which equals 3.14159265392142 and is accurate to 10 significant figures
4953             # - the returned rational will be reduced to the lowest common denominator except when
4954             # the input is a fraction in which case the input is returned unchanged
4955             # - these routines were a bit tricky, but fun to write!
4956             sub Rationalize($;$)
4957             {
4958 739     739 0 1655 my $val = shift;
4959 739 50       1999 return (1, 0) if $val eq 'inf';
4960 739 50       1833 return (0, 0) if $val eq 'undef';
4961 739 100       2540 return ($1,$2) if $val =~ m{^([-+]?\d+)/(\d+)$}; # accept fractional values
4962             # Note: Just testing "if $val" doesn't work because '0.0' is true! (ugghh!)
4963 723 100       2529 return (0, 1) if $val == 0;
4964 685 100       1712 my $sign = $val < 0 ? ($val = -$val, -1) : 1;
4965 685         1740 my ($num, $denom, @fracs);
4966 685         1227 my $frac = $val;
4967 685   100     2154 my $maxInt = shift || 0x7fffffff;
4968 685         1102 for (;;) {
4969 1550         4395 my ($n, $d) = AssembleRational(int($frac + 0.5), 1, @fracs);
4970 1550 50 33     6049 if ($n > $maxInt or $d > $maxInt) {
4971 0 0       0 last if defined $num;
4972 0 0       0 return ($sign, $maxInt) if $val < 1;
4973 0         0 return ($sign * $maxInt, 1);
4974             }
4975 1550         2884 ($num, $denom) = ($n, $d); # save last good values
4976 1550         3327 my $err = ($n/$d-$val) / $val; # get error of this rational
4977 1550 100       3744 last if abs($err) < 1e-8; # all done if error is small
4978 865         1378 my $int = int($frac);
4979 865         1604 unshift @fracs, $int;
4980 865 50       1774 last unless $frac -= $int;
4981 865         1435 $frac = 1 / $frac;
4982             }
4983 685         2674 return ($num * $sign, $denom);
4984             }
4985              
4986             #------------------------------------------------------------------------------
4987             # Utility routines to for writing binary data values
4988             # Inputs: 0) value, 1) data ref, 2) offset
4989             # Notes: prototype is (@) so values can be passed from list if desired
4990             sub Set16s(@)
4991             {
4992 188     188 0 333 my $val = shift;
4993 188 100       467 $val < 0 and $val += 0x10000;
4994 188         452 return Set16u($val, @_);
4995             }
4996             sub Set32s(@)
4997             {
4998 67     67 0 138 my $val = shift;
4999 67 100       216 $val < 0 and $val += 0xffffffff, ++$val;
5000 67         204 return Set32u($val, @_);
5001             }
5002             sub Set64u(@)
5003             {
5004 28     28 0 50 my $val = $_[0];
5005 28         63 my $hi = int($val / 4294967296);
5006 28         73 my $lo = Set32u($val - $hi * 4294967296);
5007 28         64 $hi = Set32u($hi);
5008 28 100       70 $val = GetByteOrder() eq 'MM' ? $hi . $lo : $lo . $hi;
5009 28 100       79 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  27         68  
5010 28         58 return $val;
5011             }
5012             sub Set64s(@)
5013             {
5014 0     0 0 0 my $val = shift;
5015 0 0       0 $val < 0 and $val += 4294967296 * 4294967296; # (temporary hack won't really work due to round-off errors)
5016 0         0 return Set64u($val, @_);
5017             }
5018             sub SetRational64u(@) {
5019 428     428 0 1326 my ($numer,$denom) = Rationalize($_[0],0xffffffff);
5020 428         1304 my $val = Set32u($numer) . Set32u($denom);
5021 428 50       1308 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5022 428         1466 return $val;
5023             }
5024             sub SetRational64s(@) {
5025 42     42 0 194 my ($numer,$denom) = Rationalize($_[0]);
5026 42         242 my $val = Set32s($numer) . Set32u($denom);
5027 42 50       173 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5028 42         166 return $val;
5029             }
5030             sub SetRational32u(@) {
5031 0     0 0 0 my ($numer,$denom) = Rationalize($_[0],0xffff);
5032 0         0 my $val = Set16u($numer) . Set16u($denom);
5033 0 0       0 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5034 0         0 return $val;
5035             }
5036             sub SetRational32s(@) {
5037 0     0 0 0 my ($numer,$denom) = Rationalize($_[0],0x7fff);
5038 0         0 my $val = Set16s($numer) . Set16u($denom);
5039 0 0       0 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5040 0         0 return $val;
5041             }
5042             sub SetFixed16u(@) {
5043 0     0 0 0 my $val = int(shift() * 0x100 + 0.5);
5044 0         0 return Set16u($val, @_);
5045             }
5046             sub SetFixed16s(@) {
5047 0     0 0 0 my $val = shift;
5048 0 0       0 return Set16s(int($val * 0x100 + ($val < 0 ? -0.5 : 0.5)), @_);
5049             }
5050             sub SetFixed32u(@) {
5051 0     0 0 0 my $val = int(shift() * 0x10000 + 0.5);
5052 0         0 return Set32u($val, @_);
5053             }
5054             sub SetFixed32s(@) {
5055 12     12 0 23 my $val = shift;
5056 12 100       82 return Set32s(int($val * 0x10000 + ($val < 0 ? -0.5 : 0.5)), @_);
5057             }
5058             sub SetFloat(@) {
5059 61     61 0 493 my $val = SwapBytes(pack('f',$_[0]), 4);
5060 61 50       446 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5061 61         824 return $val;
5062             }
5063             sub SetDouble(@) {
5064             # swap 32-bit words (ARM quirk) and bytes if necessary
5065 63     63 0 589 my $val = SwapBytes(SwapWords(pack('d',$_[0])), 8);
5066 63 50       300 $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
  0         0  
5067 63         388 return $val;
5068             }
5069             #------------------------------------------------------------------------------
5070             # hash lookups for writing binary data values
5071             my %writeValueProc = (
5072             int8s => \&Set8s,
5073             int8u => \&Set8u,
5074             int16s => \&Set16s,
5075             int16u => \&Set16u,
5076             int16uRev => \&Set16uRev,
5077             int32s => \&Set32s,
5078             int32u => \&Set32u,
5079             int64s => \&Set64s,
5080             int64u => \&Set64u,
5081             rational32s => \&SetRational32s,
5082             rational32u => \&SetRational32u,
5083             rational64s => \&SetRational64s,
5084             rational64u => \&SetRational64u,
5085             fixed16u => \&SetFixed16u,
5086             fixed16s => \&SetFixed16s,
5087             fixed32u => \&SetFixed32u,
5088             fixed32s => \&SetFixed32s,
5089             float => \&SetFloat,
5090             double => \&SetDouble,
5091             ifd => \&Set32u,
5092             );
5093             # verify that we can write floats on this platform
5094             {
5095             my %writeTest = (
5096             float => [ -3.14159, 'c0490fd0' ],
5097             double => [ -3.14159, 'c00921f9f01b866e' ],
5098             );
5099             my $format;
5100             my $oldOrder = GetByteOrder();
5101             SetByteOrder('MM');
5102             foreach $format (keys %writeTest) {
5103             my ($val, $hex) = @{$writeTest{$format}};
5104             # add floating point entries if we can write them
5105             next if unpack('H*', &{$writeValueProc{$format}}($val)) eq $hex;
5106             delete $writeValueProc{$format}; # we can't write them
5107             }
5108             SetByteOrder($oldOrder);
5109             }
5110              
5111             #------------------------------------------------------------------------------
5112             # write binary data value (with current byte ordering)
5113             # Inputs: 0) value, 1) format string
5114             # 2) number of values:
5115             # undef = 1 for numerical types, or data length for string/undef types
5116             # -1 = number of space-delimited values in the input string
5117             # 3) optional data reference, 4) value offset (may be negative for bytes from end)
5118             # Returns: packed value (and sets value in data) or undef on error
5119             # Notes: May modify input value to round for integer formats
5120             sub WriteValue($$;$$$$)
5121             {
5122 1598     1598 0 4155 my ($val, $format, $count, $dataPt, $offset) = @_;
5123 1598         3454 my $proc = $writeValueProc{$format};
5124 1598         2791 my $packed;
5125              
5126 1598 100 66     4142 if ($proc) {
    50          
5127 1238         4085 my @vals = split(' ',$val);
5128 1238 100       2681 if ($count) {
5129 661 100       1764 $count = @vals if $count < 0;
5130             } else {
5131 577         1023 $count = 1; # assume 1 if count not specified
5132             }
5133 1238         2143 $packed = '';
5134 1238         2957 while ($count--) {
5135 1719         3010 $val = shift @vals;
5136 1719 50       3883 return undef unless defined $val;
5137             # validate numerical formats
5138 1719 100       6774 if ($format =~ /^int/) {
    100          
5139 1232 50 33     3472 unless (IsInt($val) or IsHex($val)) {
5140 0 0       0 return undef unless IsFloat($val);
5141             # round to nearest integer
5142 0 0       0 $val = int($val + ($val < 0 ? -0.5 : 0.5));
5143 0         0 $_[0] = $val;
5144             }
5145             } elsif (not IsFloat($val)) {
5146 7 50 33     151 return undef unless $format =~ /^rational/ and ($val eq 'inf' or
      33        
5147             $val eq 'undef' or IsRational($val));
5148             }
5149 1719         4664 $packed .= &$proc($val);
5150             }
5151             } elsif ($format eq 'string' or $format eq 'undef') {
5152 360 100       1100 $format eq 'string' and $val .= "\0"; # null-terminate strings
5153 360 100 66     1247 if ($count and $count > 0) {
5154 61         177 my $diff = $count - length($val);
5155 61 100       186 if ($diff) {
5156             #warn "wrong string length!\n";
5157             # adjust length of string to match specified count
5158 29 100       90 if ($diff < 0) {
5159 22 50       60 if ($format eq 'string') {
5160 22 50       59 return undef unless $count;
5161 22         66 $val = substr($val, 0, $count - 1) . "\0";
5162             } else {
5163 0         0 $val = substr($val, 0, $count);
5164             }
5165             } else {
5166 7         24 $val .= "\0" x $diff;
5167             }
5168             }
5169             } else {
5170 299         605 $count = length($val);
5171             }
5172 360 100       809 $dataPt and substr($$dataPt, $offset, $count) = $val;
5173 360         1185 return $val;
5174             } else {
5175 0         0 warn "Sorry, Can't write $format values on this platform\n";
5176 0         0 return undef;
5177             }
5178 1238 100       3182 $dataPt and substr($$dataPt, $offset, length($packed)) = $packed;
5179 1238         3387 return $packed;
5180             }
5181              
5182             #------------------------------------------------------------------------------
5183             # Encode bit mask (the inverse of DecodeBits())
5184             # Inputs: 0) value to encode, 1) Reference to hash for encoding (or undef)
5185             # 2) optional number of bits per word (defaults to 32), 3) total bits
5186             # Returns: bit mask or undef on error (plus error string in list context)
5187             sub EncodeBits($$;$$)
5188             {
5189 100     100 0 305 my ($val, $lookup, $bits, $num) = @_;
5190 100 100       311 $bits or $bits = 32;
5191 100 100       291 $num or $num = $bits;
5192 100         399 my $words = int(($num + $bits - 1) / $bits);
5193 100         337 my @outVal = (0) x $words;
5194 100 100       316 if ($val ne '(none)') {
5195 82         382 my @vals = split /\s*,\s*/, $val;
5196 82         268 foreach $val (@vals) {
5197 42         84 my $bit;
5198 42 50       116 if ($lookup) {
5199 42         150 $bit = ReverseLookup($val, $lookup);
5200             # (Note: may get non-numerical $bit values from Unknown() tags)
5201 42 100       159 unless (defined $bit) {
5202 33 50       124 if ($val =~ /\[(\d+)\]/) { # numerical bit specification
5203 0         0 $bit = $1;
5204             } else {
5205             # don't return error string unless more than one value
5206 33 100 66     215 return undef unless @vals > 1 and wantarray;
5207 2         16 return (undef, "no match for '${val}'");
5208             }
5209             }
5210             } else {
5211 0         0 $bit = $val;
5212             }
5213 9 50 33     41 unless (IsInt($bit) and $bit < $num) {
5214 0 0       0 return undef unless wantarray;
5215 0 0       0 return (undef, IsInt($bit) ? 'bit number too high' : 'not an integer');
5216             }
5217 9         39 my $word = int($bit / $bits);
5218 9         56 $outVal[$word] |= (1 << ($bit - $word * $bits));
5219             }
5220             }
5221 67         430 return "@outVal";
5222             }
5223              
5224             #------------------------------------------------------------------------------
5225             # get current position in output file (or end of file if a scalar reference)
5226             # Inputs: 0) file or scalar reference
5227             # Returns: Current position or -1 on error
5228             sub Tell($)
5229             {
5230 324     324 0 719 my $outfile = shift;
5231 324 100       1334 if (UNIVERSAL::isa($outfile,'GLOB')) {
5232 295         1878 return tell($outfile);
5233             } else {
5234 29         207 return length($$outfile);
5235             }
5236             }
5237              
5238             #------------------------------------------------------------------------------
5239             # write to file or memory
5240             # Inputs: 0) file or scalar reference, 1-N) list of stuff to write
5241             # Returns: true on success
5242             sub Write($@)
5243             {
5244 3726     3726 0 6196 my $outfile = shift;
5245 3726 100       12750 if (UNIVERSAL::isa($outfile,'GLOB')) {
    50          
5246 2301         18942 return print $outfile @_;
5247             } elsif (ref $outfile eq 'SCALAR') {
5248 1425         6423 $$outfile .= join('', @_);
5249 1425         5414 return 1;
5250             }
5251 0         0 return 0;
5252             }
5253              
5254             #------------------------------------------------------------------------------
5255             # Write trailer buffer to file (applying fixups if necessary)
5256             # Inputs: 0) ExifTool object ref, 1) trailer dirInfo ref, 2) output file ref
5257             # Returns: 1 on success
5258             sub WriteTrailerBuffer($$$)
5259             {
5260 12     12 0 47 my ($self, $trailInfo, $outfile) = @_;
5261 12 50       62 if ($$self{DEL_GROUP}{Trailer}) {
5262 0         0 $self->VPrint(0, " Deleting trailer ($$trailInfo{Offset} bytes)\n");
5263 0         0 ++$$self{CHANGED};
5264 0         0 return 1;
5265             }
5266 12         71 my $pos = Tell($outfile);
5267 12         34 my $trailPt = $$trailInfo{OutFile};
5268             # apply fixup if necessary (AFCP requires this)
5269 12 100       60 if ($$trailInfo{Fixup}) {
5270 8 50       40 if ($pos > 0) {
5271             # shift offsets to final AFCP location and write it out
5272 8         23 $$trailInfo{Fixup}{Shift} += $pos;
5273 8         40 $$trailInfo{Fixup}->ApplyFixup($trailPt);
5274             } else {
5275 0         0 $self->Error("Can't get file position for trailer offset fixup",1);
5276             }
5277             }
5278 12         61 return Write($outfile, $$trailPt);
5279             }
5280              
5281             #------------------------------------------------------------------------------
5282             # Add trailers as a block
5283             # Inputs: 0) ExifTool object ref, 1) [optional] trailer data raf,
5284             # 1 or 2-N) trailer types to add (or none to add all)
5285             # Returns: new trailer ref, or undef
5286             # - increments CHANGED if trailer was added
5287             sub AddNewTrailers($;@)
5288             {
5289 129     129 0 505 my ($self, @types) = @_;
5290 129         265 my $trailPt;
5291 129 100       491 ref $types[0] and $trailPt = shift @types;
5292 129 100       529 $types[0] or shift @types; # (in case undef data ref is passed)
5293             # add all possible trailers if none specified (currently only CanonVRD)
5294 129 100       683 @types or @types = qw(CanonVRD CanonDR4);
5295             # add trailers as a block (if not done already)
5296 129         323 my $type;
5297 129         464 foreach $type (@types) {
5298 251 100       1306 next unless $$self{NEW_VALUE}{$Image::ExifTool::Extra{$type}};
5299 10 100       58 next if $$self{"Did$type"};
5300 9 100       45 my $val = $self->GetNewValue($type) or next;
5301             # DR4 record must be wrapped in VRD trailer package
5302 8 100       40 if ($type eq 'CanonDR4') {
5303 3 100       19 next if $$self{DidCanonVRD}; # (only allow one VRD trailer)
5304 2         25 require Image::ExifTool::CanonVRD;
5305 2         20 $val = Image::ExifTool::CanonVRD::WrapDR4($val);
5306 2         9 $$self{DidCanonVRD} = 1;
5307             }
5308 7 50       55 my $verb = $trailPt ? 'Writing' : 'Adding';
5309 7         59 $self->VPrint(0, " $verb $type as a block\n");
5310 7 50       35 if ($trailPt) {
5311 0         0 $$trailPt .= $val;
5312             } else {
5313 7         19 $trailPt = \$val;
5314             }
5315 7         32 $$self{"Did$type"} = 1;
5316 7         25 ++$$self{CHANGED};
5317             }
5318 129         443 return $trailPt;
5319             }
5320              
5321             #------------------------------------------------------------------------------
5322             # Write segment, splitting up into multiple segments if necessary
5323             # Inputs: 0) file or scalar reference, 1) segment marker
5324             # 2) segment header, 3) segment data ref, 4) segment type
5325             # Returns: number of segments written, or 0 on error
5326             # Notes: Writes a single empty segment if data is empty
5327             sub WriteMultiSegment($$$$;$)
5328             {
5329 110     110 0 521 my ($outfile, $marker, $header, $dataPt, $type) = @_;
5330 110 100       464 $type or $type = '';
5331 110         296 my $len = length($$dataPt);
5332 110         421 my $hdr = "\xff" . chr($marker);
5333 110         210 my $count = 0;
5334 110         295 my $maxLen = $maxSegmentLen - length($header);
5335 110 100       438 $maxLen -= 2 if $type eq 'ICC'; # leave room for segment counters
5336 110         434 my $num = int(($len + $maxLen - 1) / $maxLen); # number of segments to write
5337 110         243 my $n = 0;
5338             # write data, splitting into multiple segments if necessary
5339             # (each segment gets its own header)
5340 110         217 for (;;) {
5341 110         221 ++$count;
5342 110         251 my $size = $len - $n;
5343 110 50       388 if ($size > $maxLen) {
5344 0         0 $size = $maxLen;
5345             # avoid starting an Extended EXIF segment with a valid TIFF header
5346             # (because we would interpret that as a separate EXIF segment)
5347 0 0 0     0 --$size if $type eq 'EXIF' and $n+$maxLen <= $len-4 and
      0        
5348             substr($$dataPt, $n+$maxLen, 4) =~ /^(MM\0\x2a|II\x2a\0)/;
5349             }
5350 110         615 my $buff = substr($$dataPt,$n,$size);
5351 110         301 $n += $size;
5352 110         290 $size += length($header);
5353 110 100       496 if ($type eq 'ICC') {
5354 3         21 $buff = pack('CC', $count, $num) . $buff;
5355 3         14 $size += 2;
5356             }
5357             # write the new segment with appropriate header
5358 110         506 my $segHdr = $hdr . pack('n', $size + 2);
5359 110 50       423 Write($outfile, $segHdr, $header, $buff) or return 0;
5360 110 50       522 last if $n >= $len;
5361             }
5362 110         409 return $count;
5363             }
5364              
5365             #------------------------------------------------------------------------------
5366             # Write XMP segment(s) to JPEG file
5367             # Inputs: 0) ExifTool object ref, 1) outfile ref, 2) XMP data ref,
5368             # 3) extended XMP data ref, 4) 32-char extended XMP GUID (or undef if no extended data)
5369             # Returns: true on success, false on write error
5370             sub WriteMultiXMP($$$$$)
5371             {
5372 33     33 0 141 my ($self, $outfile, $dataPt, $extPt, $guid) = @_;
5373 33         77 my $success = 1;
5374              
5375             # write main XMP segment
5376 33         116 my $size = length($$dataPt) + length($xmpAPP1hdr);
5377 33 50       132 if ($size > $maxXMPLen) {
5378 0         0 $self->Error("XMP block too large for JPEG segment! ($size bytes)", 1);
5379 0         0 return 1;
5380             }
5381 33         238 my $app1hdr = "\xff\xe1" . pack('n', $size + 2);
5382 33 50       204 Write($outfile, $app1hdr, $xmpAPP1hdr, $$dataPt) or $success = 0;
5383             # write extended XMP segment(s) if necessary
5384 33 50       159 if (defined $guid) {
5385 0         0 $size = length($$extPt);
5386 0         0 my $maxLen = $maxXMPLen - 75; # maximum size without 75-byte header
5387 0         0 my $off;
5388 0         0 for ($off=0; $off<$size; $off+=$maxLen) {
5389             # header(75) = signature(35) + guid(32) + size(4) + offset(4)
5390 0         0 my $len = $size - $off;
5391 0 0       0 $len = $maxLen if $len > $maxLen;
5392 0         0 $app1hdr = "\xff\xe1" . pack('n', $len + 75 + 2);
5393 0         0 $self->VPrint(0, "Writing extended XMP segment ($len bytes)\n");
5394 0 0       0 Write($outfile, $app1hdr, $xmpExtAPP1hdr, $guid, pack('N2', $size, $off),
5395             substr($$extPt, $off, $len)) or $success = 0;
5396             }
5397             }
5398 33         170 return $success;
5399             }
5400              
5401             #------------------------------------------------------------------------------
5402             # WriteJPEG : Write JPEG image
5403             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
5404             # Returns: 1 on success, 0 if this wasn't a valid JPEG file, or -1 if
5405             # an output file was specified and a write error occurred
5406             sub WriteJPEG($$)
5407             {
5408 110     110 0 364 my ($self, $dirInfo) = @_;
5409 110         396 my $outfile = $$dirInfo{OutFile};
5410 110         320 my $raf = $$dirInfo{RAF};
5411 110         307 my ($ch, $s, $length,$err, %doneDir, $isEXV, $creatingEXV);
5412 110         379 my $verbose = $$self{OPTIONS}{Verbose};
5413 110         3859 my $out = $$self{OPTIONS}{TextOut};
5414 110         287 my $rtnVal = 0;
5415 110         463 my %dumpParms = ( Out => $out );
5416 110         292 my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known
5417              
5418             # check to be sure this is a valid JPG or EXV file
5419 110 100 100     580 unless ($raf->Read($s,2) == 2 and $s eq "\xff\xd8") {
5420 2 100 66     20 if (defined $s and length $s) {
5421 1 50 33     12 return 0 unless $s eq "\xff\x01" and $raf->Read($s,5) == 5 and $s eq 'Exiv2';
      33        
5422             } else {
5423 1 50       5 return 0 unless $$self{FILE_TYPE} eq 'EXV';
5424 1         2 $s = 'Exiv2';
5425 1         3 $creatingEXV = 1;
5426             }
5427 2 50       11 Write($outfile,"\xff\x01") or $err = 1;
5428 2         6 $isEXV = 1;
5429             }
5430 110 50       741 $dumpParms{MaxLen} = 128 unless $verbose > 3;
5431              
5432 110         337 delete $$self{PREVIEW_INFO}; # reset preview information
5433 110         307 delete $$self{DEL_PREVIEW}; # reset flag to delete preview
5434              
5435 110 50       559 Write($outfile, $s) or $err = 1;
5436             # figure out what segments we need to write for the tags we have set
5437 110         463 my $addDirs = $$self{ADD_DIRS};
5438 110         309 my $editDirs = $$self{EDIT_DIRS};
5439 110         361 my $delGroup = $$self{DEL_GROUP};
5440 110         339 my $path = $$self{PATH};
5441 110         311 my $pn = scalar @$path;
5442              
5443             # set input record separator to 0xff (the JPEG marker) to make reading quicker
5444 110         1322 local $/ = "\xff";
5445             #
5446             # pre-scan image to determine if any create-able segment already exists
5447             #
5448 110         598 my $pos = $raf->Tell();
5449 110         401 my ($marker, @dirOrder, %dirCount);
5450 110         282 Prescan: for (;;) {
5451             # read up to next marker (JPEG markers begin with 0xff)
5452 792 100       2569 $raf->ReadLine($s) or last;
5453             # JPEG markers can be padded with unlimited 0xff's
5454 791         1471 for (;;) {
5455 791 50       2287 $raf->Read($ch, 1) or last Prescan;
5456 791         1578 $marker = ord($ch);
5457 791 50       2082 last unless $marker == 0xff;
5458             }
5459 791         1277 my $dirName;
5460             # stop pre-scan at SOS (end of meta information) or EOI (end of image)
5461 791 100 100     3061 if ($marker == 0xda or $marker == 0xd9) {
5462 109         588 $dirName = $jpegMarker{$marker};
5463 109         387 push(@dirOrder, $dirName);
5464 109         374 $dirCount{$dirName} = 1;
5465 109         257 last;
5466             }
5467             # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
5468 682 100 66     5653 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    50 100        
      33        
      66        
      33        
5469 108 50       435 last unless $raf->Seek(7, 1);
5470             # read data for all markers except stand-alone
5471             # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
5472             } elsif ($marker!=0x00 and $marker!=0x01 and ($marker<0xd0 or $marker>0xd7)) {
5473             # read record length word
5474 574 50       1533 last unless $raf->Read($s, 2) == 2;
5475 574         2039 my $len = unpack('n',$s); # get data length
5476 574 50 33     2629 last unless defined($len) and $len >= 2;
5477 574         1114 $len -= 2; # subtract size of length word
5478 574 100       1475 if (($marker & 0xf0) == 0xe0) { # is this an APP segment?
5479 347 100       818 my $n = $len < 64 ? $len : 64;
5480 347 50       994 $raf->Read($s, $n) == $n or last;
5481 347         648 $len -= $n;
5482             # Note: only necessary to recognize APP segments that we can create,
5483             # or delete as a group (and the names below should match @delGroups)
5484 347 100       1929 if ($marker == 0xe0) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
5485 45 100       270 $s =~ /^JFIF\0/ and $dirName = 'JFIF';
5486 45 100       191 $s =~ /^JFXX\0\x10/ and $dirName = 'JFXX';
5487 45 100       203 $s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF';
5488             } elsif ($marker == 0xe1) {
5489 84 100       1373 if ($s =~ /^(.{0,4})$exifAPP1hdr(.{1,4})/is) {
5490 60         220 $dirName = 'IFD0';
5491 60         353 my ($junk, $bytes) = ($1, $2);
5492             # support multi-segment EXIF
5493 60 0 66     369 if (@dirOrder and $dirOrder[-1] =~ /^(IFD0|ExtendedEXIF)$/ and
      33        
      33        
5494             not length $junk and $bytes !~ /^(MM\0\x2a|II\x2a\0)/)
5495             {
5496 0         0 $dirName = 'ExtendedEXIF';
5497             }
5498             }
5499 84 100       880 $s =~ /^$xmpAPP1hdr/ and $dirName = 'XMP';
5500 84 100       723 $s =~ /^$xmpExtAPP1hdr/ and $dirName = 'XMP';
5501             } elsif ($marker == 0xe2) {
5502 55 100       246 $s =~ /^ICC_PROFILE\0/ and $dirName = 'ICC_Profile';
5503 55 100       246 $s =~ /^FPXR\0/ and $dirName = 'FlashPix';
5504 55 100       190 $s =~ /^MPF\0/ and $dirName = 'MPF';
5505             } elsif ($marker == 0xe3) {
5506 9 50       100 $s =~ /^(Meta|META|Exif)\0\0/ and $dirName = 'Meta';
5507             } elsif ($marker == 0xe5) {
5508 9 50       112 $s =~ /^RMETA\0/ and $dirName = 'RMETA';
5509             } elsif ($marker == 0xec) {
5510 19 100       152 $s =~ /^Ducky/ and $dirName = 'Ducky';
5511             } elsif ($marker == 0xed) {
5512 29 100       354 $s =~ /^$psAPP13hdr/ and $dirName = 'Photoshop';
5513             } elsif ($marker == 0xee) {
5514 16 50       176 $s =~ /^Adobe/ and $dirName = 'Adobe';
5515             }
5516             # initialize doneDir as a flag that the directory exists
5517             # (unless we are deleting it anyway)
5518 347 100 100     1900 $doneDir{$dirName} = 0 if defined $dirName and not $$delGroup{$dirName};
5519             }
5520 574 50       1712 $raf->Seek($len, 1) or last;
5521             }
5522 682 100       3070 $dirName or $dirName = JpegMarkerName($marker);
5523 682   100     3332 $dirCount{$dirName} = ($dirCount{$dirName} || 0) + 1;
5524 682         1738 push @dirOrder, $dirName;
5525             }
5526 110 100 100     783 unless ($marker and $marker == 0xda) {
5527 2 50       8 $isEXV or $self->Error('Corrupted JPEG image'), return 1;
5528 2 50 66     20 $marker and $marker != 0xd9 and $self->Error('Corrupted EXV file'), return 1;
5529             }
5530 110 50       460 $raf->Seek($pos, 0) or $self->Error('Seek error'), return 1;
5531             #
5532             # re-write the image
5533             #
5534 110         788 my ($combinedSegData, $segPos, $firstSegPos, %extendedXMP);
5535 110         0 my (@iccChunk, $iccChunkCount, $iccChunksTotal);
5536             # read through each segment in the JPEG file
5537 110         270 Marker: for (;;) {
5538              
5539             # read up to next marker (JPEG markers begin with 0xff)
5540 792         1308 my $segJunk;
5541 792 100       2853 $raf->ReadLine($segJunk) or $segJunk = '';
5542             # remove the 0xff but write the rest of the junk up to this point
5543             # (this will handle the data after the first 7 bytes of SOF segments)
5544 792         1846 chomp($segJunk);
5545 792 100       2219 Write($outfile, $segJunk) if length $segJunk;
5546             # JPEG markers can be padded with unlimited 0xff's
5547 792         1332 for (;;) {
5548 792 100       2347 if ($raf->Read($ch, 1)) {
    50          
5549 791         1605 $marker = ord($ch);
5550 791 50       2180 last unless $marker == 0xff;
5551             } elsif ($creatingEXV) {
5552             # create EXV from scratch
5553 1         5 $marker = 0xd9; # EOI
5554 1         6 push @dirOrder, 'EOI';
5555 1         4 $dirCount{EOI} = 1;
5556 1         4 last;
5557             } else {
5558 0         0 $self->Error('Format error');
5559 0         0 return 1;
5560             }
5561             }
5562             # read the segment data
5563 792         1645 my $segData;
5564             # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
5565 792 100 66     8157 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    100 100        
      33        
      66        
      66        
      66        
5566 108 50       542 last unless $raf->Read($segData, 7) == 7;
5567             # read data for all markers except stand-alone
5568             # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, EOI, RST0-RST7)
5569             } elsif ($marker!=0x00 and $marker!=0x01 and $marker!=0xd9 and
5570             ($marker<0xd0 or $marker>0xd7))
5571             {
5572             # read record length word
5573 682 50       1833 last unless $raf->Read($s, 2) == 2;
5574 682         2070 my $len = unpack('n',$s); # get data length
5575 682 50 33     3139 last unless defined($len) and $len >= 2;
5576 682         1948 $segPos = $raf->Tell();
5577 682         1291 $len -= 2; # subtract size of length word
5578 682 50       1728 last unless $raf->Read($segData, $len) == $len;
5579             }
5580             # initialize variables for this segment
5581 792         2480 my $hdr = "\xff" . chr($marker); # segment header
5582 792         2411 my $markerName = JpegMarkerName($marker);
5583 792         1963 my $dirName = shift @dirOrder; # get directory name
5584             #
5585             # create all segments that must come before this one
5586             # (nothing comes before SOI or after SOS)
5587             #
5588 792         2388 while ($markerName ne 'SOI') {
5589 792 100 100     2634 if (exists $$addDirs{JFIF} and not defined $doneDir{JFIF}) {
5590 1         4 $doneDir{JFIF} = 1;
5591 1 50       5 if (defined $doneDir{Adobe}) {
5592             # JFIF overrides Adobe APP14 colour components, so don't allow this
5593             # (ref https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html)
5594 1         6 $self->Warn('Not creating JFIF in JPEG with Adobe APP14');
5595             } else {
5596 0 0       0 if ($verbose) {
5597 0         0 print $out "Creating APP0:\n";
5598 0         0 print $out " Creating JFIF with default values\n";
5599             }
5600 0         0 my $jfif = "\x01\x02\x01\0\x48\0\x48\0\0";
5601 0         0 SetByteOrder('MM');
5602 0         0 my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
5603 0         0 my %dirInfo = (
5604             DataPt => \$jfif,
5605             DirStart => 0,
5606             DirLen => length $jfif,
5607             Parent => 'JFIF',
5608             );
5609             # must temporarily remove JFIF from DEL_GROUP so we can
5610             # delete JFIF and add it back again in a single step
5611 0         0 my $delJFIF = $$delGroup{JFIF};
5612 0         0 delete $$delGroup{JFIF};
5613 0         0 $$path[$pn] = 'JFIF';
5614 0         0 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5615 0 0       0 $$delGroup{JFIF} = $delJFIF if defined $delJFIF;
5616 0 0 0     0 if (defined $newData and length $newData) {
5617 0         0 my $app0hdr = "\xff\xe0" . pack('n', length($newData) + 7);
5618 0 0       0 Write($outfile,$app0hdr,"JFIF\0",$newData) or $err = 1;
5619             }
5620             }
5621             }
5622             # don't create anything before APP0 or APP1 EXIF (containing IFD0)
5623 792 100 100     4848 last if $markerName eq 'APP0' or $dirCount{IFD0} or $dirCount{ExtendedEXIF};
      66        
5624             # EXIF information must come immediately after APP0
5625 687 100 100     2683 if (exists $$addDirs{IFD0} and not defined $doneDir{IFD0}) {
5626 31         101 $doneDir{IFD0} = 1;
5627 31 100       132 $verbose and print $out "Creating APP1:\n";
5628             # write new EXIF data
5629 31         130 $$self{TIFF_TYPE} = 'APP1';
5630 31         147 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
5631 31         210 my %dirInfo = (
5632             DirName => 'IFD0',
5633             Parent => 'APP1',
5634             );
5635 31         111 $$path[$pn] = 'APP1';
5636 31         242 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
5637 31 100 66     334 if (defined $buff and length $buff) {
5638 29 50       182 if (length($buff) + length($exifAPP1hdr) > $maxSegmentLen) {
5639 0 0       0 if ($self->Options('NoMultiExif')) {
5640 0         0 $self->Error('EXIF is too large for JPEG segment');
5641             } else {
5642 0         0 $self->Warn('Creating multi-segment EXIF',1);
5643             }
5644             }
5645             # switch to buffered output if required
5646 29 50 33     315 if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer}) and not $oldOutfile) {
      33        
5647 0         0 $writeBuffer = '';
5648 0         0 $oldOutfile = $outfile;
5649 0         0 $outfile = \$writeBuffer;
5650             # account for segment, EXIF and TIFF headers
5651 0 0       0 $$self{PREVIEW_INFO}{Fixup}{Start} += 18 if $$self{PREVIEW_INFO};
5652 0 0       0 $$self{LeicaTrailer}{Fixup}{Start} += 18 if $$self{LeicaTrailer};
5653             }
5654             # write as multi-segment
5655 29         163 my $n = WriteMultiSegment($outfile, 0xe1, $exifAPP1hdr, \$buff, 'EXIF');
5656 29 50 33     247 if (not $n) {
    50          
5657 0         0 $err = 1;
5658             } elsif ($n > 1 and $oldOutfile) {
5659             # (punt on this because updating the pointers would be a real pain)
5660 0         0 $self->Error("Can't write multi-segment EXIF with external pointers");
5661             }
5662 29         168 ++$$self{CHANGED};
5663             }
5664             }
5665             # APP13 Photoshop segment next
5666 687 100       1747 last if $dirCount{Photoshop};
5667 505 100 100     1689 if (exists $$addDirs{Photoshop} and not defined $doneDir{Photoshop}) {
5668 19         77 $doneDir{Photoshop} = 1;
5669 19 50       77 $verbose and print $out "Creating APP13:\n";
5670             # write new APP13 Photoshop record to memory
5671 19         98 my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
5672 19         112 my %dirInfo = (
5673             Parent => 'APP13',
5674             );
5675 19         80 $$path[$pn] = 'APP13';
5676 19         117 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5677 19 50 33     163 if (defined $buff and length $buff) {
5678 19 50       193 WriteMultiSegment($outfile, 0xed, $psAPP13hdr, \$buff) or $err = 1;
5679 19         112 ++$$self{CHANGED};
5680             }
5681             }
5682             # then APP1 XMP segment
5683 505 100       1342 last if $dirCount{XMP};
5684 490 100 100     1684 if (exists $$addDirs{XMP} and not defined $doneDir{XMP}) {
5685 26         95 $doneDir{XMP} = 1;
5686 26 50       155 $verbose and print $out "Creating APP1:\n";
5687             # write new XMP data
5688 26         149 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
5689 26         222 my %dirInfo = (
5690             Parent => 'APP1',
5691             # specify MaxDataLen so XMP is split if required
5692             MaxDataLen => $maxXMPLen - length($xmpAPP1hdr),
5693             );
5694 26         104 $$path[$pn] = 'APP1';
5695 26         154 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5696 26 50 33     273 if (defined $buff and length $buff) {
5697             WriteMultiXMP($self, $outfile, \$buff, $dirInfo{ExtendedXMP},
5698 26 50       191 $dirInfo{ExtendedGUID}) or $err = 1;
5699             }
5700             }
5701             # then APP2 ICC_Profile segment
5702 490 100       1396 last if $dirCount{ICC_Profile};
5703 485 100 100     1433 if (exists $$addDirs{ICC_Profile} and not defined $doneDir{ICC_Profile}) {
5704 3         12 $doneDir{ICC_Profile} = 1;
5705 3 50 66     28 next if $$delGroup{ICC_Profile} and $$delGroup{ICC_Profile} != 2;
5706 3 50       16 $verbose and print $out "Creating APP2:\n";
5707             # write new ICC_Profile data
5708 3         16 my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
5709 3         19 my %dirInfo = (
5710             Parent => 'APP2',
5711             );
5712 3         13 $$path[$pn] = 'APP2';
5713 3         18 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5714 3 50 33     31 if (defined $buff and length $buff) {
5715 3 50       19 WriteMultiSegment($outfile, 0xe2, "ICC_PROFILE\0", \$buff, 'ICC') or $err = 1;
5716 3         15 ++$$self{CHANGED};
5717             }
5718             }
5719             # then APP12 Ducky segment
5720 485 100       1146 last if $dirCount{Ducky};
5721 484 100 100     1417 if (exists $$addDirs{Ducky} and not defined $doneDir{Ducky}) {
5722 2         6 $doneDir{Ducky} = 1;
5723 2 50       10 $verbose and print $out "Creating APP12 Ducky:\n";
5724             # write new Ducky segment data
5725 2         12 my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
5726 2         11 my %dirInfo = (
5727             Parent => 'APP12',
5728             );
5729 2         8 $$path[$pn] = 'APP12';
5730 2         10 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
5731 2 50 33     23 if (defined $buff and length $buff) {
5732 2         6 my $size = length($buff) + 5;
5733 2 50       8 if ($size <= $maxSegmentLen) {
5734             # write the new segment with appropriate header
5735 2         10 my $app12hdr = "\xff\xec" . pack('n', $size + 2);
5736 2 50       10 Write($outfile, $app12hdr, 'Ducky', $buff) or $err = 1;
5737             } else {
5738 0         0 $self->Warn("APP12 Ducky segment too large! ($size bytes)");
5739             }
5740             }
5741             }
5742             # then APP14 Adobe segment
5743 484 100       1186 last if $dirCount{Adobe};
5744 459 50 33     1330 if (exists $$addDirs{Adobe} and not defined $doneDir{Adobe}) {
5745 0         0 $doneDir{Adobe} = 1;
5746 0         0 my $buff = $self->GetNewValue('Adobe');
5747 0 0       0 if ($buff) {
5748 0 0       0 $verbose and print $out "Creating APP14:\n Creating Adobe segment\n";
5749 0         0 my $size = length($buff);
5750 0 0       0 if ($size <= $maxSegmentLen) {
5751             # write the new segment with appropriate header
5752 0         0 my $app14hdr = "\xff\xee" . pack('n', $size + 2);
5753 0 0       0 Write($outfile, $app14hdr, $buff) or $err = 1;
5754 0         0 ++$$self{CHANGED};
5755             } else {
5756 0         0 $self->Warn("APP14 Adobe segment too large! ($size bytes)");
5757             }
5758             }
5759             }
5760             # finally, COM segment
5761 459 100       1087 last if $dirCount{COM};
5762 439 100 100     1330 if (exists $$addDirs{COM} and not defined $doneDir{COM}) {
5763 5         13 $doneDir{COM} = 1;
5764 5 50 33     21 next if $$delGroup{File} and $$delGroup{File} != 2;
5765 5         19 my $newComment = $self->GetNewValue('Comment');
5766 5 50       31 if (defined $newComment) {
5767 5 50       19 if ($verbose) {
5768 0         0 print $out "Creating COM:\n";
5769 0         0 $self->VerboseValue('+ Comment', $newComment);
5770             }
5771 5 50       25 WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1;
5772 5         17 ++$$self{CHANGED};
5773             }
5774             }
5775 439         759 last; # didn't want to loop anyway
5776             }
5777 792         1753 $$path[$pn] = $markerName;
5778             # decrement counter for this directory since we are about to process it
5779 792         1895 --$dirCount{$dirName};
5780             #
5781             # rewrite existing segments
5782             #
5783             # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
5784 792 100 66     7589 if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
    100 100        
    100 66        
    50 33        
      66        
      33        
5785 108 100       505 $verbose and print $out "JPEG $markerName:\n";
5786 108 50       394 Write($outfile, $hdr, $segData) or $err = 1;
5787 108         366 next;
5788             } elsif ($marker == 0xda) { # SOS
5789 108         440 pop @$path;
5790 108 100       442 $verbose and print $out "JPEG SOS\n";
5791             # write SOS segment
5792 108         524 $s = pack('n', length($segData) + 2);
5793 108 50       372 Write($outfile, $hdr, $s, $segData) or $err = 1;
5794 108         412 my ($buff, $endPos, $trailInfo);
5795 108         348 my $delPreview = $$self{DEL_PREVIEW};
5796 108 100       769 $trailInfo = IdentifyTrailer($raf) unless $$delGroup{Trailer};
5797 108         1054 my $nvTrail = $self->GetNewValueHash($Image::ExifTool::Extra{Trailer});
5798 108 100 66     1544 unless ($oldOutfile or $delPreview or $trailInfo or $$delGroup{Trailer} or $nvTrail) {
      100        
      100        
      66        
5799             # blindly copy the rest of the file
5800 92         445 while ($raf->Read($buff, 65536)) {
5801 92 50       551 Write($outfile, $buff) or $err = 1, last;
5802             }
5803 92         311 $rtnVal = 1; # success unless we have a file write error
5804 92         276 last; # all done
5805             }
5806             # write the rest of the image (as quickly as possible) up to the EOI
5807 16         48 my $endedWithFF;
5808 16         41 for (;;) {
5809 16 50       75 my $n = $raf->Read($buff, 65536) or last Marker;
5810 16 50 33     252 if (($endedWithFF and $buff =~ m/^\xd9/sg) or
      33        
5811             $buff =~ m/\xff\xd9/sg)
5812             {
5813 16         56 $rtnVal = 1; # the JPEG is OK
5814             # write up to the EOI
5815 16         49 my $pos = pos($buff);
5816 16 50       78 Write($outfile, substr($buff, 0, $pos)) or $err = 1;
5817 16         120 $buff = substr($buff, $pos);
5818 16         47 last;
5819             }
5820 0 0       0 unless ($n == 65536) {
5821 0         0 $self->Error('JPEG EOI marker not found');
5822 0         0 last Marker;
5823             }
5824 0 0       0 Write($outfile, $buff) or $err = 1;
5825 0 0       0 $endedWithFF = substr($buff, 65535, 1) eq "\xff" ? 1 : 0;
5826             }
5827             # remember position of last data copied
5828 16         116 $endPos = $raf->Tell() - length($buff);
5829             # write new trailer if specified
5830 16 50       83 if ($nvTrail) {
5831             # access new value directly to avoid copying a potentially very large data block
5832 0 0 0     0 if ($$nvTrail{Value} and $$nvTrail{Value}[0]) { # (note: "0" will also delete the trailer)
    0 0        
5833 0         0 $self->VPrint(0, ' Writing new trailer');
5834 0 0       0 Write($outfile, $$nvTrail{Value}[0]) or $err = 1;
5835 0         0 ++$$self{CHANGED};
5836             } elsif ($raf->Seek(0, 2) and $raf->Tell() != $endPos) {
5837 0         0 $self->VPrint(0, ' Deleting trailer (', $raf->Tell() - $endPos, ' bytes)');
5838 0         0 ++$$self{CHANGED}; # changed if there was previously a trailer
5839             }
5840 0         0 last; # all done
5841             }
5842             # rewrite existing trailers
5843 16 100       84 if ($trailInfo) {
5844 11         35 my $tbuf = '';
5845 11         54 $raf->Seek(-length($buff), 1); # seek back to just after EOI
5846 11         96 $$trailInfo{OutFile} = \$tbuf; # rewrite the trailer
5847 11         44 $$trailInfo{ScanForAFCP} = 1; # scan if necessary
5848 11 50       67 $self->ProcessTrailers($trailInfo) or undef $trailInfo;
5849             }
5850 16 100       83 if (not $oldOutfile) {
    50          
5851             # do nothing special
5852             } elsif ($$self{LeicaTrailer}) {
5853 0         0 my $trailLen;
5854 0 0       0 if ($trailInfo) {
5855 0         0 $trailLen = $$trailInfo{DataPos} - $endPos;
5856             } else {
5857 0 0       0 $raf->Seek(0, 2) or $err = 1;
5858 0         0 $trailLen = $raf->Tell() - $endPos;
5859             }
5860 0         0 my $fixup = $$self{LeicaTrailer}{Fixup};
5861 0         0 $$self{LeicaTrailer}{TrailPos} = $endPos;
5862 0         0 $$self{LeicaTrailer}{TrailLen} = $trailLen;
5863             # get _absolute_ position of new Leica trailer
5864 0         0 my $absPos = Tell($oldOutfile) + length($$outfile);
5865 0         0 require Image::ExifTool::Panasonic;
5866 0         0 my $dat = Image::ExifTool::Panasonic::ProcessLeicaTrailer($self, $absPos);
5867             # allow some junk before Leica trailer (just in case)
5868 0         0 my $junk = $$self{LeicaTrailerPos} - $endPos;
5869             # set MakerNote pointer and size (subtract 10 for segment and EXIF headers)
5870 0         0 $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', length($$outfile) - 10 + $junk);
5871             # use this fixup to set the size too (sneaky)
5872 0 0       0 my $trailSize = defined($dat) ? length($dat) - $junk : $$self{LeicaTrailer}{Size};
5873 0         0 $$fixup{Start} -= 4; $$fixup{Shift} += 4;
  0         0  
5874 0 0       0 $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', $trailSize) if defined $trailSize;
5875 0         0 $$fixup{Start} += 4; $$fixup{Shift} -= 4;
  0         0  
5876             # clean up and write the buffered data
5877 0         0 $outfile = $oldOutfile;
5878 0         0 undef $oldOutfile;
5879 0 0       0 Write($outfile, $writeBuffer) or $err = 1;
5880 0         0 undef $writeBuffer;
5881 0 0       0 if (defined $dat) {
5882 0 0       0 Write($outfile, $dat) or $err = 1; # write new Leica trailer
5883 0         0 $delPreview = 1; # delete existing Leica trailer
5884             }
5885             } else {
5886             # locate preview image and fix up preview offsets
5887 1 50       6 my $scanLen = $$self{Make} =~ /^SONY/i ? 65536 : 1024;
5888 1 50       6 if (length($buff) < $scanLen) { # make sure we have enough trailer to scan
5889 1         3 my $buf2;
5890 1 50       5 $buff .= $buf2 if $raf->Read($buf2, $scanLen - length($buff));
5891             }
5892             # get new preview image position, relative to EXIF base
5893 1         6 my $newPos = length($$outfile) - 10; # (subtract 10 for segment and EXIF headers)
5894 1         3 my $junkLen;
5895             # adjust position if image isn't at the start (eg. Olympus E-1/E-300)
5896 1 50       5 if ($buff =~ /(\xff\xd8\xff.|.\xd8\xff\xdb)(..)/sg) {
5897 0         0 my ($jpegHdr, $segLen) = ($1, $2);
5898 0         0 $junkLen = pos($buff) - 6;
5899             # Sony previewimage trailer has a 32 byte header
5900 0 0 0     0 if ($$self{Make} =~ /^SONY/i and $junkLen > 32) {
5901             # with some newer Sony models, the makernotes preview pointer
5902             # points to JPEG at end of EXIF inside MPImage preview (what a pain!)
5903 0 0       0 if ($jpegHdr eq "\xff\xd8\xff\xe1") { # is the first segment EXIF?
5904 0         0 $segLen = unpack('n', $segLen); # the EXIF segment length
5905             # Sony PreviewImage starts with last 2 bytes of EXIF segment
5906             # (and first byte is usually "\0", not "\xff", so don't check this)
5907 0 0 0     0 if (length($buff) > $junkLen + $segLen + 6 and
5908             substr($buff, $junkLen + $segLen + 3, 3) eq "\xd8\xff\xdb")
5909             {
5910 0         0 $junkLen += $segLen + 2;
5911             # (note: this will not copy the trailer after PreviewImage,
5912             # which is a 14kB block full of zeros for the A77)
5913             }
5914             }
5915 0         0 $junkLen -= 32;
5916             }
5917 0         0 $newPos += $junkLen;
5918             }
5919             # fix up the preview offsets to point to the start of the new image
5920 1         4 my $previewInfo = $$self{PREVIEW_INFO};
5921 1         4 delete $$self{PREVIEW_INFO};
5922 1         3 my $fixup = $$previewInfo{Fixup};
5923 1   50     7 $newPos += ($$previewInfo{BaseShift} || 0);
5924             # adjust to absolute file offset if necessary (Samsung STMN)
5925 1 50       5 $newPos += Tell($oldOutfile) + 10 if $$previewInfo{Absolute};
5926 1 50       4 if ($$previewInfo{Relative}) {
    0          
5927             # adjust for our base by looking at how far the pointer got shifted
5928 1   50     6 $newPos -= ($fixup->GetMarkerPointers($outfile, 'PreviewImage') || 0);
5929             } elsif ($$previewInfo{ChangeBase}) {
5930             # Leica S2 uses relative offsets for the preview only (leica sucks)
5931 0         0 my $makerOffset = $fixup->GetMarkerPointers($outfile, 'LeicaTrailer');
5932 0 0       0 $newPos -= $makerOffset if $makerOffset;
5933             }
5934 1         6 $fixup->SetMarkerPointers($outfile, 'PreviewImage', $newPos);
5935             # clean up and write the buffered data
5936 1         5 $outfile = $oldOutfile;
5937 1         4 undef $oldOutfile;
5938 1 50       5 Write($outfile, $writeBuffer) or $err = 1;
5939 1         33 undef $writeBuffer;
5940             # write preview image
5941 1 50       18 if ($$previewInfo{Data} ne 'LOAD_PREVIEW') {
5942             # write any junk that existed before the preview image
5943 0 0 0     0 Write($outfile, substr($buff,0,$junkLen)) or $err = 1 if $junkLen;
5944             # write the saved preview image
5945 0 0       0 Write($outfile, $$previewInfo{Data}) or $err = 1;
5946 0         0 delete $$previewInfo{Data};
5947             # (don't increment CHANGED because we could be rewriting existing preview)
5948 0         0 $delPreview = 1; # remove old preview
5949             }
5950             }
5951             # copy over preview image if necessary
5952 16 50       70 unless ($delPreview) {
5953 16         41 my $extra;
5954 16 100       68 if ($trailInfo) {
5955             # copy everything up to start of first processed trailer
5956 11         37 $extra = $$trailInfo{DataPos} - $endPos;
5957             } else {
5958             # copy everything up to end of file
5959 5 50       38 $raf->Seek(0, 2) or $err = 1;
5960 5         26 $extra = $raf->Tell() - $endPos;
5961             }
5962 16 100       69 if ($extra > 0) {
5963 3 100       13 if ($$delGroup{Trailer}) {
5964 2 50       8 $verbose and print $out " Deleting unknown trailer ($extra bytes)\n";
5965 2         7 ++$$self{CHANGED};
5966             } else {
5967             # copy over unknown trailer
5968 1 50       3 $verbose and print $out " Preserving unknown trailer ($extra bytes)\n";
5969 1 50       4 $raf->Seek($endPos, 0) or $err = 1;
5970 1 50       7 CopyBlock($raf, $outfile, $extra) or $err = 1;
5971             }
5972             }
5973             }
5974             # write trailer if necessary
5975 16 100       58 if ($trailInfo) {
5976 11 50       68 $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1;
5977 11         88 undef $trailInfo;
5978             }
5979 16         62 last; # all done parsing file
5980              
5981             } elsif ($marker==0xd9 and $isEXV) {
5982             # write EXV EOI (any trailer will be lost)
5983 2 50       8 Write($outfile, "\xff\xd9") or $err = 1;
5984 2         8 $rtnVal = 1;
5985 2         11 last;
5986              
5987             } elsif ($marker==0x00 or $marker==0x01 or ($marker>=0xd0 and $marker<=0xd7)) {
5988 0 0 0     0 $verbose and $marker and print $out "JPEG $markerName:\n";
5989             # handle stand-alone markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
5990 0 0       0 Write($outfile, $hdr) or $err = 1;
5991 0         0 next;
5992             }
5993             #
5994             # NOTE: A 'next' statement after this point will cause $$segDataPt
5995             # not to be written if there is an output file, so in this case
5996             # the $$self{CHANGED} flags must be updated
5997             #
5998 574         1195 my $segDataPt = \$segData;
5999 574         1110 $length = length($segData);
6000 574 100       1343 if ($verbose) {
6001 2         8 print $out "JPEG $markerName ($length bytes):\n";
6002 2 50 33     8 if ($verbose > 2 and $markerName =~ /^APP/) {
6003 0         0 HexDump($segDataPt, undef, %dumpParms);
6004             }
6005             }
6006             # group delete of APP segments
6007 574 100       1579 if ($$delGroup{$dirName}) {
6008 55 50       120 $verbose and print $out " Deleting $dirName segment\n";
6009 55 100       156 $self->Warn('ICC_Profile deleted. Image colors may be affected') if $dirName eq 'ICC_Profile';
6010 55         99 ++$$self{CHANGED};
6011 55         152 next Marker;
6012             }
6013 519         953 my ($segType, $del);
6014             # rewrite this segment only if we are changing a tag which is contained in its
6015             # directory (or deleting '*', in which case we need to identify the segment type)
6016 519   100     2473 while (exists $$editDirs{$markerName} or $$delGroup{'*'}) {
6017 131 100       931 if ($marker == 0xe0) { # APP0 (JFIF, CIFF)
    100          
    50          
    100          
    50          
    100          
    100          
    100          
    100          
6018 31 100       330 if ($$segDataPt =~ /^JFIF\0/) {
    100          
    100          
6019 11         31 $segType = 'JFIF';
6020 11 50       57 $$delGroup{JFIF} and $del = 1, last;
6021 11 50       47 last unless $$editDirs{JFIF};
6022 11         65 SetByteOrder('MM');
6023 11         52 my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
6024 11         103 my %dirInfo = (
6025             DataPt => $segDataPt,
6026             DataPos => $segPos,
6027             DataLen => $length,
6028             DirStart => 5, # directory starts after identifier
6029             DirLen => $length-5,
6030             Parent => $markerName,
6031             );
6032 11         69 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6033 11 50 33     124 if (defined $newData and length $newData) {
6034 11         66 $$segDataPt = "JFIF\0" . $newData;
6035             }
6036             } elsif ($$segDataPt =~ /^JFXX\0\x10/) {
6037 8         27 $segType = 'JFXX';
6038 8 100       40 $$delGroup{JFIF} and $del = 1;
6039             } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) {
6040 6         22 $segType = 'CIFF';
6041 6 50       24 $$delGroup{CIFF} and $del = 1, last;
6042 6 100       27 last unless $$editDirs{CIFF};
6043 4         14 my $newData = '';
6044 4         32 my %dirInfo = (
6045             RAF => new File::RandomAccess($segDataPt),
6046             OutFile => \$newData,
6047             );
6048 4         39 require Image::ExifTool::CanonRaw;
6049 4 50       34 if (Image::ExifTool::CanonRaw::WriteCRW($self, \%dirInfo) > 0) {
6050 4 50       14 if (length $newData) {
6051 4         16 $$segDataPt = $newData;
6052             } else {
6053 0         0 undef $segDataPt;
6054 0         0 $del = 1; # delete this segment
6055             }
6056             }
6057             }
6058             } elsif ($marker == 0xe1) { # APP1 (EXIF, XMP)
6059             # check for EXIF data
6060 73 100 0     1481 if ($$segDataPt =~ /^(.{0,4})$exifAPP1hdr/is) {
    50          
    0          
6061 52         172 my $hdrLen = length $exifAPP1hdr;
6062 52 50       477 if (length $1) {
    50          
6063 0         0 $hdrLen += length $1;
6064 0         0 $self->Error('Unknown garbage at start of EXIF segment',1);
6065             } elsif ($$segDataPt !~ /^Exif\0/) {
6066 0         0 $self->Error('Incorrect EXIF segment identifier',1);
6067             }
6068 52         1059 $segType = 'EXIF';
6069 52 100       758 last unless $$editDirs{IFD0};
6070             # add this data to the combined data if it exists
6071 51 50       238 if (defined $combinedSegData) {
6072 0         0 $combinedSegData .= substr($$segDataPt,$hdrLen);
6073 0         0 $segDataPt = \$combinedSegData;
6074 0         0 $segPos = $firstSegPos;
6075 0         0 $length = length $combinedSegData; # update length
6076             }
6077             # peek ahead to see if the next segment is extended EXIF
6078 51 50       242 if ($dirOrder[0] eq 'ExtendedEXIF') {
6079             # initialize combined data if necessary
6080 0 0       0 unless (defined $combinedSegData) {
6081 0         0 $combinedSegData = $$segDataPt;
6082 0         0 $firstSegPos = $segPos;
6083 0         0 $self->Warn('File contains multi-segment EXIF',1);
6084             }
6085 0         0 next Marker; # get the next segment to combine
6086             }
6087 51 50       205 $doneDir{IFD0} and $self->Warn('Multiple APP1 EXIF records');
6088 51         164 $doneDir{IFD0} = 1;
6089             # check del groups now so we can change byte order in one step
6090 51 100 66     404 if ($$delGroup{IFD0} or $$delGroup{EXIF}) {
6091 1         3 delete $doneDir{IFD0}; # delete so we will create a new one
6092 1         3 $del = 1;
6093 1         5 last;
6094             }
6095             # rewrite EXIF as if this were a TIFF file in memory
6096 50         555 my %dirInfo = (
6097             DataPt => $segDataPt,
6098             DataPos => -$hdrLen, # (remember: relative to Base!)
6099             DirStart => $hdrLen,
6100             Base => $segPos + $hdrLen,
6101             Parent => $markerName,
6102             DirName => 'IFD0',
6103             );
6104             # write new EXIF data to memory
6105 50         907 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
6106 50         442 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
6107 50 50       253 if (defined $buff) {
6108 50         162 undef $$segDataPt; # free the old buffer
6109 50         151 $segDataPt = \$buff;
6110             } else {
6111 0 0       0 last Marker unless $self->Options('IgnoreMinorErrors');
6112             }
6113             # delete segment if IFD contains no entries
6114 50 100       248 length $$segDataPt or $del = 1, last;
6115 46 50       234 if (length($$segDataPt) + length($exifAPP1hdr) > $maxSegmentLen) {
6116 0 0       0 if ($self->Options('NoMultiExif')) {
6117 0         0 $self->Error('EXIF is too large for JPEG segment');
6118             } else {
6119 0         0 $self->Warn('Writing multi-segment EXIF',1);
6120             }
6121             }
6122             # switch to buffered output if required
6123 46 100 66     432 if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer}) and not $oldOutfile) {
      66        
6124 1         4 $writeBuffer = '';
6125 1         3 $oldOutfile = $outfile;
6126 1         4 $outfile = \$writeBuffer;
6127             # must account for segment, EXIF and TIFF headers
6128 1 50       6 $$self{PREVIEW_INFO}{Fixup}{Start} += 18 if $$self{PREVIEW_INFO};
6129 1 50       6 $$self{LeicaTrailer}{Fixup}{Start} += 18 if $$self{LeicaTrailer};
6130             }
6131             # write as multi-segment
6132 46         307 my $n = WriteMultiSegment($outfile, $marker, $exifAPP1hdr, $segDataPt, 'EXIF');
6133 46 50 33     402 if (not $n) {
    50          
6134 0         0 $err = 1;
6135             } elsif ($n > 1 and $oldOutfile) {
6136             # (punt on this because updating the pointers would be a real pain)
6137 0         0 $self->Error("Can't write multi-segment EXIF with external pointers");
6138             }
6139 46         152 undef $combinedSegData;
6140 46         114 undef $$segDataPt;
6141 46         459 next Marker;
6142             # check for XMP data
6143             } elsif ($$segDataPt =~ /^($xmpAPP1hdr|$xmpExtAPP1hdr)/) {
6144 21         80 $segType = 'XMP';
6145 21 50       94 $$delGroup{XMP} and $del = 1, last;
6146 21   100     136 $doneDir{XMP} = ($doneDir{XMP} || 0) + 1;
6147 21 100       94 last unless $$editDirs{XMP};
6148 14 100       65 if ($doneDir{XMP} + $dirCount{XMP} > 1) {
6149             # must assemble all XMP segments before writing
6150 3         7 my ($guid, $extXMP);
6151 3 100       31 if ($$segDataPt =~ /^$xmpExtAPP1hdr/) {
6152             # save extended XMP data
6153 2 50       14 if (length $$segDataPt < 75) {
6154 0         0 $extendedXMP{Error} = 'Truncated data';
6155             } else {
6156 2         11 my ($size, $off) = unpack('x67N2', $$segDataPt);
6157 2         9 $guid = substr($$segDataPt, 35, 32);
6158 2 50       9 if ($guid =~ /[^A-Za-z0-9]/) { # (technically, should be uppercase)
6159 0         0 $extendedXMP{Error} = 'Invalid GUID';
6160             } else {
6161             # remember extended data for each GUID
6162 2         8 $extXMP = $extendedXMP{$guid};
6163 2 100       7 if ($extXMP) {
6164 1 50       7 $size == $$extXMP{Size} or $extendedXMP{Error} = 'Inconsistent size';
6165             } else {
6166 1         9 $extXMP = $extendedXMP{$guid} = { };
6167             }
6168 2         6 $$extXMP{Size} = $size;
6169 2         9 $$extXMP{$off} = substr($$segDataPt, 75);
6170             }
6171             }
6172             } else {
6173             # save all main XMP segments (should normally be only one)
6174 1 50       7 $extendedXMP{Main} = [] unless $extendedXMP{Main};
6175 1         3 push @{$extendedXMP{Main}}, substr($$segDataPt, length $xmpAPP1hdr);
  1         5  
6176             }
6177             # continue processing only if we have read all the segments
6178 3 100       15 next Marker if $dirCount{XMP};
6179             # reconstruct an XMP super-segment
6180 1         8 $$segDataPt = $xmpAPP1hdr;
6181 1         4 my $goodGuid = '';
6182 1         2 foreach (@{$extendedXMP{Main}}) {
  1         5  
6183             # get the HasExtendedXMP GUID if it exists
6184 1 50       10 if (/:HasExtendedXMP\s*(=\s*['"]|>)(\w{32})/) {
6185             # warn of subsequent XMP blocks specifying a different
6186             # HasExtendedXMP (have never seen this)
6187 1 50 33     6 if ($goodGuid and $goodGuid ne $2) {
6188 0         0 $self->WarnOnce('Multiple XMP segments specifying different extended XMP GUID');
6189             }
6190 1         4 $goodGuid = $2; # GUID for the standard extended XMP
6191             }
6192 1         4 $$segDataPt .= $_;
6193             }
6194             # GUID of the extended XMP that we want to read
6195 1   50     6 my $readGuid = $$self{OPTIONS}{ExtendedXMP} || 0;
6196 1 50       10 $readGuid = $goodGuid if $readGuid eq '1';
6197 1         6 foreach $guid (sort keys %extendedXMP) {
6198 2 100       8 next unless length $guid == 32; # ignore other (internal) keys
6199 1 50 33     7 if ($guid ne $readGuid and $readGuid ne '2') {
6200 0 0       0 my $non = $guid eq $goodGuid ? '' : 'non-';
6201 0         0 $self->Warn("Ignored ${non}standard extended XMP (GUID $guid)");
6202 0         0 next;
6203             }
6204 1 50       4 if ($guid ne $goodGuid) {
6205 0         0 $self->Warn("Reading non-standard extended XMP (GUID $guid)");
6206             }
6207 1         3 $extXMP = $extendedXMP{$guid};
6208 1 50       6 next unless ref $extXMP eq 'HASH'; # (just to be safe)
6209 1         4 my $size = $$extXMP{Size};
6210 1         3 my (@offsets, $off);
6211 1         8 for ($off=0; $off<$size; ) {
6212 2 50       10 last unless defined $$extXMP{$off};
6213 2         5 push @offsets, $off;
6214 2         5 $off += length $$extXMP{$off};
6215             }
6216 1 50       5 if ($off == $size) {
6217             # add all XMP to super-segment
6218 1         7 $$segDataPt .= $$extXMP{$_} foreach @offsets;
6219             } else {
6220 0         0 $self->Error("Incomplete extended XMP (GUID $guid)", 1);
6221             }
6222             }
6223 1 50       14 $self->Error("$extendedXMP{Error} in extended XMP", 1) if $extendedXMP{Error};
6224             }
6225 12         38 my $start = length $xmpAPP1hdr;
6226 12         60 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
6227 12         129 my %dirInfo = (
6228             DataPt => $segDataPt,
6229             DirStart => $start,
6230             Parent => $markerName,
6231             # limit XMP size and create extended XMP if necessary
6232             MaxDataLen => $maxXMPLen - length($xmpAPP1hdr),
6233             );
6234 12         67 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6235 12 100       59 if (defined $newData) {
6236 9         28 undef %extendedXMP;
6237 9 100       33 if (length $newData) {
6238             # write multi-segment XMP (XMP plus extended XMP if necessary)
6239             WriteMultiXMP($self, $outfile, \$newData, $dirInfo{ExtendedXMP},
6240 7 50       50 $dirInfo{ExtendedGUID}) or $err = 1;
6241 7         25 undef $$segDataPt; # free the old buffer
6242 7         49 next Marker;
6243             } else {
6244 2         7 $$segDataPt = ''; # delete the XMP
6245             }
6246             } else {
6247 3 50       15 $verbose and print $out " [XMP rewritten with no changes]\n";
6248 3 50       16 if ($doneDir{XMP} > 1) {
6249             # re-write original multi-segment XMP
6250 0         0 my ($dat, $guid, $extXMP, $off);
6251 0         0 foreach $dat (@{$extendedXMP{Main}}) { # main XMP
  0         0  
6252 0 0       0 next unless length $dat;
6253 0         0 $s = pack('n', length($xmpAPP1hdr) + length($dat) + 2);
6254 0 0       0 Write($outfile, $hdr, $s, $xmpAPP1hdr, $dat) or $err = 1;
6255             }
6256 0         0 foreach $guid (sort keys %extendedXMP) { # extended XMP
6257 0 0       0 next unless length $guid == 32;
6258 0         0 $extXMP = $extendedXMP{$guid};
6259 0 0       0 next unless ref $extXMP eq 'HASH';
6260 0 0       0 my $size = $$extXMP{Size} or next;
6261 0         0 for ($off=0; defined $$extXMP{$off}; $off += length $$extXMP{$off}) {
6262 0         0 $s = pack('n', length($xmpExtAPP1hdr) + length($$extXMP{$off}) + 42);
6263             Write($outfile, $hdr, $s, $xmpExtAPP1hdr, $guid,
6264 0 0       0 pack('N2', $size, $off), $$extXMP{$off}) or $err = 1;
6265             }
6266             }
6267 0         0 undef $$segDataPt; # free the old buffer
6268 0         0 undef %extendedXMP;
6269 0         0 next Marker;
6270             }
6271             # continue on to re-write original single-segment XMP
6272             }
6273 5 100       40 $del = 1 unless length $$segDataPt;
6274             } elsif ($$segDataPt =~ /^http/ or $$segDataPt =~ /
6275 0         0 $self->Warn('Ignored APP1 XMP segment with non-standard header', 1);
6276             }
6277             } elsif ($marker == 0xe2) { # APP2 (ICC Profile, FPXR, MPF)
6278 0 0 0     0 if ($$segDataPt =~ /^ICC_PROFILE\0/ and $length >= 14) {
    0          
    0          
6279 0         0 $segType = 'ICC_Profile';
6280 0 0       0 $$delGroup{ICC_Profile} and $del = 1, last;
6281             # must concatenate blocks of profile
6282 0         0 my $chunkNum = Get8u($segDataPt, 12);
6283 0         0 my $chunksTot = Get8u($segDataPt, 13);
6284 0 0       0 if (defined $iccChunksTotal) {
6285             # abort parsing ICC_Profile if the total chunk count is inconsistent
6286 0 0 0     0 if ($chunksTot != $iccChunksTotal and defined $iccChunkCount) {
6287             # an error because the accumulated profile data will be lost
6288 0         0 $self->Error('Inconsistent ICC_Profile chunk count', 1);
6289 0         0 undef $iccChunkCount; # abort ICC_Profile parsing
6290 0         0 undef $chunkNum; # avoid 2nd warning below
6291 0         0 ++$$self{CHANGED}; # we are deleting the bad chunks before this one
6292             }
6293             } else {
6294 0         0 $iccChunkCount = 0;
6295 0         0 $iccChunksTotal = $chunksTot;
6296 0 0       0 $self->Warn('ICC_Profile chunk count is zero') if !$chunksTot;
6297             }
6298 0 0       0 if (defined $iccChunkCount) {
    0          
6299             # save this chunk
6300 0 0       0 if (defined $iccChunk[$chunkNum]) {
6301 0         0 $self->Warn("Duplicate ICC_Profile chunk number $chunkNum");
6302 0         0 $iccChunk[$chunkNum] .= substr($$segDataPt, 14);
6303             } else {
6304 0         0 $iccChunk[$chunkNum] = substr($$segDataPt, 14);
6305             }
6306             # continue accumulating chunks unless we have all of them
6307 0 0       0 next Marker unless ++$iccChunkCount >= $iccChunksTotal;
6308 0         0 undef $iccChunkCount; # prevent reprocessing
6309 0         0 $doneDir{ICC_Profile} = 1;
6310             # combine the ICC_Profile chunks
6311 0         0 my $icc_profile = '';
6312 0   0     0 defined $_ and $icc_profile .= $_ foreach @iccChunk;
6313 0         0 undef @iccChunk; # free memory
6314 0         0 $segDataPt = \$icc_profile;
6315 0         0 $length = length $icc_profile;
6316 0         0 my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
6317 0         0 my %dirInfo = (
6318             DataPt => $segDataPt,
6319             DataPos => $segPos + 14,
6320             DataLen => $length,
6321             DirStart => 0,
6322             DirLen => $length,
6323             Parent => $markerName,
6324             );
6325 0         0 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6326 0 0       0 if (defined $newData) {
6327 0         0 undef $$segDataPt; # free the old buffer
6328 0         0 $segDataPt = \$newData;
6329             }
6330 0 0       0 length $$segDataPt or $del = 1, last;
6331             # write as ICC multi-segment
6332 0 0       0 WriteMultiSegment($outfile, $marker, "ICC_PROFILE\0", $segDataPt, 'ICC') or $err = 1;
6333 0         0 undef $$segDataPt;
6334 0         0 next Marker;
6335             } elsif (defined $chunkNum) {
6336 0         0 $self->WarnOnce('Invalid or extraneous ICC_Profile chunk(s)');
6337             # fall through to preserve this extra profile...
6338             }
6339             } elsif ($$segDataPt =~ /^FPXR\0/) {
6340 0         0 $segType = 'FPXR';
6341 0 0       0 $$delGroup{FlashPix} and $del = 1;
6342             } elsif ($$segDataPt =~ /^MPF\0/) {
6343 0         0 $segType = 'MPF';
6344 0 0       0 $$delGroup{MPF} and $del = 1;
6345             }
6346             } elsif ($marker == 0xe3) { # APP3 (Kodak Meta)
6347 1 50       9 if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) {
6348 1         4 $segType = 'Kodak Meta';
6349 1 50       5 $$delGroup{Meta} and $del = 1, last;
6350 1 50       5 $doneDir{Meta} and $self->Warn('Multiple APP3 Meta segments');
6351 1         2 $doneDir{Meta} = 1;
6352 1 50       5 last unless $$editDirs{Meta};
6353             # rewrite Meta IFD as if this were a TIFF file in memory
6354 1         8 my %dirInfo = (
6355             DataPt => $segDataPt,
6356             DataPos => -6, # (remember: relative to Base!)
6357             DirStart => 6,
6358             Base => $segPos + 6,
6359             Parent => $markerName,
6360             DirName => 'Meta',
6361             );
6362             # write new data to memory
6363 1         7 my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta');
6364 1         8 my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
6365 1 50       5 if (defined $buff) {
6366             # update segment with new data
6367 1         5 $$segDataPt = substr($$segDataPt,0,6) . $buff;
6368             } else {
6369 0 0       0 last Marker unless $self->Options('IgnoreMinorErrors');
6370             }
6371             # delete segment if IFD contains no entries
6372 1 50       9 $del = 1 unless length($$segDataPt) > 6;
6373             }
6374             } elsif ($marker == 0xe5) { # APP5 (Ricoh RMETA)
6375 0 0       0 if ($$segDataPt =~ /^RMETA\0/) {
6376 0         0 $segType = 'Ricoh RMETA';
6377 0 0       0 $$delGroup{RMETA} and $del = 1;
6378             }
6379             } elsif ($marker == 0xec) { # APP12 (Ducky)
6380 1 50       11 if ($$segDataPt =~ /^Ducky/) {
6381 1         3 $segType = 'Ducky';
6382 1 50       7 $$delGroup{Ducky} and $del = 1, last;
6383 1 50       7 $doneDir{Ducky} and $self->Warn('Multiple APP12 Ducky segments');
6384 1         3 $doneDir{Ducky} = 1;
6385 1 50       6 last unless $$editDirs{Ducky};
6386 1         4 my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
6387 1         11 my %dirInfo = (
6388             DataPt => $segDataPt,
6389             DataPos => $segPos,
6390             DataLen => $length,
6391             DirStart => 5, # directory starts after identifier
6392             DirLen => $length-5,
6393             Parent => $markerName,
6394             );
6395 1         5 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6396 1 50       7 if (defined $newData) {
6397 1         3 undef $$segDataPt; # free the old buffer
6398             # add header to new segment unless empty
6399 1 50       6 $newData = 'Ducky' . $newData if length $newData;
6400 1         3 $segDataPt = \$newData;
6401             }
6402 1 50       8 $del = 1 unless length $$segDataPt;
6403             }
6404             } elsif ($marker == 0xed) { # APP13 (Photoshop)
6405 9 100       164 if ($$segDataPt =~ /^$psAPP13hdr/) {
6406 8         28 $segType = 'Photoshop';
6407             # add this data to the combined data if it exists
6408 8 50       33 if (defined $combinedSegData) {
6409 0         0 $combinedSegData .= substr($$segDataPt,length($psAPP13hdr));
6410 0         0 $segDataPt = \$combinedSegData;
6411 0         0 $length = length $combinedSegData; # update length
6412             }
6413             # peek ahead to see if the next segment is photoshop data too
6414 8 50       36 if ($dirOrder[0] eq 'Photoshop') {
6415             # initialize combined data if necessary
6416 0 0       0 $combinedSegData = $$segDataPt unless defined $combinedSegData;
6417 0         0 next Marker; # get the next segment to combine
6418             }
6419 8 50       38 if ($doneDir{Photoshop}) {
6420 0         0 $self->Warn('Multiple Photoshop records');
6421             # only rewrite the first Photoshop segment when deleting this group
6422             # (to remove multiples when deleting and adding back in one step)
6423 0 0       0 $$delGroup{Photoshop} and $del = 1, last;
6424             }
6425 8         27 $doneDir{Photoshop} = 1;
6426             # process APP13 Photoshop record
6427 8         42 my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
6428 8         76 my %dirInfo = (
6429             DataPt => $segDataPt,
6430             DataPos => $segPos,
6431             DataLen => $length,
6432             DirStart => 14, # directory starts after identifier
6433             DirLen => $length-14,
6434             Parent => $markerName,
6435             );
6436 8         46 my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
6437 8 50       50 if (defined $newData) {
6438 8         30 undef $$segDataPt; # free the old buffer
6439 8         30 $segDataPt = \$newData;
6440             }
6441 8 100       48 length $$segDataPt or $del = 1, last;
6442             # write as multi-segment
6443 6 50       30 WriteMultiSegment($outfile, $marker, $psAPP13hdr, $segDataPt) or $err = 1;
6444 6         17 undef $combinedSegData;
6445 6         22 undef $$segDataPt;
6446 6         38 next Marker;
6447             }
6448             } elsif ($marker == 0xee) { # APP14 (Adobe)
6449 4 50       29 if ($$segDataPt =~ /^Adobe/) {
6450 4         13 $segType = 'Adobe';
6451             # delete it and replace it later if editing
6452 4 50 33     34 if ($$delGroup{Adobe} or $$editDirs{Adobe}) {
6453 0         0 $del = 1;
6454 0         0 undef $doneDir{Adobe}; # so we can add it back again above
6455             }
6456             }
6457             } elsif ($marker == 0xfe) { # COM (JPEG comment)
6458 4         14 my $newComment;
6459 4 50       18 unless ($doneDir{COM}) {
6460 4         14 $doneDir{COM} = 1;
6461 4 100 100     33 unless ($$delGroup{File} and $$delGroup{File} != 2) {
6462 3         22 my $tagInfo = $Image::ExifTool::Extra{Comment};
6463 3         15 my $nvHash = $self->GetNewValueHash($tagInfo);
6464 3         8 my $val = $segData;
6465 3         13 $val =~ s/\0+$//; # allow for stupid software that adds NULL terminator
6466 3 50 33     13 if ($self->IsOverwriting($nvHash, $val) or $$delGroup{File}) {
6467 3         13 $newComment = $self->GetNewValue($nvHash);
6468             } else {
6469 0         0 delete $$editDirs{COM}; # we aren't editing COM after all
6470 0         0 last;
6471             }
6472             }
6473             }
6474 4         27 $self->VerboseValue('- Comment', $$segDataPt);
6475 4 100       15 if (defined $newComment) {
6476             # write out the comments
6477 2         12 $self->VerboseValue('+ Comment', $newComment);
6478 2 50       9 WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1;
6479             } else {
6480 2 50       9 $verbose and print $out " Deleting COM segment\n";
6481             }
6482 4         9 ++$$self{CHANGED}; # increment the changed flag
6483 4         8 undef $segDataPt; # don't write existing comment
6484             }
6485 53         164 last; # didn't want to loop anyway
6486             }
6487              
6488             # delete necessary segments (including unknown segments if deleting all)
6489 458 100 100     2135 if ($del or ($$delGroup{'*'} and not $segType and $marker>=0xe0 and $marker<=0xef)) {
      100        
      100        
      100        
6490 13 100       46 $segType = 'unknown' unless $segType;
6491 13 50       46 $verbose and print $out " Deleting $markerName $segType segment\n";
6492 13         42 ++$$self{CHANGED};
6493 13         43 next Marker;
6494             }
6495             # write out this segment if $segDataPt is still defined
6496 445 100 66     1934 if (defined $segDataPt and defined $$segDataPt) {
6497             # write the data for this record (the data could have been
6498             # modified, so recalculate the length word)
6499 441         874 my $size = length($$segDataPt);
6500 441 50       1187 if ($size > $maxSegmentLen) {
6501 0 0       0 $segType or $segType = 'Unknown';
6502 0         0 $self->Error("$segType $markerName segment too large! ($size bytes)");
6503 0         0 $err = 1;
6504             } else {
6505 441         1345 $s = pack('n', length($$segDataPt) + 2);
6506 441 50       1724 Write($outfile, $hdr, $s, $$segDataPt) or $err = 1;
6507             }
6508 441         1124 undef $$segDataPt; # free the buffer
6509 441         1033 undef $segDataPt;
6510             }
6511             }
6512             # make sure the ICC_Profile was complete
6513 110 50       476 $self->Error('Incomplete ICC_Profile record', 1) if defined $iccChunkCount;
6514 110 100       444 pop @$path if @$path > $pn;
6515             # if oldOutfile is still set, there was an error copying the JPEG
6516 110 50       384 $oldOutfile and return 0;
6517 110 50       398 if ($rtnVal) {
6518             # add any new trailers we are creating
6519 110         630 my $trailPt = $self->AddNewTrailers();
6520 110 100 50     472 Write($outfile, $$trailPt) or $err = 1 if $trailPt;
6521             }
6522             # set return value to -1 if we only had a write error
6523 110 50 33     698 $rtnVal = -1 if $rtnVal and $err;
6524 110 50 66     505 if ($creatingEXV and $rtnVal > 0 and not $$self{CHANGED}) {
      66        
6525 0         0 $self->Error('Nothing written');
6526 0         0 $rtnVal = -1;
6527             }
6528 110         1268 return $rtnVal;
6529             }
6530              
6531             #------------------------------------------------------------------------------
6532             # Validate an image for writing
6533             # Inputs: 0) ExifTool object reference, 1) raw value reference
6534             # Returns: error string or undef on success
6535             sub CheckImage($$)
6536             {
6537 132     132 0 458 my ($self, $valPtr) = @_;
6538 132 100 100     1108 if (length($$valPtr) and $$valPtr!~/^\xff\xd8/ and not
      100        
6539             $self->Options('IgnoreMinorErrors'))
6540             {
6541 25         253 return '[Minor] Not a valid image';
6542             }
6543 107         921 return undef;
6544             }
6545              
6546             #------------------------------------------------------------------------------
6547             # check a value for validity
6548             # Inputs: 0) value reference, 1) format string, 2) optional count
6549             # Returns: error string, or undef on success
6550             # Notes: May modify value (if a count is specified for a string, it is null-padded
6551             # to the specified length, and floating point values are rounded to integer if required)
6552             sub CheckValue($$;$)
6553             {
6554 19010     19010 0 47126 my ($valPtr, $format, $count) = @_;
6555 19010         31529 my (@vals, $val, $n);
6556              
6557 19010 100 100     70798 if ($format eq 'string' or $format eq 'undef') {
6558 2449 100 66     10288 return undef unless $count and $count > 0;
6559 315         821 my $len = length($$valPtr);
6560 315 100       927 if ($format eq 'string') {
6561 213 100       695 $len >= $count and return 'String too long';
6562             } else {
6563 102 50       353 $len > $count and return 'Data too long';
6564             }
6565 305 100       864 if ($len < $count) {
6566 246         911 $$valPtr .= "\0" x ($count - $len);
6567             }
6568 305         1075 return undef;
6569             }
6570 16561 100 66     42905 if ($count and $count != 1) {
6571 1922         6327 @vals = split(' ',$$valPtr);
6572 1922 100 100     5127 $count < 0 and ($count = @vals or return undef);
6573             } else {
6574 14639         22786 $count = 1;
6575 14639         33082 @vals = ( $$valPtr );
6576             }
6577 16542 100       41965 if (@vals != $count) {
6578 913 100       2611 my $str = @vals > $count ? 'Too many' : 'Not enough';
6579 913         3637 return "$str values specified ($count required)";
6580             }
6581 15629         40159 for ($n=0; $n<$count; ++$n) {
6582 18574         32905 $val = shift @vals;
6583 18574 100 100     67505 if ($format =~ /^int/) {
    100 100        
6584             # make sure the value is integer
6585 17210 100       52105 unless (IsInt($val)) {
6586 3009 100       7589 if (IsHex($val)) {
6587 6         23 $val = $$valPtr = hex($val);
6588             } else {
6589             # round single floating point values to the nearest integer
6590 3003 100 100     8538 return 'Not an integer' unless IsFloat($val) and $count == 1;
6591 1266 100       5275 $val = $$valPtr = int($val + ($val < 0 ? -0.5 : 0.5));
6592             }
6593             }
6594 15473 50       48239 my $rng = $intRange{$format} or return "Bad int format: $format";
6595 15473 100       38764 return "Value below $format minimum" if $val < $$rng[0];
6596             # (allow 0xfeedfeed code as value for 16-bit pointers)
6597 15172 100 66     52075 return "Value above $format maximum" if $val > $$rng[1] and $val != 0xfeedfeed;
6598             } elsif ($format =~ /^rational/ or $format eq 'float' or $format eq 'double') {
6599             # make sure the value is a valid floating point number
6600 1343 100       4522 unless (IsFloat($val)) {
6601             # allow 'inf', 'undef' and fractional rational values
6602 263 100       1061 if ($format =~ /^rational/) {
6603 227 100 66     1081 next if $val eq 'inf' or $val eq 'undef';
6604 226 100       827 if ($val =~ m{^([-+]?\d+)/(\d+)$}) {
6605 70 50 66     412 next unless $1 < 0 and $format =~ /u$/;
6606 0         0 return 'Must be an unsigned rational';
6607             }
6608             }
6609 192         774 return 'Not a floating point number';
6610             }
6611 1080 50 66     6894 if ($format =~ /^rational\d+u$/ and $val < 0) {
6612 0         0 return 'Must be a positive number';
6613             }
6614             }
6615             }
6616 13395         35191 return undef; # success!
6617             }
6618              
6619             #------------------------------------------------------------------------------
6620             # check new value for binary data block
6621             # Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref
6622             # Returns: error string or undef (and may modify value) on success
6623             sub CheckBinaryData($$$)
6624             {
6625 11758     11758 0 26031 my ($self, $tagInfo, $valPtr) = @_;
6626 11758         27075 my $format = $$tagInfo{Format};
6627 11758 100       26521 unless ($format) {
6628 4400         8701 my $table = $$tagInfo{Table};
6629 4400 100 66     17484 if ($table and $$table{FORMAT}) {
6630 3099         7215 $format = $$table{FORMAT};
6631             } else {
6632             # use default 'int8u' unless specified
6633 1301         2845 $format = 'int8u';
6634             }
6635             }
6636 11758         16797 my $count;
6637 11758 100       35477 if ($format =~ /(.*)\[(.*)\]/) {
6638 1636         4544 $format = $1;
6639 1636         3453 $count = $2;
6640             # can't evaluate $count now because we don't know $size yet
6641 1636 50       3797 undef $count if $count =~ /\$size/;
6642             }
6643 11758         29159 return CheckValue($valPtr, $format, $count);
6644             }
6645              
6646             #------------------------------------------------------------------------------
6647             # Rename a file (with patch for Windows Unicode file names, and other problem)
6648             # Inputs: 0) ExifTool ref, 1) old name, 2) new name
6649             # Returns: true on success
6650             sub Rename($$$)
6651             {
6652 3     3 0 13 my ($self, $old, $new) = @_;
6653 3         12 my ($result, $try, $winUni);
6654              
6655 3 50       20 if ($self->EncodeFileName($old)) {
    50          
6656 0         0 $self->EncodeFileName($new, 1);
6657 0         0 $winUni = 1;
6658             } elsif ($self->EncodeFileName($new)) {
6659 0         0 $old = $_[1];
6660 0         0 $self->EncodeFileName($old, 1);
6661 0         0 $winUni = 1;
6662             }
6663 3         9 for (;;) {
6664 3 50       11 if ($winUni) {
6665 0         0 $result = eval { Win32API::File::MoveFileExW($old, $new,
  0         0  
6666             Win32API::File::MOVEFILE_REPLACE_EXISTING() |
6667             Win32API::File::MOVEFILE_COPY_ALLOWED()) };
6668             } else {
6669 3         395 $result = rename($old, $new);
6670             }
6671 3 50 33     27 last if $result or $^O ne 'MSWin32';
6672             # keep trying for up to 0.5 seconds
6673             # (patch for Windows denial-of-service susceptibility)
6674 0   0     0 $try = ($try || 1) + 1;
6675 0 0       0 last if $try > 50;
6676 0         0 select(undef,undef,undef,0.01); # sleep for 0.01 sec
6677             }
6678 3         19 return $result;
6679             }
6680              
6681             #------------------------------------------------------------------------------
6682             # Delete a file (with patch for Windows Unicode file names)
6683             # Inputs: 0) ExifTool ref, 1-N) names of files to delete
6684             # Returns: number of files deleted
6685             sub Unlink($@)
6686             {
6687 0     0 0 0 my $self = shift;
6688 0         0 my $result = 0;
6689 0         0 while (@_) {
6690 0         0 my $file = shift;
6691 0 0       0 if ($self->EncodeFileName($file)) {
6692 0 0       0 ++$result if eval { Win32API::File::DeleteFileW($file) };
  0         0  
6693             } else {
6694 0 0       0 ++$result if unlink $file;
6695             }
6696             }
6697 0         0 return $result;
6698             }
6699              
6700             #------------------------------------------------------------------------------
6701             # Set file times (Unix seconds since the epoch)
6702             # Inputs: 0) ExifTool ref, 1) file name or ref, 2) access time, 3) modification time,
6703             # 4) inode change or creation time (or undef for any time to avoid setting)
6704             # 5) flag to suppress warning
6705             # Returns: 1 on success, 0 on error
6706             my $k32SetFileTime;
6707             sub SetFileTime($$;$$$$)
6708             {
6709 0     0 0 0 my ($self, $file, $atime, $mtime, $ctime, $noWarn) = @_;
6710 0         0 my $saveFile;
6711 0         0 local *FH;
6712              
6713             # open file by name if necessary
6714 0 0       0 unless (ref $file) {
6715             # (file will be automatically closed when *FH goes out of scope)
6716 0 0       0 unless ($self->Open(\*FH, $file, '+<')) {
6717 0         0 my $success;
6718 0 0 0     0 if (defined $atime or defined $mtime) {
6719 0         0 my ($a, $m, $c) = $self->GetFileTime($file);
6720 0 0       0 $atime = $a unless defined $atime;
6721 0 0       0 $mtime = $m unless defined $mtime;
6722 0 0 0     0 $success = eval { utime($atime, $mtime, $file) } if defined $atime and defined $mtime;
  0         0  
6723             }
6724 0 0       0 $self->Warn('Error opening file for update') unless $success;
6725 0         0 return $success;
6726             }
6727 0         0 $saveFile = $file;
6728 0         0 $file = \*FH;
6729             }
6730             # on Windows, try to work around incorrect file times when daylight saving time is in effect
6731 0 0       0 if ($^O eq 'MSWin32') {
6732 0 0       0 if (not eval { require Win32::API }) {
  0 0       0  
6733 0         0 $self->WarnOnce('Install Win32::API for proper handling of Windows file times');
6734 0         0 } elsif (not eval { require Win32API::File }) {
6735 0         0 $self->WarnOnce('Install Win32API::File for proper handling of Windows file times');
6736             } else {
6737             # get Win32 handle, needed for SetFileTime
6738 0         0 my $win32Handle = eval { Win32API::File::GetOsFHandle($file) };
  0         0  
6739 0 0       0 unless ($win32Handle) {
6740 0         0 $self->Warn('Win32API::File::GetOsFHandle returned invalid handle');
6741 0         0 return 0;
6742             }
6743             # convert Unix seconds to FILETIME structs
6744 0         0 my $time;
6745 0         0 foreach $time ($atime, $mtime, $ctime) {
6746             # set to NULL if not defined (i.e. do not change)
6747 0 0       0 defined $time or $time = 0, next;
6748             # convert to 100 ns intervals since 0:00 UTC Jan 1, 1601
6749             # (89 leap years between 1601 and 1970)
6750 0         0 my $wt = ($time + (((1970-1601)*365+89)*24*3600)) * 1e7;
6751 0         0 my $hi = int($wt / 4294967296);
6752 0         0 $time = pack 'LL', int($wt - $hi * 4294967296), $hi; # pack FILETIME struct
6753             }
6754 0 0       0 unless ($k32SetFileTime) {
6755 0 0       0 return 0 if defined $k32SetFileTime;
6756 0         0 $k32SetFileTime = new Win32::API('KERNEL32', 'SetFileTime', 'NPPP', 'I');
6757 0 0       0 unless ($k32SetFileTime) {
6758 0         0 $self->Warn('Error calling Win32::API::SetFileTime');
6759 0         0 $k32SetFileTime = 0;
6760 0         0 return 0;
6761             }
6762             }
6763 0 0       0 unless ($k32SetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) {
6764 0         0 $self->Warn('Win32::API::SetFileTime returned ' . Win32::GetLastError());
6765 0         0 return 0;
6766             }
6767 0         0 return 1;
6768             }
6769             }
6770             # other OS (or Windows fallback)
6771 0 0 0     0 if (defined $atime and defined $mtime) {
6772 0         0 my $success;
6773 0         0 local $SIG{'__WARN__'} = \&SetWarning; # (this may not be necessary)
6774 0         0 for (;;) {
6775 0         0 undef $evalWarning;
6776             # (this may fail on the first try if futimes is not implemented)
6777 0         0 $success = eval { utime($atime, $mtime, $file) };
  0         0  
6778 0 0 0     0 last if $success or not defined $saveFile;
6779 0         0 close $file;
6780 0         0 $file = $saveFile;
6781 0         0 undef $saveFile;
6782             }
6783 0 0       0 unless ($noWarn) {
6784 0 0 0     0 if ($@ or $evalWarning) {
    0          
6785 0   0     0 $self->Warn(CleanWarning($@ || $evalWarning));
6786             } elsif (not $success) {
6787 0         0 $self->Warn('Error setting file time');
6788             }
6789             }
6790 0         0 return $success;
6791             }
6792 0         0 return 1; # (nothing to do)
6793             }
6794              
6795             #------------------------------------------------------------------------------
6796             # Copy data block from RAF to output file in max 64kB chunks
6797             # Inputs: 0) RAF ref, 1) outfile ref, 2) block size
6798             # Returns: 1 on success, 0 on read error, undef on write error
6799             sub CopyBlock($$$)
6800             {
6801 69     69 0 225 my ($raf, $outfile, $size) = @_;
6802 69         148 my $buff;
6803 69         132 for (;;) {
6804 122 100       364 last unless $size > 0;
6805 53 50       186 my $n = $size > 65536 ? 65536 : $size;
6806 53 50       199 $raf->Read($buff, $n) == $n or return 0;
6807 53 50       949 Write($outfile, $buff) or return undef;
6808 53         180 $size -= $n;
6809             }
6810 69         235 return 1;
6811             }
6812              
6813             #------------------------------------------------------------------------------
6814             # Copy image data from one file to another
6815             # Inputs: 0) ExifTool object reference
6816             # 1) reference to list of image data [ position, size, pad bytes ]
6817             # 2) output file ref
6818             # Returns: true on success
6819             sub CopyImageData($$$)
6820             {
6821 13     13 0 55 my ($self, $imageDataBlocks, $outfile) = @_;
6822 13         47 my $raf = $$self{RAF};
6823 13         29 my ($dataBlock, $err);
6824 13         38 my $num = @$imageDataBlocks;
6825 13 50       133 $self->VPrint(0, " Copying $num image data blocks\n") if $num;
6826 13         47 foreach $dataBlock (@$imageDataBlocks) {
6827 24         71 my ($pos, $size, $pad) = @$dataBlock;
6828 24 50       96 $raf->Seek($pos, 0) or $err = 'read', last;
6829 24         135 my $result = CopyBlock($raf, $outfile, $size);
6830 24 0       73 $result or $err = defined $result ? 'read' : 'writ';
    50          
6831             # pad if necessary
6832 24 100 50     79 Write($outfile, "\0" x $pad) or $err = 'writ' if $pad;
6833 24 50       82 last if $err;
6834             }
6835 13 50       65 if ($err) {
6836 0         0 $self->Error("Error ${err}ing image data");
6837 0         0 return 0;
6838             }
6839 13         59 return 1;
6840             }
6841              
6842             #------------------------------------------------------------------------------
6843             # Write to binary data block
6844             # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
6845             # Returns: Binary data block or undefined on error
6846             sub WriteBinaryData($$$)
6847             {
6848 14789     14789 0 28501 my ($self, $dirInfo, $tagTablePtr) = @_;
6849 14789 100       51682 $self or return 1; # allow dummy access to autoload this package
6850              
6851             # get default format ('int8u' unless specified)
6852 450 50       1254 my $dataPt = $$dirInfo{DataPt} or return undef;
6853 450   100     1685 my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
6854 450         1352 my $increment = FormatSize($defaultFormat);
6855 450 50       1159 unless ($increment) {
6856 0         0 warn "Unknown format $defaultFormat\n";
6857 0         0 return undef;
6858             }
6859             # extract data members first if necessary
6860 450         739 my @varOffsets;
6861 450 100       1330 if ($$tagTablePtr{DATAMEMBER}) {
6862 192         485 $$dirInfo{DataMember} = $$tagTablePtr{DATAMEMBER};
6863 192         496 $$dirInfo{VarFormatData} = \@varOffsets;
6864 192         878 $self->ProcessBinaryData($dirInfo, $tagTablePtr);
6865 192         478 delete $$dirInfo{DataMember};
6866 192         410 delete $$dirInfo{VarFormatData};
6867             }
6868 450   100     1575 my $dirStart = $$dirInfo{DirStart} || 0;
6869 450   66     1549 my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
6870 450 50       1732 my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
6871 450         949 my $dirName = $$dirInfo{DirName};
6872 450         775 my $varSize = 0;
6873 450         924 my @varInfo = @varOffsets;
6874 450         708 my $tagInfo;
6875 450         834 $dataPt = \$newData;
6876 450         1421 foreach $tagInfo (sort { $$a{TagID} <=> $$b{TagID} } $self->GetNewTagInfoList($tagTablePtr)) {
  645         1124  
6877 227         493 my $tagID = $$tagInfo{TagID};
6878             # evaluate conditional tags now if necessary
6879 227 100 100     1198 if (ref $$tagTablePtr{$tagID} eq 'ARRAY' or $$tagInfo{Condition}) {
6880 22         85 my $writeInfo = $self->GetTagInfo($tagTablePtr, $tagID);
6881 22 100 100     162 next unless $writeInfo and $writeInfo eq $tagInfo;
6882             }
6883             # add offsets for variable-sized tags if necessary
6884 218   100     680 while (@varInfo and $varInfo[0][0] < $tagID) {
6885 10         22 $varSize = $varInfo[0][1]; # get accumulated variable size
6886 10         31 shift @varInfo;
6887             }
6888 218         406 my $count = 1;
6889 218         437 my $format = $$tagInfo{Format};
6890 218         457 my $entry = int($tagID) * $increment + $varSize; # relative offset of this entry
6891 218 100       510 if ($format) {
6892 87 100       432 if ($format =~ /(.*)\[(.*)\]/) {
    100          
6893 36         112 $format = $1;
6894 36         86 $count = $2;
6895 36         66 my $size = $dirLen; # used in eval
6896             # evaluate count to allow count to be based on previous values
6897             #### eval Format size ($size, $self) - NOTE: %val not supported for writing
6898 36         1685 $count = eval $count;
6899 36 50       178 $@ and warn($@), next;
6900             } elsif ($format eq 'string') {
6901             # string with no specified count runs to end of block
6902 1 50       5 $count = ($dirLen > $entry) ? $dirLen - $entry : 0;
6903             }
6904             } else {
6905 131         217 $format = $defaultFormat;
6906             }
6907             # read/write using variable format if changed in Hook
6908 218 100 66     642 $format = $varInfo[0][2] if @varInfo and $varInfo[0][0] == $tagID;
6909 218         755 my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen-$entry);
6910 218 100       558 next unless defined $val;
6911 215         959 my $nvHash = $self->GetNewValueHash($tagInfo, $$self{CUR_WRITE_GROUP});
6912 215 100       728 next unless $self->IsOverwriting($nvHash, $val) > 0;
6913 214         571 my $newVal = $self->GetNewValue($nvHash);
6914 214 100       534 next unless defined $newVal; # can't delete from a binary table
6915             # update DataMember with new value if necessary
6916 213 100       562 $$self{$$tagInfo{DataMember}} = $newVal if $$tagInfo{DataMember};
6917             # only write masked bits if specified
6918 213         443 my $mask = $$tagInfo{Mask};
6919 213 100       461 $newVal = (($newVal << $$tagInfo{BitShift}) & $mask) | ($val & ~$mask) if $mask;
6920             # set the size
6921 213 50 33     584 if ($$tagInfo{DataTag} and not $$tagInfo{IsOffset}) {
6922 0 0       0 warn 'Internal error' unless $newVal == 0xfeedfeed;
6923 0         0 my $data = $self->GetNewValue($$tagInfo{DataTag});
6924 0 0       0 $newVal = length($data) if defined $data;
6925 0   0     0 my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u';
6926 0 0 0     0 if ($format =~ /^int16/ and $newVal > 0xffff) {
6927 0         0 $self->Error("$$tagInfo{DataTag} is too large (64 kB max. for this file)");
6928             }
6929             }
6930 213         575 my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $entry);
6931 213 50       499 if (defined $rtnVal) {
6932 213         1207 $self->VerboseValue("- $dirName:$$tagInfo{Name}", $val);
6933 213         719 $self->VerboseValue("+ $dirName:$$tagInfo{Name}", $newVal);
6934 213         585 ++$$self{CHANGED};
6935             }
6936             }
6937             # add necessary fixups for any offsets
6938 450 50 66     1455 if ($$tagTablePtr{IS_OFFSET} and $$dirInfo{Fixup}) {
6939 1         2 $varSize = 0;
6940 1         3 @varInfo = @varOffsets;
6941 1         2 my $fixup = $$dirInfo{Fixup};
6942 1         2 my $tagID;
6943 1         2 foreach $tagID (@{$$tagTablePtr{IS_OFFSET}}) {
  1         4  
6944 1 50       4 $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID) or next;
6945 1   33     6 while (@varInfo and $varInfo[0][0] < $tagID) {
6946 0         0 $varSize = $varInfo[0][1];
6947 0         0 shift @varInfo;
6948             }
6949 1         2 my $entry = $tagID * $increment + $varSize; # (no offset to dirStart for new dir data)
6950 1 50       19 next unless $entry <= $dirLen - 4;
6951             # (Ricoh has 16-bit preview image offsets, so can't just assume int32u)
6952 0   0     0 my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u';
6953 0         0 my $offset = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry);
6954             # ignore if offset is zero (eg. Ricoh DNG uses this to indicate no preview)
6955 0 0       0 next unless $offset;
6956 0         0 $fixup->AddFixup($entry, $$tagInfo{DataTag}, $format);
6957             # handle the preview image now if this is a JPEG file
6958             next unless $$self{FILE_TYPE} eq 'JPEG' and $$tagInfo{DataTag} and
6959 0 0 0     0 $$tagInfo{DataTag} eq 'PreviewImage' and defined $$tagInfo{OffsetPair};
      0        
      0        
6960             # NOTE: here we assume there are no var-sized tags between the
6961             # OffsetPair tags. If this ever becomes possible we must recalculate
6962             # $varSize for the OffsetPair tag here!
6963 0         0 $entry = $$tagInfo{OffsetPair} * $increment + $varSize;
6964 0         0 my $size = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry);
6965 0         0 my $previewInfo = $$self{PREVIEW_INFO};
6966             $previewInfo or $previewInfo = $$self{PREVIEW_INFO} = {
6967 0 0       0 Fixup => new Image::ExifTool::Fixup,
6968             };
6969             # set flag indicating we are using short pointers
6970 0 0       0 $$previewInfo{IsShort} = 1 unless $format eq 'int32u';
6971 0 0 0     0 $$previewInfo{Absolute} = 1 if $$tagInfo{IsOffset} and $$tagInfo{IsOffset} eq '3';
6972             # get the value of the Composite::PreviewImage tag
6973 0         0 $$previewInfo{Data} = $self->GetNewValue(GetCompositeTagInfo('PreviewImage'));
6974 0 0       0 unless (defined $$previewInfo{Data}) {
6975 0 0 0     0 if ($offset >= 0 and $offset + $size <= $$dirInfo{DataLen}) {
6976 0         0 $$previewInfo{Data} = substr(${$$dirInfo{DataPt}},$offset,$size);
  0         0  
6977             } else {
6978 0         0 $$previewInfo{Data} = 'LOAD_PREVIEW'; # flag to load preview later
6979             }
6980             }
6981             }
6982             }
6983             # write any necessary SubDirectories
6984 450 100       1278 if ($$tagTablePtr{IS_SUBDIR}) {
6985 12         47 $varSize = 0;
6986 12         49 @varInfo = @varOffsets;
6987 12         27 my $tagID;
6988 12         33 foreach $tagID (@{$$tagTablePtr{IS_SUBDIR}}) {
  12         47  
6989 13         61 my $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID);
6990 13 100       77 next unless defined $tagInfo;
6991 4   33     23 while (@varInfo and $varInfo[0][0] < $tagID) {
6992 0         0 $varSize = $varInfo[0][1];
6993 0         0 shift @varInfo;
6994             }
6995 4         13 my $entry = int($tagID) * $increment + $varSize;
6996 4 50       15 last if $entry >= $dirLen;
6997             # get value for Condition if necessary
6998 4 50       17 unless ($tagInfo) {
6999 0         0 my $more = $dirLen - $entry;
7000 0 0       0 $more = 128 if $more > 128;
7001 0         0 my $v = substr($newData, $entry, $more);
7002 0         0 $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID, \$v);
7003 0 0       0 next unless $tagInfo;
7004             }
7005 4 50       17 next unless $$tagInfo{SubDirectory}; # (just to be safe)
7006 4         22 my %subdirInfo = ( DataPt => \$newData, DirStart => $entry );
7007 4         17 my $subTablePtr = GetTagTable($$tagInfo{SubDirectory}{TagTable});
7008 4         37 my $dat = $self->WriteDirectory(\%subdirInfo, $subTablePtr);
7009 4 50 33     43 substr($newData, $entry) = $dat if defined $dat and length $dat;
7010             }
7011             }
7012 450         1595 return $newData;
7013             }
7014              
7015             #------------------------------------------------------------------------------
7016             # Write TIFF as a directory
7017             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
7018             # Returns: New directory data or undefined on error
7019             sub WriteTIFF($$$)
7020             {
7021 111     111 0 441 my ($self, $dirInfo, $tagTablePtr) = @_;
7022 111 50       448 $self or return 1; # allow dummy access
7023 111         363 my $buff = '';
7024 111         411 $$dirInfo{OutFile} = \$buff;
7025 111 50       678 return $buff if $self->ProcessTIFF($dirInfo, $tagTablePtr) > 0;
7026 0           return undef;
7027             }
7028              
7029             1; # end
7030              
7031             __END__