File Coverage

blib/lib/Image/ExifTool/WriteIPTC.pl
Criterion Covered Total %
statement 274 353 77.6
branch 165 248 66.5
condition 79 132 59.8
subroutine 9 11 81.8
pod 0 10 0.0
total 527 754 69.8


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: WriteIPTC.pl
3             #
4             # Description: Write IPTC meta information
5             #
6             # Revisions: 12/15/2004 - P. Harvey Created
7             #------------------------------------------------------------------------------
8              
9             package Image::ExifTool::IPTC;
10              
11 21     21   184 use strict;
  21         75  
  21         93456  
12              
13             # mandatory IPTC tags for each record
14             my %mandatory = (
15             1 => {
16             0 => 4, # EnvelopeRecordVersion
17             },
18             2 => {
19             0 => 4, # ApplicationRecordVersion
20             },
21             3 => {
22             0 => 4, # NewsPhotoVersion
23             },
24             );
25              
26             # manufacturer strings for IPTCPictureNumber
27             my %manufacturer = (
28             1 => 'Associated Press, USA',
29             2 => 'Eastman Kodak Co, USA',
30             3 => 'Hasselblad Electronic Imaging, Sweden',
31             4 => 'Tecnavia SA, Switzerland',
32             5 => 'Nikon Corporation, Japan',
33             6 => 'Coatsworth Communications Inc, Canada',
34             7 => 'Agence France Presse, France',
35             8 => 'T/One Inc, USA',
36             9 => 'Associated Newspapers, UK',
37             10 => 'Reuters London',
38             11 => 'Sandia Imaging Systems Inc, USA',
39             12 => 'Visualize, Spain',
40             );
41              
42             my %iptcCharsetInv = ( 'UTF8' => "\x1b%G", 'UTF-8' => "\x1b%G" );
43              
44             # ISO 2022 Character Coding Notes
45             # -------------------------------
46             # Character set designation: (0x1b I F, or 0x1b I I F)
47             # Initial character 0x1b (ESC)
48             # Intermediate character I:
49             # 0x28 ('(') - G0, 94 chars
50             # 0x29 (')') - G1, 94 chars
51             # 0x2a ('*') - G2, 94 chars
52             # 0x2b ('+') - G3, 94 chars
53             # 0x2c (',') - G1, 96 chars
54             # 0x2d ('-') - G2, 96 chars
55             # 0x2e ('.') - G3, 96 chars
56             # 0x24 I ('$I') - multiple byte graphic sets (I from above)
57             # I 0x20 ('I ') - dynamically redefinable character sets
58             # Final character:
59             # 0x30 - 0x3f = private character set
60             # 0x40 - 0x7f = standardized character set
61             # Character set invocation:
62             # G0 : SI = 0x15
63             # G1 : SO = 0x14, LS1R = 0x1b 0x7e ('~')
64             # G2 : LS2 = 0x1b 0x6e ('n'), LS2R = 0x1b 0x7d ('}')
65             # G3 : LS3 = 0x1b 0x6f ('o'), LS3R = 0x1b 0x7c ('|')
66             # (the locking shift "R" codes shift into 0x80-0xff space)
67             # Single character invocation:
68             # G2 : SS2 = 0x1b 0x8e (or 0x4e in 7-bit)
69             # G3 : SS3 = 0x1b 0x8f (or 0x4f in 7-bit)
70             # Control chars (designated and invoked)
71             # C0 : 0x1b 0x21 F (0x21 = '!')
72             # C1 : 0x1b 0x22 F (0x22 = '"')
73             # Complete codes (control+graphics, designated and invoked)
74             # 0x1b 0x25 F (0x25 = '%')
75             # 0x1b 0x25 I F
76             # 0x1b 0x25 0x47 ("\x1b%G") - UTF-8
77             # 0x1b 0x25 0x40 ("\x1b%@") - return to ISO 2022
78             # -------------------------------
79              
80             #------------------------------------------------------------------------------
81             # Inverse print conversion for CodedCharacterSet
82             # Inputs: 0) value
83             sub PrintInvCodedCharset($)
84             {
85 2     2 0 7 my $val = shift;
86 2         9 my $code = $iptcCharsetInv{uc($val)};
87 2 50       10 unless ($code) {
88 0 0       0 if (($code = $val) =~ s/ESC */\x1b/ig) { # translate ESC chars
89 0         0 $code =~ s/, \x1b/\x1b/g; # remove comma separators
90 0         0 $code =~ tr/ //d; # remove spaces
91             } else {
92 0         0 warn "Bad syntax (use 'UTF8' or 'ESC X Y[, ...]')\n";
93             }
94             }
95 2         8 return $code;
96             }
97              
98             #------------------------------------------------------------------------------
99             # validate raw values for writing
100             # Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref
101             # Returns: error string or undef (and possibly changes value) on success
102             sub CheckIPTC($$$)
103             {
104 295     295 0 859 my ($et, $tagInfo, $valPtr) = @_;
105 295   50     1133 my $format = $$tagInfo{Format} || $$tagInfo{Table}{FORMAT} || '';
106 295 100       2242 if ($format =~ /^int(\d+)/) {
    50          
107 45   50     329 my $bytes = int(($1 || 0) / 8);
108 45 50 66     388 if ($bytes != 1 and $bytes != 2 and $bytes != 4) {
      66        
109 0         0 return "Can't write $bytes-byte integer";
110             }
111 45         150 my $val = $$valPtr;
112 45 100       192 unless (Image::ExifTool::IsInt($val)) {
113 4 50       17 return 'Not an integer' unless Image::ExifTool::IsHex($val);
114 0         0 $val = $$valPtr = hex($val);
115             }
116 41         108 my $n;
117 41         186 for ($n=0; $n<$bytes; ++$n) { $val >>= 8; }
  138         320  
118 41 50       141 return "Value too large for $bytes-byte format" if $val;
119             } elsif ($format =~ /^(string|digits|undef)\[?(\d+),?(\d*)\]?$/) {
120 250         1106 my ($fmt, $minlen, $maxlen) = ($1, $2, $3);
121 250         529 my $len = length $$valPtr;
122 250 100       778 if ($fmt eq 'digits') {
123 25 50       155 return 'Non-numeric characters in value' unless $$valPtr =~ /^\d*$/;
124 25 100 66     158 if ($len < $minlen and $len) {
125             # left pad with zeros if necessary
126 3         13 $$valPtr = ('0' x ($minlen - $len)) . $$valPtr;
127 3         8 $len = $minlen;
128             }
129             }
130 250 100 66     1318 if (defined $minlen and $fmt ne 'string') { # (must truncate strings later, after recoding)
131 25 50       100 $maxlen or $maxlen = $minlen;
132 25 50 33     305 if ($len < $minlen) {
    50          
133 0 0       0 unless ($$et{OPTIONS}{IgnoreMinorErrors}) {
134 0         0 return "[Minor] String too short (minlen is $minlen)";
135             }
136 0         0 $$et{CHECK_WARN} = "String too short for IPTC:$$tagInfo{Name} (written anyway)";
137             } elsif ($len > $maxlen and not $$et{OPTIONS}{IgnoreMinorErrors}) {
138 0         0 $$et{CHECK_WARN} = "[Minor] IPTC:$$tagInfo{Name} exceeds length limit (truncated)";
139 0         0 $$valPtr = substr($$valPtr, 0, $maxlen);
140             }
141             }
142             } else {
143 0         0 return "Bad IPTC Format ($format)";
144             }
145 291         925 return undef;
146             }
147              
148             #------------------------------------------------------------------------------
149             # format IPTC data for writing
150             # Inputs: 0) ExifTool object ref, 1) tagInfo pointer,
151             # 2) value reference (changed if necessary),
152             # 3) reference to character set for translation (changed if necessary)
153             # 4) record number, 5) flag set to read value (instead of write)
154             sub FormatIPTC($$$$$;$)
155             {
156 314     314 0 767 my ($et, $tagInfo, $valPtr, $xlatPtr, $rec, $read) = @_;
157 314   66     869 my $format = $$tagInfo{Format} || $$tagInfo{Table}{FORMAT};
158 314 50       853 return unless $format;
159 314 100       1477 if ($format =~ /^int(\d+)/) {
    100          
160 41 100       152 if ($read) {
161 6         19 my $len = length($$valPtr);
162 6 50       38 if ($len <= 8) { # limit integer conversion to 8 bytes long
163 6         21 my $val = 0;
164 6         14 my $i;
165 6         47 for ($i=0; $i<$len; ++$i) {
166 16         61 $val = $val * 256 + ord(substr($$valPtr, $i, 1));
167             }
168 6         34 $$valPtr = $val;
169             }
170             } else {
171 35   50     274 my $len = int(($1 || 0) / 8);
172 35 50       219 if ($len == 1) { # 1 byte
    100          
173 0         0 $$valPtr = chr($$valPtr & 0xff);
174             } elsif ($len == 2) { # 2-byte integer
175 33         184 $$valPtr = pack('n', $$valPtr);
176             } else { # 4-byte integer
177 2         16 $$valPtr = pack('N', $$valPtr);
178             }
179             }
180             } elsif ($format =~ /^string/) {
181 252 100 66     1492 if ($rec == 1) {
    100 100        
182 2 50       12 if ($$tagInfo{Name} eq 'CodedCharacterSet') {
183 2         13 $$xlatPtr = HandleCodedCharset($et, $$valPtr);
184             }
185             } elsif ($$xlatPtr and $rec < 7 and $$valPtr =~ /[\x80-\xff]/) {
186 1         5 TranslateCodedString($et, $valPtr, $xlatPtr, $read);
187             }
188             # must check length now (after any string recoding)
189 252 100 66     1256 if (not $read and $format =~ /^string\[(\d+),?(\d*)\]$/) {
190 165         584 my ($minlen, $maxlen) = ($1, $2);
191 165         313 my $len = length $$valPtr;
192 165 100       345 $maxlen or $maxlen = $minlen;
193 165 50       671 if ($len < $minlen) {
    100          
194 0 0       0 if ($et->Warn("String too short for IPTC:$$tagInfo{Name} (padded)", 2)) {
195 0         0 $$valPtr .= ' ' x ($minlen - $len);
196             }
197             } elsif ($len > $maxlen) {
198 2 50       22 if ($et->Warn("IPTC:$$tagInfo{Name} exceeds length limit (truncated)", 2)) {
199 2         16 $$valPtr = substr($$valPtr, 0, $maxlen);
200             # make sure UTF-8 is still valid
201 2 100 66     16 if (($$xlatPtr || $et->Options('Charset')) eq 'UTF8') {
202 1         10 require Image::ExifTool::XMP;
203 1         9 Image::ExifTool::XMP::FixUTF8($valPtr,'.');
204             }
205             }
206             }
207             }
208             }
209             }
210              
211             #------------------------------------------------------------------------------
212             # generate IPTC-format date
213             # Inputs: 0) EXIF-format date string (YYYY:mm:dd) or date/time string
214             # Returns: IPTC-format date string (YYYYmmdd), or undef and issue warning on error
215             sub IptcDate($)
216             {
217 12     12 0 39 my $val = shift;
218 12 50       186 unless ($val =~ s{^.*(\d{4})[-:/.]?(\d{2})[-:/.]?(\d{2}).*}{$1$2$3}s) {
219 0         0 warn "Invalid date format (use YYYY:mm:dd)\n";
220 0         0 undef $val;
221             }
222 12         111 return $val;
223             }
224              
225             #------------------------------------------------------------------------------
226             # generate IPTC-format time
227             # Inputs: 0) EXIF-format time string (HH:MM:SS[+/-HH:MM]) or date/time string
228             # Returns: IPTC-format time string (HHMMSS+HHMM), or undef and issue warning on error
229             sub IptcTime($)
230             {
231 1     1 0 4 my $val = shift;
232 1 50 33     18 if ($val =~ /(.*?)\b(\d{1,2})(:?)(\d{2})(:?)(\d{2})(\S*)\s*$/s and ($3 or not $5)) {
      33        
233 1         12 $val = sprintf("%.2d%.2d%.2d",$2,$4,$6);
234 1         5 my ($date, $tz) = ($1, $7);
235 1 50       7 if ($tz =~ /([+-]\d{1,2}):?(\d{2})/) {
    0          
236 1         5 $tz = sprintf("%+.2d%.2d",$1,$2);
237             } elsif ($tz =~ /Z/i) {
238 0         0 $tz = '+0000'; # UTC
239             } else {
240             # use local system timezone by default
241 0         0 my (@tm, $time);
242 0 0 0     0 if ($date and $date =~ /^(\d{4}):(\d{2}):(\d{2})\s*$/ and eval { require Time::Local }) {
  0   0     0  
243             # we were given a date too, so determine the local timezone
244             # offset at the specified date/time
245 0         0 my @d = ($3,$2-1,$1);
246 0         0 $val =~ /(\d{2})(\d{2})(\d{2})/;
247 0         0 @tm = ($3,$2,$1,@d);
248 0         0 $time = Image::ExifTool::TimeLocal(@tm);
249             } else {
250             # it is difficult to get the proper local timezone offset for this
251             # time because the date tag is written separately. (The offset may be
252             # different on a different date due to daylight savings time.) In this
253             # case the best we can do easily is to use the current timezone offset.
254 0         0 $time = time;
255 0         0 @tm = localtime($time);
256             }
257 0         0 ($tz = Image::ExifTool::TimeZoneString(\@tm, $time)) =~ tr/://d;
258             }
259 1         4 $val .= $tz;
260             } else {
261 0         0 warn "Invalid time format (use HH:MM:SS[+/-HH:MM])\n";
262 0         0 undef $val; # time format error
263             }
264 1         9 return $val;
265             }
266              
267             #------------------------------------------------------------------------------
268             # Inverse print conversion for IPTC date or time value
269             # Inputs: 0) ExifTool ref, 1) IPTC date or 'now'
270             # Returns: IPTC date
271             sub InverseDateOrTime($$)
272             {
273 11     11 0 77 my ($et, $val) = @_;
274 11 50       57 return $et->TimeNow() if lc($val) eq 'now';
275 11         93 return $val;
276             }
277              
278             #------------------------------------------------------------------------------
279             # Convert picture number
280             # Inputs: 0) value
281             # Returns: Converted value
282             sub ConvertPictureNumber($)
283             {
284 0     0 0 0 my $val = shift;
285 0 0       0 if ($val eq "\0" x 16) {
    0          
286 0         0 $val = 'Unknown';
287             } elsif (length $val >= 16) {
288 0         0 my @vals = unpack('nNA8n', $val);
289 0         0 $val = $vals[0];
290 0         0 my $manu = $manufacturer{$val};
291 0 0       0 $val .= " ($manu)" if $manu;
292 0         0 $val .= ', equip ' . $vals[1];
293 0         0 $vals[2] =~ s/(\d{4})(\d{2})(\d{2})/$1:$2:$3/;
294 0         0 $val .= ", $vals[2], no. $vals[3]";
295             } else {
296 0         0 $val = ''
297             }
298 0         0 return $val;
299             }
300              
301             #------------------------------------------------------------------------------
302             # Inverse picture number conversion
303             # Inputs: 0) value
304             # Returns: Converted value (or undef on error)
305             sub InvConvertPictureNumber($)
306             {
307 0     0 0 0 my $val = shift;
308 0         0 $val =~ s/\(.*\)//g; # remove manufacturer description
309 0         0 $val =~ tr/://d; # remove date separators
310 0         0 $val =~ tr/0-9/ /c; # turn remaining non-numbers to spaces
311 0         0 my @vals = split ' ', $val;
312 0 0       0 if (@vals >= 4) {
    0          
313 0         0 $val = pack('nNA8n', @vals);
314             } elsif ($val =~ /unknown/i) {
315 0         0 $val = "\0" x 16;
316             } else {
317 0         0 undef $val;
318             }
319 0         0 return $val;
320             }
321              
322             #------------------------------------------------------------------------------
323             # Write IPTC data record
324             # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
325             # Returns: IPTC data block (may be empty if no IPTC data)
326             # Notes: Increments ExifTool CHANGED flag for each tag changed
327             sub DoWriteIPTC($$$)
328             {
329 65     65 0 217 my ($et, $dirInfo, $tagTablePtr) = @_;
330 65         273 my $verbose = $et->Options('Verbose');
331 65         291 my $out = $et->Options('TextOut');
332              
333             # avoid editing IPTC directory unless necessary:
334             # - improves speed
335             # - avoids changing current MD5 digest unnecessarily
336             # - avoids adding mandatory tags unless some other IPTC is changed
337             return undef unless exists $$et{EDIT_DIRS}{$$dirInfo{DirName}} or
338             # standard IPTC tags in other locations should be edited too (eg. AFCP_IPTC)
339 65 50 66     636 ($tagTablePtr eq \%Image::ExifTool::IPTC::Main and exists $$et{EDIT_DIRS}{IPTC});
      66        
340 40         116 my $dataPt = $$dirInfo{DataPt};
341 40 100       160 unless ($dataPt) {
342 22         60 my $emptyData = '';
343 22         69 $dataPt = \$emptyData;
344             }
345 40   100     241 my $start = $$dirInfo{DirStart} || 0;
346 40         143 my $dirLen = $$dirInfo{DirLen};
347 40         104 my ($tagInfo, %iptcInfo, $tag);
348              
349             # start by assuming default IPTC encoding
350 40         187 my $xlat = $et->Options('CharsetIPTC');
351 40 100       205 undef $xlat if $xlat eq $et->Options('Charset');
352              
353             # make sure our dataLen is defined (note: allow zero length directory)
354 40 100       240 unless (defined $dirLen) {
355 22         69 my $dataLen = $$dirInfo{DataLen};
356 22 50       106 $dataLen = length($$dataPt) unless defined $dataLen;
357 22         99 $dirLen = $dataLen - $start;
358             }
359             # quick check for improperly byte-swapped IPTC
360 40 50 66     318 if ($dirLen >= 4 and substr($$dataPt, $start, 1) ne "\x1c" and
      33        
361             substr($$dataPt, $start + 3, 1) eq "\x1c")
362             {
363 0         0 $et->Warn('IPTC data was improperly byte-swapped');
364 0         0 my $newData = pack('N*', unpack('V*', substr($$dataPt, $start, $dirLen) . "\0\0\0"));
365 0         0 $dataPt = \$newData;
366 0         0 $start = 0;
367             # NOTE: MUST NOT access $dirInfo DataPt, DirStart or DataLen after this!
368             }
369             # generate lookup so we can find the record numbers
370 40         119 my %recordNum;
371 40         176 foreach $tag (Image::ExifTool::TagTableKeys($tagTablePtr)) {
372 268         598 $tagInfo = $$tagTablePtr{$tag};
373 268 50       734 $$tagInfo{SubDirectory} or next;
374 268 50       836 my $table = $$tagInfo{SubDirectory}{TagTable} or next;
375 268         607 my $subTablePtr = Image::ExifTool::GetTagTable($table);
376 268         1008 $recordNum{$subTablePtr} = $tag;
377             }
378              
379             # loop through new values and accumulate all IPTC information
380             # into lists based on their IPTC record type
381 40         324 foreach $tagInfo ($et->GetNewTagInfoList()) {
382 2252         3633 my $table = $$tagInfo{Table};
383 2252         3569 my $record = $recordNum{$table};
384             # ignore tags we aren't writing to this directory
385 2252 100       4363 next unless defined $record;
386 170 100       564 $iptcInfo{$record} = [] unless defined $iptcInfo{$record};
387 170         291 push @{$iptcInfo{$record}}, $tagInfo;
  170         445  
388             }
389              
390             # get sorted list of records used. Might as well be organized and
391             # write our records in order of record number first, then tag number
392 40         385 my @recordList = sort { $a <=> $b } keys %iptcInfo;
  4         28  
393 40         132 my ($record, %set);
394 40         137 foreach $record (@recordList) {
395             # sort tagInfo lists by tagID
396 44         110 @{$iptcInfo{$record}} = sort { $$a{TagID} <=> $$b{TagID} } @{$iptcInfo{$record}};
  44         147  
  390         682  
  44         152  
397             # build hash of all tagIDs to set
398 44         115 foreach $tagInfo (@{$iptcInfo{$record}}) {
  44         146  
399 170         531 $set{$record}->{$$tagInfo{TagID}} = $tagInfo;
400             }
401             }
402             # run through the old IPTC data, inserting our records in
403             # sequence and deleting existing records where necessary
404             # (the IPTC specification states that records must occur in
405             # numerical order, but tags within records need not be ordered)
406 40         123 my $pos = $start;
407 40         117 my $tail = $pos; # old data written up to this point
408 40         114 my $dirEnd = $start + $dirLen;
409 40         119 my $newData = '';
410 40         88 my $lastRec = -1;
411 40         96 my $lastRecPos = 0;
412 40         93 my $allMandatory = 0;
413 40         100 my %foundRec; # found flags: 0x01-existed before, 0x02-deleted, 0x04-created
414             my $addNow;
415              
416 40         101 for (;;$tail=$pos) {
417             # get next IPTC record from input directory
418 287         507 my ($id, $rec, $tag, $len, $valuePtr);
419 287 100       671 if ($pos + 5 <= $dirEnd) {
420 248         454 my $buff = substr($$dataPt, $pos, 5);
421 248         717 ($id, $rec, $tag, $len) = unpack("CCCn", $buff);
422 248 100       531 if ($id == 0x1c) {
423 247 50       497 if ($rec < $lastRec) {
424 0 0       0 if ($rec == 0) {
425 0 0       0 return undef if $et->Warn("IPTC record 0 encountered, subsequent records ignored", 2);
426 0         0 undef $rec;
427 0         0 $pos = $dirEnd;
428 0         0 $len = 0;
429             } else {
430 0 0       0 return undef if $et->Warn("IPTC doesn't conform to spec: Records out of sequence", 2);
431             }
432             }
433             # handle extended IPTC entry if necessary
434 247         359 $pos += 5; # step to after field header
435 247 50       518 if ($len & 0x8000) {
436 0         0 my $n = $len & 0x7fff; # get num bytes in length field
437 0 0 0     0 if ($pos + $n <= $dirEnd and $n <= 8) {
438             # determine length (a big-endian, variable sized int)
439 0         0 for ($len = 0; $n; ++$pos, --$n) {
440 0         0 $len = $len * 256 + ord(substr($$dataPt, $pos, 1));
441             }
442             } else {
443 0         0 $len = $dirEnd; # invalid length
444             }
445             }
446 247         345 $valuePtr = $pos;
447 247         360 $pos += $len; # step $pos to next entry
448             # make sure we don't go past the end of data
449             # (this can only happen if original data is bad)
450 247 50       547 $pos = $dirEnd if $pos > $dirEnd;
451             } else {
452 1         4 undef $rec;
453             }
454             }
455             # write out all our records that come before this one
456 287   100     879 my $writeRec = (not defined $rec or $rec != $lastRec);
457 287 100 100     955 if ($writeRec or $addNow) {
458 127         211 for (;;) {
459 300         511 my $newRec = $recordList[0];
460 300 100 100     1153 if ($addNow) {
    100          
461 72         140 $tagInfo = $addNow;
462             } elsif (not defined $newRec or $newRec != $lastRec) {
463             # handle mandatory tags in last record unless it was empty
464 84 100       294 if (length $newData > $lastRecPos) {
465 44 100 66     539 if ($allMandatory > 1) {
    100          
466             # entire lastRec contained mandatory tags, and at least one tag
467             # was deleted, so delete entire record unless we specifically
468             # added a mandatory tag
469 3         7 my $num = 0;
470 3         6 foreach (keys %{$foundRec{$lastRec}}) {
  3         25  
471 6         12 my $code = $foundRec{$lastRec}->{$_};
472 6 50       16 $num = 0, last if $code & 0x04;
473 6 100       20 ++$num if ($code & 0x03) == 0x01;
474             }
475 3 50       12 if ($num) {
476 3         9 $newData = substr($newData, 0, $lastRecPos);
477 3 50       10 $verbose > 1 and print $out " - $num mandatory tags\n";
478             }
479             } elsif ($mandatory{$lastRec} and
480             $tagTablePtr eq \%Image::ExifTool::IPTC::Main)
481             {
482             # add required mandatory tags
483 39         122 my $mandatory = $mandatory{$lastRec};
484 39         127 my ($mandTag, $subTablePtr);
485 39         224 foreach $mandTag (sort { $a <=> $b } keys %$mandatory) {
  0         0  
486 39 100       165 next if $foundRec{$lastRec}->{$mandTag};
487 22 50       88 unless ($subTablePtr) {
488 22         59 $tagInfo = $$tagTablePtr{$lastRec};
489 22 50 33     188 $tagInfo and $$tagInfo{SubDirectory} or warn("WriteIPTC: Internal error 1\n"), next;
490 22 50       103 $$tagInfo{SubDirectory}{TagTable} or next;
491 22         116 $subTablePtr = Image::ExifTool::GetTagTable($$tagInfo{SubDirectory}{TagTable});
492             }
493 22 50       128 $tagInfo = $$subTablePtr{$mandTag} or warn("WriteIPTC: Internal error 2\n"), next;
494 22         74 my $value = $$mandatory{$mandTag};
495 22         255 $et->VerboseValue("+ IPTC:$$tagInfo{Name}", $value, ' (mandatory)');
496             # apply necessary format conversions
497 22         117 FormatIPTC($et, $tagInfo, \$value, \$xlat, $lastRec);
498 22         56 $len = length $value;
499             # generate our new entry
500 22         139 my $entry = pack("CCCn", 0x1c, $lastRec, $mandTag, length($value));
501 22         117 $newData .= $entry . $value; # add entry to new IPTC data
502             # (don't mark as changed if just mandatory tags changed)
503             # ++$$et{CHANGED};
504             }
505             }
506             }
507 84 100       268 last unless defined $newRec;
508 44         118 $lastRec = $newRec;
509 44         114 $lastRecPos = length $newData;
510 44         101 $allMandatory = 1;
511             }
512 260 100       569 unless ($addNow) {
513             # compare current entry with entry next in line to write out
514             # (write out our tags in numerical order even though
515             # this isn't required by the IPTC spec)
516 188 100 100     511 last if defined $rec and $rec <= $newRec;
517 170         262 $tagInfo = ${$iptcInfo{$newRec}}[0];
  170         380  
518             }
519 242         536 my $newTag = $$tagInfo{TagID};
520 242         728 my $nvHash = $et->GetNewValueHash($tagInfo);
521             # only add new values if...
522 242         483 my ($doSet, @values);
523 242   100     772 my $found = $foundRec{$newRec}->{$newTag} || 0;
524 242 100       715 if ($found & 0x02) {
    100          
525             # ...tag existed before and was deleted (unless we already added it)
526 148 100       311 $doSet = 1 unless $found & 0x04;
527             } elsif ($$tagInfo{List}) {
528             # ...tag is List and it existed before or we are creating it
529 32 100       158 $doSet = 1 if $found ? not $$nvHash{CreateOnly} : $$nvHash{IsCreating};
    100          
530             } else {
531             # ...tag didn't exist before and we are creating it
532 62 50 66     302 $doSet = 1 if not $found and $$nvHash{IsCreating};
533             }
534 242 100       570 if ($doSet) {
535 167         535 @values = $et->GetNewValue($nvHash);
536 167 100       686 @values and $foundRec{$newRec}->{$newTag} = $found | 0x04;
537             # write tags for each value in list
538 167         320 my $value;
539 167         354 foreach $value (@values) {
540 193         1045 $et->VerboseValue("+ $$dirInfo{DirName}:$$tagInfo{Name}", $value);
541             # reset allMandatory flag if a non-mandatory tag is written
542 193 100       457 if ($allMandatory) {
543 40         166 my $mandatory = $mandatory{$newRec};
544 40 100 66     256 $allMandatory = 0 unless $mandatory and $$mandatory{$newTag};
545             }
546             # apply necessary format conversions
547 193         663 FormatIPTC($et, $tagInfo, \$value, \$xlat, $newRec);
548             # (note: IPTC string values are NOT null terminated)
549 193         350 $len = length $value;
550             # generate our new entry
551 193         570 my $entry = pack("CCC", 0x1c, $newRec, $newTag);
552 193 50       436 if ($len <= 0x7fff) {
553 193         449 $entry .= pack("n", $len);
554             } else {
555             # extended dataset tag
556 0         0 $entry .= pack("nN", 0x8004, $len);
557             }
558 193         471 $newData .= $entry . $value; # add entry to new IPTC data
559 193         496 ++$$et{CHANGED};
560             }
561             }
562             # continue on with regular programming if done adding tag now
563 242 100       559 if ($addNow) {
564 72         130 undef $addNow;
565 72 100       174 next if $writeRec;
566 69         156 last;
567             }
568             # remove this tagID from the sorted write list
569 170         280 shift @{$iptcInfo{$newRec}};
  170         342  
570 170 100       269 shift @recordList unless @{$iptcInfo{$newRec}};
  170         562  
571             }
572 127 100       352 if ($writeRec) {
573             # all done if no more records to write
574 58 100       227 last unless defined $rec;
575             # update last record variables
576 18         43 $lastRec = $rec;
577 18         39 $lastRecPos = length $newData;
578 18         42 $allMandatory = 1;
579             }
580             }
581             # set flag indicating we found this tag
582 247   100     1204 $foundRec{$rec}->{$tag} = ($foundRec{$rec}->{$tag} || 0) || 0x01;
583             # write out this record unless we are setting it with a new value
584 247         470 $tagInfo = $set{$rec}->{$tag};
585 247 100 66     639 if ($tagInfo) {
    50          
586 99         332 my $nvHash = $et->GetNewValueHash($tagInfo);
587 99         207 $len = $pos - $valuePtr;
588 99         239 my $val = substr($$dataPt, $valuePtr, $len);
589             # remove null terminator if it exists (written by braindead software like Picasa 2.0)
590 99 100 100     707 $val =~ s/\0+$// if $$tagInfo{Format} and $$tagInfo{Format} =~ /^string/;
591 99         193 my $oldXlat = $xlat;
592 99         332 FormatIPTC($et, $tagInfo, \$val, \$xlat, $rec, 1);
593 99 100       337 if ($et->IsOverwriting($nvHash, $val)) {
594 89         165 $xlat = $oldXlat; # don't change translation (not writing this value)
595 89         504 $et->VerboseValue("- $$dirInfo{DirName}:$$tagInfo{Name}", $val);
596 89         192 ++$$et{CHANGED};
597             # set deleted flag to indicate we found and deleted this tag
598 89         190 $foundRec{$rec}->{$tag} |= 0x02;
599             # increment allMandatory flag to indicate a tag was removed
600 89 100       209 $allMandatory and ++$allMandatory;
601             # write this tag now if overwriting an existing value
602 89 100 66     281 if ($$nvHash{Value} and @{$$nvHash{Value}} and @recordList and
  84   66     703  
      33        
      66        
603             $recordList[0] == $rec and not $foundRec{$rec}->{$tag} & 0x04)
604             {
605 72         138 $addNow = $tagInfo;
606             }
607 89         230 next;
608             }
609             } elsif ($rec == 1 and $tag == 90) {
610             # handle CodedCharacterSet tag
611 0         0 my $val = substr($$dataPt, $valuePtr, $pos - $valuePtr);
612 0         0 $xlat = HandleCodedCharset($et, $val);
613             }
614             # reset allMandatory flag if a non-mandatory tag is written
615 158 100       313 if ($allMandatory) {
616 20         61 my $mandatory = $mandatory{$rec};
617 20 100 66     129 unless ($mandatory and $$mandatory{$tag}) {
618 8         19 $allMandatory = 0;
619             }
620             }
621             # write out the record
622 158         440 $newData .= substr($$dataPt, $tail, $pos-$tail);
623             }
624             # make sure the rest of the data is zero
625 40 100       168 if ($tail < $dirEnd) {
626 4         21 my $pad = substr($$dataPt, $tail, $dirEnd-$tail);
627 4 50       26 if ($pad =~ /[^\0]/) {
628 0 0       0 return undef if $et->Warn('Unrecognized data in IPTC padding', 2);
629             }
630             }
631 40         395 return $newData;
632             }
633              
634             #------------------------------------------------------------------------------
635             # Write IPTC data record and calculate NewIPTCDigest
636             # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
637             # Returns: IPTC data block (may be empty if no IPTC data)
638             # Notes: Increments ExifTool CHANGED flag for each tag changed
639             sub WriteIPTC($$$)
640             {
641 435     435 0 1192 my ($et, $dirInfo, $tagTablePtr) = @_;
642 435 100       2107 $et or return 1; # allow dummy access to autoload this package
643              
644 65         346 my $newData = DoWriteIPTC($et, $dirInfo, $tagTablePtr);
645              
646             # calculate standard IPTC digests only if we are writing or deleting
647             # Photoshop:IPTCDigest with a value of 'new' or 'old'
648 65         293 while ($Image::ExifTool::Photoshop::iptcDigestInfo) {
649 61         232 my $nvHash = $$et{NEW_VALUE}{$Image::ExifTool::Photoshop::iptcDigestInfo};
650 61 100       268 last unless defined $nvHash;
651 1 50       6 last unless IsStandardIPTC($et->MetadataPath());
652 1         6 my @values = $et->GetNewValue($nvHash);
653 1 50       5 push @values, @{$$nvHash{DelValue}} if $$nvHash{DelValue};
  1         18  
654 1         9 my $new = grep /^new$/, @values;
655 1         5 my $old = grep /^old$/, @values;
656 1 50 33     5 last unless $new or $old;
657 1 50       3 unless (eval { require Digest::MD5 }) {
  1         14  
658 0         0 $et->Warn('Digest::MD5 must be installed to calculate IPTC digest');
659 0         0 last;
660             }
661 1         4 my $dataPt;
662 1 50       3 if ($new) {
663 1 50       12 if (defined $newData) {
664 1         4 $dataPt = \$newData;
665             } else {
666 0         0 $dataPt = $$dirInfo{DataPt};
667 0 0 0     0 if ($$dirInfo{DirStart} or length($$dataPt) != $$dirInfo{DirLen}) {
668 0         0 my $buff = substr($$dataPt, $$dirInfo{DirStart}, $$dirInfo{DirLen});
669 0         0 $dataPt = \$buff;
670             }
671             }
672             # set NewIPTCDigest data member unless IPTC is being deleted
673 1 50       14 $$et{NewIPTCDigest} = Digest::MD5::md5($$dataPt) if length $$dataPt;
674             }
675 1 50       4 if ($old) {
676 1 50 33     11 if ($new and not defined $newData) {
    50          
677 0         0 $$et{OldIPTCDigest} = $$et{NewIPTCDigest};
678             } elsif ($$dirInfo{DataPt}) { #(may be undef if creating new IPTC)
679 1         3 $dataPt = $$dirInfo{DataPt};
680 1 50 33     16 if ($$dirInfo{DirStart} or length($$dataPt) != $$dirInfo{DirLen}) {
681 1         6 my $buff = substr($$dataPt, $$dirInfo{DirStart}, $$dirInfo{DirLen});
682 1         3 $dataPt = \$buff;
683             }
684 1 50       16 $$et{OldIPTCDigest} = Digest::MD5::md5($$dataPt) if length $$dataPt;
685             }
686             }
687 1         6 last;
688             }
689             # set changed if ForceWrite tag was set to "IPTC"
690 65 50 100     538 ++$$et{CHANGED} if defined $newData and length $newData and $$et{FORCE_WRITE}{IPTC};
      66        
691 65         241 return $newData;
692             }
693              
694              
695             1; # end
696              
697             __END__