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__ |