line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
2
|
|
|
|
|
|
|
# File: XMPStruct.pl |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# Description: XMP structure support |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# Revisions: 01/01/2011 - P. Harvey Created |
7
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
package Image::ExifTool::XMP; |
10
|
|
|
|
|
|
|
|
11
|
15
|
|
|
15
|
|
130
|
use strict; |
|
15
|
|
|
|
|
43
|
|
|
15
|
|
|
|
|
708
|
|
12
|
15
|
|
|
15
|
|
106
|
use vars qw(%specialStruct %stdXlatNS); |
|
15
|
|
|
|
|
38
|
|
|
15
|
|
|
|
|
854
|
|
13
|
|
|
|
|
|
|
|
14
|
15
|
|
|
15
|
|
105
|
use Image::ExifTool qw(:Utils); |
|
15
|
|
|
|
|
33
|
|
|
15
|
|
|
|
|
2143
|
|
15
|
15
|
|
|
15
|
|
1232
|
use Image::ExifTool::XMP; |
|
15
|
|
|
|
|
83
|
|
|
15
|
|
|
|
|
93215
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub SerializeStruct($;$); |
18
|
|
|
|
|
|
|
sub InflateStruct($;$); |
19
|
|
|
|
|
|
|
sub DumpStruct($;$); |
20
|
|
|
|
|
|
|
sub CheckStruct($$$); |
21
|
|
|
|
|
|
|
sub AddNewStruct($$$$$$); |
22
|
|
|
|
|
|
|
sub ConvertStruct($$$$;$); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
25
|
|
|
|
|
|
|
# Serialize a structure (or other object) into a simple string |
26
|
|
|
|
|
|
|
# Inputs: 0) HASH ref, ARRAY ref, or SCALAR, 1) closing bracket (or undef) |
27
|
|
|
|
|
|
|
# Returns: serialized structure string |
28
|
|
|
|
|
|
|
# eg) "{field=text with {braces|}|, and a comma, field2=val2,field3={field4=[a,b]}}" |
29
|
|
|
|
|
|
|
sub SerializeStruct($;$) |
30
|
|
|
|
|
|
|
{ |
31
|
0
|
|
|
0
|
0
|
0
|
my ($obj, $ket) = @_; |
32
|
0
|
|
|
|
|
0
|
my ($key, $val, @vals, $rtnVal); |
33
|
|
|
|
|
|
|
|
34
|
0
|
0
|
|
|
|
0
|
if (ref $obj eq 'HASH') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# support hashes with ordered keys |
36
|
0
|
0
|
|
|
|
0
|
my @keys = $$obj{_ordered_keys_} ? @{$$obj{_ordered_keys_}} : sort keys %$obj; |
|
0
|
|
|
|
|
0
|
|
37
|
0
|
|
|
|
|
0
|
foreach $key (@keys) { |
38
|
0
|
|
|
|
|
0
|
push @vals, $key . '=' . SerializeStruct($$obj{$key}, '}'); |
39
|
|
|
|
|
|
|
} |
40
|
0
|
|
|
|
|
0
|
$rtnVal = '{' . join(',', @vals) . '}'; |
41
|
|
|
|
|
|
|
} elsif (ref $obj eq 'ARRAY') { |
42
|
0
|
|
|
|
|
0
|
foreach $val (@$obj) { |
43
|
0
|
|
|
|
|
0
|
push @vals, SerializeStruct($val, ']'); |
44
|
|
|
|
|
|
|
} |
45
|
0
|
|
|
|
|
0
|
$rtnVal = '[' . join(',', @vals) . ']'; |
46
|
|
|
|
|
|
|
} elsif (defined $obj) { |
47
|
0
|
0
|
|
|
|
0
|
$obj = $$obj if ref $obj eq 'SCALAR'; |
48
|
|
|
|
|
|
|
# escape necessary characters in string (closing bracket plus "," and "|") |
49
|
0
|
0
|
|
|
|
0
|
my $pat = $ket ? "\\$ket|,|\\|" : ',|\\|'; |
50
|
0
|
|
|
|
|
0
|
($rtnVal = $obj) =~ s/($pat)/|$1/g; |
51
|
|
|
|
|
|
|
# also must escape opening bracket or whitespace at start of string |
52
|
0
|
|
|
|
|
0
|
$rtnVal =~ s/^([\s\[\{])/|$1/; |
53
|
|
|
|
|
|
|
} else { |
54
|
0
|
|
|
|
|
0
|
$rtnVal = ''; # allow undefined list items |
55
|
|
|
|
|
|
|
} |
56
|
0
|
|
|
|
|
0
|
return $rtnVal; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
60
|
|
|
|
|
|
|
# Inflate structure (or other object) from a serialized string |
61
|
|
|
|
|
|
|
# Inputs: 0) reference to object in string form (serialized using the '|' escape) |
62
|
|
|
|
|
|
|
# 1) extra delimiter for scalar values delimiters |
63
|
|
|
|
|
|
|
# Returns: 0) object as a SCALAR, HASH ref, or ARRAY ref (or undef on error), |
64
|
|
|
|
|
|
|
# 1) warning string (or undef) |
65
|
|
|
|
|
|
|
# Notes: modifies input string to remove parsed objects |
66
|
|
|
|
|
|
|
sub InflateStruct($;$) |
67
|
|
|
|
|
|
|
{ |
68
|
104
|
|
|
104
|
0
|
263
|
my ($obj, $delim) = @_; |
69
|
104
|
|
|
|
|
234
|
my ($val, $warn, $part); |
70
|
|
|
|
|
|
|
|
71
|
104
|
100
|
|
|
|
619
|
if ($$obj =~ s/^\s*\{//) { |
|
|
100
|
|
|
|
|
|
72
|
13
|
|
|
|
|
48
|
my %struct; |
73
|
13
|
|
|
|
|
94
|
while ($$obj =~ s/^\s*([-\w:]+#?)\s*=//s) { |
74
|
19
|
|
|
|
|
54
|
my $tag = $1; |
75
|
19
|
|
|
|
|
90
|
my ($v, $w) = InflateStruct($obj, '}'); |
76
|
19
|
50
|
33
|
|
|
67
|
$warn = $w if $w and not $warn; |
77
|
19
|
50
|
|
|
|
46
|
return(undef, $warn) unless defined $v; |
78
|
19
|
|
|
|
|
82
|
$struct{$tag} = $v; |
79
|
|
|
|
|
|
|
# eat comma separator, or all done if there wasn't one |
80
|
19
|
100
|
|
|
|
101
|
last unless $$obj =~ s/^\s*,//s; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
# eat closing brace and warn if we didn't find one |
83
|
13
|
50
|
33
|
|
|
72
|
unless ($$obj =~ s/^\s*\}//s or $warn) { |
84
|
0
|
0
|
|
|
|
0
|
if (length $$obj) { |
85
|
0
|
|
|
|
|
0
|
($part = $$obj) =~ s/^\s*//s; |
86
|
0
|
|
|
|
|
0
|
$part =~ s/[\x0d\x0a].*//s; |
87
|
0
|
0
|
|
|
|
0
|
$part = substr($part,0,27) . '...' if length($part) > 30; |
88
|
0
|
|
|
|
|
0
|
$warn = "Invalid structure field at '${part}'"; |
89
|
|
|
|
|
|
|
} else { |
90
|
0
|
|
|
|
|
0
|
$warn = 'Missing closing brace for structure'; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
} |
93
|
13
|
|
|
|
|
39
|
$val = \%struct; |
94
|
|
|
|
|
|
|
} elsif ($$obj =~ s/^\s*\[//) { |
95
|
5
|
|
|
|
|
13
|
my @list; |
96
|
5
|
|
|
|
|
14
|
for (;;) { |
97
|
9
|
|
|
|
|
28
|
my ($v, $w) = InflateStruct($obj, ']'); |
98
|
9
|
50
|
33
|
|
|
35
|
$warn = $w if $w and not $warn; |
99
|
9
|
50
|
|
|
|
21
|
return(undef, $warn) unless defined $v; |
100
|
9
|
|
|
|
|
31
|
push @list, $v; |
101
|
9
|
100
|
|
|
|
45
|
last unless $$obj =~ s/^\s*,//s; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
# eat closing bracket and warn if we didn't find one |
104
|
5
|
50
|
33
|
|
|
51
|
$$obj =~ s/^\s*\]//s or $warn or $warn = 'Missing closing bracket for list'; |
105
|
5
|
|
|
|
|
15
|
$val = \@list; |
106
|
|
|
|
|
|
|
} else { |
107
|
86
|
|
|
|
|
322
|
$$obj =~ s/^\s+//s; # remove leading whitespace |
108
|
|
|
|
|
|
|
# read scalar up to specified delimiter (or "," if not defined) |
109
|
86
|
|
|
|
|
200
|
$val = ''; |
110
|
86
|
100
|
|
|
|
250
|
$delim = $delim ? "\\$delim|,|\\||\$" : ',|\\||$'; |
111
|
86
|
|
|
|
|
173
|
for (;;) { |
112
|
86
|
50
|
|
|
|
1452
|
$$obj =~ s/^(.*?)($delim)//s or last; |
113
|
86
|
|
|
|
|
346
|
$val .= $1; |
114
|
86
|
100
|
|
|
|
336
|
last unless $2; |
115
|
26
|
50
|
|
|
|
142
|
$2 eq '|' or $$obj = $2 . $$obj, last; |
116
|
0
|
0
|
|
|
|
0
|
$$obj =~ s/^(.)//s and $val .= $1; # add escaped character |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
} |
119
|
104
|
|
|
|
|
434
|
return($val, $warn); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
123
|
|
|
|
|
|
|
# Get XMP language code from tag name string |
124
|
|
|
|
|
|
|
# Inputs: 0) tag name string |
125
|
|
|
|
|
|
|
# Returns: 0) separated tag name, 1) language code (in standard case), or '' if |
126
|
|
|
|
|
|
|
# language code was 'x-default', or undef if the tag had no language code |
127
|
|
|
|
|
|
|
sub GetLangCode($) |
128
|
|
|
|
|
|
|
{ |
129
|
8
|
|
|
8
|
0
|
19
|
my $tag = shift; |
130
|
8
|
50
|
|
|
|
52
|
if ($tag =~ /^(\w+)[-_]([a-z]{2,3}|[xi])([-_][a-z\d]{2,8}([-_][a-z\d]{1,8})*)?$/i) { |
131
|
|
|
|
|
|
|
# normalize case of language codes |
132
|
8
|
|
|
|
|
34
|
my ($tg, $langCode) = ($1, lc($2)); |
133
|
8
|
50
|
|
|
|
34
|
$langCode .= (length($3) == 3 ? uc($3) : lc($3)) if $3; |
|
|
100
|
|
|
|
|
|
134
|
8
|
|
|
|
|
18
|
$langCode =~ tr/_/-/; # RFC 3066 specifies '-' as a separator |
135
|
8
|
50
|
|
|
|
25
|
$langCode = '' if lc($langCode) eq 'x-default'; |
136
|
8
|
|
|
|
|
32
|
return($tg, $langCode); |
137
|
|
|
|
|
|
|
} else { |
138
|
0
|
|
|
|
|
0
|
return($tag, undef); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
143
|
|
|
|
|
|
|
# Debugging routine to dump a structure, list or scalar |
144
|
|
|
|
|
|
|
# Inputs: 0) scalar, ARRAY ref or HASH ref, 1) indent (or undef) |
145
|
|
|
|
|
|
|
sub DumpStruct($;$) |
146
|
|
|
|
|
|
|
{ |
147
|
0
|
|
|
0
|
0
|
0
|
local $_; |
148
|
0
|
|
|
|
|
0
|
my ($obj, $indent) = @_; |
149
|
|
|
|
|
|
|
|
150
|
0
|
0
|
|
|
|
0
|
$indent or $indent = ''; |
151
|
0
|
0
|
|
|
|
0
|
if (ref $obj eq 'HASH') { |
|
|
0
|
|
|
|
|
|
152
|
0
|
|
|
|
|
0
|
print "{\n"; |
153
|
0
|
|
|
|
|
0
|
foreach (sort keys %$obj) { |
154
|
0
|
|
|
|
|
0
|
print "$indent $_ = "; |
155
|
0
|
|
|
|
|
0
|
DumpStruct($$obj{$_}, "$indent "); |
156
|
|
|
|
|
|
|
} |
157
|
0
|
|
|
|
|
0
|
print $indent, "},\n"; |
158
|
|
|
|
|
|
|
} elsif (ref $obj eq 'ARRAY') { |
159
|
0
|
|
|
|
|
0
|
print "[\n"; |
160
|
0
|
|
|
|
|
0
|
foreach (@$obj) { |
161
|
0
|
|
|
|
|
0
|
print "$indent "; |
162
|
0
|
|
|
|
|
0
|
DumpStruct($_, "$indent "); |
163
|
|
|
|
|
|
|
} |
164
|
0
|
|
|
|
|
0
|
print $indent, "],\n", |
165
|
|
|
|
|
|
|
} else { |
166
|
0
|
|
|
|
|
0
|
print "\"$obj\",\n"; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
171
|
|
|
|
|
|
|
# Recursively validate structure fields (tags) |
172
|
|
|
|
|
|
|
# Inputs: 0) ExifTool ref, 1) Structure ref, 2) structure table definition ref |
173
|
|
|
|
|
|
|
# Returns: 0) validated structure ref, 1) error string, or undef on success |
174
|
|
|
|
|
|
|
# Notes: |
175
|
|
|
|
|
|
|
# - fixes field names in structure and applies inverse conversions to values |
176
|
|
|
|
|
|
|
# - copies structure to avoid interdependencies with calling code on referenced values |
177
|
|
|
|
|
|
|
# - handles lang-alt tags, and '#' on field names |
178
|
|
|
|
|
|
|
# - resets UTF-8 flag of SCALAR values |
179
|
|
|
|
|
|
|
# - un-escapes for XML or HTML as per Escape option setting |
180
|
|
|
|
|
|
|
sub CheckStruct($$$) |
181
|
|
|
|
|
|
|
{ |
182
|
51
|
|
|
51
|
0
|
150
|
my ($et, $struct, $strTable) = @_; |
183
|
|
|
|
|
|
|
|
184
|
51
|
|
66
|
|
|
252
|
my $strName = $$strTable{STRUCT_NAME} || ('XMP ' . RegisterNamespace($strTable)); |
185
|
51
|
0
|
|
|
|
178
|
ref $struct eq 'HASH' or return wantarray ? (undef, "Expecting $strName structure") : undef; |
|
|
50
|
|
|
|
|
|
186
|
|
|
|
|
|
|
|
187
|
51
|
|
|
|
|
119
|
my ($key, $err, $warn, %copy, $rtnVal, $val); |
188
|
|
|
|
|
|
|
Key: |
189
|
51
|
|
|
|
|
193
|
foreach $key (keys %$struct) { |
190
|
85
|
|
|
|
|
166
|
my $tag = $key; |
191
|
|
|
|
|
|
|
# allow trailing '#' to disable print conversion on a per-field basis |
192
|
85
|
|
|
|
|
142
|
my ($type, $fieldInfo); |
193
|
85
|
100
|
|
|
|
263
|
$type = 'ValueConv' if $tag =~ s/#$//; |
194
|
85
|
50
|
|
|
|
299
|
$fieldInfo = $$strTable{$tag} unless $specialStruct{$tag}; |
195
|
|
|
|
|
|
|
# fix case of field name if necessary |
196
|
85
|
100
|
|
|
|
231
|
unless ($fieldInfo) { |
197
|
|
|
|
|
|
|
# (sort in reverse to get lower case (not special) tags first) |
198
|
44
|
|
|
|
|
991
|
my ($fix) = reverse sort grep /^$tag$/i, keys %$strTable; |
199
|
44
|
100
|
66
|
|
|
302
|
$fieldInfo = $$strTable{$tag = $fix} if $fix and not $specialStruct{$fix}; |
200
|
|
|
|
|
|
|
} |
201
|
85
|
|
|
|
|
282
|
until (ref $fieldInfo eq 'HASH') { |
202
|
|
|
|
|
|
|
# generate wildcard fields on the fly (eg. mwg-rs:Extensions) |
203
|
15
|
100
|
|
|
|
51
|
unless ($$strTable{NAMESPACE}) { |
204
|
10
|
|
|
|
|
24
|
my ($grp, $tg, $langCode); |
205
|
10
|
100
|
|
|
|
63
|
($grp, $tg) = $tag =~ /^(.+):(.+)/ ? (lc $1, $2) : ('', $tag); |
206
|
10
|
50
|
|
|
|
30
|
undef $grp if $grp eq 'XMP'; # (a group of 'XMP' is implied) |
207
|
10
|
|
|
|
|
58
|
require Image::ExifTool::TagLookup; |
208
|
10
|
|
|
|
|
42
|
my @matches = Image::ExifTool::TagLookup::FindTagInfo($tg); |
209
|
|
|
|
|
|
|
# also look for lang-alt tags |
210
|
10
|
100
|
|
|
|
38
|
unless (@matches) { |
211
|
3
|
|
|
|
|
12
|
($tg, $langCode) = GetLangCode($tg); |
212
|
3
|
50
|
|
|
|
18
|
@matches = Image::ExifTool::TagLookup::FindTagInfo($tg) if defined $langCode; |
213
|
|
|
|
|
|
|
} |
214
|
10
|
|
|
|
|
22
|
my ($tagInfo, $priority, $ti, $g1); |
215
|
|
|
|
|
|
|
# find best matching tag |
216
|
10
|
|
|
|
|
21
|
foreach $ti (@matches) { |
217
|
28
|
|
|
|
|
73
|
my @grps = $et->GetGroup($ti); |
218
|
28
|
100
|
|
|
|
84
|
next unless $grps[0] eq 'XMP'; |
219
|
10
|
50
|
66
|
|
|
63
|
next if $grp and $grp ne lc $grps[1]; |
220
|
|
|
|
|
|
|
# must be lang-alt tag if we are writing an alternate language |
221
|
10
|
50
|
33
|
|
|
66
|
next if defined $langCode and not ($$ti{Writable} and $$ti{Writable} eq 'lang-alt'); |
|
|
|
66
|
|
|
|
|
222
|
10
|
|
50
|
|
|
56
|
my $pri = $$ti{Priority} || 1; |
223
|
10
|
50
|
|
|
|
31
|
$pri -= 10 if $$ti{Avoid}; |
224
|
10
|
50
|
33
|
|
|
37
|
next if defined $priority and $priority >= $pri; |
225
|
10
|
|
|
|
|
14
|
$priority = $pri; |
226
|
10
|
|
|
|
|
21
|
$tagInfo = $ti; |
227
|
10
|
|
|
|
|
27
|
$g1 = $grps[1]; |
228
|
|
|
|
|
|
|
} |
229
|
10
|
50
|
|
|
|
24
|
$tagInfo or $warn = "'${tag}' is not a writable XMP tag", next Key; |
230
|
10
|
|
|
|
|
44
|
GetPropertyPath($tagInfo); # make sure property path is generated for this tag |
231
|
10
|
|
|
|
|
26
|
$tag = $$tagInfo{Name}; |
232
|
10
|
100
|
|
|
|
36
|
$tag = "$g1:$tag" if $grp; |
233
|
10
|
100
|
|
|
|
29
|
$tag .= "-$langCode" if $langCode; |
234
|
10
|
|
|
|
|
37
|
$fieldInfo = $$strTable{$tag}; |
235
|
|
|
|
|
|
|
# create new structure field if necessary |
236
|
|
|
|
|
|
|
$fieldInfo or $fieldInfo = $$strTable{$tag} = { |
237
|
|
|
|
|
|
|
%$tagInfo, # (also copies the necessary TagID and PropertyPath) |
238
|
|
|
|
|
|
|
Namespace => $$tagInfo{Namespace} || $$tagInfo{Table}{NAMESPACE}, |
239
|
10
|
50
|
33
|
|
|
177
|
LangCode => $langCode, |
240
|
|
|
|
|
|
|
}; |
241
|
|
|
|
|
|
|
# delete stuff we don't need (shouldn't cause harm, but better safe than sorry) |
242
|
|
|
|
|
|
|
# - need to keep StructType and Table in case we need to call AddStructType later |
243
|
10
|
|
|
|
|
33
|
delete $$fieldInfo{Description}; |
244
|
10
|
|
|
|
|
22
|
delete $$fieldInfo{Groups}; |
245
|
10
|
|
|
|
|
28
|
last; # write this dynamically-generated field |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
# generate lang-alt fields on the fly (eg. Iptc4xmpExt:AOTitle) |
248
|
5
|
|
|
|
|
17
|
my ($tg, $langCode) = GetLangCode($tag); |
249
|
5
|
50
|
|
|
|
14
|
if (defined $langCode) { |
250
|
5
|
50
|
|
|
|
20
|
$fieldInfo = $$strTable{$tg} unless $specialStruct{$tg}; |
251
|
5
|
100
|
|
|
|
15
|
unless ($fieldInfo) { |
252
|
1
|
|
|
|
|
27
|
my ($fix) = reverse sort grep /^$tg$/i, keys %$strTable; |
253
|
1
|
50
|
33
|
|
|
14
|
$fieldInfo = $$strTable{$tg = $fix} if $fix and not $specialStruct{$fix}; |
254
|
|
|
|
|
|
|
} |
255
|
5
|
50
|
33
|
|
|
580
|
if (ref $fieldInfo eq 'HASH' and $$fieldInfo{Writable} and |
|
|
|
33
|
|
|
|
|
256
|
|
|
|
|
|
|
$$fieldInfo{Writable} eq 'lang-alt') |
257
|
|
|
|
|
|
|
{ |
258
|
5
|
|
|
|
|
12
|
my $srcInfo = $fieldInfo; |
259
|
5
|
50
|
|
|
|
19
|
$tag = $tg . '-' . $langCode if $langCode; |
260
|
5
|
|
|
|
|
11
|
$fieldInfo = $$strTable{$tag}; |
261
|
|
|
|
|
|
|
# create new structure field if necessary |
262
|
5
|
50
|
|
|
|
36
|
$fieldInfo or $fieldInfo = $$strTable{$tag} = { |
263
|
|
|
|
|
|
|
%$srcInfo, |
264
|
|
|
|
|
|
|
TagID => $tg, |
265
|
|
|
|
|
|
|
LangCode => $langCode, |
266
|
|
|
|
|
|
|
}; |
267
|
5
|
|
|
|
|
14
|
last; # write this lang-alt field |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
} |
270
|
0
|
|
|
|
|
0
|
$warn = "'${tag}' is not a field of $strName"; |
271
|
0
|
|
|
|
|
0
|
next Key; |
272
|
|
|
|
|
|
|
} |
273
|
85
|
100
|
|
|
|
438
|
if (ref $$struct{$key} eq 'HASH') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
274
|
10
|
50
|
|
|
|
38
|
$$fieldInfo{Struct} or $warn = "$tag is not a structure in $strName", next Key; |
275
|
|
|
|
|
|
|
# recursively check this structure |
276
|
10
|
|
|
|
|
94
|
($val, $err) = CheckStruct($et, $$struct{$key}, $$fieldInfo{Struct}); |
277
|
10
|
50
|
|
|
|
33
|
$err and $warn = $err, next Key; |
278
|
10
|
|
|
|
|
32
|
$copy{$tag} = $val; |
279
|
|
|
|
|
|
|
} elsif (ref $$struct{$key} eq 'ARRAY') { |
280
|
13
|
50
|
|
|
|
42
|
$$fieldInfo{List} or $warn = "$tag is not a list in $strName", next Key; |
281
|
|
|
|
|
|
|
# check all items in the list |
282
|
13
|
|
|
|
|
20
|
my ($item, @copy); |
283
|
13
|
|
|
|
|
28
|
my $i = 0; |
284
|
13
|
|
|
|
|
18
|
foreach $item (@{$$struct{$key}}) { |
|
13
|
|
|
|
|
37
|
|
285
|
21
|
100
|
|
|
|
59
|
if (not ref $item) { |
|
|
50
|
|
|
|
|
|
286
|
13
|
50
|
|
|
|
32
|
$item = '' unless defined $item; # use empty string for missing items |
287
|
13
|
100
|
|
|
|
33
|
if ($$fieldInfo{Struct}) { |
288
|
|
|
|
|
|
|
# (allow empty structures) |
289
|
2
|
50
|
|
|
|
11
|
$item =~ /^\s*$/ or $warn = "$tag items are not valid structures", next Key; |
290
|
2
|
|
|
|
|
9
|
$copy[$i] = { }; # create hash for empty structure |
291
|
|
|
|
|
|
|
} else { |
292
|
11
|
|
|
|
|
46
|
$et->Sanitize(\$item); |
293
|
11
|
|
|
|
|
40
|
($copy[$i],$err) = $et->ConvInv($item,$fieldInfo,$tag,$strName,$type,''); |
294
|
11
|
50
|
|
|
|
47
|
$copy[$i] = '' unless defined $copy[$i]; # avoid undefined item |
295
|
11
|
50
|
|
|
|
26
|
$err and $warn = $err, next Key; |
296
|
11
|
|
|
|
|
40
|
$err = CheckXMP($et, $fieldInfo, \$copy[$i]); |
297
|
11
|
50
|
|
|
|
40
|
$err and $warn = "$err in $strName $tag", next Key; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
} elsif (ref $item eq 'HASH') { |
300
|
8
|
50
|
|
|
|
26
|
$$fieldInfo{Struct} or $warn = "$tag is not a structure in $strName", next Key; |
301
|
8
|
|
|
|
|
25
|
($copy[$i], $err) = CheckStruct($et, $item, $$fieldInfo{Struct}); |
302
|
8
|
50
|
|
|
|
21
|
$err and $warn = $err, next Key; |
303
|
|
|
|
|
|
|
} else { |
304
|
0
|
|
|
|
|
0
|
$warn = "Invalid value for $tag in $strName"; |
305
|
0
|
|
|
|
|
0
|
next Key; |
306
|
|
|
|
|
|
|
} |
307
|
21
|
|
|
|
|
41
|
++$i; |
308
|
|
|
|
|
|
|
} |
309
|
13
|
|
|
|
|
53
|
$copy{$tag} = \@copy; |
310
|
|
|
|
|
|
|
} elsif ($$fieldInfo{Struct}) { |
311
|
0
|
|
|
|
|
0
|
$warn = "Improperly formed structure in $strName $tag"; |
312
|
|
|
|
|
|
|
} else { |
313
|
62
|
|
|
|
|
306
|
$et->Sanitize(\$$struct{$key}); |
314
|
62
|
|
|
|
|
377
|
($val,$err) = $et->ConvInv($$struct{$key},$fieldInfo,$tag,$strName,$type,''); |
315
|
62
|
50
|
|
|
|
203
|
$err and $warn = $err, next Key; |
316
|
62
|
50
|
|
|
|
163
|
next Key unless defined $val; # check for undefined |
317
|
62
|
|
|
|
|
239
|
$err = CheckXMP($et, $fieldInfo, \$val); |
318
|
62
|
50
|
|
|
|
202
|
$err and $warn = "$err in $strName $tag", next Key; |
319
|
|
|
|
|
|
|
# turn this into a list if necessary |
320
|
62
|
100
|
|
|
|
310
|
$copy{$tag} = $$fieldInfo{List} ? [ $val ] : $val; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
} |
323
|
51
|
50
|
66
|
|
|
202
|
if (%copy or not $warn) { |
324
|
51
|
|
|
|
|
98
|
$rtnVal = \%copy; |
325
|
51
|
|
|
|
|
86
|
undef $err; |
326
|
51
|
50
|
|
|
|
133
|
$$et{CHECK_WARN} = $warn if $warn; |
327
|
|
|
|
|
|
|
} else { |
328
|
0
|
|
|
|
|
0
|
$err = $warn; |
329
|
|
|
|
|
|
|
} |
330
|
51
|
50
|
|
|
|
239
|
return wantarray ? ($rtnVal, $err) : $rtnVal; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
334
|
|
|
|
|
|
|
# Delete matching structures from existing linearized XMP |
335
|
|
|
|
|
|
|
# Inputs: 0) ExifTool ref, 1) capture hash ref, 2) structure path ref, |
336
|
|
|
|
|
|
|
# 3) new value hash ref, 4) reference to change counter |
337
|
|
|
|
|
|
|
# Returns: 0) delete flag, 1) list index of deleted structure if adding to list |
338
|
|
|
|
|
|
|
# 2) flag set if structure existed |
339
|
|
|
|
|
|
|
# Notes: updates path to new base path for structure to be added |
340
|
|
|
|
|
|
|
sub DeleteStruct($$$$$) |
341
|
|
|
|
|
|
|
{ |
342
|
196
|
|
|
196
|
0
|
463
|
my ($et, $capture, $pathPt, $nvHash, $changed) = @_; |
343
|
196
|
|
|
|
|
582
|
my ($deleted, $added, $existed, $p, $pp, $val, $delPath); |
344
|
196
|
|
|
|
|
0
|
my (@structPaths, @matchingPaths, @delPaths); |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
# find all existing elements belonging to this structure |
347
|
196
|
|
|
|
|
1206
|
($pp = $$pathPt) =~ s/ \d+/ \\d\+/g; |
348
|
196
|
|
|
|
|
12617
|
@structPaths = sort grep(/^$pp(\/|$)/, keys %$capture); |
349
|
196
|
100
|
|
|
|
1214
|
$existed = 1 if @structPaths; |
350
|
|
|
|
|
|
|
# delete only structures with matching fields if necessary |
351
|
196
|
100
|
|
|
|
802
|
if ($$nvHash{DelValue}) { |
|
|
100
|
|
|
|
|
|
352
|
4
|
50
|
|
|
|
14
|
if (@{$$nvHash{DelValue}}) { |
|
4
|
|
|
|
|
19
|
|
353
|
4
|
|
|
|
|
13
|
my $strTable = $$nvHash{TagInfo}{Struct}; |
354
|
|
|
|
|
|
|
# all fields must match corresponding elements in the same |
355
|
|
|
|
|
|
|
# root structure for it to be deleted |
356
|
4
|
|
|
|
|
8
|
foreach $val (@{$$nvHash{DelValue}}) { |
|
4
|
|
|
|
|
17
|
|
357
|
4
|
50
|
|
|
|
20
|
next unless ref $val eq 'HASH'; |
358
|
0
|
|
|
|
|
0
|
my (%cap, $p2, %match); |
359
|
0
|
0
|
|
|
|
0
|
next unless AddNewStruct(undef, undef, \%cap, $$pathPt, $val, $strTable); |
360
|
0
|
|
|
|
|
0
|
foreach $p (keys %cap) { |
361
|
0
|
0
|
|
|
|
0
|
if ($p =~ / /) { |
362
|
0
|
|
|
|
|
0
|
($p2 = $p) =~ s/ \d+/ \\d\+/g; |
363
|
0
|
|
|
|
|
0
|
@matchingPaths = sort grep(/^$p2$/, @structPaths); |
364
|
|
|
|
|
|
|
} else { |
365
|
0
|
|
|
|
|
0
|
push @matchingPaths, $p; |
366
|
|
|
|
|
|
|
} |
367
|
0
|
|
|
|
|
0
|
foreach $p2 (@matchingPaths) { |
368
|
0
|
0
|
|
|
|
0
|
$p2 =~ /^($pp)/ or next; |
369
|
|
|
|
|
|
|
# language attribute must also match if it exists |
370
|
0
|
|
|
|
|
0
|
my $attr = $cap{$p}[1]; |
371
|
0
|
0
|
|
|
|
0
|
if ($$attr{'xml:lang'}) { |
372
|
0
|
|
|
|
|
0
|
my $a2 = $$capture{$p2}[1]; |
373
|
0
|
0
|
0
|
|
|
0
|
next unless $$a2{'xml:lang'} and $$a2{'xml:lang'} eq $$attr{'xml:lang'}; |
374
|
|
|
|
|
|
|
} |
375
|
0
|
0
|
0
|
|
|
0
|
if ($$capture{$p2} and $$capture{$p2}[0] eq $cap{$p}[0]) { |
376
|
|
|
|
|
|
|
# ($1 contains root path for this structure) |
377
|
0
|
|
0
|
|
|
0
|
$match{$1} = ($match{$1} || 0) + 1; |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
} |
381
|
0
|
|
|
|
|
0
|
my $num = scalar(keys %cap); |
382
|
0
|
|
|
|
|
0
|
foreach $p (keys %match) { |
383
|
|
|
|
|
|
|
# do nothing unless all fields matched the same structure |
384
|
0
|
0
|
|
|
|
0
|
next unless $match{$p} == $num; |
385
|
|
|
|
|
|
|
# delete all elements of this structure |
386
|
0
|
|
|
|
|
0
|
foreach $p2 (@structPaths) { |
387
|
0
|
0
|
|
|
|
0
|
push @delPaths, $p2 if $p2 =~ /^$p/; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
# remember path of first deleted structure |
390
|
0
|
0
|
0
|
|
|
0
|
$delPath = $p if not $delPath or $delPath gt $p; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
} # (else don't delete anything) |
394
|
|
|
|
|
|
|
} elsif (@structPaths) { |
395
|
2
|
|
|
|
|
11
|
@delPaths = @structPaths; # delete all |
396
|
2
|
|
|
|
|
33
|
$structPaths[0] =~ /^($pp)/; |
397
|
2
|
|
|
|
|
12
|
$delPath = $1; |
398
|
|
|
|
|
|
|
} |
399
|
196
|
100
|
|
|
|
1026
|
if (@delPaths) { |
|
|
100
|
|
|
|
|
|
400
|
2
|
|
|
|
|
17
|
my $verbose = $et->Options('Verbose'); |
401
|
2
|
50
|
|
|
|
24
|
@delPaths = sort @delPaths if $verbose > 1; |
402
|
2
|
|
|
|
|
12
|
foreach $p (@delPaths) { |
403
|
6
|
50
|
|
|
|
23
|
if ($verbose > 1) { |
404
|
0
|
|
|
|
|
0
|
my $p2 = $p; |
405
|
0
|
0
|
|
|
|
0
|
$p2 =~ s/^(\w+)/$stdXlatNS{$1} || $1/e; |
|
0
|
|
|
|
|
0
|
|
406
|
0
|
|
|
|
|
0
|
$et->VerboseValue("- XMP-$p2", $$capture{$p}[0]); |
407
|
|
|
|
|
|
|
} |
408
|
6
|
|
|
|
|
40
|
delete $$capture{$p}; |
409
|
6
|
|
|
|
|
12
|
$deleted = 1; |
410
|
6
|
|
|
|
|
12
|
++$$changed; |
411
|
|
|
|
|
|
|
} |
412
|
2
|
50
|
|
|
|
14
|
$delPath or warn("Internal error 1 in DeleteStruct\n"), return(undef,undef,$existed); |
413
|
2
|
|
|
|
|
11
|
$$pathPt = $delPath; # return path of first element deleted |
414
|
|
|
|
|
|
|
} elsif ($$nvHash{TagInfo}{List}) { |
415
|
|
|
|
|
|
|
# NOTE: we don't yet properly handle lang-alt elements!!!! |
416
|
145
|
100
|
|
|
|
322
|
if (@structPaths) { |
417
|
1
|
50
|
|
|
|
22
|
$structPaths[-1] =~ /^($pp)/ or warn("Internal error 2 in DeleteStruct\n"), return(undef,undef,$existed); |
418
|
1
|
|
|
|
|
5
|
my $path = $1; |
419
|
|
|
|
|
|
|
# delete any improperly formatted xmp |
420
|
1
|
50
|
|
|
|
8
|
if ($$capture{$path}) { |
421
|
0
|
|
|
|
|
0
|
my $cap = $$capture{$path}; |
422
|
|
|
|
|
|
|
# an error unless this was an empty structure |
423
|
0
|
0
|
0
|
|
|
0
|
$et->Error("Improperly structured XMP ($path)",1) if ref $cap ne 'ARRAY' or $$cap[0]; |
424
|
0
|
|
|
|
|
0
|
delete $$capture{$path}; |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
# (match last index to put in same lang-alt list for Bag of lang-alt items) |
427
|
1
|
50
|
|
|
|
8
|
$path =~ m/.* (\d+)/g or warn("Internal error 3 in DeleteStruct\n"), return(undef,undef,$existed); |
428
|
1
|
|
|
|
|
4
|
$added = $1; |
429
|
|
|
|
|
|
|
# add after last item in list |
430
|
1
|
|
|
|
|
4
|
my $len = length $added; |
431
|
1
|
|
|
|
|
4
|
my $pos = pos($path) - $len; |
432
|
1
|
|
|
|
|
4
|
my $nxt = substr($added, 1) + 1; |
433
|
1
|
|
|
|
|
5
|
substr($path, $pos, $len) = length($nxt) . $nxt; |
434
|
1
|
|
|
|
|
5
|
$$pathPt = $path; |
435
|
|
|
|
|
|
|
} else { |
436
|
144
|
|
|
|
|
290
|
$added = '10'; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
} |
439
|
196
|
|
|
|
|
962
|
return($deleted, $added, $existed); |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
443
|
|
|
|
|
|
|
# Add new element to XMP capture hash |
444
|
|
|
|
|
|
|
# Inputs: 0) ExifTool ref, 1) TagInfo ref, 2) capture hash ref, |
445
|
|
|
|
|
|
|
# 3) resource path, 4) value ref, 5) hash ref for last used index numbers |
446
|
|
|
|
|
|
|
sub AddNewTag($$$$$$) |
447
|
|
|
|
|
|
|
{ |
448
|
71
|
|
|
71
|
0
|
179
|
my ($et, $tagInfo, $capture, $path, $valPtr, $langIdx) = @_; |
449
|
71
|
|
|
|
|
248
|
my $val = EscapeXML($$valPtr); |
450
|
71
|
|
|
|
|
144
|
my %attrs; |
451
|
|
|
|
|
|
|
# support writing RDF "resource" values |
452
|
71
|
100
|
|
|
|
214
|
if ($$tagInfo{Resource}) { |
453
|
2
|
|
|
|
|
10
|
$attrs{'rdf:resource'} = $val; |
454
|
2
|
|
|
|
|
5
|
$val = ''; |
455
|
|
|
|
|
|
|
} |
456
|
71
|
100
|
100
|
|
|
334
|
if ($$tagInfo{Writable} and $$tagInfo{Writable} eq 'lang-alt') { |
457
|
|
|
|
|
|
|
# write the lang-alt tag |
458
|
20
|
|
|
|
|
66
|
my $langCode = $$tagInfo{LangCode}; |
459
|
|
|
|
|
|
|
# add indexed lang-alt list properties |
460
|
20
|
|
100
|
|
|
66
|
my $i = $$langIdx{$path} || 0; |
461
|
20
|
|
|
|
|
74
|
$$langIdx{$path} = $i + 1; # save next list index |
462
|
20
|
100
|
|
|
|
42
|
if ($i) { |
463
|
8
|
|
|
|
|
25
|
my $idx = length($i) . $i; |
464
|
8
|
|
|
|
|
62
|
$path =~ s/(.*) \d+/$1 $idx/; # set list index |
465
|
|
|
|
|
|
|
} |
466
|
20
|
|
100
|
|
|
95
|
$attrs{'xml:lang'} = $langCode || 'x-default'; |
467
|
|
|
|
|
|
|
} |
468
|
71
|
|
|
|
|
320
|
$$capture{$path} = [ $val, \%attrs ]; |
469
|
|
|
|
|
|
|
# print verbose message |
470
|
71
|
50
|
33
|
|
|
352
|
if ($et and $et->Options('Verbose') > 1) { |
471
|
0
|
|
|
|
|
0
|
my $p = $path; |
472
|
0
|
0
|
|
|
|
0
|
$p =~ s/^(\w+)/$stdXlatNS{$1} || $1/e; |
|
0
|
|
|
|
|
0
|
|
473
|
0
|
|
|
|
|
0
|
$et->VerboseValue("+ XMP-$p", $val); |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
478
|
|
|
|
|
|
|
# Add new structure to capture hash for writing |
479
|
|
|
|
|
|
|
# Inputs: 0) ExifTool object ref (or undef for no warnings), |
480
|
|
|
|
|
|
|
# 1) tagInfo ref (or undef if no ExifTool), 2) capture hash ref, |
481
|
|
|
|
|
|
|
# 3) base path, 4) struct ref, 5) struct hash ref |
482
|
|
|
|
|
|
|
# Returns: number of tags changed |
483
|
|
|
|
|
|
|
# Notes: Escapes values for XML |
484
|
|
|
|
|
|
|
sub AddNewStruct($$$$$$) |
485
|
|
|
|
|
|
|
{ |
486
|
50
|
|
|
50
|
0
|
160
|
my ($et, $tagInfo, $capture, $basePath, $struct, $strTable) = @_; |
487
|
50
|
50
|
|
|
|
244
|
my $verbose = $et ? $et->Options('Verbose') : 0; |
488
|
50
|
|
|
|
|
126
|
my ($tag, %langIdx); |
489
|
|
|
|
|
|
|
|
490
|
50
|
|
100
|
|
|
269
|
my $ns = $$strTable{NAMESPACE} || ''; |
491
|
50
|
|
|
|
|
98
|
my $changed = 0; |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# add dummy field to allow empty structures (name starts with '~' so it will come |
494
|
|
|
|
|
|
|
# after all valid structure fields, which is necessary when serializing the XMP later) |
495
|
50
|
100
|
|
|
|
170
|
%$struct or $$struct{'~dummy~'} = ''; |
496
|
|
|
|
|
|
|
|
497
|
50
|
|
|
|
|
271
|
foreach $tag (sort keys %$struct) { |
498
|
85
|
|
|
|
|
195
|
my $fieldInfo = $$strTable{$tag}; |
499
|
85
|
100
|
|
|
|
220
|
unless ($fieldInfo) { |
500
|
3
|
50
|
|
|
|
17
|
next unless $tag eq '~dummy~'; # check for dummy field |
501
|
3
|
|
|
|
|
7
|
$fieldInfo = { }; # create dummy field info for dummy structure |
502
|
|
|
|
|
|
|
} |
503
|
85
|
|
|
|
|
193
|
my $val = $$struct{$tag}; |
504
|
85
|
|
|
|
|
198
|
my $propPath = $$fieldInfo{PropertyPath}; |
505
|
85
|
100
|
|
|
|
240
|
unless ($propPath) { |
506
|
37
|
|
66
|
|
|
268
|
$propPath = ($$fieldInfo{Namespace} || $ns) . ':' . ($$fieldInfo{TagID} || $tag); |
|
|
|
66
|
|
|
|
|
507
|
37
|
100
|
|
|
|
105
|
if ($$fieldInfo{List}) { |
508
|
7
|
|
|
|
|
27
|
$propPath .= "/rdf:$$fieldInfo{List}/rdf:li 10"; |
509
|
|
|
|
|
|
|
} |
510
|
37
|
100
|
100
|
|
|
198
|
if ($$fieldInfo{Writable} and $$fieldInfo{Writable} eq 'lang-alt') { |
511
|
7
|
|
|
|
|
19
|
$propPath .= "/rdf:Alt/rdf:li 10"; |
512
|
|
|
|
|
|
|
} |
513
|
37
|
|
|
|
|
103
|
$$fieldInfo{PropertyPath} = $propPath; # save for next time |
514
|
|
|
|
|
|
|
} |
515
|
85
|
|
|
|
|
297
|
my $path = $basePath . '/' . ConformPathToNamespace($et, $propPath); |
516
|
85
|
|
|
|
|
189
|
my $addedTag; |
517
|
85
|
100
|
|
|
|
310
|
if (ref $val eq 'HASH') { |
|
|
100
|
|
|
|
|
|
518
|
10
|
50
|
|
|
|
45
|
my $subStruct = $$fieldInfo{Struct} or next; |
519
|
10
|
|
|
|
|
63
|
$changed += AddNewStruct($et, $tagInfo, $capture, $path, $val, $subStruct); |
520
|
|
|
|
|
|
|
} elsif (ref $val eq 'ARRAY') { |
521
|
15
|
50
|
|
|
|
45
|
next unless $$fieldInfo{List}; |
522
|
15
|
|
|
|
|
34
|
my $i = 0; |
523
|
15
|
|
|
|
|
24
|
my ($item, $p); |
524
|
15
|
|
|
|
|
74
|
my $level = scalar(() = ($propPath =~ / \d+/g)); |
525
|
|
|
|
|
|
|
# loop through all list items (note: can't yet write multi-dimensional lists) |
526
|
15
|
|
|
|
|
31
|
foreach $item (@{$val}) { |
|
15
|
|
|
|
|
36
|
|
527
|
23
|
100
|
|
|
|
50
|
if ($i) { |
528
|
|
|
|
|
|
|
# update first index in field property (may be list of lang-alt lists) |
529
|
8
|
|
|
|
|
24
|
$p = ConformPathToNamespace($et, $propPath); |
530
|
8
|
|
|
|
|
29
|
my $idx = length($i) . $i; |
531
|
8
|
|
|
|
|
52
|
$p =~ s/ \d+/ $idx/; |
532
|
8
|
|
|
|
|
31
|
$p = "$basePath/$p"; |
533
|
|
|
|
|
|
|
} else { |
534
|
15
|
|
|
|
|
29
|
$p = $path; |
535
|
|
|
|
|
|
|
} |
536
|
23
|
100
|
33
|
|
|
120
|
if (ref $item eq 'HASH') { |
|
|
100
|
66
|
|
|
|
|
537
|
10
|
50
|
|
|
|
33
|
my $subStruct = $$fieldInfo{Struct} or next; |
538
|
10
|
50
|
|
|
|
33
|
AddNewStruct($et, $tagInfo, $capture, $p, $item, $subStruct) or next; |
539
|
|
|
|
|
|
|
# don't write empty items in upper-level list |
540
|
|
|
|
|
|
|
} elsif (length $item or (defined $item and $level == 1)) { |
541
|
11
|
|
|
|
|
37
|
AddNewTag($et, $fieldInfo, $capture, $p, \$item, \%langIdx); |
542
|
11
|
|
|
|
|
35
|
$addedTag = 1; |
543
|
|
|
|
|
|
|
} |
544
|
23
|
|
|
|
|
38
|
++$changed; |
545
|
23
|
|
|
|
|
47
|
++$i; |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
} else { |
548
|
60
|
|
|
|
|
226
|
AddNewTag($et, $fieldInfo, $capture, $path, \$val, \%langIdx); |
549
|
60
|
|
|
|
|
123
|
$addedTag = 1; |
550
|
60
|
|
|
|
|
108
|
++$changed; |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
# this is tricky, but we must add the rdf:type for contained structures |
553
|
|
|
|
|
|
|
# in the case that a whole hierarchy was added at once by writing a |
554
|
|
|
|
|
|
|
# flattened tag inside a variable-namespace structure |
555
|
85
|
50
|
100
|
|
|
428
|
if ($addedTag and $$fieldInfo{StructType} and $$fieldInfo{Table}) { |
|
|
|
66
|
|
|
|
|
556
|
1
|
|
|
|
|
7
|
AddStructType($et, $$fieldInfo{Table}, $capture, $propPath, $basePath); |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
# add 'rdf:type' property if necessary |
560
|
50
|
100
|
66
|
|
|
243
|
if ($$strTable{TYPE} and $changed) { |
561
|
3
|
|
|
|
|
16
|
my $path = $basePath . '/' . ConformPathToNamespace($et, "rdf:type"); |
562
|
3
|
50
|
|
|
|
20
|
unless ($$capture{$path}) { |
563
|
3
|
|
|
|
|
18
|
$$capture{$path} = [ '', { 'rdf:resource' => $$strTable{TYPE} } ]; |
564
|
3
|
50
|
|
|
|
14
|
if ($verbose > 1) { |
565
|
0
|
|
|
|
|
0
|
my $p = $path; |
566
|
0
|
0
|
|
|
|
0
|
$p =~ s/^(\w+)/$stdXlatNS{$1} || $1/e; |
|
0
|
|
|
|
|
0
|
|
567
|
0
|
|
|
|
|
0
|
$et->VerboseValue("+ XMP-$p", $$strTable{TYPE}); |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
} |
571
|
50
|
|
|
|
|
201
|
return $changed; |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
575
|
|
|
|
|
|
|
# Convert structure field values for printing |
576
|
|
|
|
|
|
|
# Inputs: 0) ExifTool ref, 1) tagInfo ref for structure tag, 2) value, |
577
|
|
|
|
|
|
|
# 3) conversion type: PrintConv, ValueConv or Raw (Both not allowed) |
578
|
|
|
|
|
|
|
# 4) tagID of parent structure (needed only if there was no flattened tag) |
579
|
|
|
|
|
|
|
# Notes: Makes a copy of the hash so any applied escapes won't affect raw values |
580
|
|
|
|
|
|
|
sub ConvertStruct($$$$;$) |
581
|
|
|
|
|
|
|
{ |
582
|
204
|
|
|
204
|
0
|
527
|
my ($et, $tagInfo, $value, $type, $parentID) = @_; |
583
|
204
|
100
|
|
|
|
572
|
if (ref $value eq 'HASH') { |
|
|
100
|
|
|
|
|
|
584
|
108
|
|
|
|
|
190
|
my (%struct, $key); |
585
|
108
|
|
|
|
|
205
|
my $table = $$tagInfo{Table}; |
586
|
108
|
100
|
|
|
|
327
|
$parentID = $$tagInfo{TagID} unless $parentID; |
587
|
108
|
|
|
|
|
393
|
foreach $key (keys %$value) { |
588
|
212
|
|
|
|
|
565
|
my $tagID = $parentID . ucfirst($key); |
589
|
212
|
|
|
|
|
429
|
my $flatInfo = $$table{$tagID}; |
590
|
212
|
100
|
|
|
|
454
|
unless ($flatInfo) { |
591
|
|
|
|
|
|
|
# handle variable-namespace structures |
592
|
16
|
100
|
|
|
|
80
|
if ($key =~ /^XMP-(.*?:)(.*)/) { |
593
|
13
|
|
|
|
|
53
|
$tagID = $1 . $parentID . ucfirst($2); |
594
|
13
|
|
|
|
|
29
|
$flatInfo = $$table{$tagID}; |
595
|
|
|
|
|
|
|
} |
596
|
16
|
100
|
|
|
|
43
|
$flatInfo or $flatInfo = $tagInfo; |
597
|
|
|
|
|
|
|
} |
598
|
212
|
|
|
|
|
432
|
my $v = $$value{$key}; |
599
|
212
|
100
|
|
|
|
448
|
if (ref $v) { |
600
|
48
|
|
|
|
|
167
|
$v = ConvertStruct($et, $flatInfo, $v, $type, $tagID); |
601
|
|
|
|
|
|
|
} else { |
602
|
164
|
|
|
|
|
434
|
$v = $et->GetValue($flatInfo, $type, $v); |
603
|
|
|
|
|
|
|
} |
604
|
212
|
50
|
|
|
|
770
|
$struct{$key} = $v if defined $v; # save the converted value |
605
|
|
|
|
|
|
|
} |
606
|
108
|
|
|
|
|
461
|
return \%struct; |
607
|
|
|
|
|
|
|
} elsif (ref $value eq 'ARRAY') { |
608
|
66
|
50
|
|
|
|
187
|
if (defined $$et{OPTIONS}{ListItem}) { |
609
|
0
|
|
|
|
|
0
|
my $li = $$et{OPTIONS}{ListItem}; |
610
|
0
|
0
|
|
|
|
0
|
return undef unless defined $$value[$li]; |
611
|
0
|
|
|
|
|
0
|
undef $$et{OPTIONS}{ListItem}; # only do top-level list |
612
|
0
|
|
|
|
|
0
|
my $val = ConvertStruct($et, $tagInfo, $$value[$li], $type, $parentID); |
613
|
0
|
|
|
|
|
0
|
$$et{OPTIONS}{ListItem} = $li; |
614
|
0
|
|
|
|
|
0
|
return $val; |
615
|
|
|
|
|
|
|
} else { |
616
|
66
|
|
|
|
|
117
|
my (@list, $val); |
617
|
66
|
|
|
|
|
173
|
foreach $val (@$value) { |
618
|
98
|
|
|
|
|
271
|
my $v = ConvertStruct($et, $tagInfo, $val, $type, $parentID); |
619
|
98
|
50
|
|
|
|
358
|
push @list, $v if defined $v; |
620
|
|
|
|
|
|
|
} |
621
|
66
|
|
|
|
|
246
|
return \@list; |
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
} else { |
624
|
30
|
|
|
|
|
112
|
return $et->GetValue($tagInfo, $type, $value); |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
629
|
|
|
|
|
|
|
# Restore XMP structures in extracted information |
630
|
|
|
|
|
|
|
# Inputs: 0) ExifTool object ref, 1) flag to keep original flattened tags |
631
|
|
|
|
|
|
|
# Notes: also restores lists (including multi-dimensional) |
632
|
|
|
|
|
|
|
sub RestoreStruct($;$) |
633
|
|
|
|
|
|
|
{ |
634
|
28
|
|
|
28
|
0
|
73
|
local $_; |
635
|
28
|
|
|
|
|
101
|
my ($et, $keepFlat) = @_; |
636
|
28
|
|
|
|
|
86
|
my ($key, %structs, %var, %lists, $si, %listKeys, @siList); |
637
|
28
|
|
|
|
|
97
|
my $valueHash = $$et{VALUE}; |
638
|
28
|
|
|
|
|
76
|
my $fileOrder = $$et{FILE_ORDER}; |
639
|
28
|
|
|
|
|
81
|
my $tagExtra = $$et{TAG_EXTRA}; |
640
|
28
|
|
|
|
|
76
|
foreach $key (keys %{$$et{TAG_INFO}}) { |
|
28
|
|
|
|
|
614
|
|
641
|
2326
|
100
|
|
|
|
4521
|
$$tagExtra{$key} or next; |
642
|
1337
|
100
|
|
|
|
2879
|
my $structProps = $$tagExtra{$key}{Struct} or next; |
643
|
329
|
|
|
|
|
589
|
delete $$tagExtra{$key}{Struct}; # (don't re-use) |
644
|
329
|
|
|
|
|
624
|
my $tagInfo = $$et{TAG_INFO}{$key}; # tagInfo for flattened tag |
645
|
329
|
|
|
|
|
576
|
my $table = $$tagInfo{Table}; |
646
|
329
|
|
|
|
|
545
|
my $prop = shift @$structProps; |
647
|
329
|
|
|
|
|
699
|
my $tag = $$prop[0]; |
648
|
|
|
|
|
|
|
# get reference to structure tag (or normal list tag if not a structure) |
649
|
329
|
100
|
|
|
|
767
|
my $strInfo = @$structProps ? $$table{$tag} : $tagInfo; |
650
|
329
|
100
|
|
|
|
585
|
if ($strInfo) { |
651
|
326
|
50
|
|
|
|
810
|
ref $strInfo eq 'HASH' or next; # (just to be safe) |
652
|
326
|
50
|
66
|
|
|
1061
|
if (@$structProps and not $$strInfo{Struct}) { |
653
|
|
|
|
|
|
|
# this could happen for invalid XMP containing mixed lists |
654
|
|
|
|
|
|
|
# (or for something like this -- what should we do here?: |
655
|
|
|
|
|
|
|
# test) |
656
|
0
|
0
|
|
|
|
0
|
$et->Warn("$$strInfo{Name} is not a structure!") unless $$et{NO_STRUCT_WARN}; |
657
|
0
|
|
|
|
|
0
|
next; |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
} else { |
660
|
|
|
|
|
|
|
# create new entry in tag table for this structure |
661
|
3
|
|
50
|
|
|
17
|
my $g1 = $$table{GROUPS}{0} || 'XMP'; |
662
|
3
|
|
|
|
|
8
|
my $name = $tag; |
663
|
|
|
|
|
|
|
# tag keys will have a group 1 prefix when coming from import of XML from -X option |
664
|
3
|
50
|
|
|
|
21
|
if ($tag =~ /(.+):(.+)/) { |
665
|
3
|
|
|
|
|
8
|
my $ns; |
666
|
3
|
|
|
|
|
11
|
($ns, $name) = ($1, $2); |
667
|
3
|
|
|
|
|
7
|
$ns =~ s/^XMP-//; # remove leading "XMP-" if it exists because we add it later |
668
|
3
|
50
|
|
|
|
11
|
$ns = $stdXlatNS{$ns} if $stdXlatNS{$ns}; |
669
|
3
|
|
|
|
|
9
|
$g1 .= "-$ns"; |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
$strInfo = { |
672
|
3
|
|
|
|
|
17
|
Name => ucfirst $name, |
673
|
|
|
|
|
|
|
Groups => { 1 => $g1 }, |
674
|
|
|
|
|
|
|
Struct => 'Unknown', |
675
|
|
|
|
|
|
|
}; |
676
|
|
|
|
|
|
|
# add Struct entry if this is a structure |
677
|
3
|
50
|
|
|
|
10
|
if (@$structProps) { |
|
|
0
|
|
|
|
|
|
678
|
|
|
|
|
|
|
# this is a structure |
679
|
3
|
50
|
|
|
|
16
|
$$strInfo{Struct} = { STRUCT_NAME => 'XMP Unknown' } if @$structProps; |
680
|
|
|
|
|
|
|
} elsif ($$tagInfo{LangCode}) { |
681
|
|
|
|
|
|
|
# this is lang-alt list |
682
|
0
|
|
|
|
|
0
|
$tag = $tag . '-' . $$tagInfo{LangCode}; |
683
|
0
|
|
|
|
|
0
|
$$strInfo{LangCode} = $$tagInfo{LangCode}; |
684
|
|
|
|
|
|
|
} |
685
|
3
|
|
|
|
|
11
|
AddTagToTable($table, $tag, $strInfo); |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
# use strInfo ref for base key to avoid collisions |
688
|
329
|
|
|
|
|
556
|
$tag = $strInfo; |
689
|
329
|
|
|
|
|
564
|
my $struct = \%structs; |
690
|
329
|
|
|
|
|
665
|
my $oldStruct = $structs{$strInfo}; |
691
|
|
|
|
|
|
|
# (fyi: 'lang-alt' Writable type will be valid even if tag is not pre-defined) |
692
|
329
|
|
100
|
|
|
979
|
my $writable = $$tagInfo{Writable} || ''; |
693
|
|
|
|
|
|
|
# walk through the stored structure property information |
694
|
|
|
|
|
|
|
# to rebuild this structure |
695
|
329
|
|
|
|
|
499
|
my ($err, $i); |
696
|
329
|
|
|
|
|
457
|
for (;;) { |
697
|
579
|
|
|
|
|
932
|
my $index = $$prop[1]; |
698
|
579
|
100
|
100
|
|
|
1545
|
if ($index and not @$structProps) { |
699
|
|
|
|
|
|
|
# ignore this list if it is a simple lang-alt tag |
700
|
216
|
100
|
|
|
|
486
|
if ($writable eq 'lang-alt') { |
701
|
90
|
|
|
|
|
179
|
pop @$prop; # remove lang-alt index |
702
|
90
|
100
|
|
|
|
288
|
undef $index if @$prop < 2; |
703
|
|
|
|
|
|
|
} |
704
|
|
|
|
|
|
|
# add language code if necessary |
705
|
216
|
100
|
100
|
|
|
616
|
if ($$tagInfo{LangCode} and not ref $tag) { |
706
|
24
|
|
|
|
|
63
|
$tag = $tag . '-' . $$tagInfo{LangCode}; |
707
|
|
|
|
|
|
|
} |
708
|
|
|
|
|
|
|
} |
709
|
579
|
|
|
|
|
983
|
my $nextStruct = $$struct{$tag}; |
710
|
579
|
100
|
|
|
|
1072
|
if (defined $index) { |
711
|
|
|
|
|
|
|
# the field is a list |
712
|
276
|
|
|
|
|
504
|
$index = substr $index, 1; # remove digit count |
713
|
276
|
100
|
|
|
|
510
|
if ($nextStruct) { |
714
|
160
|
50
|
|
|
|
407
|
ref $nextStruct eq 'ARRAY' or $err = 2, last; |
715
|
160
|
|
|
|
|
252
|
$struct = $nextStruct; |
716
|
|
|
|
|
|
|
} else { |
717
|
116
|
|
|
|
|
415
|
$struct = $$struct{$tag} = [ ]; |
718
|
|
|
|
|
|
|
} |
719
|
276
|
|
|
|
|
495
|
$nextStruct = $$struct[$index]; |
720
|
|
|
|
|
|
|
# descend into multi-dimensional lists |
721
|
276
|
|
|
|
|
654
|
for ($i=2; $$prop[$i]; ++$i) { |
722
|
0
|
0
|
|
|
|
0
|
if ($nextStruct) { |
723
|
0
|
0
|
|
|
|
0
|
ref $nextStruct eq 'ARRAY' or last; |
724
|
0
|
|
|
|
|
0
|
$struct = $nextStruct; |
725
|
|
|
|
|
|
|
} else { |
726
|
0
|
|
|
|
|
0
|
$lists{$struct} = $struct; |
727
|
0
|
|
|
|
|
0
|
$struct = $$struct[$index] = [ ]; |
728
|
|
|
|
|
|
|
} |
729
|
0
|
|
|
|
|
0
|
$nextStruct = $$struct[$index]; |
730
|
0
|
|
|
|
|
0
|
$index = substr $$prop[$i], 1; |
731
|
|
|
|
|
|
|
} |
732
|
276
|
100
|
|
|
|
698
|
if (ref $nextStruct eq 'HASH') { |
|
|
100
|
|
|
|
|
|
733
|
61
|
|
|
|
|
96
|
$struct = $nextStruct; # continue building sub-structure |
734
|
|
|
|
|
|
|
} elsif (@$structProps) { |
735
|
66
|
|
|
|
|
237
|
$lists{$struct} = $struct; |
736
|
66
|
|
|
|
|
185
|
$struct = $$struct[$index] = { }; |
737
|
|
|
|
|
|
|
} else { |
738
|
149
|
|
|
|
|
355
|
$lists{$struct} = $struct; |
739
|
149
|
|
|
|
|
335
|
$$struct[$index] = $$valueHash{$key}; |
740
|
149
|
|
|
|
|
271
|
last; |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
} else { |
743
|
303
|
100
|
|
|
|
649
|
if ($nextStruct) { |
|
|
100
|
|
|
|
|
|
744
|
93
|
50
|
|
|
|
255
|
ref $nextStruct eq 'HASH' or $err = 3, last; |
745
|
93
|
|
|
|
|
134
|
$struct = $nextStruct; |
746
|
|
|
|
|
|
|
} elsif (@$structProps) { |
747
|
30
|
|
|
|
|
109
|
$struct = $$struct{$tag} = { }; |
748
|
|
|
|
|
|
|
} else { |
749
|
180
|
|
|
|
|
1708
|
$$struct{$tag} = $$valueHash{$key}; |
750
|
180
|
|
|
|
|
318
|
last; |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
} |
753
|
250
|
50
|
|
|
|
638
|
$prop = shift @$structProps or last; |
754
|
250
|
|
|
|
|
482
|
$tag = $$prop[0]; |
755
|
250
|
100
|
|
|
|
626
|
if ($tag =~ /(.+):(.+)/) { |
756
|
|
|
|
|
|
|
# tag in variable-namespace tables will have a leading |
757
|
|
|
|
|
|
|
# XMP namespace on the tag name. In this case, add |
758
|
|
|
|
|
|
|
# the corresponding group1 name to the tag ID. |
759
|
17
|
|
|
|
|
59
|
my ($ns, $name) = ($1, $2); |
760
|
17
|
100
|
|
|
|
60
|
$ns = $stdXlatNS{$ns} if $stdXlatNS{$ns}; |
761
|
17
|
|
|
|
|
58
|
$tag = "XMP-$ns:" . ucfirst $name; |
762
|
|
|
|
|
|
|
} else { |
763
|
233
|
|
|
|
|
513
|
$tag = ucfirst $tag; |
764
|
|
|
|
|
|
|
} |
765
|
|
|
|
|
|
|
} |
766
|
329
|
50
|
|
|
|
853
|
if ($err) { |
|
|
100
|
|
|
|
|
|
767
|
|
|
|
|
|
|
# this may happen if we have a structural error in the XMP |
768
|
|
|
|
|
|
|
# (like an improperly contained list for example) |
769
|
0
|
0
|
|
|
|
0
|
unless ($$et{NO_STRUCT_WARN}) { |
770
|
0
|
|
0
|
|
|
0
|
my $ns = $$tagInfo{Namespace} || $$tagInfo{Table}{NAMESPACE} || ''; |
771
|
0
|
|
|
|
|
0
|
$et->Warn("Error $err placing $ns:$$tagInfo{TagID} in structure or list", 1); |
772
|
|
|
|
|
|
|
} |
773
|
0
|
0
|
|
|
|
0
|
delete $structs{$strInfo} unless $oldStruct; |
774
|
|
|
|
|
|
|
} elsif ($tagInfo eq $strInfo) { |
775
|
|
|
|
|
|
|
# just a regular list tag (or an empty structure) |
776
|
178
|
100
|
|
|
|
377
|
if ($oldStruct) { |
777
|
|
|
|
|
|
|
# keep tag with lowest numbered key (well, not exactly, since |
778
|
|
|
|
|
|
|
# "Tag (10)" is lt "Tag (2)", but at least "Tag" is lt |
779
|
|
|
|
|
|
|
# everything else, and this is really what we care about) |
780
|
75
|
|
|
|
|
172
|
my $k = $listKeys{$oldStruct}; |
781
|
75
|
50
|
|
|
|
195
|
if ($k) { # ($k will be undef for an empty structure) |
782
|
75
|
100
|
|
|
|
206
|
if ($k lt $key) { |
783
|
|
|
|
|
|
|
# keep lowest file order |
784
|
44
|
100
|
|
|
|
144
|
$$fileOrder{$k} = $$fileOrder{$key} if $$fileOrder{$k} > $$fileOrder{$key}; |
785
|
44
|
|
|
|
|
184
|
$et->DeleteTag($key); |
786
|
44
|
|
|
|
|
135
|
next; |
787
|
|
|
|
|
|
|
} |
788
|
31
|
100
|
|
|
|
106
|
$$fileOrder{$key} = $$fileOrder{$k} if $$fileOrder{$key} > $$fileOrder{$k}; |
789
|
31
|
|
|
|
|
164
|
$et->DeleteTag($k); # remove tag with greater copy number |
790
|
|
|
|
|
|
|
} |
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
# replace existing value with new list |
793
|
134
|
|
|
|
|
294
|
$$valueHash{$key} = $structs{$strInfo}; |
794
|
134
|
|
|
|
|
553
|
$listKeys{$structs{$strInfo}} = $key; # save key for this list tag |
795
|
|
|
|
|
|
|
} else { |
796
|
|
|
|
|
|
|
# save strInfo ref and file order |
797
|
151
|
100
|
|
|
|
370
|
if ($var{$strInfo}) { |
798
|
|
|
|
|
|
|
# set file order to just before the first associated flattened tag |
799
|
104
|
100
|
|
|
|
399
|
if ($var{$strInfo}[1] > $$fileOrder{$key}) { |
800
|
34
|
|
|
|
|
97
|
$var{$strInfo}[1] = $$fileOrder{$key} - 0.5; |
801
|
|
|
|
|
|
|
} |
802
|
|
|
|
|
|
|
} else { |
803
|
47
|
|
|
|
|
219
|
$var{$strInfo} = [ $strInfo, $$fileOrder{$key} - 0.5 ]; |
804
|
|
|
|
|
|
|
} |
805
|
|
|
|
|
|
|
# preserve original flattened tags if requested |
806
|
151
|
100
|
|
|
|
321
|
if ($keepFlat) { |
807
|
81
|
50
|
|
|
|
243
|
my $extra = $$tagExtra{$key} or next; |
808
|
|
|
|
|
|
|
# restore list behaviour of this flattened tag |
809
|
81
|
100
|
|
|
|
323
|
if ($$extra{NoList}) { |
|
|
100
|
|
|
|
|
|
810
|
6
|
|
|
|
|
40
|
$$valueHash{$key} = $$extra{NoList}; |
811
|
6
|
|
|
|
|
22
|
delete $$extra{NoList}; |
812
|
|
|
|
|
|
|
} elsif ($$extra{NoListDel}) { |
813
|
|
|
|
|
|
|
# delete this tag since its value was included another list |
814
|
8
|
|
|
|
|
34
|
$et->DeleteTag($key); |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
} else { |
817
|
70
|
|
|
|
|
216
|
$et->DeleteTag($key); # delete the flattened tag |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
} |
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
# fill in undefined items in lists. In theory, undefined list items should |
822
|
|
|
|
|
|
|
# be fine, but in practice the calling code may not check for this (and |
823
|
|
|
|
|
|
|
# historically this wasn't necessary, so do this for backward compatibility) |
824
|
28
|
|
|
|
|
316
|
foreach $si (keys %lists) { |
825
|
116
|
|
100
|
|
|
190
|
defined $_ or $_ = '' foreach @{$lists{$si}}; |
|
116
|
|
|
|
|
444
|
|
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
# make a list of all new structures we generated |
828
|
28
|
|
66
|
|
|
318
|
$var{$_} and push @siList, $_ foreach keys %structs; |
829
|
|
|
|
|
|
|
# save new structures in the same order they were read from file |
830
|
28
|
|
|
|
|
178
|
foreach $si (sort { $var{$a}[1] <=> $var{$b}[1] } @siList) { |
|
42
|
|
|
|
|
105
|
|
831
|
|
|
|
|
|
|
# test to see if a tag for this structure has already been generated |
832
|
|
|
|
|
|
|
# (this could happen only if one of the structures in a list was empty) |
833
|
47
|
|
|
|
|
160
|
$key = $var{$si}[0]{Name}; |
834
|
47
|
|
|
|
|
108
|
my $found; |
835
|
47
|
50
|
|
|
|
135
|
if ($$valueHash{$key}) { |
836
|
0
|
|
|
|
|
0
|
my @keys = grep /^$key( \(\d+\))?$/, keys %$valueHash; |
837
|
0
|
|
|
|
|
0
|
foreach $key (@keys) { |
838
|
0
|
0
|
|
|
|
0
|
next unless $$valueHash{$key} eq $structs{$si}; |
839
|
0
|
|
|
|
|
0
|
$found = 1; |
840
|
0
|
|
|
|
|
0
|
last; |
841
|
|
|
|
|
|
|
} |
842
|
|
|
|
|
|
|
} |
843
|
47
|
50
|
|
|
|
146
|
unless ($found) { |
844
|
|
|
|
|
|
|
# otherwise, generate a new tag for this structure |
845
|
47
|
|
|
|
|
169
|
$key = $et->FoundTag($var{$si}[0], ''); |
846
|
47
|
|
|
|
|
225
|
$$valueHash{$key} = $structs{$si}; |
847
|
|
|
|
|
|
|
} |
848
|
47
|
|
|
|
|
331
|
$$fileOrder{$key} = $var{$si}[1]; |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
} |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
1; #end |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
__END__ |