File Coverage

blib/lib/Image/ExifTool/WriteIPTC.pl
Criterion Covered Total %
statement 276 355 77.7
branch 166 250 66.4
condition 79 132 59.8
subroutine 9 11 81.8
pod 0 10 0.0
total 530 758 69.9


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   146 use strict;
  21         47  
  21         74201  
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       8 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         6 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 647 my ($et, $tagInfo, $valPtr) = @_;
105 295   50     920 my $format = $$tagInfo{Format} || $$tagInfo{Table}{FORMAT} || '';
106 295 100       1767 if ($format =~ /^int(\d+)/) {
    50          
107 45   50     265 my $bytes = int(($1 || 0) / 8);
108 45 50 66     302 if ($bytes != 1 and $bytes != 2 and $bytes != 4) {
      66        
109 0         0 return "Can't write $bytes-byte integer";
110             }
111 45         91 my $val = $$valPtr;
112 45 100       158 unless (Image::ExifTool::IsInt($val)) {
113 4 50       9 return 'Not an integer' unless Image::ExifTool::IsHex($val);
114 0         0 $val = $$valPtr = hex($val);
115             }
116 41         94 my $n;
117 41         136 for ($n=0; $n<$bytes; ++$n) { $val >>= 8; }
  138         255  
118 41 50       113 return "Value too large for $bytes-byte format" if $val;
119             } elsif ($format =~ /^(string|digits|undef)\[?(\d+),?(\d*)\]?$/) {
120 250         974 my ($fmt, $minlen, $maxlen) = ($1, $2, $3);
121 250         434 my $len = length $$valPtr;
122 250 100       601 if ($fmt eq 'digits') {
123 25 50       115 return 'Non-numeric characters in value' unless $$valPtr =~ /^\d*$/;
124 25 100 66     116 if ($len < $minlen and $len) {
125             # left pad with zeros if necessary
126 3         10 $$valPtr = ('0' x ($minlen - $len)) . $$valPtr;
127 3         8 $len = $minlen;
128             }
129             }
130 250 100 66     976 if (defined $minlen and $fmt ne 'string') { # (must truncate strings later, after recoding)
131 25 50       75 $maxlen or $maxlen = $minlen;
132 25 50 33     135 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         735 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 680 my ($et, $tagInfo, $valPtr, $xlatPtr, $rec, $read) = @_;
157 314   66     658 my $format = $$tagInfo{Format} || $$tagInfo{Table}{FORMAT};
158 314 50       530 return unless $format;
159 314 100       1146 if ($format =~ /^int(\d+)/) {
    100          
160 41 100       129 if ($read) {
161 6         16 my $len = length($$valPtr);
162 6 50       24 if ($len <= 8) { # limit integer conversion to 8 bytes long
163 6         22 my $val = 0;
164 6         15 my $i;
165 6         24 for ($i=0; $i<$len; ++$i) {
166 16         46 $val = $val * 256 + ord(substr($$valPtr, $i, 1));
167             }
168 6         26 $$valPtr = $val;
169             }
170             } else {
171 35   50     204 my $len = int(($1 || 0) / 8);
172 35 50       162 if ($len == 1) { # 1 byte
    100          
173 0         0 $$valPtr = chr($$valPtr & 0xff);
174             } elsif ($len == 2) { # 2-byte integer
175 33         137 $$valPtr = pack('n', $$valPtr);
176             } else { # 4-byte integer
177 2         10 $$valPtr = pack('N', $$valPtr);
178             }
179             }
180             } elsif ($format =~ /^string/) {
181 252 100 66     1340 if ($rec == 1) {
    100 100        
182 2 50       9 if ($$tagInfo{Name} eq 'CodedCharacterSet') {
183 2         19 $$xlatPtr = HandleCodedCharset($et, $$valPtr);
184             }
185             } elsif ($$xlatPtr and $rec < 7 and $$valPtr =~ /[\x80-\xff]/) {
186 1         6 TranslateCodedString($et, $valPtr, $xlatPtr, $read);
187             }
188             # must check length now (after any string recoding)
189 252 100 66     1003 if (not $read and $format =~ /^string\[(\d+),?(\d*)\]$/) {
190 165         506 my ($minlen, $maxlen) = ($1, $2);
191 165         255 my $len = length $$valPtr;
192 165 100       301 $maxlen or $maxlen = $minlen;
193 165 50       544 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       17 if ($et->Warn("IPTC:$$tagInfo{Name} exceeds length limit (truncated)", 2)) {
199 2         9 $$valPtr = substr($$valPtr, 0, $maxlen);
200             # make sure UTF-8 is still valid
201 2 100 66     24 if (($$xlatPtr || $et->Options('Charset')) eq 'UTF8') {
202 1         7 require Image::ExifTool::XMP;
203 1         5 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 37 my $val = shift;
218 12 50       131 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         95 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 3 my $val = shift;
232 1 50 33     20 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         6 my ($date, $tz) = ($1, $7);
235 1 50       7 if ($tz =~ /([+-]\d{1,2}):?(\d{2})/) {
    0          
236 1         7 $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         3 $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         8 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 36 my ($et, $val) = @_;
274 11 50       46 return $et->TimeNow() if lc($val) eq 'now';
275 11         76 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 162 my ($et, $dirInfo, $tagTablePtr) = @_;
330 65         222 my $verbose = $et->Options('Verbose');
331 65         203 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 65 50 66     397 unless (exists $$et{EDIT_DIRS}{$$dirInfo{DirName}} or
      66        
338             # standard IPTC tags in other locations should be edited too (eg. AFCP_IPTC)
339             ($tagTablePtr eq \%Image::ExifTool::IPTC::Main and exists $$et{EDIT_DIRS}{IPTC}))
340             {
341 25 50       67 print $out "$$et{INDENT} [nothing changed]\n" if $verbose;
342 25         66 return undef;
343             }
344 40         99 my $dataPt = $$dirInfo{DataPt};
345 40 100       118 unless ($dataPt) {
346 22         51 my $emptyData = '';
347 22         47 $dataPt = \$emptyData;
348             }
349 40   100     214 my $start = $$dirInfo{DirStart} || 0;
350 40         108 my $dirLen = $$dirInfo{DirLen};
351 40         102 my ($tagInfo, %iptcInfo, $tag);
352              
353             # start by assuming default IPTC encoding
354 40         117 my $xlat = $et->Options('CharsetIPTC');
355 40 100       130 undef $xlat if $xlat eq $et->Options('Charset');
356              
357             # make sure our dataLen is defined (note: allow zero length directory)
358 40 100       201 unless (defined $dirLen) {
359 22         49 my $dataLen = $$dirInfo{DataLen};
360 22 50       73 $dataLen = length($$dataPt) unless defined $dataLen;
361 22         50 $dirLen = $dataLen - $start;
362             }
363             # quick check for improperly byte-swapped IPTC
364 40 50 66     259 if ($dirLen >= 4 and substr($$dataPt, $start, 1) ne "\x1c" and
      33        
365             substr($$dataPt, $start + 3, 1) eq "\x1c")
366             {
367 0         0 $et->Warn('IPTC data was improperly byte-swapped');
368 0         0 my $newData = pack('N*', unpack('V*', substr($$dataPt, $start, $dirLen) . "\0\0\0"));
369 0         0 $dataPt = \$newData;
370 0         0 $start = 0;
371             # NOTE: MUST NOT access $dirInfo DataPt, DirStart or DataLen after this!
372             }
373             # generate lookup so we can find the record numbers
374 40         89 my %recordNum;
375 40         134 foreach $tag (Image::ExifTool::TagTableKeys($tagTablePtr)) {
376 268         451 $tagInfo = $$tagTablePtr{$tag};
377 268 50       614 $$tagInfo{SubDirectory} or next;
378 268 50       617 my $table = $$tagInfo{SubDirectory}{TagTable} or next;
379 268         489 my $subTablePtr = Image::ExifTool::GetTagTable($table);
380 268         788 $recordNum{$subTablePtr} = $tag;
381             }
382              
383             # loop through new values and accumulate all IPTC information
384             # into lists based on their IPTC record type
385 40         193 foreach $tagInfo ($et->GetNewTagInfoList()) {
386 2251         2999 my $table = $$tagInfo{Table};
387 2251         2841 my $record = $recordNum{$table};
388             # ignore tags we aren't writing to this directory
389 2251 100       3537 next unless defined $record;
390 170 100       391 $iptcInfo{$record} = [] unless defined $iptcInfo{$record};
391 170         222 push @{$iptcInfo{$record}}, $tagInfo;
  170         363  
392             }
393              
394             # get sorted list of records used. Might as well be organized and
395             # write our records in order of record number first, then tag number
396 40         258 my @recordList = sort { $a <=> $b } keys %iptcInfo;
  4         23  
397 40         111 my ($record, %set);
398 40         96 foreach $record (@recordList) {
399             # sort tagInfo lists by tagID
400 44         77 @{$iptcInfo{$record}} = sort { $$a{TagID} <=> $$b{TagID} } @{$iptcInfo{$record}};
  44         115  
  411         552  
  44         135  
401             # build hash of all tagIDs to set
402 44         91 foreach $tagInfo (@{$iptcInfo{$record}}) {
  44         104  
403 170         394 $set{$record}->{$$tagInfo{TagID}} = $tagInfo;
404             }
405             }
406             # run through the old IPTC data, inserting our records in
407             # sequence and deleting existing records where necessary
408             # (the IPTC specification states that records must occur in
409             # numerical order, but tags within records need not be ordered)
410 40         85 my $pos = $start;
411 40         84 my $tail = $pos; # old data written up to this point
412 40         115 my $dirEnd = $start + $dirLen;
413 40         96 my $newData = '';
414 40         72 my $lastRec = -1;
415 40         115 my $lastRecPos = 0;
416 40         70 my $allMandatory = 0;
417 40         85 my %foundRec; # found flags: 0x01-existed before, 0x02-deleted, 0x04-created
418             my $addNow;
419              
420 40         95 for (;;$tail=$pos) {
421             # get next IPTC record from input directory
422 287         413 my ($id, $rec, $tag, $len, $valuePtr);
423 287 100       522 if ($pos + 5 <= $dirEnd) {
424 248         377 my $buff = substr($$dataPt, $pos, 5);
425 248         557 ($id, $rec, $tag, $len) = unpack("CCCn", $buff);
426 248 100       434 if ($id == 0x1c) {
427 247 50       415 if ($rec < $lastRec) {
428 0 0       0 if ($rec == 0) {
429 0 0       0 return undef if $et->Warn("IPTC record 0 encountered, subsequent records ignored", 2);
430 0         0 undef $rec;
431 0         0 $pos = $dirEnd;
432 0         0 $len = 0;
433             } else {
434 0 0       0 return undef if $et->Warn("IPTC doesn't conform to spec: Records out of sequence", 2);
435             }
436             }
437             # handle extended IPTC entry if necessary
438 247         289 $pos += 5; # step to after field header
439 247 50       385 if ($len & 0x8000) {
440 0         0 my $n = $len & 0x7fff; # get num bytes in length field
441 0 0 0     0 if ($pos + $n <= $dirEnd and $n <= 8) {
442             # determine length (a big-endian, variable sized int)
443 0         0 for ($len = 0; $n; ++$pos, --$n) {
444 0         0 $len = $len * 256 + ord(substr($$dataPt, $pos, 1));
445             }
446             } else {
447 0         0 $len = $dirEnd; # invalid length
448             }
449             }
450 247         278 $valuePtr = $pos;
451 247         283 $pos += $len; # step $pos to next entry
452             # make sure we don't go past the end of data
453             # (this can only happen if original data is bad)
454 247 50       396 $pos = $dirEnd if $pos > $dirEnd;
455             } else {
456 1         3 undef $rec;
457             }
458             }
459             # write out all our records that come before this one
460 287   100     714 my $writeRec = (not defined $rec or $rec != $lastRec);
461 287 100 100     772 if ($writeRec or $addNow) {
462 127         189 for (;;) {
463 300         402 my $newRec = $recordList[0];
464 300 100 100     945 if ($addNow) {
    100          
465 72         90 $tagInfo = $addNow;
466             } elsif (not defined $newRec or $newRec != $lastRec) {
467             # handle mandatory tags in last record unless it was empty
468 84 100       292 if (length $newData > $lastRecPos) {
469 44 100 66     353 if ($allMandatory > 1) {
    100          
470             # entire lastRec contained mandatory tags, and at least one tag
471             # was deleted, so delete entire record unless we specifically
472             # added a mandatory tag
473 3         6 my $num = 0;
474 3         6 foreach (keys %{$foundRec{$lastRec}}) {
  3         12  
475 6         12 my $code = $foundRec{$lastRec}->{$_};
476 6 50       10 $num = 0, last if $code & 0x04;
477 6 100       17 ++$num if ($code & 0x03) == 0x01;
478             }
479 3 50       9 if ($num) {
480 3         6 $newData = substr($newData, 0, $lastRecPos);
481 3 50       8 $verbose > 1 and print $out " - $num mandatory tags\n";
482             }
483             } elsif ($mandatory{$lastRec} and
484             $tagTablePtr eq \%Image::ExifTool::IPTC::Main)
485             {
486             # add required mandatory tags
487 39         88 my $mandatory = $mandatory{$lastRec};
488 39         72 my ($mandTag, $subTablePtr);
489 39         163 foreach $mandTag (sort { $a <=> $b } keys %$mandatory) {
  0         0  
490 39 100       139 next if $foundRec{$lastRec}->{$mandTag};
491 22 50       81 unless ($subTablePtr) {
492 22         53 $tagInfo = $$tagTablePtr{$lastRec};
493 22 50 33     131 $tagInfo and $$tagInfo{SubDirectory} or warn("WriteIPTC: Internal error 1\n"), next;
494 22 50       127 $$tagInfo{SubDirectory}{TagTable} or next;
495 22         95 $subTablePtr = Image::ExifTool::GetTagTable($$tagInfo{SubDirectory}{TagTable});
496             }
497 22 50       93 $tagInfo = $$subTablePtr{$mandTag} or warn("WriteIPTC: Internal error 2\n"), next;
498 22         46 my $value = $$mandatory{$mandTag};
499 22         128 $et->VerboseValue("+ IPTC:$$tagInfo{Name}", $value, ' (mandatory)');
500             # apply necessary format conversions
501 22         88 FormatIPTC($et, $tagInfo, \$value, \$xlat, $lastRec);
502 22         59 $len = length $value;
503             # generate our new entry
504 22         88 my $entry = pack("CCCn", 0x1c, $lastRec, $mandTag, length($value));
505 22         81 $newData .= $entry . $value; # add entry to new IPTC data
506             # (don't mark as changed if just mandatory tags changed)
507             # ++$$et{CHANGED};
508             }
509             }
510             }
511 84 100       248 last unless defined $newRec;
512 44         80 $lastRec = $newRec;
513 44         80 $lastRecPos = length $newData;
514 44         81 $allMandatory = 1;
515             }
516 260 100       763 unless ($addNow) {
517             # compare current entry with entry next in line to write out
518             # (write out our tags in numerical order even though
519             # this isn't required by the IPTC spec)
520 188 100 100     439 last if defined $rec and $rec <= $newRec;
521 170         226 $tagInfo = ${$iptcInfo{$newRec}}[0];
  170         342  
522             }
523 242         388 my $newTag = $$tagInfo{TagID};
524 242         578 my $nvHash = $et->GetNewValueHash($tagInfo);
525             # only add new values if...
526 242         350 my ($doSet, @values);
527 242   100     769 my $found = $foundRec{$newRec}->{$newTag} || 0;
528 242 100       525 if ($found & 0x02) {
    100          
529             # ...tag existed before and was deleted (unless we already added it)
530 148 100       265 $doSet = 1 unless $found & 0x04;
531             } elsif ($$tagInfo{List}) {
532             # ...tag is List and it existed before or we are creating it
533 32 100       129 $doSet = 1 if $found ? not $$nvHash{CreateOnly} : $$nvHash{IsCreating};
    100          
534             } else {
535             # ...tag didn't exist before and we are creating it
536 62 50 66     228 $doSet = 1 if not $found and $$nvHash{IsCreating};
537             }
538 242 100       420 if ($doSet) {
539 167         471 @values = $et->GetNewValue($nvHash);
540 167 100       485 @values and $foundRec{$newRec}->{$newTag} = $found | 0x04;
541             # write tags for each value in list
542 167         226 my $value;
543 167         289 foreach $value (@values) {
544 193         785 $et->VerboseValue("+ $$dirInfo{DirName}:$$tagInfo{Name}", $value);
545             # reset allMandatory flag if a non-mandatory tag is written
546 193 100       382 if ($allMandatory) {
547 40         117 my $mandatory = $mandatory{$newRec};
548 40 100 66     236 $allMandatory = 0 unless $mandatory and $$mandatory{$newTag};
549             }
550             # apply necessary format conversions
551 193         519 FormatIPTC($et, $tagInfo, \$value, \$xlat, $newRec);
552             # (note: IPTC string values are NOT null terminated)
553 193         281 $len = length $value;
554             # generate our new entry
555 193         466 my $entry = pack("CCC", 0x1c, $newRec, $newTag);
556 193 50       322 if ($len <= 0x7fff) {
557 193         355 $entry .= pack("n", $len);
558             } else {
559             # extended dataset tag
560 0         0 $entry .= pack("nN", 0x8004, $len);
561             }
562 193         364 $newData .= $entry . $value; # add entry to new IPTC data
563 193         371 ++$$et{CHANGED};
564             }
565             }
566             # continue on with regular programming if done adding tag now
567 242 100       784 if ($addNow) {
568 72         104 undef $addNow;
569 72 100       117 next if $writeRec;
570 69         123 last;
571             }
572             # remove this tagID from the sorted write list
573 170         207 shift @{$iptcInfo{$newRec}};
  170         286  
574 170 100       227 shift @recordList unless @{$iptcInfo{$newRec}};
  170         422  
575             }
576 127 100       248 if ($writeRec) {
577             # all done if no more records to write
578 58 100       176 last unless defined $rec;
579             # update last record variables
580 18         40 $lastRec = $rec;
581 18         27 $lastRecPos = length $newData;
582 18         31 $allMandatory = 1;
583             }
584             }
585             # set flag indicating we found this tag
586 247   100     950 $foundRec{$rec}->{$tag} = ($foundRec{$rec}->{$tag} || 0) || 0x01;
587             # write out this record unless we are setting it with a new value
588 247         398 $tagInfo = $set{$rec}->{$tag};
589 247 100 66     521 if ($tagInfo) {
    50          
590 99         266 my $nvHash = $et->GetNewValueHash($tagInfo);
591 99         152 $len = $pos - $valuePtr;
592 99         187 my $val = substr($$dataPt, $valuePtr, $len);
593             # remove null terminator if it exists (written by braindead software like Picasa 2.0)
594 99 100 100     516 $val =~ s/\0+$// if $$tagInfo{Format} and $$tagInfo{Format} =~ /^string/;
595 99         159 my $oldXlat = $xlat;
596 99         272 FormatIPTC($et, $tagInfo, \$val, \$xlat, $rec, 1);
597 99 100       262 if ($et->IsOverwriting($nvHash, $val)) {
598 89         119 $xlat = $oldXlat; # don't change translation (not writing this value)
599 89         347 $et->VerboseValue("- $$dirInfo{DirName}:$$tagInfo{Name}", $val);
600 89         145 ++$$et{CHANGED};
601             # set deleted flag to indicate we found and deleted this tag
602 89         161 $foundRec{$rec}->{$tag} |= 0x02;
603             # increment allMandatory flag to indicate a tag was removed
604 89 100       163 $allMandatory and ++$allMandatory;
605             # write this tag now if overwriting an existing value
606 89 100 66     201 if ($$nvHash{Value} and @{$$nvHash{Value}} and @recordList and
  84   66     610  
      33        
      66        
607             $recordList[0] == $rec and not $foundRec{$rec}->{$tag} & 0x04)
608             {
609 72         109 $addNow = $tagInfo;
610             }
611 89         177 next;
612             }
613             } elsif ($rec == 1 and $tag == 90) {
614             # handle CodedCharacterSet tag
615 0         0 my $val = substr($$dataPt, $valuePtr, $pos - $valuePtr);
616 0         0 $xlat = HandleCodedCharset($et, $val);
617             }
618             # reset allMandatory flag if a non-mandatory tag is written
619 158 100       237 if ($allMandatory) {
620 20         42 my $mandatory = $mandatory{$rec};
621 20 100 66     121 unless ($mandatory and $$mandatory{$tag}) {
622 8         14 $allMandatory = 0;
623             }
624             }
625             # write out the record
626 158         332 $newData .= substr($$dataPt, $tail, $pos-$tail);
627             }
628             # make sure the rest of the data is zero
629 40 100       145 if ($tail < $dirEnd) {
630 4         14 my $pad = substr($$dataPt, $tail, $dirEnd-$tail);
631 4 50       17 if ($pad =~ /[^\0]/) {
632 0 0       0 return undef if $et->Warn('Unrecognized data in IPTC padding', 2);
633             }
634             }
635 40         312 return $newData;
636             }
637              
638             #------------------------------------------------------------------------------
639             # Write IPTC data record and calculate NewIPTCDigest
640             # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
641             # Returns: IPTC data block (may be empty if no IPTC data)
642             # Notes: Increments ExifTool CHANGED flag for each tag changed
643             sub WriteIPTC($$$)
644             {
645 435     435 0 938 my ($et, $dirInfo, $tagTablePtr) = @_;
646 435 100       1684 $et or return 1; # allow dummy access to autoload this package
647              
648 65         254 my $newData = DoWriteIPTC($et, $dirInfo, $tagTablePtr);
649              
650             # calculate standard IPTC digests only if we are writing or deleting
651             # Photoshop:IPTCDigest with a value of 'new' or 'old'
652 65         218 while ($Image::ExifTool::Photoshop::iptcDigestInfo) {
653 61         168 my $nvHash = $$et{NEW_VALUE}{$Image::ExifTool::Photoshop::iptcDigestInfo};
654 61 100       203 last unless defined $nvHash;
655 1 50       16 last unless IsStandardIPTC($et->MetadataPath());
656 1         6 my @values = $et->GetNewValue($nvHash);
657 1 50       6 push @values, @{$$nvHash{DelValue}} if $$nvHash{DelValue};
  1         5  
658 1         9 my $new = grep /^new$/, @values;
659 1         6 my $old = grep /^old$/, @values;
660 1 50 33     4 last unless $new or $old;
661 1 50       4 unless (eval { require Digest::MD5 }) {
  1         16  
662 0         0 $et->Warn('Digest::MD5 must be installed to calculate IPTC digest');
663 0         0 last;
664             }
665 1         3 my $dataPt;
666 1 50       3 if ($new) {
667 1 50       3 if (defined $newData) {
668 1         3 $dataPt = \$newData;
669             } else {
670 0         0 $dataPt = $$dirInfo{DataPt};
671 0 0 0     0 if ($$dirInfo{DirStart} or length($$dataPt) != $$dirInfo{DirLen}) {
672 0         0 my $buff = substr($$dataPt, $$dirInfo{DirStart}, $$dirInfo{DirLen});
673 0         0 $dataPt = \$buff;
674             }
675             }
676             # set NewIPTCDigest data member unless IPTC is being deleted
677 1 50       12 $$et{NewIPTCDigest} = Digest::MD5::md5($$dataPt) if length $$dataPt;
678             }
679 1 50       4 if ($old) {
680 1 50 33     18 if ($new and not defined $newData) {
    50          
681 0         0 $$et{OldIPTCDigest} = $$et{NewIPTCDigest};
682             } elsif ($$dirInfo{DataPt}) { #(may be undef if creating new IPTC)
683 1         4 $dataPt = $$dirInfo{DataPt};
684 1 50 33     7 if ($$dirInfo{DirStart} or length($$dataPt) != $$dirInfo{DirLen}) {
685 1         11 my $buff = substr($$dataPt, $$dirInfo{DirStart}, $$dirInfo{DirLen});
686 1         3 $dataPt = \$buff;
687             }
688 1 50       11 $$et{OldIPTCDigest} = Digest::MD5::md5($$dataPt) if length $$dataPt;
689             }
690             }
691 1         3 last;
692             }
693             # set changed if ForceWrite tag was set to "IPTC"
694 65 50 100     421 ++$$et{CHANGED} if defined $newData and length $newData and $$et{FORCE_WRITE}{IPTC};
      66        
695 65         194 return $newData;
696             }
697              
698              
699             1; # end
700              
701             __END__