File Coverage

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