| 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 |  | 86 | use Image::MetaData::JPEG::data::Tables qw(:Endianness :TagsAPP1_Exif); | 
|  | 14 |  |  |  |  | 32 |  | 
|  | 14 |  |  |  |  | 4336 |  | 
| 8 | 14 |  |  | 14 |  | 92 | use Image::MetaData::JPEG::Segment; | 
|  | 14 |  |  |  |  | 31 |  | 
|  | 14 |  |  |  |  | 336 |  | 
| 9 | 14 |  |  | 14 |  | 79 | no  integer; | 
|  | 14 |  |  |  |  | 31 |  | 
|  | 14 |  |  |  |  | 86 |  | 
| 10 | 14 |  |  | 14 |  | 435 | use strict; | 
|  | 14 |  |  |  |  | 31 |  | 
|  | 14 |  |  |  |  | 411 |  | 
| 11 | 14 |  |  | 14 |  | 90 | use warnings; | 
|  | 14 |  |  |  |  | 28 |  | 
|  | 14 |  |  |  |  | 8769 |  | 
| 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 | 4958 | my ($this, $index) = @_; | 
| 23 |  |  |  |  |  |  | # prepare the segment reference to be returned | 
| 24 | 64 |  |  |  |  | 131 | my $chosen_segment = undef; | 
| 25 |  |  |  |  |  |  | # $index defaults to zero if undefined | 
| 26 | 64 | 100 |  |  |  | 267 | $index = 0 unless defined $index; | 
| 27 |  |  |  |  |  |  | # get the references of all APP1 segments | 
| 28 | 64 |  |  |  |  | 444 | my @references = $this->get_segments('APP1$'); | 
| 29 |  |  |  |  |  |  | # filter out those without Exif information | 
| 30 | 64 |  |  |  |  | 159 | @references = grep { $_->is_app1_Exif() } @references; | 
|  | 64 |  |  |  |  | 317 |  | 
| 31 |  |  |  |  |  |  | # if $index is -1, return the size of @references | 
| 32 | 64 | 100 |  |  |  | 207 | return scalar @references if $index == -1; | 
| 33 |  |  |  |  |  |  | # return the $index-th such segment, or undef if absent | 
| 34 | 55 | 100 |  |  |  | 299 | 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 | 1102 | my ($this) = @_; | 
| 47 |  |  |  |  |  |  | # get the references of all APP1 segments | 
| 48 | 137 |  |  |  |  | 1199 | my @app1_refs = $this->get_segments('APP1$'); | 
| 49 |  |  |  |  |  |  | # filter out those without Exif information | 
| 50 | 137 |  |  |  |  | 396 | my @Exif_refs = grep { $_->is_app1_Exif() } @app1_refs; | 
|  | 128 |  |  |  |  | 644 |  | 
| 51 |  |  |  |  |  |  | # if @Exif_refs is not empty, return the first segment | 
| 52 | 137 | 100 |  |  |  | 723 | 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 |  |  |  |  | 82 | my $minimal_exif = $APP1_EXIF_TAG . $BIG_ENDIAN | 
| 58 |  |  |  |  |  |  | . pack "nNnN", $APP1_TIFF_SIG, 8, 0, 0; | 
| 59 | 9 |  |  |  |  | 71 | 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 |  |  |  |  | 77 | my $position = $this->find_new_app_segment_position('APP1'); | 
| 63 |  |  |  |  |  |  | # actually insert the segment | 
| 64 | 9 |  |  |  |  | 67 | $this->insert_segments($Exif, $position); | 
| 65 |  |  |  |  |  |  | # return a reference to the new segment | 
| 66 | 9 |  |  |  |  | 31 | 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 | 40185 | my ($this, $index) = @_; | 
| 76 |  |  |  |  |  |  | # the default value for $index is -1 | 
| 77 | 8 | 100 |  |  |  | 45 | $index = -1 unless defined $index; | 
| 78 |  |  |  |  |  |  | # this is the list of segments to be purged (initially empty) | 
| 79 | 8 |  |  |  |  | 25 | my %deleteme = (); | 
| 80 |  |  |  |  |  |  | # call the selection routine and save the segment reference | 
| 81 | 8 |  |  |  |  | 40 | 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 |  |  |  | 38 | $segment->{name} = "deleteme" if ref $segment; | 
| 87 | 8 | 50 |  |  |  | 81 | if ($index == -1) { $this->retrieve_app1_Exif_segment($_) | 
| 88 | 8 |  |  |  |  | 48 | ->{name} = "deleteme" for 0..($segment-1); } | 
| 89 |  |  |  |  |  |  | # remove marked segments from the file | 
| 90 | 8 |  |  |  |  | 58 | $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 | 25798 | 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 |  |  |  |  | 141 | my $segment = $this->retrieve_app1_Exif_segment(); | 
| 107 |  |  |  |  |  |  | # return undef if not suitable segment exists | 
| 108 | 33 | 50 |  |  |  | 109 | return undef unless defined $segment; | 
| 109 |  |  |  |  |  |  | # pass the arguments through to the Segment method | 
| 110 | 33 |  |  |  |  | 166 | 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 | 108583 | 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 |  |  |  |  | 772 | my $segment = $this->provide_app1_Exif_segment(); | 
| 128 |  |  |  |  |  |  | # pass the arguments through to the Segment method | 
| 129 | 134 |  |  |  |  | 707 | 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 | 688 | my $this = shift; | 
| 143 |  |  |  |  |  |  | # get the real picture dimensions | 
| 144 | 2 |  |  |  |  | 11 | my ($x_dim, $y_dim) = $this->get_dimensions(); | 
| 145 |  |  |  |  |  |  | # prepare a table of records for the Interop. IFD | 
| 146 | 2 |  |  |  |  | 15 | 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 |  |  |  |  | 10 | 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 |  | 92 | use Image::MetaData::JPEG::data::Tables qw(:Lookups); | 
|  | 14 |  |  |  |  | 54 |  | 
|  | 14 |  |  |  |  | 76501 |  | 
| 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 | 907 | my ($this) = @_; | 
| 181 |  |  |  |  |  |  | # return undef if this segment is not APP1 | 
| 182 | 543 | 50 |  |  |  | 1888 | 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 |  |  |  |  | 5737 | my $identifier = $this->search_record_value('Identifier'); | 
| 186 | 543 | 50 | 33 |  |  | 4749 | return undef unless defined $identifier && $identifier eq $APP1_EXIF_TAG; | 
| 187 |  |  |  |  |  |  | # return ok | 
| 188 | 543 |  |  |  |  | 2004 | 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 | 66981 | my ($this, $what, $type) = @_; | 
| 245 |  |  |  |  |  |  | # refuse to work unless you are an Exif APP1 segment | 
| 246 | 154 | 50 |  |  |  | 429 | return undef unless $this->is_app1_Exif(); | 
| 247 |  |  |  |  |  |  | # set the default section and type, if undefined; | 
| 248 | 154 | 100 |  |  |  | 554 | $what = 'ALL'       unless defined $what; | 
| 249 | 154 | 100 |  |  |  | 622 | $type = 'TEXTUAL'   unless defined $type; | 
| 250 |  |  |  |  |  |  | # reject unknown types (return undef, which means 'error') | 
| 251 | 154 | 100 |  |  |  | 690 | return undef unless $type =~ /^NUMERIC$|^TEXTUAL$/; | 
| 252 |  |  |  |  |  |  | # a reference to the hash to be returned, initially empty | 
| 253 | 152 |  |  |  |  | 259 | my $pairs = {}; | 
| 254 |  |  |  |  |  |  | # ========= SPECIAL CASES ==================================== | 
| 255 |  |  |  |  |  |  | # IMAGE_DATA means IFD0_DATA and SUBIFD_DATA (merged) | 
| 256 | 152 | 100 |  |  |  | 1369 | if ($what eq 'IMAGE_DATA') { | 
| 257 | 10 |  |  |  |  | 30 | for ('IFD0_DATA', 'SUBIFD_DATA') { | 
| 258 | 20 |  |  |  |  | 79 | my $h = $this->get_Exif_data($_, $type); | 
| 259 | 20 |  |  |  |  | 264 | @$pairs{keys %$h} = values %$h; } return $pairs; } | 
|  | 10 |  |  |  |  | 54 |  | 
| 260 |  |  |  |  |  |  | # ALL means a hash of hashes with all subdirs (even if emtpy) | 
| 261 | 142 | 100 |  |  |  | 337 | if ($what eq 'ALL') { | 
| 262 | 8 |  |  |  |  | 64 | $$pairs{$_} = $this->get_Exif_data($_, $type) for keys %WHAT2IFD; | 
| 263 | 8 |  |  |  |  | 51 | 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 |  |  |  | 305 | if ($what eq 'THUMBNAIL') { | 
| 269 | 8 |  |  |  |  | 29 | my $thumbnail = $this->search_record_value('ThumbnailData'); | 
| 270 | 8 | 100 |  |  |  | 106 | return $thumbnail ? \ $thumbnail : \ (my $ns = ''); } | 
| 271 |  |  |  |  |  |  | # IFD1_DATA is an alias for THUMB_DATA | 
| 272 | 126 | 100 |  |  |  | 305 | $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 |  |  |  | 368 | 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 |  |  |  |  | 236 | my $path = $WHAT2IFD{$what}; | 
| 281 |  |  |  |  |  |  | # follow the path blindly, get undef on problems | 
| 282 | 125 |  |  |  |  | 426 | 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 |  |  |  | 350 | unless (defined $dirref) { | 
| 287 | 27 |  |  |  |  | 324 | $path =~ s/(.*@|)([^@]*)/$1/; | 
| 288 | 27 |  |  |  |  | 103 | my $partial_dirref = $this->search_record_value($path); | 
| 289 | 422 |  |  |  |  | 1718 | $path .= $_->{key}, $dirref = $_->get_value(), last | 
| 290 | 27 |  |  |  |  | 86 | 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 |  |  |  | 451 | 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 |  |  |  |  | 4580 | %$pairs = map  { $_->{key} => [ @{$_->{values}} ] } | 
|  | 1546 |  |  |  |  | 8279 |  | 
|  | 1660 |  |  |  |  | 3466 |  | 
| 299 | 112 |  |  |  |  | 226 | 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 |  |  |  | 625 | if ($type eq "TEXTUAL") { | 
| 305 |  |  |  |  |  |  | # get the right numeric-to-textual conversion table with $path | 
| 306 | 90 |  |  |  |  | 508 | my $table = JPEG_lookup($this->{name}, $path); | 
| 307 |  |  |  |  |  |  | # run the translation (create a name also for unknown tags) | 
| 308 | 90 | 100 |  |  |  | 490 | %$pairs = map { (($_!~/^\d+$/)?$_:(exists $$table{$_}) ? $$table{$_} : | 
|  | 1203 | 100 |  |  |  | 7968 |  | 
| 309 |  |  |  |  |  |  | "Unknown_tag_$_") => $$pairs{$_} } keys %$pairs; } | 
| 310 |  |  |  |  |  |  | # return the reference to the hash containing all data | 
| 311 | 112 |  |  |  |  | 745 | 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 | 52933 | my ($this, $data, $what, $action, $dontupdate) = @_; | 
| 384 |  |  |  |  |  |  | # refuse to work unless you are an Exif APP1 segment | 
| 385 | 197 | 50 |  |  |  | 778 | return {'ERROR'=>'Not an Exif APP1 segment'} unless $this->is_app1_Exif(); | 
| 386 |  |  |  |  |  |  | # set the default action, if undefined | 
| 387 | 197 | 100 |  |  |  | 770 | $action = 'REPLACE' unless defined $action; | 
| 388 |  |  |  |  |  |  | # refuse to work for unkwnon actions | 
| 389 | 197 | 100 |  |  |  | 1349 | return {'ERROR'=>"Unknown action $action"} unless $action =~ /ADD|REPLACE/; | 
| 390 |  |  |  |  |  |  | # return immediately if $data is undefined | 
| 391 | 194 | 100 |  |  |  | 538 | 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 |  |  |  | 1596 | if ($what eq 'IMAGE_DATA') { | 
| 396 | 16 |  |  |  |  | 62 | my $rejected = $this->set_Exif_data($data, 'SUBIFD_DATA', $action, 1); | 
| 397 | 16 |  |  |  |  | 514 | return $this->set_Exif_data($rejected, 'IFD0_DATA', $action); } | 
| 398 |  |  |  |  |  |  | # THUMBNAIL requires a very specific treatment | 
| 399 | 177 | 100 |  |  |  | 561 | return $this->set_Exif_thumbnail($data) if $what eq 'THUMBNAIL'; | 
| 400 |  |  |  |  |  |  | # 'THUMB_DATA' is an alias to 'IFD1_DATA' | 
| 401 | 172 | 50 |  |  |  | 513 | $what = 'IFD1_DATA' if $what eq 'THUMB_DATA'; | 
| 402 |  |  |  |  |  |  | # ============================================================ | 
| 403 |  |  |  |  |  |  | # $data must be a hash reference (from this point on) | 
| 404 | 172 | 50 |  |  |  | 808 | 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 |  |  |  | 802 | return {'ERROR'=>"Unknown section $what"} unless exists $WHAT2IFD{$what}; | 
| 407 |  |  |  |  |  |  | # translate $what into a path specification | 
| 408 | 168 |  |  |  |  | 521 | my $path = 'APP1' . $WHAT2IFD{$what}; | 
| 409 |  |  |  |  |  |  | # the mandatory records list must be present (debug point) | 
| 410 | 168 | 50 |  |  |  | 4500 | return {'ERROR'=>'no $mandatory records'} unless exists | 
| 411 |  |  |  |  |  |  | $IFD_SUBDIRS{$path}{'__mandatory'}; | 
| 412 |  |  |  |  |  |  | # get the mandatory record list | 
| 413 | 168 |  |  |  |  | 429 | 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 |  |  |  |  | 923 | 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 |  |  |  |  | 875 | 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 |  |  |  | 647 | my $save = $action eq 'REPLACE' ? 'p' : '.'; | 
| 425 | 168 |  |  |  |  | 471 | my $old_records = [ grep {$_->get_category() =~ $save} @$record_list ]; | 
|  | 2846 |  |  |  |  | 7781 |  | 
| 426 | 168 |  |  |  |  | 775 | $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 |  |  |  |  | 726 | my ($notempty, $values) = $this->screen_records($mandatory, $path, 'B'); | 
| 432 | 168 | 50 |  |  |  | 706 | $this->die('Mandatory values rejected') if %$notempty; | 
| 433 |  |  |  |  |  |  | # merge in mandatory records, if they are not already present | 
| 434 | 168 |  |  |  |  | 769 | $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 |  |  |  |  | 717 | @$record_list = ordered_record_list($accepted, $path); | 
| 438 |  |  |  |  |  |  | # commit changes to the data area unless explicitely forbidden | 
| 439 | 168 | 100 |  |  |  | 4110 | $this->update() unless $dontupdate; | 
| 440 |  |  |  |  |  |  | # that's it, return the reference to the rejected data hash | 
| 441 | 168 |  |  |  |  | 4296 | 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 | 12 | my ($this, $dataref) = @_; | 
| 463 |  |  |  |  |  |  | # this variable holds the thumbnail format | 
| 464 | 5 |  |  |  |  | 10 | 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 |  |  |  | 18 | 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 |  |  |  | 18 | if ('Image::MetaData::JPEG' eq ref $dataref) { | 
| 471 | 2 |  |  |  |  | 4 | my $r = ""; $dataref->save(\ $r); $dataref = \ $r; $type = 'JPEG'; } | 
|  | 2 |  |  |  |  | 14 |  | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 5 |  | 
| 472 |  |  |  |  |  |  | # $dataref must now be a scalar reference; everything else is an error | 
| 473 | 5 | 50 |  |  |  | 19 | 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 |  |  | 31 | $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 |  |  |  | 50 | 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 |  |  |  |  | 11 | my %thumb_records = map { $_ => 1 } | 
|  | 40 |  |  |  |  | 95 |  | 
| 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 |  |  |  |  | 25 | 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 |  |  |  |  | 62 | @$ifd1_list = grep | 
| 492 | 4 |  |  |  |  | 11 | {! 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 |  |  |  |  | 12 | my $root_list = $this->{records}; | 
| 497 | 4 |  |  |  |  | 8 | @$root_list = grep { $_->{key} ne 'ThumbnailData' } @$root_list; | 
|  | 23 |  |  |  |  | 48 |  | 
| 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 |  |  |  | 32 | if ($type eq 'JPEG') { | 
| 503 |  |  |  |  |  |  | # we have two non-offset records: the thumbnail type and its length | 
| 504 | 3 |  |  |  |  | 13 | my $records = { 'Compression' => 6, # 6 means JPEG-compressed | 
| 505 |  |  |  |  |  |  | 'JPEGInterchangeFormatLength' => length $$dataref }; | 
| 506 |  |  |  |  |  |  | # analyse the passed records for correctness (semi-paranoia) | 
| 507 | 3 |  |  |  |  | 17 | 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 |  |  |  | 10 | return { 'Error' => 'Records rejected internally! [JPEG]' } if %$rej; | 
| 510 |  |  |  |  |  |  | # add all other old (non-thumbnail-related) records | 
| 511 | 3 |  |  |  |  | 14 | $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 |  |  |  |  | 11 | my $JIF = JPEG_lookup('APP1@IFD1', 'JPEGInterchangeFormat'); | 
| 517 | 3 |  |  |  |  | 42 | $$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 |  |  |  |  | 14 | @$ifd1_list = ordered_record_list($accepted, 'APP1@IFD1'); } | 
| 522 |  |  |  |  |  |  | # remember to commit these changes to the data area | 
| 523 | 4 |  |  |  |  | 26 | $this->update(); | 
| 524 |  |  |  |  |  |  | # return success (a reference to an empty hash) | 
| 525 | 4 |  |  |  |  | 60 | 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 | 393 | my ($data, $path) = @_; | 
| 537 |  |  |  |  |  |  | # a regular expression for an integer positive number | 
| 538 | 171 |  |  |  |  | 1014 | 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 | 21006 | 100 |  | 21006 |  | 121225 | my $tag_index = sub { return $_[0] if $_[0] =~ /$num/; | 
| 543 | 306 |  |  |  |  | 1047 | my $n = JPEG_lookup($path, $_[0]); | 
| 544 | 171 | 100 |  |  |  | 1342 | $n =~ s/^Idx-(\d+)$/$1/; $n =~ /$num/ ? $n : $_[0] }; | 
|  | 306 |  |  |  |  | 864 |  | 
|  | 306 |  |  |  |  | 2074 |  | 
| 545 |  |  |  |  |  |  | # numeric comparison when possible, stringwise comparison otherwise | 
| 546 | 171 | 100 |  | 10503 |  | 861 | my $comp = sub { (grep {!/$num/} @_) ? $_[0] cmp $_[1] : $_[0] <=> $_[1] }; | 
|  | 10503 |  |  |  |  | 15423 |  | 
|  | 21006 |  |  |  |  | 113688 |  | 
| 547 |  |  |  |  |  |  | # the actual sorting function for the sort operator | 
| 548 | 171 |  |  | 10503 |  | 767 | my $or = sub { &$comp(&$tag_index($a), &$tag_index($b)) }; | 
|  | 10503 |  |  |  |  | 17099 |  | 
| 549 |  |  |  |  |  |  | # take all records from $data and perform a sorting | 
| 550 | 171 |  |  |  |  | 1921 | map {$$data{$_}} sort {&$or} keys %$data; | 
|  | 3127 |  |  |  |  | 15740 |  | 
|  | 10503 |  |  |  |  | 18809 |  | 
| 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 | 435 | my ($this, $dirnames) = @_; | 
| 563 |  |  |  |  |  |  | # split the passed string into tokens on '@' | 
| 564 | 172 |  |  |  |  | 902 | my ($first, @dirnames) = split '@', $dirnames; | 
| 565 |  |  |  |  |  |  | # the first token must correspond to the segment name | 
| 566 | 172 | 50 |  |  |  | 714 | $this->die("Incorrect segment ($first)") unless $first eq $this->{name}; | 
| 567 |  |  |  |  |  |  | # build the whole directory tree, as requested | 
| 568 | 172 |  |  |  |  | 844 | $this->provide_subdirectory(@dirnames); | 
| 569 |  |  |  |  |  |  | # prepare two "running" variables | 
| 570 | 172 |  |  |  |  | 436 | my $dirref = $this->{records}; | 
| 571 | 172 |  |  |  |  | 493 | my $path = $first; | 
| 572 |  |  |  |  |  |  | # travel through the token list and fix the tree | 
| 573 | 172 |  |  |  |  | 632 | for my $name (@dirnames) { | 
| 574 |  |  |  |  |  |  | # get the $REFERENCE record for the subdir $name | 
| 575 | 291 |  |  |  |  | 958 | my $record = $this->search_record($name, $dirref); | 
| 576 |  |  |  |  |  |  | # if there is information in %IFD_SUBDIR ... | 
| 577 | 291 | 50 |  |  |  | 987 | if (exists $IFD_SUBDIRS{$path}) { | 
| 578 |  |  |  |  |  |  | # get the reverse (offset tag => subdir name) mapping | 
| 579 | 291 |  |  |  |  | 10967 | my %revmapping = reverse %{$IFD_SUBDIRS{$path}}; | 
|  | 291 |  |  |  |  | 2741 |  | 
| 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 |  |  |  | 1886 | $record->{extra} = JPEG_lookup($path, $revmapping{$name}) | 
| 585 |  |  |  |  |  |  | if exists $revmapping{$name}; } | 
| 586 |  |  |  |  |  |  | # update the running variables | 
| 587 | 291 |  |  |  |  | 1013 | $dirref = $record->get_value(); | 
| 588 | 291 |  |  |  |  | 1006 | $path = join '@', $path, $name; } | 
| 589 |  |  |  |  |  |  | # return the final value of $dirref | 
| 590 | 172 |  |  |  |  | 633 | 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 | 630 | my ($this, $record_container, $record_hash) = @_; | 
| 601 |  |  |  |  |  |  | # be sure that the first argument is not a scalar | 
| 602 | 339 | 50 |  |  |  | 1293 | $this->die('first arg. not a reference') unless ref $record_container; | 
| 603 |  |  |  |  |  |  | # get a record list from the record container | 
| 604 | 339 | 100 |  |  |  | 1391 | my $record_list = (ref $record_container eq 'HASH') ? | 
| 605 |  |  |  |  |  |  | [ values %$record_container ] : $record_container; | 
| 606 |  |  |  |  |  |  | # records from a list | 
| 607 | 339 |  |  |  |  | 816 | for (@$record_list) { | 
| 608 | 3150 | 100 |  |  |  | 10960 | $$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 | 753 | my ($this, $data, $path, $fregex) = @_; | 
| 646 |  |  |  |  |  |  | # prepare two hashes for rejected and accepted records | 
| 647 | 339 |  |  |  |  | 723 | my $rejected = {}; my $accepted = {}; | 
|  | 339 |  |  |  |  | 891 |  | 
| 648 |  |  |  |  |  |  | # die immediately if $data or $path are not defined | 
| 649 | 339 | 50 | 33 |  |  | 2166 | $this->die('Undefined arguments') unless defined $data && defined $path; | 
| 650 |  |  |  |  |  |  | # get a reference to the hash with all record properties | 
| 651 | 339 | 50 |  |  |  | 1624 | $this->die('Supporting hash not found') unless exists $IFD_SUBDIRS{$path}; | 
| 652 | 339 |  |  |  |  | 916 | my $syntax = $IFD_SUBDIRS{$path}{'__syntax'}; | 
| 653 | 339 | 50 |  |  |  | 810 | $this->die('Syntax specification not found') unless $syntax; | 
| 654 |  |  |  |  |  |  | # loop over entries in $data and decide whether to accept them or not | 
| 655 | 339 |  |  |  |  | 1543 | while (my ($key, $value) = each %$data) { | 
| 656 |  |  |  |  |  |  | # do a key lookup and save the result | 
| 657 | 1786 |  |  |  |  | 9798 | my $key_lookup = JPEG_lookup($path, $key); | 
| 658 |  |  |  |  |  |  | # use the looked-up key if it is numeric | 
| 659 | 1786 | 100 | 100 |  |  | 19787 | $key = $key_lookup if defined $key_lookup && $key_lookup =~ /^\d+$/; | 
| 660 |  |  |  |  |  |  | # I have never been optimist ... | 
| 661 | 1786 |  |  |  |  | 4258 | $$rejected{$key} = $value; | 
| 662 |  |  |  |  |  |  | # reject unknown keys | 
| 663 | 1786 | 100 |  |  |  | 5739 | next unless defined $key_lookup; | 
| 664 |  |  |  |  |  |  | # of course, check that $value is defined | 
| 665 | 1600 | 100 |  |  |  | 3373 | next unless defined $value; | 
| 666 |  |  |  |  |  |  | # if value is a code reference, execute it, passing $this | 
| 667 | 1599 | 50 |  |  |  | 3636 | $value = &$value($this) if ref $value eq 'CODE'; | 
| 668 |  |  |  |  |  |  | # if value is a scalar, transform it into a single-valued array | 
| 669 | 1599 | 100 |  |  |  | 4180 | $value = [ $value ] unless ref $value; | 
| 670 |  |  |  |  |  |  | # $value must now be an array reference | 
| 671 | 1599 | 50 |  |  |  | 4351 | next unless ref $value eq 'ARRAY'; | 
| 672 |  |  |  |  |  |  | # get all mandatory properties of this record | 
| 673 | 1599 |  |  |  |  | 1889 | my ($name, $type, $count, $rule, $filter) = @{$$syntax{$key}}; | 
|  | 1599 |  |  |  |  | 6167 |  | 
| 674 |  |  |  |  |  |  | # a "rule" matching 'calculated' means that this record | 
| 675 |  |  |  |  |  |  | # cannot be supplied by the user (so, we reject it) | 
| 676 | 1599 | 100 |  |  |  | 4212 | next if $rule =~ /calculated/; | 
| 677 |  |  |  |  |  |  | # very special mechanism to inhibit some tags | 
| 678 | 1572 | 100 | 66 |  |  | 4438 | 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 |  |  | 6000 | $$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 |  |  |  | 4936 | if (ref $rule eq 'CODE') { eval { &$rule(@$value) }; next if $@; } | 
|  | 109 | 100 |  |  |  | 217 |  | 
|  | 109 |  |  |  |  | 462 |  | 
|  | 109 |  |  |  |  | 356 |  | 
| 687 | 1412 | 100 |  |  |  | 2978 | else { next unless scalar @$value == grep {$_ =~ /^$rule$/s} @$value; } | 
|  | 12180 |  |  |  |  | 92284 |  | 
| 688 |  |  |  |  |  |  | # let us see if the values can actually be saved | 
| 689 |  |  |  |  |  |  | # in a record ($record remains undef on failure). | 
| 690 | 1439 | 100 |  |  |  | 6632 | 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 |  |  |  |  | 3792 | delete $$rejected{$key}; | 
| 696 | 1430 |  |  |  |  | 11041 | $$accepted{$key} = $record; | 
| 697 |  |  |  |  |  |  | } | 
| 698 |  |  |  |  |  |  | # return references to accepted and rejected data | 
| 699 | 339 |  |  |  |  | 1180 | return ($rejected, $accepted); | 
| 700 |  |  |  |  |  |  | } | 
| 701 |  |  |  |  |  |  |  | 
| 702 |  |  |  |  |  |  | # successful package load | 
| 703 |  |  |  |  |  |  | 1; |