| 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 |  | 88 | no  integer; | 
|  | 14 |  |  |  |  | 32 |  | 
|  | 14 |  |  |  |  | 102 |  | 
| 8 | 14 |  |  | 14 |  | 414 | use strict; | 
|  | 14 |  |  |  |  | 29 |  | 
|  | 14 |  |  |  |  | 442 |  | 
| 9 | 14 |  |  | 14 |  | 76 | use warnings; | 
|  | 14 |  |  |  |  | 31 |  | 
|  | 14 |  |  |  |  | 11684 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | ########################################################### | 
| 12 |  |  |  |  |  |  | # This method is for display/debug pourpouse. It returns  # | 
| 13 |  |  |  |  |  |  | # a string describing the details of the structure of the # | 
| 14 |  |  |  |  |  |  | # JPEG file linked to the current object. It can ask      # | 
| 15 |  |  |  |  |  |  | # details to sub-objects.                                 # | 
| 16 |  |  |  |  |  |  | ########################################################### | 
| 17 |  |  |  |  |  |  | sub get_description { | 
| 18 | 29 |  |  | 29 | 1 | 208692 | my ($this) = @_; | 
| 19 |  |  |  |  |  |  | # prepare the string to be returned and store | 
| 20 |  |  |  |  |  |  | # a bar and the associated filename | 
| 21 | 29 |  |  |  |  | 141 | my $description = "Original JPEG file: $this->{filename}\n"; | 
| 22 |  |  |  |  |  |  | # Print the image size | 
| 23 | 29 |  |  |  |  | 130 | $description .= sprintf "(%dx%d)\n", $this->get_dimensions(); | 
| 24 |  |  |  |  |  |  | # Loop over all segments (use the order of the array) | 
| 25 | 29 |  |  |  |  | 58 | $description .= $_->get_description() foreach @{$this->{segments}}; | 
|  | 29 |  |  |  |  | 226 |  | 
| 26 |  |  |  |  |  |  | # return the string which was cooked up | 
| 27 | 29 |  |  |  |  | 838 | return $description; | 
| 28 |  |  |  |  |  |  | } | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | ########################################################### | 
| 31 |  |  |  |  |  |  | # This method returns the image size from two specific    # | 
| 32 |  |  |  |  |  |  | # record values in the SOF segment. The return value is   # | 
| 33 |  |  |  |  |  |  | # (x-dimension, y- dimension). If there is no SOF segment # | 
| 34 |  |  |  |  |  |  | # (or more than one), the return value is (0,0). In this  # | 
| 35 |  |  |  |  |  |  | # case one should investigate, because this is not normal.# | 
| 36 |  |  |  |  |  |  | #=========================================================# | 
| 37 |  |  |  |  |  |  | # Ref: .... ?                                             # | 
| 38 |  |  |  |  |  |  | ########################################################### | 
| 39 |  |  |  |  |  |  | sub get_dimensions { | 
| 40 | 35 |  |  | 35 | 1 | 3841 | my ($this) = @_; | 
| 41 |  |  |  |  |  |  | # find the start of frame segments | 
| 42 | 35 |  |  |  |  | 208 | my @sofs = $this->get_segments("SOF"); | 
| 43 |  |  |  |  |  |  | # if there is more than one such segment, there is something | 
| 44 |  |  |  |  |  |  | # wrong. In this case it is better to return (0,0) and debug. | 
| 45 | 35 | 100 |  |  |  | 223 | return (0,0) if (scalar @sofs) != 1; | 
| 46 |  |  |  |  |  |  | # same if there is an error in the segment | 
| 47 | 21 |  |  |  |  | 47 | my $segment = $sofs[0]; | 
| 48 | 21 | 50 |  |  |  | 127 | return (0,0) if $segment->{error}; | 
| 49 |  |  |  |  |  |  | # search the relevant records and get their values: if they are | 
| 50 |  |  |  |  |  |  | # not there, we get undef, which we promptly transform into zero | 
| 51 | 21 |  | 100 |  |  | 121 | my $xdim = $segment->search_record_value('MaxSamplesPerLine') || 0; | 
| 52 | 21 |  | 100 |  |  | 87 | my $ydim = $segment->search_record_value('MaxLineNumber')     || 0; | 
| 53 |  |  |  |  |  |  | # return dimension values | 
| 54 | 21 |  |  |  |  | 118 | return ( $xdim, $ydim ); | 
| 55 |  |  |  |  |  |  | } | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | ########################################################### | 
| 58 |  |  |  |  |  |  | # This method returns a reference to a hash with a plain  # | 
| 59 |  |  |  |  |  |  | # translation of the content of the first interesting     # | 
| 60 |  |  |  |  |  |  | # APP0 segment (this is the first 'JFXX' APP0 segment,    # | 
| 61 |  |  |  |  |  |  | # if present, the first 'JFIF' APP0 segment otherwise).   # | 
| 62 |  |  |  |  |  |  | # Segments with errors are excluded. An empty hash means  # | 
| 63 |  |  |  |  |  |  | # that no valid APP0 segment is present.                  # | 
| 64 |  |  |  |  |  |  | # See Segment::parse_app0 for further details.            # | 
| 65 |  |  |  |  |  |  | #=========================================================# | 
| 66 |  |  |  |  |  |  | #     JFIF          JFXX          JFXX          JFXX      # | 
| 67 |  |  |  |  |  |  | #    (basic)    (RGB 1 byte)  (RGB 3 bytes)    (JPEG)     # | 
| 68 |  |  |  |  |  |  | #  -----------  ------------  -------------  -----------  # | 
| 69 |  |  |  |  |  |  | #   Identifier   Identifier    Identifier    Identifier   # | 
| 70 |  |  |  |  |  |  | #  MajorVersion ExtensionCode ExtensionCode ExtensionCode # | 
| 71 |  |  |  |  |  |  | #  MinorVersion  XThumbnail    XThumbnail   JPEGThumbnail # | 
| 72 |  |  |  |  |  |  | #     Units      YThumbnail    YThumbnail                 # | 
| 73 |  |  |  |  |  |  | #    XDensity   ColorPalette 3BytesThumbnail              # | 
| 74 |  |  |  |  |  |  | #    YDensity  1ByteThumbnail                             # | 
| 75 |  |  |  |  |  |  | #   XThumbnail                                            # | 
| 76 |  |  |  |  |  |  | #   YThumbnail                                            # | 
| 77 |  |  |  |  |  |  | #  ThumbnailData                                          # | 
| 78 |  |  |  |  |  |  | ########################################################### | 
| 79 |  |  |  |  |  |  | sub get_app0_data { | 
| 80 | 1 |  |  | 1 | 1 | 1059 | my ($this) = @_; | 
| 81 |  |  |  |  |  |  | # find all APP0 segments, excluding segments with errors | 
| 82 | 1 |  |  |  |  | 9 | my @app0s = grep { ! $_->{error} } $this->get_segments("APP0"); | 
|  | 1 |  |  |  |  | 5 |  | 
| 83 |  |  |  |  |  |  | # select extended JFIF segments (the identifier contains JFXX) | 
| 84 | 1 |  |  |  |  | 3 | my @jfxxs = grep { my $id = $_->search_record_value('Identifier'); | 
|  | 1 |  |  |  |  | 4 |  | 
| 85 | 1 | 50 |  |  |  | 9 | defined $id && $id =~ /JFXX/ } @app0s; | 
| 86 |  |  |  |  |  |  | # select a segment (try JFXX, then plain APP0, otherwise undef) | 
| 87 | 1 | 50 |  |  |  | 14 | my $segment = @jfxxs ? $jfxxs[0] : (@app0s ? $app0s[0] : undef); | 
|  |  | 50 |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | # prepare a hash with the records in the APP0 segment | 
| 89 | 1 |  |  |  |  | 3 | my %data = map { $_->{key} => $_->get_value() } @{$segment->{records}}; | 
|  | 8 |  |  |  |  | 21 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 90 |  |  |  |  |  |  | # return a reference to the filled hash | 
| 91 | 1 |  |  |  |  | 6 | return \ %data; | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | # successful package load | 
| 95 |  |  |  |  |  |  | 1; |