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