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