File Coverage

blib/lib/Image/MetaData/JPEG/access/app13.pl
Criterion Covered Total %
statement 176 176 100.0
branch 103 112 91.9
condition 37 42 88.1
subroutine 25 25 100.0
pod 5 20 25.0
total 346 375 92.2


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(:Lookups :TagsAPP13);
  14         18  
  14         2077  
8 14     14   75 use Image::MetaData::JPEG::Segment;
  14         19  
  14         261  
9 14     14   47 no integer;
  14         15  
  14         59  
10 14     14   210 use strict;
  14         16  
  14         367  
11 14     14   49 use warnings;
  14         23  
  14         28049  
12              
13             ###########################################################
14             # This method returns a reference to the $index-th (the #
15             # first, if $index is undefined) Photoshop-like APP13 #
16             # segment which contains information matching the $what #
17             # argument (see is_app13_ok() for details). If $index is #
18             # undefined, it defaults to zero (i.e., first segment). #
19             # If no suitable segment is available, undef is returned. #
20             # If $index is (-1), this method returns the number of #
21             # available suitable APP13 segments (which is >= 0). If #
22             # $what is invalid, an exception is thrown. Beware!, the #
23             # meaning of $index is influenced by the value of $what. #
24             ###########################################################
25             sub retrieve_app13_segment {
26 69     69 1 6354 my ($this, $index, $what) = @_;
27             # $index defaults to zero if undefined
28 69 100       182 $index = 0 unless defined $index;
29             # select all segments compatible with $what
30 69         222 my @references = grep { $_->is_app13_ok($what) } $this->get_segments();
  787         1043  
31             # if $index is -1, return the size of @references
32 67 100       193 return scalar @references if $index == -1;
33             # return the $index-th such segment, or undef if absent
34 53 100       192 return exists $references[$index] ? $references[$index] : undef;
35             }
36              
37             ###########################################################
38             # This method forces an appropriate Photoshop-like APP13 #
39             # segment to be present in the file, and returns its #
40             # reference. If at least one segment matching $what is #
41             # present, the first one is returned. Otherwise, the 1st #
42             # Photoshop-like APP13 is adapted by inserting an appro- #
43             # priate subdir record (update() is called automatically).#
44             # If not such segment exists, it is first created and #
45             # inserted. If $what is invalid, an exception is thrown. #
46             ###########################################################
47             sub provide_app13_segment {
48 72     72 1 2177 my ($this, $what) = @_;
49             # get the list of segments selected by $what
50 72         246 my @what_refs = grep { $_->is_app13_ok($what) } $this->get_segments();
  851         1102  
51             # if the list is not empty, return the first element
52 70 100       223 return $what_refs[0] if @what_refs;
53             # get the list of Photoshop-like segments (this only looks
54             # for the Photoshop identifier, special case of $what = undef);
55             # then extract the first element.
56 9         30 my @refs = grep { $_->is_app13_ok(undef) } $this->get_segments();
  111         184  
57 9 100       31 my $app13_segment = @refs ? $refs[0] : undef;
58             # if no segment is found, we surely need to generate a new
59             # one, and store it in an appropriate position in the file;
60             # remember that at least the Photoshop string must be there
61 9 100       27 unless ($app13_segment) {
62 4         29 $app13_segment = new Image::MetaData::JPEG::Segment
63             ('APP13', \ "$$APP13_PHOTOSHOP_IDS[0]");
64             # insert it into the list of JPEG segments
65             # (the position is chosen automatically)
66 4         17 $this->insert_segments($app13_segment); }
67             # ok, we must adapt the Photoshop-like segment (automatic update())
68 9         28 $app13_segment->provide_app13_subdir($what);
69             # return the modified segment
70 9         28 return $app13_segment;
71             }
72              
73             ###########################################################
74             # This method removes all traces of IPTC/non-IPTC infor- #
75             # mation (depending on $what) from the $index-th APP13 #
76             # Photoshop-style Segment. If, after this, the segment is #
77             # empty, it is eliminated from the list of segments in #
78             # the file. If $index is (-1), all segments are affected #
79             # at once. If $what is invalid an exception is thrown. #
80             # The meaning of $index depends on $what. #
81             ###########################################################
82             sub remove_app13_info {
83 10     10 1 6047 my ($this, $index, $what) = @_;
84             # this is the list of segments to be purged (initially empty)
85 10         20 my @purgeme = ();
86             # call the selection routine and store the segment reference
87 10         23 push @purgeme, $this->retrieve_app13_segment($index, $what);
88             # if $index is -1, retrieve_... returned the number of
89             # segments to be purged, not a segment reference! In this
90             # case, the selection routine is repeated with every index.
91 10 100       35 @purgeme = map { $this->retrieve_app13_segment($_, $what)
  3         8  
92             } (0..($purgeme[$#purgeme]-1)) if $index == -1;
93             # for each segment in the purge list, apply the purge routine
94             # (but don't be fooled by undefined references, i.e. invalid
95             # indexes). If only one record remains in the segment (presumably
96             # the Identifier), the segment is marked for a later deletion.
97 10         21 for (@purgeme) {
98 10 100       21 next unless defined $_;
99 9         24 $_->remove_app13_info($what);
100 9 100       11 $_->{name} = 'deleteme' if scalar @{$_->{records}} <= 1; }
  9         41  
101             # remove the marked segments from the file
102 10         35 $this->drop_segments('deleteme');
103             }
104              
105             ###########################################################
106             # This method is an interface to the method with the same #
107             # name in the Segment class. To begin with, the first #
108             # suitable APP13 segment is retrieved (if there is no #
109             # such segment, undef is returned). Then, get_app13_data #
110             # is called on this segment, passing all the arguments #
111             # through. If $what is invalid an exception is thrown #
112             # out. For further details, have a look at #
113             # Segment::get_app13_data() and retrieve_app13_segment(). #
114             ###########################################################
115             sub get_app13_data {
116 22     22 1 8605 my ($this, $type, $what) = @_;
117             # get the first suitable APP13 segment in the current JPEG
118             # file (this returns undef if no segment is present).
119 22         77 my $segment = $this->retrieve_app13_segment(undef, $what);
120             # return undef if no segment is present
121 22 50       55 return undef unless defined $segment;
122             # pass all arguments to the Segment method
123 22         63 return $segment->get_app13_data($type, $what);
124             }
125              
126             ###########################################################
127             # This method is an interface to the method with the same #
128             # name in the Segment class. To begin with, the first #
129             # suitable APP13 segment is retrieved (if there is no #
130             # such segment, one is created and initialised). Then the #
131             # set_app13_data is called on this segment passing the #
132             # arguments through. For further details, have a look at #
133             # Segment::set_app13_data() and provide_app13_segment(). #
134             ###########################################################
135             sub set_app13_data {
136 60     60 1 29302 my ($this, $data, $action, $what) = @_;
137             # get the first suitable APP13 segment in the current JPEG file
138             # (if there is no such segment, initialise one; therefore, this
139             # call cannot fail unless $what is invalid [mhh ...]).
140 60         148 my $segment = $this->provide_app13_segment($what);
141             # pass all arguments to the Segment method
142 59         139 return $segment->set_app13_data($data, $action, $what);
143             }
144              
145             ###########################################################
146             # The following routines best fit as Segment methods. #
147             ###########################################################
148             package Image::MetaData::JPEG::Segment;
149              
150             ###########################################################
151             # These helper functions have a single argument. They fix #
152             # it to some standard value, if it is undefined, then #
153             # they check that its value is a legal string and throw #
154             # an exception out if not so. 'IPTC' is treated like a #
155             # synonym of 'IPTC_2' for backward compatibility. Same #
156             # thing for 'PHOTOSHOP', a synonym for 'PS_8BIM'. #
157             # ------------------------------------------------------- #
158             # sanitise: 0=this, 1=var, 2=name, 3=regex(1st=default) #
159             ###########################################################
160 2465     2465 0 3628 sub sanitise_what { sanitise(@_, 'what' , 'IPTC|IPTC_2|IPTC_1|'.
161             'PHOTOSHOP|PS_8BIM|PS_8BPS|PS_PHUT') };
162 90     90 0 174 sub sanitise_type { sanitise(@_, 'type' , 'TEXTUAL|NUMERIC' ) };
163 79     79 0 151 sub sanitise_action { sanitise(@_, 'action', 'REPLACE|ADD|UPDATE' ) };
164 2634 100   2634 0 5206 sub sanitise { ($_[1] = $_[3]) =~ s/^([^\|]*)\|.*$/$1/ unless defined $_[1];
165 2634 100       17323 ($_[1] =~/^($_[3])$/) ?1: $_[0]->die("Unknown '$_[2]': $_[1]")};
166             my $what2dir = {'IPTC' => $APP13_IPTC_DIRNAME . '_2', # synonym
167             'IPTC_1' => $APP13_IPTC_DIRNAME . '_1',
168             'IPTC_2' => $APP13_IPTC_DIRNAME . '_2',
169             'PHOTOSHOP' => $APP13_PHOTOSHOP_DIRNAME . '_8BIM', # synonym
170             'PS_8BIM' => $APP13_PHOTOSHOP_DIRNAME . '_8BIM',
171             'PS_8BPS' => $APP13_PHOTOSHOP_DIRNAME . '_8BPS',
172             'PS_PHUT' => $APP13_PHOTOSHOP_DIRNAME . '_PHUT', };
173 1959   100 1959 0 13084 sub subdir_name { $_[0] eq $_ && return $$what2dir{$_} for keys %$what2dir; }
174              
175             ###########################################################
176             # This method inspects a segments, and return "ok" if the #
177             # segment shows the required features, undef otherwise. #
178             # The features are selected by the value of $what: #
179             # 1) ($what is undefined) the segment is an APP13 segment #
180             # and it contains the correct 'Identifier' record. #
181             # 2) ($what has a value) the segment matches 1), and #
182             # $what is accepted by sanitise_what and the segment #
183             # contains the subdir_name($what) subdirectory. #
184             # 3) (everything else) the routine dies. #
185             ###########################################################
186             sub is_app13_ok {
187 2025     2025 0 1813 my ($this, $what) = @_;
188             # intercept and die on unknown $what's (don't set a default!)
189 2025         2438 $this->sanitise_what(my $temp_what = $what);
190             # return undef if this segment is not APP13
191 2021 100       4504 return undef unless $this->{name} eq 'APP13';
192             # return undef if there is no 'Identifier' or it is not Photoshop
193 433         1047 my $id = $this->search_record_value('Identifier');
194 433 100 66     1017 return undef unless $id && grep { /^$id$/ } @$APP13_PHOTOSHOP_IDS;
  866         3666  
195             # if $what is undefined we are happy
196 431 100       910 return 'ok' unless defined $what;
197             # return "ok" if $what is defined and the appropriate subdir is there
198 317 100       564 return 'ok' if defined $this->search_record(subdir_name($what));
199             # fallback
200 36         94 return undef;
201             }
202              
203             ###########################################################
204             # This method returns the appropriate subdirectory record #
205             # reference for the current APP13 Photoshop-like segment #
206             # (undef is returned if it is not present). #
207             ###########################################################
208             sub retrieve_app13_subdir {
209 176     176 0 206 my ($this, $what) = @_;
210             # die on unknown $what's
211 176         262 $this->sanitise_what($what);
212             # return immediately if the segment is not suitable
213 176 100       267 return undef unless $this->is_app13_ok($what);
214             # return the appropriate subdirectory reference
215 166         344 return $this->search_record_value(subdir_name($what));
216             }
217              
218             ###########################################################
219             # This method returns the appropriate subdirectory record #
220             # reference for the current Photoshop-style APP13 segment.#
221             # If the subdirectory is not there, it is first created #
222             # and initialised. The routine can fail (returns undef) #
223             # only if the segment isn't a Photoshop-style one. If the #
224             # subdirectory is created, the segment is updated. #
225             #---------------------------------------------------------#
226             # The initialisation of a subdirectory can include manda- #
227             # tory records, which are now read from tables and not #
228             # hardcoded here as it used to be. #
229             ###########################################################
230             sub provide_app13_subdir {
231 88     88 0 107 my ($this, $what) = @_;
232             # die on unknown $what's
233 88         164 $this->sanitise_what($what);
234             # don't try to mess up non-APP13 segments!
235 88 50       156 return undef unless $this->is_app13_ok(undef);
236             # be positive, call retrieve first
237 88         170 my $subdir = $this->retrieve_app13_subdir($what);
238             # return this value, if it is not undef
239 88 100       269 return $subdir if defined $subdir;
240             # create the appropriate subdir in the main record directory
241 10         22 $subdir = $this->provide_subdirectory(subdir_name($what));
242             # there might be a mandatory records table; act consequently
243 10         27 my $mandatory = JPEG_lookup('APP13', subdir_name($what), '__mandatory');
244 10 100       43 $this->set_app13_data($mandatory, 'ADD', $what) if $mandatory;
245             # obviously, update the segment
246 10         29 $this->update();
247             # return the subdirectory reference
248 10         21 return $subdir;
249             }
250              
251             ###########################################################
252             # This method removes all traces of IPTC/non-IPTC infor- #
253             # mation (depending on $what) from the $index-th APP13 #
254             # Photoshop-style Segment. This routine cannot fail, #
255             # unless $what is invalid. The segment gets updated if #
256             # the modification is made. #
257             ###########################################################
258             sub remove_app13_info {
259 9     9 0 11 my ($this, $what) = @_;
260             # die on unknown $what's
261 9         16 $this->sanitise_what($what);
262             # return if there is nothing to erase
263 9 50       20 return unless $this->is_app13_ok($what);
264             # these approach is simple and crude
265 9         98 @{$this->{records}} =
  23         33  
266 9         16 grep { $_->{key} ne subdir_name($what) } @{$this->{records}};
  9         22  
267             # update the data area of the segment
268 9         29 $this->update();
269             }
270              
271             ###########################################################
272             # This method returns a reference to a hash containing a #
273             # copy of the list of records selected by $what in the #
274             # current segment, if the corresponding subdirectory is #
275             # present, undef otherwise. Each hash element is a (key, #
276             # arrayref) pair, where 'key' is a tag and 'arrayref' #
277             # points to an array with the record values. The output #
278             # format is selected by the $type argument: #
279             # - NUMERIC: hash with native numeric keys #
280             # - TEXTUAL: hash with translated textual keys (default) #
281             # If $type or $what is invalid, an exception is thrown. #
282             # If a numerical key (tag) is not known, a custom textual #
283             # key is created with 'Unknown_tag_' followed by the nu- #
284             # merical value (solving problem with non-standard tags). #
285             # ------------------------------------------------------- #
286             # Since an IPTC tag can be repeateable, @$arrayref can #
287             # actually contain more than one value. Moreover, if #
288             # $what is "non-IPTC", resource block names are appended #
289             # (so, the @$arrayref length is always even in this case, #
290             # and almost always equal to two). #
291             # ------------------------------------------------------- #
292             # Note that there is no check at all on the validity of #
293             # the Photoshop/IPTC record values: their format is not #
294             # checked and one or multiple values can be attached to #
295             # a single key independently of its repeatability. This #
296             # is, in some sense, consistent with the fact that also #
297             # "unknown" tags are included in the output. #
298             ###########################################################
299             sub get_app13_data {
300 90     90 0 4999 my ($this, $type, $what) = @_;
301             # die on unknown $type's
302 90         193 $this->sanitise_type($type);
303             # die on unknown $what's
304 88         221 $this->sanitise_what($what);
305             # retrieve the appropriate records list
306 88         285 my $records = $this->retrieve_app13_subdir($what);
307             # return undef if the directory is not present
308 88 50       217 return undef unless $records;
309             # this is the data hash to be filled
310 88         118 my $data = {};
311             # create a hash, where the keys are the numeric keys of @$records
312             # and the values are references to (initially empty) arrays.
313 88         180 $$data{$_} = [] for map { $_->{key} } @$records;
  866         1605  
314             # These arrays are then filled with the record values,
315             # accumulated according to the tag.
316 88         206 push @{$$data{$_->{key}}}, $_->get_value() for @$records;
  866         1712  
317             # if $what is "non-IPTC", append the "extra" values for each
318             # record, according to the tag (this is undef, mostly).
319 88 100       302 if ($what !~ /IPTC/) {
320 22         45 push @{$$data{$_->{key}}}, $_->{extra} for @$records; }
  281         362  
321             # if the type is textual, the tags must be translated;
322             # if there is no positive match from JPEG_lookup, create a tag
323             # carrying 'Unknown_tag_' followed by the key numerical value.
324 88 100       226 %$data = map { my $match = JPEG_lookup('APP13', subdir_name($what), $_);
  252         341  
325 252 100       727 (defined $match ? $match : "Unknown_tag_$_")
326             => $$data{$_} } keys %$data if $type eq 'TEXTUAL';
327             # return the magic scalar
328 88         233 return $data;
329             }
330              
331             ###########################################################
332             # This method accepts Photoshop data in various formats #
333             # and updates the content of a Photoshop-style APP13 #
334             # segment. The key type of each entry in the input %$data #
335             # hash can be numeric or textual, independently of the #
336             # others (the same key can appear in both forms, the #
337             # corresponding values will be put together). The value #
338             # of each entry can be an array reference or a scalar #
339             # (you can use this as a shortcut for value arrays with #
340             # only one value). The $action argument can be: #
341             # - ADD : new records are added and nothing is deleted; #
342             # however, if you try to add a non-repeatable record #
343             # which is already present, the newly supplied value #
344             # replaces the pre-existing value. #
345             # - UPDATE : new records replace those characterised by #
346             # the same tags, but the others are preserved. This #
347             # makes it possible to modify repeatable records. #
348             # - REPLACE : [default] all records in the relevant #
349             # subdir are deleted before inserting the new ones. #
350             # The return value is a reference to a hash containing #
351             # the rejected key-values entries. The entries of %$data #
352             # are not modified. #
353             # ------------------------------------------------------- #
354             # If $what implies some mandatory datasets, they are read #
355             # and from tables and added, unless already present. #
356             # If $what is "non-IPTC", UPDATE is a synonim of 'ADD', #
357             # and the second value is used as data block name. #
358             # ------------------------------------------------------- #
359             # At the end, the segment data area is updated. An entry #
360             # in the %$data hash may be rejected for various reasons: #
361             # - the tag is undefined or not known; #
362             # - the entry value is undef or points to an empty array;#
363             # [IPTC only]: #
364             # - the non-repeatable property is violated; #
365             # - the tag is marked as invalid; #
366             # - a value is undefined; #
367             # - the length of a value is invalid; #
368             # - a value does not match its mandatory regular expr. #
369             ###########################################################
370             sub set_app13_data {
371 79     79 0 5784 my ($this, $data, $action, $what) = @_;
372             # die on unknown $action's
373 79         149 $this->sanitise_action($action);
374             # die on unknown $what's
375 79         223 $this->sanitise_what($what);
376             # return immediately if $data is not a hash reference
377 79 100       283 return unless ref $data eq 'HASH';
378             # collapse UPDATE into ADD if $what is "non-IPTC"
379 78 100 100     289 $action = 'ADD' if $what !~ /IPTC/ && $action eq 'UPDATE';
380             # this is the name of the target subdirectory
381 78         141 my $subdir = subdir_name($what);
382             # prepare two hash references and initialise them
383             # with accepted and rejected data
384 78         196 my ($data_accepted, $data_rejected) = screen_data($data, $what);
385             # if $action is not 'REPLACE', old records need to be merged in;
386             # take a copy of all current records if necessary
387 78 100       275 my $oldrecs = $action eq 'REPLACE' ? {} :
388             $this->get_app13_data('NUMERIC', $what);
389             # loop over all entries in the %$oldrecs hash and insert them into the
390             # new hash if necessary (the "old hash" is of course empty if $action
391             # corresponds to 'REPLACE', so we are dealing with 'ADD' or 'UPDATE' here).
392 78         248 while (my ($tag, $oldarrayref) = each %$oldrecs) {
393             # a pre-existing tag must always remain, prepare a slot.
394 309 100       550 $$data_accepted{$tag} = [] unless exists $$data_accepted{$tag};
395             # if the tag is already covered by the new values and the
396             # $action is 'UPDATE' or $what is "non-IPTC", do nothing
397             # (I am assuming that "non-IPTC" is non-repeatable)
398 309         262 my $newarrayref = $$data_accepted{$tag};
399 309 100 100     702 next if @$newarrayref && ($action eq 'UPDATE' || $what !~ /IPTC/);
      66        
400             # ... otherwise (i.e., if $action is 'ADD' or $action is 'UPDATE'
401             # but the tag is not overwritten by new values) insert the old
402             # values at the beginning of the value array.
403 299         639 unshift @$newarrayref, @$oldarrayref; }
404             # if a mandatory dataset hash is present, and the mandatory
405             # datasets are note there, some more work is needed.
406 78 100       219 if (my $mandatory = JPEG_lookup('APP13', $subdir, '__mandatory')) {
407 65         153 my ($mand_datasets, $impossible) = screen_data($mandatory, $what);
408             # If mandatory datasets are rejected, there is a big mess
409 65 50       143 $this->die('Mandatory datasets rejected') if %$impossible;
410 65         184 while (my ($tag, $val) = each %$mand_datasets) {
411 65 100       277 $$data_accepted{$tag}=$val unless exists $$data_accepted{$tag}; }}
412             # overwrite the appropriate subdir content with accepted datasets
413 78         212 $this->insert_accepted($what, $data_accepted);
414             # remember to commit these changes to the data area
415 78         278 $this->update();
416             # return the reference of rejected tags/values
417 78         653 return $data_rejected;
418             }
419              
420             ###########################################################
421             # This routine actually overwrites the appropriate subdir #
422             # content with accepted datasets. Keys are guaranteed to #
423             # be numerically sorted (increasing). #
424             ###########################################################
425             sub insert_accepted {
426 78     78 0 121 my ($this, $what, $data) = @_;
427             # get and clear the appropriate records directory
428 78         163 my $dirref = $this->provide_app13_subdir($what); @$dirref = ();
  78         577  
429             # Remember to keep only the last value for non-repeatable records.
430 78         186 shift_non_repeatables($data, $what);
431             # loop on datasets in increasing numeric order on tags
432 78         344 for my $key (sort {$a<=>$b} keys %$data) {
  1019         859  
433             # $what is "non-IPTC". For each key, create a resource data block
434             # with the first value. If there is a second value, set "extra";
435 461 100       1603 if ($what !~ /IPTC/) {
    50          
436 131         151 my $arrayref = $$data{$key};
437             # resource data block value (the Record obj. is in @$dirref)
438 131         129 my $vref = \ $$arrayref[0];
439 131         350 $this->store_record($dirref, $key, $UNDEF, $vref, length $$vref);
440             # resource data block extra (the Record obj. is in @$dirref)
441 131 100       385 $this->search_record('LAST_RECORD', $dirref)->{extra} =
442             $$arrayref[1] if exists $$arrayref[1]; }
443             # $what is IPTC_something. For each element in the hash, create
444             # one or more Records corresponding to a dataset and insert them
445             # into the appropriate subdirectory.
446             elsif ($what =~ /^IPTC/) {
447             # each element of the array creates a new Record
448 330         1030 $this->store_record($dirref, $key, $ASCII, \ $_, length $_)
449 330         269 for @{$$data{$key}}; }
450             }
451             }
452              
453             ###########################################################
454             # This function takes a hash of candidate inputs to the #
455             # APP13 segment record list and decides whether to accept #
456             # or reject them. It returns two references to two hashes #
457             # with accepted and rejected data. All keys of accepted #
458             # records are forced to numeric form. The actual data #
459             # screening is done by value_is_OK(). #
460             ###########################################################
461             sub screen_data {
462 143     143 0 174 my ($data, $what) = @_;
463             # prepare repositories for good and bad guys
464 143         213 my ($data_accepted, $data_rejected) = ({}, {});
465             # this is the name of the target subdirectory
466 143         199 my $subdir = subdir_name($what);
467             # Force an ordering on %$data; this is necessary because the same key
468             # can be present twice, in numeric and textual form, and we want the
469             # corresponding value merging to be stable (numeric goes first).
470 143         436 for (sort keys %$data) {
471             # get copies, do not manipulate original data!
472 263         372 my ($tag, $value) = ($_, $$data{$_});
473             # accept both array references and plain scalars
474 263 100       616 $value = (ref $value) ? [ @$value ] : [ $value ];
475             # if $tag is not numeric, try a textual to numeric
476             # translation; (but don't set it to an undefined value yet)
477 263 100 66     1388 if (defined $tag && $tag !~ /^\d*$/) {
478 198         481 my $num_tag = JPEG_lookup('APP13', $subdir, $tag);
479 198 100       1257 $tag = $num_tag if defined $num_tag; }
480             # get a reference to the correct repository: an entry is
481             # accepted if it passes the value_is_OK test, rejected otherwise.
482 263 100       383 my $repository = value_is_OK($tag, $value, $what) ?
483             $data_accepted : $data_rejected;
484             # add data to the repository (do not overwrite!)
485 263 100       667 $$repository{$tag} = [ ] unless exists $$repository{$tag};
486 263         227 push @{$$repository{$tag}}, @$value; }
  263         609  
487             # return references to the two repositories
488 143         274 return ($data_accepted, $data_rejected);
489             }
490              
491             ###########################################################
492             # This function "corrects" a hash of records violating #
493             # some non-repeatable constraint. If a non-repeatable #
494             # record is found with multiple values, only the last one #
495             # is retained. $what is needed to retrieve syntax tables. #
496             ###########################################################
497             sub shift_non_repeatables {
498 78     78 0 95 my ($hashref, $what) = @_;
499             # loop over all elements in the hash
500 78         316 while (my ($tag, $arrayref) = each %$hashref) {
501             # get the constraints of this record
502 461         562 my $constraints = JPEG_lookup
503             ('APP13', subdir_name($what), '__syntax', $tag);
504             # skip unknown tags (this shouldn't happen) and repeatable records
505 461 100 100     2046 next unless $constraints && $$constraints[1] eq 'N';
506             # retain only the last element of this non-repeatable record
507 246 100       750 $$hashref{$tag} = [ $$arrayref[$#$arrayref] ] if @$arrayref != 1;
508             }
509             }
510              
511             ###########################################################
512             # This function return true if a given value fits a given #
513             # tag definition, false otherwise. The input arguments are#
514             # a numeric tag and an array reference, as usual. + $what #
515             ###########################################################
516             sub value_is_OK {
517 263     263 0 284 my ($tag, $arrayref, $what) = @_;
518             # $tag must be defined
519 263 50       372 return undef unless defined $tag;
520             # $tag must be a numeric value
521 263 100       737 return undef unless $tag =~ /^\d*$/;
522             # $arrayref must be an array reference
523 256 50 33     881 return undef unless ref $arrayref && ref $arrayref eq 'ARRAY';
524             # the referenced array must contain at least one element
525 256 100       435 return undef unless @$arrayref;
526             # if the tag is not known, it is not acceptable
527 253 100       346 return undef unless JPEG_lookup('APP13', subdir_name($what), $tag);
528             # it $what is "non-IPTC", the number of values can be only 1 or 2
529 247 100 100     852 return undef if $what !~ /IPTC/ && scalar @$arrayref > 2;
530             # the following tests are applied only if a syntax def. is present
531 246         318 my $constraints = JPEG_lookup('APP13',subdir_name($what),'__syntax',$tag);
532 246 50       448 return 1 unless defined $constraints;
533             # if the tag is non-repeatable, accept exactly one element
534 246 100 100     870 return undef if $$constraints[1] eq 'N' && @$arrayref != 1;
535             # get the mandatory "regular expression" for this tag
536 242         273 my $regex = $$constraints[4];
537             # if $regex matches 'invalid', inhibit this tag
538 242 100       436 return undef if $regex =~ /invalid/;
539             # run the following tests on all values
540 239         285 for (@$arrayref) {
541             # the second value for "non-IPTC" should not be tested
542 291 100 100     759 next if $what !~ /IPTC/ && ($_||1) ne ($$arrayref[0]||1);
      100        
      100        
543             # each value must be defined
544 277 100       406 return undef unless defined $_;
545             # each value length must fit the appropriate range
546 275 100 100     922 return undef if (length $_ < $$constraints[2] ||
547             length $_ > $$constraints[3] );
548             # each value must match the mandatory regular expression;
549             # but, if $regex matches 'binary', everything is permitted
550 272 100 100     3071 return undef unless /$regex/ || $regex =~ /binary/; }
551             # all tests were successful! return success
552 226         527 return 1;
553             }
554              
555             # successful package load
556             1;