line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
########################################################### |
2
|
|
|
|
|
|
|
# A Perl package for showing/modifying JPEG (meta)data. # |
3
|
|
|
|
|
|
|
# Copyright (C) 2004,2005,2006 Stefano Bettelli # |
4
|
|
|
|
|
|
|
# See the COPYING and LICENSE files for license terms. # |
5
|
|
|
|
|
|
|
########################################################### |
6
|
|
|
|
|
|
|
package Image::MetaData::JPEG; |
7
|
14
|
|
|
14
|
|
58
|
use Image::MetaData::JPEG::data::Tables qw(:Endianness :TagsAPP1_Exif); |
|
14
|
|
|
|
|
17
|
|
|
14
|
|
|
|
|
2843
|
|
8
|
14
|
|
|
14
|
|
71
|
use Image::MetaData::JPEG::Segment; |
|
14
|
|
|
|
|
22
|
|
|
14
|
|
|
|
|
248
|
|
9
|
14
|
|
|
14
|
|
49
|
no integer; |
|
14
|
|
|
|
|
14
|
|
|
14
|
|
|
|
|
61
|
|
10
|
14
|
|
|
14
|
|
249
|
use strict; |
|
14
|
|
|
|
|
16
|
|
|
14
|
|
|
|
|
310
|
|
11
|
14
|
|
|
14
|
|
50
|
use warnings; |
|
14
|
|
|
|
|
15
|
|
|
14
|
|
|
|
|
6091
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
########################################################### |
14
|
|
|
|
|
|
|
# This method finds the $index-th Exif APP1 segment in # |
15
|
|
|
|
|
|
|
# the file, and returns its reference. If $index is # |
16
|
|
|
|
|
|
|
# undefined, it defaults to zero (i.e., first segment). # |
17
|
|
|
|
|
|
|
# If no such segment exists, it returns undef. If $index # |
18
|
|
|
|
|
|
|
# is (-1), the routine returns the number of available # |
19
|
|
|
|
|
|
|
# Exif APP1 segments (which is >= 0). # |
20
|
|
|
|
|
|
|
########################################################### |
21
|
|
|
|
|
|
|
sub retrieve_app1_Exif_segment { |
22
|
64
|
|
|
64
|
1
|
3670
|
my ($this, $index) = @_; |
23
|
|
|
|
|
|
|
# prepare the segment reference to be returned |
24
|
64
|
|
|
|
|
91
|
my $chosen_segment = undef; |
25
|
|
|
|
|
|
|
# $index defaults to zero if undefined |
26
|
64
|
100
|
|
|
|
184
|
$index = 0 unless defined $index; |
27
|
|
|
|
|
|
|
# get the references of all APP1 segments |
28
|
64
|
|
|
|
|
233
|
my @references = $this->get_segments('APP1$'); |
29
|
|
|
|
|
|
|
# filter out those without Exif information |
30
|
64
|
|
|
|
|
110
|
@references = grep { $_->is_app1_Exif() } @references; |
|
64
|
|
|
|
|
168
|
|
31
|
|
|
|
|
|
|
# if $index is -1, return the size of @references |
32
|
64
|
100
|
|
|
|
166
|
return scalar @references if $index == -1; |
33
|
|
|
|
|
|
|
# return the $index-th such segment, or undef if absent |
34
|
55
|
100
|
|
|
|
185
|
return exists $references[$index] ? $references[$index] : undef; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
########################################################### |
38
|
|
|
|
|
|
|
# This method forces an Exif APP1 segment to be present # |
39
|
|
|
|
|
|
|
# in the file, and returns its reference. The algorithm # |
40
|
|
|
|
|
|
|
# is the following: 1) if at least one segment with these # |
41
|
|
|
|
|
|
|
# properties is already present, the first one is retur- # |
42
|
|
|
|
|
|
|
# ned; 2) if [1] fails, an APP1 segment is added and # |
43
|
|
|
|
|
|
|
# initialised with an Exif structure. # |
44
|
|
|
|
|
|
|
########################################################### |
45
|
|
|
|
|
|
|
sub provide_app1_Exif_segment { |
46
|
137
|
|
|
137
|
1
|
243
|
my ($this) = @_; |
47
|
|
|
|
|
|
|
# get the references of all APP1 segments |
48
|
137
|
|
|
|
|
585
|
my @app1_refs = $this->get_segments('APP1$'); |
49
|
|
|
|
|
|
|
# filter out those without Exif information |
50
|
137
|
|
|
|
|
251
|
my @Exif_refs = grep { $_->is_app1_Exif() } @app1_refs; |
|
128
|
|
|
|
|
362
|
|
51
|
|
|
|
|
|
|
# if @Exif_refs is not empty, return the first segment |
52
|
137
|
100
|
|
|
|
470
|
return $Exif_refs[0] if @Exif_refs; |
53
|
|
|
|
|
|
|
# if we are still here, an Exif APP1 segment must be created |
54
|
|
|
|
|
|
|
# and initialised (contrary to the IPTC case, an existing APP1 |
55
|
|
|
|
|
|
|
# segment, presumably XPM, cannot be "adapted"). We write here |
56
|
|
|
|
|
|
|
# a minimal Exif segment with no data at all (in big endian). |
57
|
9
|
|
|
|
|
61
|
my $minimal_exif = $APP1_EXIF_TAG . $BIG_ENDIAN |
58
|
|
|
|
|
|
|
. pack "nNnN", $APP1_TIFF_SIG, 8, 0, 0; |
59
|
9
|
|
|
|
|
48
|
my $Exif = new Image::MetaData::JPEG::Segment('APP1', \ $minimal_exif); |
60
|
|
|
|
|
|
|
# choose a position for the new segment (the improved version |
61
|
|
|
|
|
|
|
# of find_new_app_segment_position can now be safely used). |
62
|
9
|
|
|
|
|
44
|
my $position = $this->find_new_app_segment_position('APP1'); |
63
|
|
|
|
|
|
|
# actually insert the segment |
64
|
9
|
|
|
|
|
43
|
$this->insert_segments($Exif, $position); |
65
|
|
|
|
|
|
|
# return a reference to the new segment |
66
|
9
|
|
|
|
|
23
|
return $Exif; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
########################################################### |
70
|
|
|
|
|
|
|
# This method eliminates the $index-th Exif APP1 segment # |
71
|
|
|
|
|
|
|
# from the JPEG file segment list. If $index is (-1) or # |
72
|
|
|
|
|
|
|
# undef, all Exif APP1 segments are affected at once. # |
73
|
|
|
|
|
|
|
########################################################### |
74
|
|
|
|
|
|
|
sub remove_app1_Exif_info { |
75
|
8
|
|
|
8
|
1
|
22148
|
my ($this, $index) = @_; |
76
|
|
|
|
|
|
|
# the default value for $index is -1 |
77
|
8
|
100
|
|
|
|
38
|
$index = -1 unless defined $index; |
78
|
|
|
|
|
|
|
# this is the list of segments to be purged (initially empty) |
79
|
8
|
|
|
|
|
20
|
my %deleteme = (); |
80
|
|
|
|
|
|
|
# call the selection routine and save the segment reference |
81
|
8
|
|
|
|
|
30
|
my $segment = $this->retrieve_app1_Exif_segment($index); |
82
|
|
|
|
|
|
|
# if $segment is really a non-null segment reference, mark it |
83
|
|
|
|
|
|
|
# for deletion; otherwise, it is the number of segments to be |
84
|
|
|
|
|
|
|
# deleted (this happens if $index is -1). In this case, the |
85
|
|
|
|
|
|
|
# whole procedure is repeated for every index. |
86
|
8
|
50
|
|
|
|
28
|
$segment->{name} = "deleteme" if ref $segment; |
87
|
8
|
50
|
|
|
|
26
|
if ($index == -1) { $this->retrieve_app1_Exif_segment($_) |
88
|
8
|
|
|
|
|
34
|
->{name} = "deleteme" for 0..($segment-1); } |
89
|
|
|
|
|
|
|
# remove marked segments from the file |
90
|
8
|
|
|
|
|
38
|
$this->drop_segments('deleteme'); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
########################################################### |
94
|
|
|
|
|
|
|
# This method is an interface to the method with the same # |
95
|
|
|
|
|
|
|
# name in the Segment class. First, the first Exif APP1 # |
96
|
|
|
|
|
|
|
# segment is retrieved (if there is no such segment, the # |
97
|
|
|
|
|
|
|
# undefined value is returned). Then the get_Exif_data is # |
98
|
|
|
|
|
|
|
# called on this segment passing the arguments through. # |
99
|
|
|
|
|
|
|
# For further details, see Segment::get_Exif_data() and # |
100
|
|
|
|
|
|
|
# JPEG::retrieve_app1_Exif_segment(). # |
101
|
|
|
|
|
|
|
########################################################### |
102
|
|
|
|
|
|
|
sub get_Exif_data { |
103
|
33
|
|
|
33
|
1
|
17036
|
my $this = shift; |
104
|
|
|
|
|
|
|
# get the first Exif APP1 segment in the current JPEG |
105
|
|
|
|
|
|
|
# file (if no such segment exists, this returns undef). |
106
|
33
|
|
|
|
|
108
|
my $segment = $this->retrieve_app1_Exif_segment(); |
107
|
|
|
|
|
|
|
# return undef if not suitable segment exists |
108
|
33
|
50
|
|
|
|
89
|
return undef unless defined $segment; |
109
|
|
|
|
|
|
|
# pass the arguments through to the Segment method |
110
|
33
|
|
|
|
|
92
|
return $segment->get_Exif_data(@_); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
########################################################### |
114
|
|
|
|
|
|
|
# This method is an interface to the method with the same # |
115
|
|
|
|
|
|
|
# name in the Segment class. First, the first Exif APP1 # |
116
|
|
|
|
|
|
|
# segment is retrieved (if there is no such segment, one # |
117
|
|
|
|
|
|
|
# is created and initialised). Then the set_Exif_data is # |
118
|
|
|
|
|
|
|
# called on this segment passing the arguments through. # |
119
|
|
|
|
|
|
|
# For further details, see Segment::set_Exif_data() and # |
120
|
|
|
|
|
|
|
# JPEG::provide_app1_Exif_segment(). # |
121
|
|
|
|
|
|
|
########################################################### |
122
|
|
|
|
|
|
|
sub set_Exif_data { |
123
|
134
|
|
|
134
|
1
|
54424
|
my $this = shift; |
124
|
|
|
|
|
|
|
# get the first Exif APP1 segment in the current JPEG file |
125
|
|
|
|
|
|
|
# (if there is no such segment, initialise one; therefore, |
126
|
|
|
|
|
|
|
# this call cannot fail [mhh ...]). |
127
|
134
|
|
|
|
|
483
|
my $segment = $this->provide_app1_Exif_segment(); |
128
|
|
|
|
|
|
|
# pass the arguments through to the Segment method |
129
|
134
|
|
|
|
|
409
|
return $segment->set_Exif_data(@_); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
########################################################### |
133
|
|
|
|
|
|
|
# An Interoperability subIFD is supposed to be used for, # |
134
|
|
|
|
|
|
|
# well, inter-operability, so it should be made as stan- # |
135
|
|
|
|
|
|
|
# dard as possible. This method takes care to chose a set # |
136
|
|
|
|
|
|
|
# of "correct" values for you: the Index is set to "R98" # |
137
|
|
|
|
|
|
|
# (because we are interested in IFD0), Version to 1.0, # |
138
|
|
|
|
|
|
|
# FileFormat to Exif v.2.2, and the picture dimensions # |
139
|
|
|
|
|
|
|
# are taken from get_dimensions(). # |
140
|
|
|
|
|
|
|
########################################################### |
141
|
|
|
|
|
|
|
sub forge_interoperability_IFD { |
142
|
2
|
|
|
2
|
1
|
615
|
my $this = shift; |
143
|
|
|
|
|
|
|
# get the real picture dimensions |
144
|
2
|
|
|
|
|
9
|
my ($x_dim, $y_dim) = $this->get_dimensions(); |
145
|
|
|
|
|
|
|
# prepare a table of records for the Interop. IFD |
146
|
2
|
|
|
|
|
13
|
my $std_values = { |
147
|
|
|
|
|
|
|
'InteroperabilityIndex' => "R98", |
148
|
|
|
|
|
|
|
'InteroperabilityVersion' => "0100", |
149
|
|
|
|
|
|
|
'RelatedImageFileFormat', => "Exif JPEG Ver. 2.2", |
150
|
|
|
|
|
|
|
'RelatedImageWidth' => $x_dim, |
151
|
|
|
|
|
|
|
'RelatedImageLength' => $y_dim, }; |
152
|
|
|
|
|
|
|
# call the setter method for Exif data appropriately |
153
|
2
|
|
|
|
|
6
|
return $this->set_Exif_data($std_values, 'INTEROP_DATA', 'REPLACE'); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
########################################################### |
157
|
|
|
|
|
|
|
# The following routines best fit as Segment methods. # |
158
|
|
|
|
|
|
|
########################################################### |
159
|
|
|
|
|
|
|
package Image::MetaData::JPEG::Segment; |
160
|
14
|
|
|
14
|
|
68
|
use Image::MetaData::JPEG::data::Tables qw(:Lookups); |
|
14
|
|
|
|
|
22
|
|
|
14
|
|
|
|
|
27943
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
########################################################### |
163
|
|
|
|
|
|
|
# A private hash for get_Exif_data and set_Exif_data. # |
164
|
|
|
|
|
|
|
# Each '@' indicates the beginning of a new subdirectory. # |
165
|
|
|
|
|
|
|
########################################################### |
166
|
|
|
|
|
|
|
my %WHAT2IFD = ('ROOT_DATA' => '', |
167
|
|
|
|
|
|
|
'IFD0_DATA' => '@IFD0', |
168
|
|
|
|
|
|
|
'SUBIFD_DATA' => '@IFD0@SubIFD', |
169
|
|
|
|
|
|
|
'GPS_DATA' => '@IFD0@GPS', |
170
|
|
|
|
|
|
|
'INTEROP_DATA' => '@IFD0@SubIFD@Interop', |
171
|
|
|
|
|
|
|
'MAKERNOTE_DATA' => '@IFD0@SubIFD@MakerNoteData', |
172
|
|
|
|
|
|
|
'IFD1_DATA' => '@IFD1' ); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
########################################################### |
175
|
|
|
|
|
|
|
# This method inspects a segments, and returns "undef" if # |
176
|
|
|
|
|
|
|
# it is not an APP1 segment or if its structure is not # |
177
|
|
|
|
|
|
|
# Exif like. Otherwise, it returns "ok". # |
178
|
|
|
|
|
|
|
########################################################### |
179
|
|
|
|
|
|
|
sub is_app1_Exif { |
180
|
543
|
|
|
543
|
0
|
600
|
my ($this) = @_; |
181
|
|
|
|
|
|
|
# return undef if this segment is not APP1 |
182
|
543
|
50
|
|
|
|
1295
|
return undef unless $this->{name} eq 'APP1'; |
183
|
|
|
|
|
|
|
# return undef if there is no 'Identifier' in this segment |
184
|
|
|
|
|
|
|
# or if it does not match with an Exif-like segment |
185
|
543
|
|
|
|
|
1333
|
my $identifier = $this->search_record_value('Identifier'); |
186
|
543
|
50
|
33
|
|
|
2228
|
return undef unless defined $identifier && $identifier eq $APP1_EXIF_TAG; |
187
|
|
|
|
|
|
|
# return ok |
188
|
543
|
|
|
|
|
1293
|
return "ok"; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
########################################################### |
192
|
|
|
|
|
|
|
# This method accepts two arguments ($what and $type) and # |
193
|
|
|
|
|
|
|
# returns the content of the Exif APP1 segment packed in # |
194
|
|
|
|
|
|
|
# various forms. All Exif records are natively identified # |
195
|
|
|
|
|
|
|
# by numeric tags (keys), which can be "translated" into # |
196
|
|
|
|
|
|
|
# a human-readable form by using the Exif standard docs; # |
197
|
|
|
|
|
|
|
# only a few fields in the Exif APP1 preamble (they are # |
198
|
|
|
|
|
|
|
# not Exif records) are always identified by this module # |
199
|
|
|
|
|
|
|
# by means of textual tags. The $type argument selects # |
200
|
|
|
|
|
|
|
# the output format for the record keys (tags): # |
201
|
|
|
|
|
|
|
# - NUMERIC: record tags are native numeric keys # |
202
|
|
|
|
|
|
|
# - TEXTUAL: record tags are human-readable (default) # |
203
|
|
|
|
|
|
|
# Of course, record values are never translated. If a # |
204
|
|
|
|
|
|
|
# numeric Exif tag is not known, a custom textual key is # |
205
|
|
|
|
|
|
|
# created with "Unknown_tag_" followed by the numerical # |
206
|
|
|
|
|
|
|
# value (this solves problems with non-standard tags). # |
207
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
208
|
|
|
|
|
|
|
# Error conditions (invalid $what's and $type's) manifest # |
209
|
|
|
|
|
|
|
# themselves through an undefined return value. So, undef # |
210
|
|
|
|
|
|
|
# should not be used for other cases: use empty hashes or # |
211
|
|
|
|
|
|
|
# a reference to an empty string for the thumbnail. # |
212
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
213
|
|
|
|
|
|
|
# The subset of Exif tags returned by this method is # |
214
|
|
|
|
|
|
|
# determined by the value of $what. If $what is set equal # |
215
|
|
|
|
|
|
|
# to '*_DATA', this method returns a reference to a flat # |
216
|
|
|
|
|
|
|
# hash, corresponding to one or more IFD (sub)dirs: # |
217
|
|
|
|
|
|
|
# - ROOT_DATA APP1(TIFF header records and similar) # |
218
|
|
|
|
|
|
|
# - IFD0_DATA APP1@IFD0 (primary image TIFF tags) # |
219
|
|
|
|
|
|
|
# - SUBIFD_DATA APP1@IFD0@SubIFD (Exif private tags) # |
220
|
|
|
|
|
|
|
# - GPS_DATA APP1@IFD0@GPS (GPS data in IFD0) # |
221
|
|
|
|
|
|
|
# - INTEROP_DATA APP1@IFD0@SubIFD@Interop(erability) # |
222
|
|
|
|
|
|
|
# - IFD1_DATA APP1@IFD1 (thumbnail TIFF tags) # |
223
|
|
|
|
|
|
|
# - IMAGE_DATA a merge of IFD0_DATA and SUBIFD_DATA # |
224
|
|
|
|
|
|
|
# - THUMB_DATA an alias for IFD1_DATA # |
225
|
|
|
|
|
|
|
# Setting $what equal to 'ALL' returns a data dump very # |
226
|
|
|
|
|
|
|
# close to the Exif APP1 segment structure; the returned # |
227
|
|
|
|
|
|
|
# value is a reference to a hash of hashes: each element # |
228
|
|
|
|
|
|
|
# of the root-level hash is a pair ($name, $hashref), # |
229
|
|
|
|
|
|
|
# where $hashref points to a second-level hash containing # |
230
|
|
|
|
|
|
|
# a copy of all Exif records present in the $name IFD # |
231
|
|
|
|
|
|
|
# (sub)directory. The root-level hash includes a special # |
232
|
|
|
|
|
|
|
# root directory (named 'APP1') containing some non Exif # |
233
|
|
|
|
|
|
|
# parameters. Last, setting $what to 'THUMBNAIL' returns # |
234
|
|
|
|
|
|
|
# a reference to a copy of the actual Exif thumbnail # |
235
|
|
|
|
|
|
|
# image (not returned by 'THUMB_DATA'), if present, or a # |
236
|
|
|
|
|
|
|
# reference to an empty string, if not present. # |
237
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
238
|
|
|
|
|
|
|
# Note that the Exif record values' format is not checked # |
239
|
|
|
|
|
|
|
# to be valid according to the Exif standard. This is, in # |
240
|
|
|
|
|
|
|
# some sense, consistent with the fact that also "unknown"# |
241
|
|
|
|
|
|
|
# tags are included in the output. # |
242
|
|
|
|
|
|
|
########################################################### |
243
|
|
|
|
|
|
|
sub get_Exif_data { |
244
|
154
|
|
|
154
|
0
|
30726
|
my ($this, $what, $type) = @_; |
245
|
|
|
|
|
|
|
# refuse to work unless you are an Exif APP1 segment |
246
|
154
|
50
|
|
|
|
284
|
return undef unless $this->is_app1_Exif(); |
247
|
|
|
|
|
|
|
# set the default section and type, if undefined; |
248
|
154
|
100
|
|
|
|
306
|
$what = 'ALL' unless defined $what; |
249
|
154
|
100
|
|
|
|
272
|
$type = 'TEXTUAL' unless defined $type; |
250
|
|
|
|
|
|
|
# reject unknown types (return undef, which means 'error') |
251
|
154
|
100
|
|
|
|
535
|
return undef unless $type =~ /^NUMERIC$|^TEXTUAL$/; |
252
|
|
|
|
|
|
|
# a reference to the hash to be returned, initially empty |
253
|
152
|
|
|
|
|
182
|
my $pairs = {}; |
254
|
|
|
|
|
|
|
# ========= SPECIAL CASES ==================================== |
255
|
|
|
|
|
|
|
# IMAGE_DATA means IFD0_DATA and SUBIFD_DATA (merged) |
256
|
152
|
100
|
|
|
|
305
|
if ($what eq 'IMAGE_DATA') { |
257
|
10
|
|
|
|
|
24
|
for ('IFD0_DATA', 'SUBIFD_DATA') { |
258
|
20
|
|
|
|
|
54
|
my $h = $this->get_Exif_data($_, $type); |
259
|
20
|
|
|
|
|
174
|
@$pairs{keys %$h} = values %$h; } return $pairs; } |
|
10
|
|
|
|
|
33
|
|
260
|
|
|
|
|
|
|
# ALL means a hash of hashes with all subdirs (even if emtpy) |
261
|
142
|
100
|
|
|
|
260
|
if ($what eq 'ALL') { |
262
|
8
|
|
|
|
|
54
|
$$pairs{$_} = $this->get_Exif_data($_, $type) for keys %WHAT2IFD; |
263
|
8
|
|
|
|
|
31
|
return $pairs; } |
264
|
|
|
|
|
|
|
# $what equal to 'THUMBNAIL' is special: it returns a copy of the |
265
|
|
|
|
|
|
|
# thumbnail data area (this can be a self-contained JPEG picture |
266
|
|
|
|
|
|
|
# or an uncompressed picture needing more parameters from IFD1). |
267
|
|
|
|
|
|
|
# If no thumbnail is there, return a reference to an empty string |
268
|
134
|
100
|
|
|
|
255
|
if ($what eq 'THUMBNAIL') { |
269
|
8
|
|
|
|
|
25
|
my $thumbnail = $this->search_record_value('ThumbnailData'); |
270
|
8
|
100
|
|
|
|
46
|
return $thumbnail ? \ $thumbnail : \ (my $ns = ''); } |
271
|
|
|
|
|
|
|
# IFD1_DATA is an alias for THUMB_DATA |
272
|
126
|
100
|
|
|
|
232
|
$what = 'IFD1_DATA' if $what eq 'THUMB_DATA'; |
273
|
|
|
|
|
|
|
# ============================================================ |
274
|
|
|
|
|
|
|
# %WHAT2IFD keys must correspond to the legal $what's. It is now |
275
|
|
|
|
|
|
|
# time to reject unknown sections ('THUMBNAIL' already dealt with). |
276
|
|
|
|
|
|
|
# As usual, this error condition corresponds to returning undef. |
277
|
126
|
100
|
|
|
|
271
|
return undef unless exists $WHAT2IFD{$what}; |
278
|
|
|
|
|
|
|
# $WHAT2IFD{$what} contains a '@' separated list of dir names; |
279
|
|
|
|
|
|
|
# use it to retrieve a reference to the appropriate record list |
280
|
125
|
|
|
|
|
170
|
my $path = $WHAT2IFD{$what}; |
281
|
|
|
|
|
|
|
# follow the path blindly, get undef on problems |
282
|
125
|
|
|
|
|
290
|
my $dirref = $this->search_record_value($path); |
283
|
|
|
|
|
|
|
# give $path a second try, assuming the last part of the path |
284
|
|
|
|
|
|
|
# is just the beginning of a tag (this is needed for MakerNote). |
285
|
|
|
|
|
|
|
# This might modify $path and set $dirref to non-undefined. |
286
|
125
|
100
|
|
|
|
259
|
unless (defined $dirref) { |
287
|
27
|
|
|
|
|
176
|
$path =~ s/(.*@|)([^@]*)/$1/; |
288
|
27
|
|
|
|
|
74
|
my $partial_dirref = $this->search_record_value($path); |
289
|
422
|
|
|
|
|
969
|
$path .= $_->{key}, $dirref = $_->get_value(), last |
290
|
27
|
|
|
|
|
61
|
for (grep{$_->{key}=~/^$2/} @$partial_dirref);} |
291
|
|
|
|
|
|
|
# if $dirref is undefined, the corresponding subdirectory was not |
292
|
|
|
|
|
|
|
# present, and we are going to return a reference to an empty hash |
293
|
125
|
100
|
|
|
|
244
|
return $pairs unless $dirref; |
294
|
|
|
|
|
|
|
# map the record list reference to a full hash containing the subdir- |
295
|
|
|
|
|
|
|
# ectory records as (tag => values) pairs. Do not include $REFERENCE's |
296
|
|
|
|
|
|
|
# (private). Make COPIES of the array references found in $_->{values} |
297
|
|
|
|
|
|
|
# (the caller could use them to corrupt the internal structures). |
298
|
1546
|
|
|
|
|
1220
|
%$pairs = map { $_->{key} => [ @{$_->{values}} ] } |
|
1546
|
|
|
|
|
3853
|
|
|
1660
|
|
|
|
|
1912
|
|
299
|
112
|
|
|
|
|
169
|
grep { $_->{type} != $REFERENCE } @$dirref; |
300
|
|
|
|
|
|
|
# up to now, all record keys (tags) are numeric (exception made for |
301
|
|
|
|
|
|
|
# some MakerNote keys and all keys in the "root" directory, for which |
302
|
|
|
|
|
|
|
# there is no numeric counterpart). If $type is 'TEXTUAL', they must |
303
|
|
|
|
|
|
|
# be translated (test explicitely that they are numeric). |
304
|
112
|
100
|
|
|
|
349
|
if ($type eq "TEXTUAL") { |
305
|
|
|
|
|
|
|
# get the right numeric-to-textual conversion table with $path |
306
|
90
|
|
|
|
|
271
|
my $table = JPEG_lookup($this->{name}, $path); |
307
|
|
|
|
|
|
|
# run the translation (create a name also for unknown tags) |
308
|
90
|
100
|
|
|
|
338
|
%$pairs = map { (($_!~/^\d+$/)?$_:(exists $$table{$_}) ? $$table{$_} : |
|
1203
|
100
|
|
|
|
3926
|
|
309
|
|
|
|
|
|
|
"Unknown_tag_$_") => $$pairs{$_} } keys %$pairs; } |
310
|
|
|
|
|
|
|
# return the reference to the hash containing all data |
311
|
112
|
|
|
|
|
499
|
return $pairs; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
########################################################### |
315
|
|
|
|
|
|
|
# This method is the entry point for setting Exif data in # |
316
|
|
|
|
|
|
|
# the current APP1 segment. The mandatory arguments are: # |
317
|
|
|
|
|
|
|
# $data (hash reference, with new records to be written), # |
318
|
|
|
|
|
|
|
# $what (a scalar, selecting the concerned portion of the # |
319
|
|
|
|
|
|
|
# Exif APP1 segment) and $action (a scalar specifying the # |
320
|
|
|
|
|
|
|
# requested action). Valid values are: # |
321
|
|
|
|
|
|
|
# $action --> ADD | REPLACE # |
322
|
|
|
|
|
|
|
# $what --> IFD0_DATA, IFD1_DATA, INTEROP_DATA, # |
323
|
|
|
|
|
|
|
# GPS_DATA, SUBIFD_DATA (see get_Exif_data) # |
324
|
|
|
|
|
|
|
# THUMB_DATA (an alias for IFD1_DATA) # |
325
|
|
|
|
|
|
|
# IMAGE_DATA (IFD0_DATA or SUBIFD_DATA) # |
326
|
|
|
|
|
|
|
# ROOT_DATA (only 'Endianness' can be set) # |
327
|
|
|
|
|
|
|
# .- THUMBNAIL (including automatic fields) # |
328
|
|
|
|
|
|
|
# \____.--> $data is a scalar reference here ... # |
329
|
|
|
|
|
|
|
# The behaviour of $action is similar to that for IPTC # |
330
|
|
|
|
|
|
|
# data. Note that Exif records are non-repeatable in # |
331
|
|
|
|
|
|
|
# nature, so there is no need for an 'UPDATE' action in # |
332
|
|
|
|
|
|
|
# addition to 'ADD' (they would both overwrite an old re- # |
333
|
|
|
|
|
|
|
# cord with the same tag as a new record); $action equal # |
334
|
|
|
|
|
|
|
# to 'REPLACE', on the other hand, clears the appropriate # |
335
|
|
|
|
|
|
|
# record list(s) before the insertions. Records are # |
336
|
|
|
|
|
|
|
# rewritten in increasing (numerical) tag order. # |
337
|
|
|
|
|
|
|
# The elements of $data which can be converted to valid # |
338
|
|
|
|
|
|
|
# records are inserted in the appropriate (sub)IFD, the # |
339
|
|
|
|
|
|
|
# others are returned. The return value is always a hash # |
340
|
|
|
|
|
|
|
# reference; in general it contains rejected records. If # |
341
|
|
|
|
|
|
|
# an error occurs in a very early stage of the setter, # |
342
|
|
|
|
|
|
|
# this reference contains a single entry with key='ERROR' # |
343
|
|
|
|
|
|
|
# and value set to some meaningful error message. So, a # |
344
|
|
|
|
|
|
|
# reference to an empty hash means that everything was OK.# |
345
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
346
|
|
|
|
|
|
|
# $what equal to 'THUMBNAIL' is meant to replace the IFD1 # |
347
|
|
|
|
|
|
|
# thumbnail. $data should be a reference to a scalar or # |
348
|
|
|
|
|
|
|
# to a JPEG object containing the new thumbnail ; if it # |
349
|
|
|
|
|
|
|
# points to an emtpy string, the thumbnail is erased. # |
350
|
|
|
|
|
|
|
# Corresponding fields follow the thumbnail (all this is # |
351
|
|
|
|
|
|
|
# dealt with by a private method). $data undefined DOES # |
352
|
|
|
|
|
|
|
# NOT erase the thumbnail, it is an error (too dangerous).# |
353
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
354
|
|
|
|
|
|
|
# When $what is 'IMAGE_DATA', try to insert first into # |
355
|
|
|
|
|
|
|
# SubIFD, then, into IFD0. This favours SubIFD standard # |
356
|
|
|
|
|
|
|
# tags in front of IFD company-related non-standard tags. # |
357
|
|
|
|
|
|
|
# For security reasons however, these non-standard tags # |
358
|
|
|
|
|
|
|
# should be labelled as invalid: this would prevent them # |
359
|
|
|
|
|
|
|
# from being set but not from being recognised if present.# |
360
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
361
|
|
|
|
|
|
|
# Remeber that, even for $action eq REPLACE, we cannot # |
362
|
|
|
|
|
|
|
# delete all the records. We must preserve $REFERENCE # |
363
|
|
|
|
|
|
|
# records, otherwise the corresponding directories would # |
364
|
|
|
|
|
|
|
# be forgotten; we don't want that, for instance, SubIFD # |
365
|
|
|
|
|
|
|
# is deleted when the records of IFD0 are REPLACED. # |
366
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
367
|
|
|
|
|
|
|
# The fourth argument ($dontupdate) is to be considered # |
368
|
|
|
|
|
|
|
# strictly private. It is used by set_Exif_data itself # |
369
|
|
|
|
|
|
|
# when called with $action eq 'IMAGE_DATA', so that the # |
370
|
|
|
|
|
|
|
# update() routine can be called only once (not twice). # |
371
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
372
|
|
|
|
|
|
|
# First, some basic argument checking is performed: the # |
373
|
|
|
|
|
|
|
# segment must be of the appropriate type, $data must be # |
374
|
|
|
|
|
|
|
# a hash reference, $action and $what must be valid. # |
375
|
|
|
|
|
|
|
# Then, the appropriate record (sub)directory is created # |
376
|
|
|
|
|
|
|
# (this can trigger the creation of other directories), # |
377
|
|
|
|
|
|
|
# if it is not present. Then records are screened and # |
378
|
|
|
|
|
|
|
# set. Mandatory data are added, if not present, at the # |
379
|
|
|
|
|
|
|
# end of the process (see Tables.pm for this). Note that # |
380
|
|
|
|
|
|
|
# there are some record intercorrelations still neglected.# |
381
|
|
|
|
|
|
|
########################################################### |
382
|
|
|
|
|
|
|
sub set_Exif_data { |
383
|
197
|
|
|
197
|
0
|
21698
|
my ($this, $data, $what, $action, $dontupdate) = @_; |
384
|
|
|
|
|
|
|
# refuse to work unless you are an Exif APP1 segment |
385
|
197
|
50
|
|
|
|
431
|
return {'ERROR'=>'Not an Exif APP1 segment'} unless $this->is_app1_Exif(); |
386
|
|
|
|
|
|
|
# set the default action, if undefined |
387
|
197
|
100
|
|
|
|
614
|
$action = 'REPLACE' unless defined $action; |
388
|
|
|
|
|
|
|
# refuse to work for unkwnon actions |
389
|
197
|
100
|
|
|
|
949
|
return {'ERROR'=>"Unknown action $action"} unless $action =~ /ADD|REPLACE/; |
390
|
|
|
|
|
|
|
# return immediately if $data is undefined |
391
|
194
|
100
|
|
|
|
438
|
return {'ERROR'=>'Undefined data reference'} unless defined $data; |
392
|
|
|
|
|
|
|
# ========= SPECIAL CASES ==================================== |
393
|
|
|
|
|
|
|
# IMAGE_DATA: first, try to insert all tags into SubIFD, then, try |
394
|
|
|
|
|
|
|
# to insert rejected data into IFD0, last, return doubly rejected data. |
395
|
193
|
100
|
|
|
|
491
|
if ($what eq 'IMAGE_DATA') { |
396
|
16
|
|
|
|
|
51
|
my $rejected = $this->set_Exif_data($data, 'SUBIFD_DATA', $action, 1); |
397
|
16
|
|
|
|
|
79
|
return $this->set_Exif_data($rejected, 'IFD0_DATA', $action); } |
398
|
|
|
|
|
|
|
# THUMBNAIL requires a very specific treatment |
399
|
177
|
100
|
|
|
|
389
|
return $this->set_Exif_thumbnail($data) if $what eq 'THUMBNAIL'; |
400
|
|
|
|
|
|
|
# 'THUMB_DATA' is an alias to 'IFD1_DATA' |
401
|
172
|
50
|
|
|
|
420
|
$what = 'IFD1_DATA' if $what eq 'THUMB_DATA'; |
402
|
|
|
|
|
|
|
# ============================================================ |
403
|
|
|
|
|
|
|
# $data must be a hash reference (from this point on) |
404
|
172
|
50
|
|
|
|
414
|
return {'ERROR'=>'$data not a hash reference'} unless ref $data eq 'HASH'; |
405
|
|
|
|
|
|
|
# return with an error if $what is not a valid key in %WHAT2IFD |
406
|
172
|
100
|
|
|
|
546
|
return {'ERROR'=>"Unknown section $what"} unless exists $WHAT2IFD{$what}; |
407
|
|
|
|
|
|
|
# translate $what into a path specification |
408
|
168
|
|
|
|
|
384
|
my $path = 'APP1' . $WHAT2IFD{$what}; |
409
|
|
|
|
|
|
|
# the mandatory records list must be present (debug point) |
410
|
168
|
50
|
|
|
|
579
|
return {'ERROR'=>'no $mandatory records'} unless exists |
411
|
|
|
|
|
|
|
$IFD_SUBDIRS{$path}{'__mandatory'}; |
412
|
|
|
|
|
|
|
# get the mandatory record list |
413
|
168
|
|
|
|
|
301
|
my $mandatory = $IFD_SUBDIRS{$path}{'__mandatory'}; |
414
|
|
|
|
|
|
|
# all arguments look healty, go to stage two; get the record list |
415
|
|
|
|
|
|
|
# of the appropriate (sub)directory; this call creates the supporting |
416
|
|
|
|
|
|
|
# directory tree if necessary, taking care of gory details. |
417
|
168
|
|
|
|
|
479
|
my $record_list = $this->build_IFD_directory_tree($path); |
418
|
|
|
|
|
|
|
# analyse the passed records for correctness (syntactical rules); |
419
|
|
|
|
|
|
|
# the following function divides them into two obvious categories |
420
|
168
|
|
|
|
|
501
|
my ($rejected, $accepted) = $this->screen_records($data, $path); |
421
|
|
|
|
|
|
|
# For $action equal to 'ADD', we read the old records and insert |
422
|
|
|
|
|
|
|
# them in the $accepted hash, unless they are already present. |
423
|
|
|
|
|
|
|
# If $action is 'REPLACE' we preserve only the subdirectories |
424
|
168
|
100
|
|
|
|
486
|
my $save = $action eq 'REPLACE' ? 'p' : '.'; |
425
|
168
|
|
|
|
|
377
|
my $old_records = [ grep {$_->get_category() =~ $save} @$record_list ]; |
|
2846
|
|
|
|
|
4384
|
|
426
|
168
|
|
|
|
|
472
|
$this->complement_records($old_records, $accepted); |
427
|
|
|
|
|
|
|
# retrieve the section about mandatory values for this $path and transform |
428
|
|
|
|
|
|
|
# them into Records (there is also a syntactical analysis, but all records |
429
|
|
|
|
|
|
|
# should be accepted here, so I take the return value in scalar context). |
430
|
|
|
|
|
|
|
# ('B' is currently necessary for stupid root-level mandatory records) |
431
|
168
|
|
|
|
|
388
|
my ($notempty, $values) = $this->screen_records($mandatory, $path, 'B'); |
432
|
168
|
50
|
|
|
|
436
|
$this->die('Mandatory values rejected') if %$notempty; |
433
|
|
|
|
|
|
|
# merge in mandatory records, if they are not already present |
434
|
168
|
|
|
|
|
384
|
$this->complement_records($values, $accepted); |
435
|
|
|
|
|
|
|
# take all records from $accepted and set them into the record |
436
|
|
|
|
|
|
|
# list (their order must anambiguous, so perform a clever sorting). |
437
|
168
|
|
|
|
|
485
|
@$record_list = ordered_record_list($accepted, $path); |
438
|
|
|
|
|
|
|
# commit changes to the data area unless explicitely forbidden |
439
|
168
|
100
|
|
|
|
1124
|
$this->update() unless $dontupdate; |
440
|
|
|
|
|
|
|
# that's it, return the reference to the rejected data hash |
441
|
168
|
|
|
|
|
2562
|
return $rejected; |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
########################################################### |
445
|
|
|
|
|
|
|
# This private method is called by set_Exif_data when the # |
446
|
|
|
|
|
|
|
# $what argument is set to 'THUMBNAIL'. $data must be a # |
447
|
|
|
|
|
|
|
# reference to a JPEG object or a reference to a scalar # |
448
|
|
|
|
|
|
|
# value containing a valid JPEG stream (an undefined ref. # |
449
|
|
|
|
|
|
|
# is considered an error!). First, we erase all thumbnail # |
450
|
|
|
|
|
|
|
# related records from IFD1 then we reinsert those which # |
451
|
|
|
|
|
|
|
# are appropriate. Last, the update method is called # |
452
|
|
|
|
|
|
|
# (this also fixes some fields). # |
453
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
454
|
|
|
|
|
|
|
# ($$data is ''): nothing else to do, thumbnail erased. # |
455
|
|
|
|
|
|
|
# ($$data is a JPEG stream or a JPEG object): thumbnail # |
456
|
|
|
|
|
|
|
# data are saved in the root level directory, and a few # |
457
|
|
|
|
|
|
|
# records are added to IFD1: 'JPEGInterchangeFormat', # |
458
|
|
|
|
|
|
|
# 'JPEGInterchangeFormatLength', and 'Compression' set # |
459
|
|
|
|
|
|
|
# to six (this indicates a JPEG thumbnail). # |
460
|
|
|
|
|
|
|
########################################################### |
461
|
|
|
|
|
|
|
sub set_Exif_thumbnail { |
462
|
5
|
|
|
5
|
0
|
7
|
my ($this, $dataref) = @_; |
463
|
|
|
|
|
|
|
# this variable holds the thumbnail format |
464
|
5
|
|
|
|
|
5
|
my $type = undef; |
465
|
|
|
|
|
|
|
# $dataref must be a valid reference: I don't want the user to be |
466
|
|
|
|
|
|
|
# able to erase the thumbnail by passing an erroneously undef ref. |
467
|
5
|
50
|
|
|
|
23
|
return { 'ERROR' => 'argument is not a reference' } unless ref $dataref; |
468
|
|
|
|
|
|
|
# if $dataref points to an Image::MetaData::JPEG object, replace it |
469
|
|
|
|
|
|
|
# with a reference to its bare content and set $type to 'JPEG'. |
470
|
5
|
100
|
|
|
|
16
|
if ('Image::MetaData::JPEG' eq ref $dataref) { |
471
|
2
|
|
|
|
|
5
|
my $r = ""; $dataref->save(\ $r); $dataref = \ $r; $type = 'JPEG'; } |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
3
|
|
472
|
|
|
|
|
|
|
# $dataref must now be a scalar reference; everything else is an error |
473
|
5
|
50
|
|
|
|
20
|
return { 'ERROR' => 'not a good reference' } if ref $dataref ne 'SCALAR'; |
474
|
|
|
|
|
|
|
# try to recognise the content of $$dataref. If it is defined but empty, |
475
|
|
|
|
|
|
|
# we just need to erase the thumbnail. If it is accepted by the JPEG |
476
|
|
|
|
|
|
|
# ctor or $type is already 'JPEG', we consider it a regular JPEG stream. |
477
|
5
|
100
|
|
|
|
13
|
$type = 'NONE' if length $$dataref == 0; |
478
|
5
|
100
|
100
|
|
|
21
|
$type = 'JPEG' if ! $type && Image::MetaData::JPEG->new($dataref, ''); |
479
|
|
|
|
|
|
|
# If $type is not yet set, generate an error (TIFF not yet supported ...) |
480
|
5
|
100
|
|
|
|
41
|
return { 'Error' => 'unsupported thumbnail format' } unless $type; |
481
|
|
|
|
|
|
|
# the following lists contain all records to be erased before inserting |
482
|
|
|
|
|
|
|
# the new thumbnail. They are inserted in a hash for faster lookup |
483
|
4
|
|
|
|
|
13
|
my %thumb_records = map { $_ => 1 } |
|
40
|
|
|
|
|
65
|
|
484
|
|
|
|
|
|
|
('Compression', 'JPEGInterchangeFormat', 'JPEGInterchangeFormatLength', |
485
|
|
|
|
|
|
|
'StripOffsets','ImageWidth','ImageLength','BitsPerSample', |
486
|
|
|
|
|
|
|
'SamplesPerPixel', 'RowsPerStrip', 'StripByteCounts'); |
487
|
|
|
|
|
|
|
# get the appropriate record lists (IFD1) (build it if not present) |
488
|
4
|
|
|
|
|
16
|
my $ifd1_list = $this->build_IFD_directory_tree('APP1@IFD1'); |
489
|
|
|
|
|
|
|
# delete all tags mentioned in %forbidden. This is a fresh start before |
490
|
|
|
|
|
|
|
# inserting a new thumbnail (and the whole story if $type is 'NONE') |
491
|
18
|
|
|
|
|
39
|
@$ifd1_list = grep |
492
|
4
|
|
|
|
|
10
|
{! exists $thumb_records{JPEG_lookup('APP1@IFD1', $_->{key})}} @$ifd1_list; |
493
|
|
|
|
|
|
|
# delete existing thumbnail data and replace it if necessary; this |
494
|
|
|
|
|
|
|
# "record" is in the root directory, and a regular expression check |
495
|
|
|
|
|
|
|
# is really impossible. So, we adopt a low-level approach here ... |
496
|
4
|
|
|
|
|
9
|
my $root_list = $this->{records}; |
497
|
4
|
|
|
|
|
7
|
@$root_list = grep { $_->{key} ne 'ThumbnailData' } @$root_list; |
|
23
|
|
|
|
|
36
|
|
498
|
|
|
|
|
|
|
# insert the thumbnail, if necessary (this must be the last record) |
499
|
4
|
50
|
|
|
|
26
|
push @$root_list, new Image::MetaData::JPEG::Record |
500
|
|
|
|
|
|
|
('ThumbnailData', $UNDEF, $dataref, length $$dataref) if $dataref; |
501
|
|
|
|
|
|
|
# if $type is 'JPEG', we need to insert some records in IFD1 ... |
502
|
4
|
100
|
|
|
|
13
|
if ($type eq 'JPEG') { |
503
|
|
|
|
|
|
|
# we have two non-offset records: the thumbnail type and its length |
504
|
3
|
|
|
|
|
11
|
my $records = { 'Compression' => 6, # 6 means JPEG-compressed |
505
|
|
|
|
|
|
|
'JPEGInterchangeFormatLength' => length $$dataref }; |
506
|
|
|
|
|
|
|
# analyse the passed records for correctness (semi-paranoia) |
507
|
3
|
|
|
|
|
12
|
my ($rej, $accepted) = $this->screen_records($records,'APP1@IFD1','T'); |
508
|
|
|
|
|
|
|
# $rej must be an empty hash, or we have a problem |
509
|
3
|
50
|
|
|
|
8
|
return { 'Error' => 'Records rejected internally! [JPEG]' } if %$rej; |
510
|
|
|
|
|
|
|
# add all other old (non-thumbnail-related) records |
511
|
3
|
|
|
|
|
9
|
$this->complement_records($ifd1_list, $accepted); |
512
|
|
|
|
|
|
|
# add the 'JPEGInterchangeFormat' record (an offset). This is really |
513
|
|
|
|
|
|
|
# dummy, it is here to trigger the correct behaviour in update(), but |
514
|
|
|
|
|
|
|
# I really should modify update() to make it calculate the field on |
515
|
|
|
|
|
|
|
# its own (since it already calcuates its value anyway). |
516
|
3
|
|
|
|
|
8
|
my $JIF = JPEG_lookup('APP1@IFD1', 'JPEGInterchangeFormat'); |
517
|
3
|
|
|
|
|
26
|
$$accepted{$JIF} = new |
518
|
|
|
|
|
|
|
Image::MetaData::JPEG::Record($JIF, $LONG, \ ("\000" x 4), 1); |
519
|
|
|
|
|
|
|
# take all records from $accepted and set them into the record |
520
|
|
|
|
|
|
|
# list (their order must anambiguous, so perform a clever sorting). |
521
|
3
|
|
|
|
|
10
|
@$ifd1_list = ordered_record_list($accepted, 'APP1@IFD1'); } |
522
|
|
|
|
|
|
|
# remember to commit these changes to the data area |
523
|
4
|
|
|
|
|
17
|
$this->update(); |
524
|
|
|
|
|
|
|
# return success (a reference to an empty hash) |
525
|
4
|
|
|
|
|
38
|
return {}; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
########################################################### |
529
|
|
|
|
|
|
|
# This helper function returns an ordered list of records.# |
530
|
|
|
|
|
|
|
# Records are sorted according to the numerical value of # |
531
|
|
|
|
|
|
|
# their key; if the key is not numeric, but its transla- # |
532
|
|
|
|
|
|
|
# tion matches Idx-n, n is used. If even this fails, a # |
533
|
|
|
|
|
|
|
# stringwise comparison is performed ($REFERENCE records).# |
534
|
|
|
|
|
|
|
########################################################### |
535
|
|
|
|
|
|
|
sub ordered_record_list { |
536
|
171
|
|
|
171
|
0
|
258
|
my ($data, $path) = @_; |
537
|
|
|
|
|
|
|
# a regular expression for an integer positive number |
538
|
171
|
|
|
|
|
571
|
my $num = qr/^\d+$/o; |
539
|
|
|
|
|
|
|
# tag to number translation; if the tag is not numeric and translates |
540
|
|
|
|
|
|
|
# to Idx-n, return n. If even this fails, return the textual tag itself |
541
|
|
|
|
|
|
|
# (the last case should be restricted to subdirectory entries). |
542
|
21410
|
100
|
|
21410
|
|
70560
|
my $tag_index = sub { return $_[0] if $_[0] =~ /$num/; |
543
|
365
|
|
|
|
|
787
|
my $n = JPEG_lookup($path, $_[0]); |
544
|
171
|
100
|
|
|
|
846
|
$n =~ s/^Idx-(\d+)$/$1/; $n =~ /$num/ ? $n : $_[0] }; |
|
365
|
|
|
|
|
569
|
|
|
365
|
|
|
|
|
1540
|
|
545
|
|
|
|
|
|
|
# numeric comparison when possible, stringwise comparison otherwise |
546
|
171
|
100
|
|
10705
|
|
522
|
my $comp = sub { (grep {!/$num/} @_) ? $_[0] cmp $_[1] : $_[0] <=> $_[1] }; |
|
10705
|
|
|
|
|
9686
|
|
|
21410
|
|
|
|
|
60949
|
|
547
|
|
|
|
|
|
|
# the actual sorting function for the sort operator |
548
|
171
|
|
|
10705
|
|
474
|
my $or = sub { &$comp(&$tag_index($a), &$tag_index($b)) }; |
|
10705
|
|
|
|
|
10867
|
|
549
|
|
|
|
|
|
|
# take all records from $data and perform a sorting |
550
|
171
|
|
|
|
|
1048
|
map {$$data{$_}} sort {&$or} keys %$data; |
|
3127
|
|
|
|
|
5627
|
|
|
10705
|
|
|
|
|
9937
|
|
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
########################################################### |
554
|
|
|
|
|
|
|
# This method, obviously, creates a (sub)directory tree # |
555
|
|
|
|
|
|
|
# in an IFD-like segment (i.e. APP1/APP3). The argument # |
556
|
|
|
|
|
|
|
# is a string describing the tree, like 'APP1@IFD0@GPS'. # |
557
|
|
|
|
|
|
|
# This method takes care of the "extra" field of the # |
558
|
|
|
|
|
|
|
# newly created directories if mandatory or useful. The # |
559
|
|
|
|
|
|
|
# return value is the record list of the deepest subdir. # |
560
|
|
|
|
|
|
|
########################################################### |
561
|
|
|
|
|
|
|
sub build_IFD_directory_tree { |
562
|
172
|
|
|
172
|
0
|
259
|
my ($this, $dirnames) = @_; |
563
|
|
|
|
|
|
|
# split the passed string into tokens on '@' |
564
|
172
|
|
|
|
|
679
|
my ($first, @dirnames) = split '@', $dirnames; |
565
|
|
|
|
|
|
|
# the first token must correspond to the segment name |
566
|
172
|
50
|
|
|
|
471
|
$this->die("Incorrect segment ($first)") unless $first eq $this->{name}; |
567
|
|
|
|
|
|
|
# build the whole directory tree, as requested |
568
|
172
|
|
|
|
|
566
|
$this->provide_subdirectory(@dirnames); |
569
|
|
|
|
|
|
|
# prepare two "running" variables |
570
|
172
|
|
|
|
|
278
|
my $dirref = $this->{records}; |
571
|
172
|
|
|
|
|
261
|
my $path = $first; |
572
|
|
|
|
|
|
|
# travel through the token list and fix the tree |
573
|
172
|
|
|
|
|
432
|
for my $name (@dirnames) { |
574
|
|
|
|
|
|
|
# get the $REFERENCE record for the subdir $name |
575
|
291
|
|
|
|
|
686
|
my $record = $this->search_record($name, $dirref); |
576
|
|
|
|
|
|
|
# if there is information in %IFD_SUBDIR ... |
577
|
291
|
50
|
|
|
|
703
|
if (exists $IFD_SUBDIRS{$path}) { |
578
|
|
|
|
|
|
|
# get the reverse (offset tag => subdir name) mapping |
579
|
291
|
|
|
|
|
330
|
my %revmapping = reverse %{$IFD_SUBDIRS{$path}}; |
|
291
|
|
|
|
|
1471
|
|
580
|
|
|
|
|
|
|
# if $name is present in %revmapping, set the "extra" field |
581
|
|
|
|
|
|
|
# of $record. This used to be necessary during the dump stage; |
582
|
|
|
|
|
|
|
# now, it could be avoided by using %IFD_SUBDIRS, but displaying |
583
|
|
|
|
|
|
|
# this kind of information is nonetheless usefull. |
584
|
291
|
100
|
|
|
|
1155
|
$record->{extra} = JPEG_lookup($path, $revmapping{$name}) |
585
|
|
|
|
|
|
|
if exists $revmapping{$name}; } |
586
|
|
|
|
|
|
|
# update the running variables |
587
|
291
|
|
|
|
|
644
|
$dirref = $record->get_value(); |
588
|
291
|
|
|
|
|
678
|
$path = join '@', $path, $name; } |
589
|
|
|
|
|
|
|
# return the final value of $dirref |
590
|
172
|
|
|
|
|
340
|
return $dirref; |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
########################################################### |
594
|
|
|
|
|
|
|
# This private method takes a reference to a Record list # |
595
|
|
|
|
|
|
|
# or hash and a reference to a Record hash, and inserts # |
596
|
|
|
|
|
|
|
# all records from the first container into the hash, # |
597
|
|
|
|
|
|
|
# unless its key is already present. # |
598
|
|
|
|
|
|
|
########################################################### |
599
|
|
|
|
|
|
|
sub complement_records { |
600
|
339
|
|
|
339
|
0
|
407
|
my ($this, $record_container, $record_hash) = @_; |
601
|
|
|
|
|
|
|
# be sure that the first argument is not a scalar |
602
|
339
|
50
|
|
|
|
654
|
$this->die('first arg. not a reference') unless ref $record_container; |
603
|
|
|
|
|
|
|
# get a record list from the record container |
604
|
339
|
100
|
|
|
|
862
|
my $record_list = (ref $record_container eq 'HASH') ? |
605
|
|
|
|
|
|
|
[ values %$record_container ] : $record_container; |
606
|
|
|
|
|
|
|
# records from a list |
607
|
339
|
|
|
|
|
534
|
for (@$record_list) { |
608
|
3150
|
100
|
|
|
|
6908
|
$$record_hash{$_->{key}} = $_ |
609
|
|
|
|
|
|
|
unless exists $$record_hash{$_->{key}}; } |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
########################################################### |
613
|
|
|
|
|
|
|
# This method takes a hash reference [$data] and an IFD # |
614
|
|
|
|
|
|
|
# path specification [$path] (like 'APP1@IFD0@GPS'). It # |
615
|
|
|
|
|
|
|
# then tries to convert the elements of $data into valid # |
616
|
|
|
|
|
|
|
# records according to the specific syntactical rules of # |
617
|
|
|
|
|
|
|
# the corresponding IFD. It returns a list of two hash # |
618
|
|
|
|
|
|
|
# references: the first list contains the key-recordref # |
619
|
|
|
|
|
|
|
# pairs for successful conversions, the other one the # |
620
|
|
|
|
|
|
|
# key-value(ref) pairs for unsuccessful ones. # |
621
|
|
|
|
|
|
|
#---------------------------------------------------------# |
622
|
|
|
|
|
|
|
# Records' tags can be give textually or numerically. # |
623
|
|
|
|
|
|
|
# First, the tags are checked for validity and converted # |
624
|
|
|
|
|
|
|
# to numeric form (records with undefined values are # |
625
|
|
|
|
|
|
|
# immediately rejected). Then, the specifications for # |
626
|
|
|
|
|
|
|
# each tag are read from a helper table and values are # |
627
|
|
|
|
|
|
|
# matched against a regular expression (or a surrogate, # |
628
|
|
|
|
|
|
|
# see %special_screen_rules). Then a Record object is # |
629
|
|
|
|
|
|
|
# forged and evaluated to see if it is valid and it # |
630
|
|
|
|
|
|
|
# corresponds to the user will. # |
631
|
|
|
|
|
|
|
#---------------------------------------------------------# |
632
|
|
|
|
|
|
|
# New feature: if the record value is a code reference # |
633
|
|
|
|
|
|
|
# instead of an array reference, the corresponding code # |
634
|
|
|
|
|
|
|
# is executed (passing the segment reference through) and # |
635
|
|
|
|
|
|
|
# the result is stored. This is necessary for mandatory # |
636
|
|
|
|
|
|
|
# records which need to know the current segment. # |
637
|
|
|
|
|
|
|
#---------------------------------------------------------# |
638
|
|
|
|
|
|
|
# New feature. The syntax hash can have a fifth field, # |
639
|
|
|
|
|
|
|
# acting as a filter. Unless it matches the optional # |
640
|
|
|
|
|
|
|
# $fregex argument, the record is rejected. This allows # |
641
|
|
|
|
|
|
|
# us to exclude some tags from general usage. If $fregex # |
642
|
|
|
|
|
|
|
# is undefined, all tags with a filter are rejected. # |
643
|
|
|
|
|
|
|
########################################################### |
644
|
|
|
|
|
|
|
sub screen_records { |
645
|
339
|
|
|
339
|
0
|
461
|
my ($this, $data, $path, $fregex) = @_; |
646
|
|
|
|
|
|
|
# prepare two hashes for rejected and accepted records |
647
|
339
|
|
|
|
|
481
|
my $rejected = {}; my $accepted = {}; |
|
339
|
|
|
|
|
567
|
|
648
|
|
|
|
|
|
|
# die immediately if $data or $path are not defined |
649
|
339
|
50
|
33
|
|
|
1367
|
$this->die('Undefined arguments') unless defined $data && defined $path; |
650
|
|
|
|
|
|
|
# get a reference to the hash with all record properties |
651
|
339
|
50
|
|
|
|
1200
|
$this->die('Supporting hash not found') unless exists $IFD_SUBDIRS{$path}; |
652
|
339
|
|
|
|
|
649
|
my $syntax = $IFD_SUBDIRS{$path}{'__syntax'}; |
653
|
339
|
50
|
|
|
|
652
|
$this->die('Syntax specification not found') unless $syntax; |
654
|
|
|
|
|
|
|
# loop over entries in $data and decide whether to accept them or not |
655
|
339
|
|
|
|
|
1146
|
while (my ($key, $value) = each %$data) { |
656
|
|
|
|
|
|
|
# do a key lookup and save the result |
657
|
1786
|
|
|
|
|
3933
|
my $key_lookup = JPEG_lookup($path, $key); |
658
|
|
|
|
|
|
|
# use the looked-up key if it is numeric |
659
|
1786
|
100
|
100
|
|
|
13948
|
$key = $key_lookup if defined $key_lookup && $key_lookup =~ /^\d+$/; |
660
|
|
|
|
|
|
|
# I have never been optimist ... |
661
|
1786
|
|
|
|
|
2900
|
$$rejected{$key} = $value; |
662
|
|
|
|
|
|
|
# reject unknown keys |
663
|
1786
|
100
|
|
|
|
3239
|
next unless defined $key_lookup; |
664
|
|
|
|
|
|
|
# of course, check that $value is defined |
665
|
1600
|
100
|
|
|
|
2319
|
next unless defined $value; |
666
|
|
|
|
|
|
|
# if value is a code reference, execute it, passing $this |
667
|
1599
|
50
|
|
|
|
2778
|
$value = &$value($this) if ref $value eq 'CODE'; |
668
|
|
|
|
|
|
|
# if value is a scalar, transform it into a single-valued array |
669
|
1599
|
100
|
|
|
|
3167
|
$value = [ $value ] unless ref $value; |
670
|
|
|
|
|
|
|
# $value must now be an array reference |
671
|
1599
|
50
|
|
|
|
2630
|
next unless ref $value eq 'ARRAY'; |
672
|
|
|
|
|
|
|
# get all mandatory properties of this record |
673
|
1599
|
|
|
|
|
1354
|
my ($name, $type, $count, $rule, $filter) = @{$$syntax{$key}}; |
|
1599
|
|
|
|
|
3660
|
|
674
|
|
|
|
|
|
|
# a "rule" matching 'calculated' means that this record |
675
|
|
|
|
|
|
|
# cannot be supplied by the user (so, we reject it) |
676
|
1599
|
100
|
|
|
|
2804
|
next if $rule =~ /calculated/; |
677
|
|
|
|
|
|
|
# very special mechanism to inhibit some tags |
678
|
1572
|
100
|
66
|
|
|
3375
|
next if defined $filter && ((!defined $fregex)||($filter!~/$fregex/)); |
|
|
|
100
|
|
|
|
|
679
|
|
|
|
|
|
|
# if $type is $ASCII and $$value[0] is not null terminated, |
680
|
|
|
|
|
|
|
# we are going to add the null character for the lazy user |
681
|
1521
|
100
|
66
|
|
|
4420
|
$$value[0].="\000" if $type==$ASCII && @$value && $$value[0]!~/\000$/; |
|
|
|
100
|
|
|
|
|
682
|
|
|
|
|
|
|
# if $rule points to an anonymous subroutine (i.e., a special rule, |
683
|
|
|
|
|
|
|
# execute the corresponding code and reject if it fails (i.e. dies); |
684
|
|
|
|
|
|
|
# otherwise, $rule must be interpreted as a regular expression (if |
685
|
|
|
|
|
|
|
# the record is multi-valued, $rule must match all the elements). |
686
|
1521
|
100
|
|
|
|
2336
|
if (ref $rule eq 'CODE') { eval { &$rule(@$value) }; next if $@; } |
|
109
|
100
|
|
|
|
142
|
|
|
109
|
|
|
|
|
352
|
|
|
109
|
|
|
|
|
258
|
|
687
|
1412
|
100
|
|
|
|
1796
|
else { next unless scalar @$value == grep {$_ =~ /^$rule$/s} @$value; } |
|
12180
|
|
|
|
|
47536
|
|
688
|
|
|
|
|
|
|
# let us see if the values can actually be saved |
689
|
|
|
|
|
|
|
# in a record ($record remains undef on failure). |
690
|
1439
|
100
|
|
|
|
4614
|
next unless my $record = |
691
|
|
|
|
|
|
|
Image::MetaData::JPEG::Record->check_consistency |
692
|
|
|
|
|
|
|
($key, $type, $count, $value); |
693
|
|
|
|
|
|
|
# well, it seems that the record is OK, so my pessimism |
694
|
|
|
|
|
|
|
# was not justified. Let us change the record status |
695
|
1430
|
|
|
|
|
2946
|
delete $$rejected{$key}; |
696
|
1430
|
|
|
|
|
6376
|
$$accepted{$key} = $record; |
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
# return references to accepted and rejected data |
699
|
339
|
|
|
|
|
746
|
return ($rejected, $accepted); |
700
|
|
|
|
|
|
|
} |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
# successful package load |
703
|
|
|
|
|
|
|
1; |