| 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 |  | 83 | use Image::MetaData::JPEG::data::Tables qw(:Lookups :TagsAPP13); | 
|  | 14 |  |  |  |  | 32 |  | 
|  | 14 |  |  |  |  | 3705 |  | 
| 8 | 14 |  |  | 14 |  | 92 | use Image::MetaData::JPEG::Segment; | 
|  | 14 |  |  |  |  | 33 |  | 
|  | 14 |  |  |  |  | 302 |  | 
| 9 | 14 |  |  | 14 |  | 72 | no  integer; | 
|  | 14 |  |  |  |  | 52 |  | 
|  | 14 |  |  |  |  | 99 |  | 
| 10 | 14 |  |  | 14 |  | 392 | use strict; | 
|  | 14 |  |  |  |  | 28 |  | 
|  | 14 |  |  |  |  | 466 |  | 
| 11 | 14 |  |  | 14 |  | 74 | use warnings; | 
|  | 14 |  |  |  |  | 30 |  | 
|  | 14 |  |  |  |  | 52373 |  | 
| 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 | 11129 | my ($this, $index, $what) = @_; | 
| 27 |  |  |  |  |  |  | # $index defaults to zero if undefined | 
| 28 | 69 | 100 |  |  |  | 221 | $index = 0 unless defined $index; | 
| 29 |  |  |  |  |  |  | # select all segments compatible with $what | 
| 30 | 69 |  |  |  |  | 308 | my @references = grep { $_->is_app13_ok($what) } $this->get_segments(); | 
|  | 787 |  |  |  |  | 2000 |  | 
| 31 |  |  |  |  |  |  | # if $index is -1, return the size of @references | 
| 32 | 67 | 100 |  |  |  | 285 | return scalar @references if $index == -1; | 
| 33 |  |  |  |  |  |  | # return the $index-th such segment, or undef if absent | 
| 34 | 53 | 100 |  |  |  | 239 | 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 | 4426 | my ($this, $what) = @_; | 
| 49 |  |  |  |  |  |  | # get the list of segments selected by $what | 
| 50 | 72 |  |  |  |  | 498 | my @what_refs = grep { $_->is_app13_ok($what) } $this->get_segments(); | 
|  | 851 |  |  |  |  | 1811 |  | 
| 51 |  |  |  |  |  |  | # if the list is not empty, return the first element | 
| 52 | 70 | 100 |  |  |  | 350 | 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 |  |  |  |  | 42 | my @refs = grep { $_->is_app13_ok(undef) } $this->get_segments(); | 
|  | 111 |  |  |  |  | 229 |  | 
| 57 | 9 | 100 |  |  |  | 42 | 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 |  |  |  | 31 | unless ($app13_segment) { | 
| 62 | 4 |  |  |  |  | 74 | $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 |  |  |  |  | 24 | $this->insert_segments($app13_segment); } | 
| 67 |  |  |  |  |  |  | # ok, we must adapt the Photoshop-like segment (automatic update()) | 
| 68 | 9 |  |  |  |  | 44 | $app13_segment->provide_app13_subdir($what); | 
| 69 |  |  |  |  |  |  | # return the modified segment | 
| 70 | 9 |  |  |  |  | 39 | 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 | 10707 | my ($this, $index, $what) = @_; | 
| 84 |  |  |  |  |  |  | # this is the list of segments to be purged (initially empty) | 
| 85 | 10 |  |  |  |  | 23 | my @purgeme = (); | 
| 86 |  |  |  |  |  |  | # call the selection routine and store the segment reference | 
| 87 | 10 |  |  |  |  | 34 | 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 |  |  |  | 42 | @purgeme = map { $this->retrieve_app13_segment($_, $what) | 
|  | 3 |  |  |  |  | 12 |  | 
| 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 |  |  |  |  | 23 | for (@purgeme) { | 
| 98 | 10 | 100 |  |  |  | 32 | next unless defined $_; | 
| 99 | 9 |  |  |  |  | 34 | $_->remove_app13_info($what); | 
| 100 | 9 | 100 |  |  |  | 18 | $_->{name} = 'deleteme' if scalar @{$_->{records}} <= 1; } | 
|  | 9 |  |  |  |  | 58 |  | 
| 101 |  |  |  |  |  |  | # remove the marked segments from the file | 
| 102 | 10 |  |  |  |  | 54 | $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 | 15139 | 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 |  |  |  |  | 109 | my $segment = $this->retrieve_app13_segment(undef, $what); | 
| 120 |  |  |  |  |  |  | # return undef if no segment is present | 
| 121 | 22 | 50 |  |  |  | 80 | return undef unless defined $segment; | 
| 122 |  |  |  |  |  |  | # pass all arguments to the Segment method | 
| 123 | 22 |  |  |  |  | 158 | 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 | 57682 | 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 |  |  |  |  | 231 | my $segment = $this->provide_app13_segment($what); | 
| 141 |  |  |  |  |  |  | # pass all arguments to the Segment method | 
| 142 | 59 |  |  |  |  | 229 | 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 | 5880 | sub sanitise_what   { sanitise(@_, 'what'  , 'IPTC|IPTC_2|IPTC_1|'. | 
| 161 |  |  |  |  |  |  | 'PHOTOSHOP|PS_8BIM|PS_8BPS|PS_PHUT') }; | 
| 162 | 90 |  |  | 90 | 0 | 356 | sub sanitise_type   { sanitise(@_, 'type'  , 'TEXTUAL|NUMERIC'    ) }; | 
| 163 | 79 |  |  | 79 | 0 | 228 | sub sanitise_action { sanitise(@_, 'action', 'REPLACE|ADD|UPDATE' ) }; | 
| 164 | 2634 | 100 |  | 2634 | 0 | 7540 | sub sanitise { ($_[1] = $_[3]) =~ s/^([^\|]*)\|.*$/$1/ unless defined $_[1]; | 
| 165 | 2634 | 100 |  |  |  | 32314 | ($_[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 | 20629 | 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 | 2753 | my ($this, $what) = @_; | 
| 188 |  |  |  |  |  |  | # intercept and die on unknown $what's (don't set a default!) | 
| 189 | 2025 |  |  |  |  | 4590 | $this->sanitise_what(my $temp_what = $what); | 
| 190 |  |  |  |  |  |  | # return undef if this segment is not APP13 | 
| 191 | 2021 | 100 |  |  |  | 7716 | return undef unless $this->{name} eq 'APP13'; | 
| 192 |  |  |  |  |  |  | # return undef if there is no 'Identifier' or it is not Photoshop | 
| 193 | 433 |  |  |  |  | 1694 | my $id = $this->search_record_value('Identifier'); | 
| 194 | 433 | 100 | 66 |  |  | 1407 | return undef unless $id && grep { /^$id$/ } @$APP13_PHOTOSHOP_IDS; | 
|  | 866 |  |  |  |  | 4876 |  | 
| 195 |  |  |  |  |  |  | # if $what is undefined we are happy | 
| 196 | 431 | 100 |  |  |  | 1156 | return 'ok' unless defined $what; | 
| 197 |  |  |  |  |  |  | # return "ok" if $what is defined and the appropriate subdir is there | 
| 198 | 317 | 100 |  |  |  | 719 | return 'ok' if defined $this->search_record(subdir_name($what)); | 
| 199 |  |  |  |  |  |  | # fallback | 
| 200 | 36 |  |  |  |  | 130 | 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 | 303 | my ($this, $what) = @_; | 
| 210 |  |  |  |  |  |  | # die on unknown $what's | 
| 211 | 176 |  |  |  |  | 412 | $this->sanitise_what($what); | 
| 212 |  |  |  |  |  |  | # return immediately if the segment is not suitable | 
| 213 | 176 | 100 |  |  |  | 2480 | return undef unless $this->is_app13_ok($what); | 
| 214 |  |  |  |  |  |  | # return the appropriate subdirectory reference | 
| 215 | 166 |  |  |  |  | 549 | 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 | 142 | my ($this, $what) = @_; | 
| 232 |  |  |  |  |  |  | # die on unknown $what's | 
| 233 | 88 |  |  |  |  | 238 | $this->sanitise_what($what); | 
| 234 |  |  |  |  |  |  | # don't try to mess up non-APP13 segments! | 
| 235 | 88 | 50 |  |  |  | 250 | return undef unless $this->is_app13_ok(undef); | 
| 236 |  |  |  |  |  |  | # be positive, call retrieve first | 
| 237 | 88 |  |  |  |  | 235 | my $subdir = $this->retrieve_app13_subdir($what); | 
| 238 |  |  |  |  |  |  | # return this value, if it is not undef | 
| 239 | 88 | 100 |  |  |  | 369 | return $subdir if defined $subdir; | 
| 240 |  |  |  |  |  |  | # create the appropriate subdir in the main record directory | 
| 241 | 10 |  |  |  |  | 29 | $subdir = $this->provide_subdirectory(subdir_name($what)); | 
| 242 |  |  |  |  |  |  | # there might be a mandatory records table; act consequently | 
| 243 | 10 |  |  |  |  | 38 | my $mandatory = JPEG_lookup('APP13', subdir_name($what), '__mandatory'); | 
| 244 | 10 | 100 |  |  |  | 68 | $this->set_app13_data($mandatory, 'ADD', $what) if $mandatory; | 
| 245 |  |  |  |  |  |  | # obviously, update the segment | 
| 246 | 10 |  |  |  |  | 47 | $this->update(); | 
| 247 |  |  |  |  |  |  | # return the subdirectory reference | 
| 248 | 10 |  |  |  |  | 26 | 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 | 21 | my ($this, $what) = @_; | 
| 260 |  |  |  |  |  |  | # die on unknown $what's | 
| 261 | 9 |  |  |  |  | 24 | $this->sanitise_what($what); | 
| 262 |  |  |  |  |  |  | # return if there is nothing to erase | 
| 263 | 9 | 50 |  |  |  | 31 | return unless $this->is_app13_ok($what); | 
| 264 |  |  |  |  |  |  | # these approach is simple and crude | 
| 265 | 9 |  |  |  |  | 156 | @{$this->{records}} = | 
|  | 23 |  |  |  |  | 54 |  | 
| 266 | 9 |  |  |  |  | 25 | grep { $_->{key} ne subdir_name($what) } @{$this->{records}}; | 
|  | 9 |  |  |  |  | 22 |  | 
| 267 |  |  |  |  |  |  | # update the data area of the segment | 
| 268 | 9 |  |  |  |  | 47 | $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 | 9657 | my ($this, $type, $what) = @_; | 
| 301 |  |  |  |  |  |  | # die on unknown $type's | 
| 302 | 90 |  |  |  |  | 644 | $this->sanitise_type($type); | 
| 303 |  |  |  |  |  |  | # die on unknown $what's | 
| 304 | 88 |  |  |  |  | 350 | $this->sanitise_what($what); | 
| 305 |  |  |  |  |  |  | # retrieve the appropriate records list | 
| 306 | 88 |  |  |  |  | 457 | my $records = $this->retrieve_app13_subdir($what); | 
| 307 |  |  |  |  |  |  | # return undef if the directory is not present | 
| 308 | 88 | 50 |  |  |  | 279 | return undef unless $records; | 
| 309 |  |  |  |  |  |  | # this is the data hash to be filled | 
| 310 | 88 |  |  |  |  | 159 | 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 |  |  |  |  | 207 | $$data{$_} = [] for map { $_->{key} } @$records; | 
|  | 866 |  |  |  |  | 3044 |  | 
| 314 |  |  |  |  |  |  | # These arrays are then filled with the record values, | 
| 315 |  |  |  |  |  |  | # accumulated according to the tag. | 
| 316 | 88 |  |  |  |  | 294 | push @{$$data{$_->{key}}}, $_->get_value() for @$records; | 
|  | 866 |  |  |  |  | 8130 |  | 
| 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 |  |  |  | 384 | if ($what !~ /IPTC/) { | 
| 320 | 22 |  |  |  |  | 58 | push @{$$data{$_->{key}}}, $_->{extra} for @$records; } | 
|  | 281 |  |  |  |  | 753 |  | 
| 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 |  |  |  | 366 | %$data = map { my $match = JPEG_lookup('APP13', subdir_name($what), $_); | 
|  | 252 |  |  |  |  | 481 |  | 
| 325 | 252 | 100 |  |  |  | 1320 | (defined $match ? $match : "Unknown_tag_$_") | 
| 326 |  |  |  |  |  |  | => $$data{$_} } keys %$data if $type eq 'TEXTUAL'; | 
| 327 |  |  |  |  |  |  | # return the magic scalar | 
| 328 | 88 |  |  |  |  | 357 | 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 | 9769 | my ($this, $data, $action, $what) = @_; | 
| 372 |  |  |  |  |  |  | # die on unknown $action's | 
| 373 | 79 |  |  |  |  | 260 | $this->sanitise_action($action); | 
| 374 |  |  |  |  |  |  | # die on unknown $what's | 
| 375 | 79 |  |  |  |  | 344 | $this->sanitise_what($what); | 
| 376 |  |  |  |  |  |  | # return immediately if $data is not a hash reference | 
| 377 | 79 | 100 |  |  |  | 440 | return unless ref $data eq 'HASH'; | 
| 378 |  |  |  |  |  |  | # collapse UPDATE into ADD if $what is "non-IPTC" | 
| 379 | 78 | 100 | 100 |  |  | 457 | $action = 'ADD' if $what !~ /IPTC/ && $action eq 'UPDATE'; | 
| 380 |  |  |  |  |  |  | # this is the name of the target subdirectory | 
| 381 | 78 |  |  |  |  | 200 | my $subdir = subdir_name($what); | 
| 382 |  |  |  |  |  |  | # prepare two hash references and initialise them | 
| 383 |  |  |  |  |  |  | # with accepted and rejected data | 
| 384 | 78 |  |  |  |  | 322 | 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 |  |  |  | 386 | 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 |  |  |  |  | 702 | while (my ($tag, $oldarrayref) = each %$oldrecs) { | 
| 393 |  |  |  |  |  |  | # a pre-existing tag must always remain, prepare a slot. | 
| 394 | 309 | 100 |  |  |  | 910 | $$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 |  |  |  |  | 427 | my $newarrayref = $$data_accepted{$tag}; | 
| 399 | 309 | 100 | 100 |  |  | 759 | 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 |  |  |  |  | 1806 | 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 |  |  |  | 319 | if (my $mandatory = JPEG_lookup('APP13', $subdir, '__mandatory')) { | 
| 407 | 65 |  |  |  |  | 187 | my ($mand_datasets, $impossible) = screen_data($mandatory, $what); | 
| 408 |  |  |  |  |  |  | # If mandatory datasets are rejected, there is a big mess | 
| 409 | 65 | 50 |  |  |  | 192 | $this->die('Mandatory datasets rejected') if %$impossible; | 
| 410 | 65 |  |  |  |  | 342 | while (my ($tag, $val) = each %$mand_datasets) { | 
| 411 | 65 | 100 |  |  |  | 464 | $$data_accepted{$tag}=$val unless exists $$data_accepted{$tag}; }} | 
| 412 |  |  |  |  |  |  | # overwrite the appropriate subdir content with accepted datasets | 
| 413 | 78 |  |  |  |  | 357 | $this->insert_accepted($what, $data_accepted); | 
| 414 |  |  |  |  |  |  | # remember to commit these changes to the data area | 
| 415 | 78 |  |  |  |  | 421 | $this->update(); | 
| 416 |  |  |  |  |  |  | # return the reference of rejected tags/values | 
| 417 | 78 |  |  |  |  | 1010 | 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 | 188 | my ($this, $what, $data) = @_; | 
| 427 |  |  |  |  |  |  | # get and clear the appropriate records directory | 
| 428 | 78 |  |  |  |  | 273 | my $dirref = $this->provide_app13_subdir($what); @$dirref = (); | 
|  | 78 |  |  |  |  | 784 |  | 
| 429 |  |  |  |  |  |  | # Remember to keep only the last value for non-repeatable records. | 
| 430 | 78 |  |  |  |  | 252 | shift_non_repeatables($data, $what); | 
| 431 |  |  |  |  |  |  | # loop on datasets in increasing numeric order on tags | 
| 432 | 78 |  |  |  |  | 523 | for my $key (sort {$a<=>$b} keys %$data) { | 
|  | 1044 |  |  |  |  | 1362 |  | 
| 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 |  |  |  | 1921 | if ($what !~ /IPTC/) { | 
|  |  | 50 |  |  |  |  |  | 
| 436 | 131 |  |  |  |  | 207 | my $arrayref = $$data{$key}; | 
| 437 |  |  |  |  |  |  | # resource data block value (the Record obj. is in @$dirref) | 
| 438 | 131 |  |  |  |  | 194 | my $vref = \ $$arrayref[0]; | 
| 439 | 131 |  |  |  |  | 480 | $this->store_record($dirref, $key, $UNDEF, $vref, length $$vref); | 
| 440 |  |  |  |  |  |  | # resource data block extra (the Record obj. is in @$dirref) | 
| 441 | 131 | 100 |  |  |  | 923 | $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 |  |  |  |  | 1692 | $this->store_record($dirref, $key, $ASCII, \ $_, length $_) | 
| 449 | 330 |  |  |  |  | 359 | 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 | 248 | my ($data, $what) = @_; | 
| 463 |  |  |  |  |  |  | # prepare repositories for good and bad guys | 
| 464 | 143 |  |  |  |  | 350 | my ($data_accepted, $data_rejected) = ({}, {}); | 
| 465 |  |  |  |  |  |  | # this is the name of the target subdirectory | 
| 466 | 143 |  |  |  |  | 305 | 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 |  |  |  |  | 8403 | for (sort keys %$data) { | 
| 471 |  |  |  |  |  |  | # get copies, do not manipulate original data! | 
| 472 | 263 |  |  |  |  | 590 | my ($tag, $value) = ($_, $$data{$_}); | 
| 473 |  |  |  |  |  |  | # accept both array references and plain scalars | 
| 474 | 263 | 100 |  |  |  | 879 | $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 |  |  | 1760 | if (defined $tag && $tag !~ /^\d*$/) { | 
| 478 | 198 |  |  |  |  | 674 | my $num_tag = JPEG_lookup('APP13', $subdir, $tag); | 
| 479 | 198 | 100 |  |  |  | 1866 | $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 |  |  |  | 797 | 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 |  |  |  | 1007 | $$repository{$tag} = [ ] unless exists $$repository{$tag}; | 
| 486 | 263 |  |  |  |  | 365 | push @{$$repository{$tag}}, @$value; } | 
|  | 263 |  |  |  |  | 1008 |  | 
| 487 |  |  |  |  |  |  | # return references to the two repositories | 
| 488 | 143 |  |  |  |  | 420 | 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 | 131 | my ($hashref, $what) = @_; | 
| 499 |  |  |  |  |  |  | # loop over all elements in the hash | 
| 500 | 78 |  |  |  |  | 414 | while (my ($tag, $arrayref) = each %$hashref) { | 
| 501 |  |  |  |  |  |  | # get the constraints of this record | 
| 502 | 461 |  |  |  |  | 883 | 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 |  |  | 3445 | next unless $constraints && $$constraints[1] eq 'N'; | 
| 506 |  |  |  |  |  |  | # retain only the last element of this non-repeatable record | 
| 507 | 246 | 100 |  |  |  | 1599 | $$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 | 468 | my ($tag, $arrayref, $what) = @_; | 
| 518 |  |  |  |  |  |  | # $tag must be defined | 
| 519 | 263 | 50 |  |  |  | 832 | return undef unless defined $tag; | 
| 520 |  |  |  |  |  |  | # $tag must be a numeric value | 
| 521 | 263 | 100 |  |  |  | 941 | return undef unless $tag =~ /^\d*$/; | 
| 522 |  |  |  |  |  |  | # $arrayref must be an array reference | 
| 523 | 256 | 50 | 33 |  |  | 1330 | return undef unless ref $arrayref && ref $arrayref eq 'ARRAY'; | 
| 524 |  |  |  |  |  |  | # the referenced array must contain at least one element | 
| 525 | 256 | 100 |  |  |  | 565 | return undef unless @$arrayref; | 
| 526 |  |  |  |  |  |  | # if the tag is not known, it is not acceptable | 
| 527 | 253 | 100 |  |  |  | 481 | 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 |  |  | 1120 | return undef if $what !~ /IPTC/ && scalar @$arrayref > 2; | 
| 530 |  |  |  |  |  |  | # the following tests are applied only if a syntax def. is present | 
| 531 | 246 |  |  |  |  | 518 | my $constraints = JPEG_lookup('APP13',subdir_name($what),'__syntax',$tag); | 
| 532 | 246 | 50 |  |  |  | 734 | return 1 unless defined $constraints; | 
| 533 |  |  |  |  |  |  | # if the tag is non-repeatable, accept exactly one element | 
| 534 | 246 | 100 | 100 |  |  | 1158 | return undef if $$constraints[1] eq 'N' && @$arrayref != 1; | 
| 535 |  |  |  |  |  |  | # get the mandatory "regular expression" for this tag | 
| 536 | 242 |  |  |  |  | 407 | my $regex = $$constraints[4]; | 
| 537 |  |  |  |  |  |  | # if $regex matches 'invalid', inhibit this tag | 
| 538 | 242 | 100 |  |  |  | 611 | return undef if $regex =~ /invalid/; | 
| 539 |  |  |  |  |  |  | # run the following tests on all values | 
| 540 | 239 |  |  |  |  | 454 | for (@$arrayref) { | 
| 541 |  |  |  |  |  |  | # the second value for "non-IPTC" should not be tested | 
| 542 | 291 | 100 | 100 |  |  | 1085 | next if $what !~ /IPTC/ && ($_||1) ne ($$arrayref[0]||1); | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 543 |  |  |  |  |  |  | # each value must be defined | 
| 544 | 277 | 100 |  |  |  | 589 | return undef unless defined $_; | 
| 545 |  |  |  |  |  |  | # each value length must fit the appropriate range | 
| 546 | 275 | 100 | 100 |  |  | 1331 | 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 |  |  | 4251 | return undef unless /$regex/ || $regex =~ /binary/; } | 
| 551 |  |  |  |  |  |  | # all tests were successful! return success | 
| 552 | 226 |  |  |  |  | 1024 | return 1; | 
| 553 |  |  |  |  |  |  | } | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | # successful package load | 
| 556 |  |  |  |  |  |  | 1; |