File Coverage

blib/lib/Image/MetaData/JPEG/parsers/app0.pl
Criterion Covered Total %
statement 54 59 91.5
branch 13 18 72.2
condition 1 3 33.3
subroutine 5 5 100.0
pod 0 1 0.0
total 73 86 84.8


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 15     15   64 use Image::MetaData::JPEG::data::Tables qw(:TagsAPP0);
  15         19  
  15         2065  
7 15     15   72 no integer;
  15         18  
  15         63  
8 15     15   256 use strict;
  15         22  
  15         320  
9 15     15   52 use warnings;
  15         14  
  15         6004  
10              
11             ###########################################################
12             # This method parses an APP0 segment. APP0 segments are #
13             # written by older cameras adopting the JFIF (JPEG File #
14             # Interchange Format) for storing images. JFIF uses the #
15             # APP0 application segment for inserting configuration #
16             # data and a thumbnail image. The format is as follows: #
17             #---------------------------------------------------------#
18             # 5 bytes identifier ('JFIF\000' = 0x4a46494600) #
19             # 1 byte major version (e.g. 0x01) #
20             # 1 byte minor version (e.g. 0x01 or 0x02) #
21             # 1 byte units (0: densities give aspect ratio #
22             # 1: density values are dots per inch #
23             # 2: density values are dots per cm) #
24             # 2 bytes Xdensity (Horizontal pixel density) #
25             # 2 bytes Ydensity (Vertical pixel density) #
26             # 1 byte Xthumbnail (Thumbnail horizontal pixel count) #
27             # 1 byte Ythumbnail (Thumbnail vertical pixel count) #
28             # 3n bytes (RGB)n, packed (24-bit) RGB values for the #
29             # thumbnail pixels, n = Xthumbnail * Ythumbnail #
30             #---------------------------------------------------------#
31             # There is also an "extended" version of JFIF (only pos- #
32             # sible for JFIF versions 1.02 and above). In this case #
33             # the identifier is not 'JFIF' but 'JFXX'. The syntax in #
34             # this case is modified as follows: #
35             #---------------------------------------------------------#
36             # 5 bytes identifier ('JFXX\000' = 0x4a46585800) #
37             # 1 byte extension (0x10 Thumbnail coded using JPEG #
38             # 0x11 Thumbnail using 1 byte/pixel #
39             # 0x13 Thumbnail using 3 bytes/pixel#
40             #---------------------------------------------------------#
41             # The remainder of the segment varies with the extension. #
42             #---------------------------------------------------------#
43             # Thumbnail coded using JPEG: the compressed thumbnail #
44             # immediately follows the extension code in the extension #
45             # data field and the length must be included in the JFIF #
46             # extension APP0 marker length field. The extension data #
47             # field conforms to the syntax for a JPEG file (SOI .... #
48             # SOF ... EOI); however, no 'JFIF' or 'JFXX' marker seg- #
49             # ments shall be present. #
50             #---------------------------------------------------------#
51             # Thumbnail stored using one byte per pixel: this must #
52             # include a thumbnail and a colour palette as follows: #
53             # 1 byte Xthumbnail (Thumbnail horizontal pixel count) #
54             # 1 byte Ythumbnail (Thumbnail vertical pixel count) #
55             # 768 bytes palette (24-bit RGB pixel values for the #
56             # colour palette. These values define #
57             # the colors represented by each value #
58             # of an 8-bit binary encoding (0-255)) #
59             # n bytes pixels (8-bit values for the thumbnail #
60             # pixels: n = Xthumbnail * Ythumbnail) #
61             #---------------------------------------------------------#
62             # Thumbnail stored using three bytes per pixel: in this #
63             # case there is no colour palette: #
64             # 1 byte Xthumbnail (Thumbnail horizontal pixel count) #
65             # 1 byte Ythumbnail (Thumbnail vertical pixel count) #
66             # 3n bytes pixels (24-bit RGB values for the thumbnail #
67             # pixels, n = Xthumbnail * Ythumbnail) #
68             #---------------------------------------------------------#
69             # Ref: http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/JPEG.txt #
70             ###########################################################
71             sub parse_app0 {
72 40     40 0 62 my ($this) = @_;
73 40         51 my $offset = 0;
74 40         45 my $thumb_x_dim = 0; my $thumb_y_dim = 0;
  40         51  
75              
76             # Decode the APP0 app-extension identifier. It's an arbitrarily
77             # long string, terminated by binary zero. Find length:
78 40         44 my $length = 1; # assuming the app id is at least one byte long should be safe
79 40         52 for(;;){
80 175         317 my $byte = $this->data($length, 1);
81 175 100       340 last if $byte eq "\x00";
82 136 100       258 last if $length >= $this->size(); # no infinite loop
83 135         191 $length++;
84             }
85 40         157 my $identifier = $this->store_record
86             ('Identifier', $ASCII, $offset, $length + 1)->get_value(); # +1 as Tables.pm currently includes the null-terminator
87             # go to the appropriate decoding routine depending on found identifier
88 39 100       154 goto APP0_simple if $identifier eq $APP0_JFIF_TAG;
89 4 100       17 goto APP0_extended if $identifier eq $APP0_JFXX_TAG;
90             # JPEG specs tell us to simply ignore unknown identifiers
91             # $this->die("Unknown identifier ($identifier)");
92 2         6 $offset += ($this->size() - $length - 1); # skip over (NOPARSE) unknown data
93 2         6 goto APP0_END;
94 35         136 APP0_simple:
95             # as far as I know, in a JFIF APP0 there are always the following
96             # seven fields, even if the thumbnail is absent. This means that
97             # at least 14 bytes (including the initial identifier) must be there.
98             # Do a test size and then read the fields.
99             $this->test_size($offset + 9);
100 35         110 $this->store_record('MajorVersion', $BYTE , $offset);
101 35         97 $this->store_record('MinorVersion', $BYTE , $offset);
102 35         107 $this->store_record('Units' , $BYTE , $offset);
103 35         97 $this->store_record('XDensity' , $SHORT, $offset);
104 35         106 $this->store_record('YDensity' , $SHORT, $offset);
105 35         112 $thumb_x_dim =$this->store_record('XThumbnail',$BYTE,$offset)->get_value();
106 35         122 $thumb_y_dim =$this->store_record('YThumbnail',$BYTE,$offset)->get_value();
107             # now calculate the size of the thumbnail data area. This
108             # is three times the product of the two previous dimensions.
109 35         76 my $thumb_size = 3 * $thumb_x_dim * $thumb_y_dim;
110             # issue an error if the thumbnail data area is not there
111 35         118 $this->test_size($offset + $thumb_size, "corrupted thumbnail");
112             # if size is positive, get the packed thumbnail as unknown
113 35 50       88 $this->store_record('ThumbnailData', $UNDEF, $offset, $thumb_size)
114             if $thumb_size > 0;
115 35         200 goto APP0_END;
116 2         7 APP0_extended:
117             # so this is an extended JFIF (JFXX). Get the extension code
118             my $ext_code = $this->store_record
119             ('ExtensionCode', $BYTE, $offset)->get_value();
120             # now, depending on it, go to another parsing segment
121 2 100       8 goto APP0_ext_jpeg if $ext_code == $APP0_JFXX_JPG;
122 1 50 33     9 goto APP0_ext_bytes if ($ext_code == $APP0_JFXX_1B ||
123             $ext_code == $APP0_JFXX_3B);
124             # if we are still here, die of unknown extension code
125 0         0 $this->die("Unknown extension code ($ext_code)");
126 1         4 APP0_ext_jpeg:
127             # in this case, the rest of the data area is a jpeg image
128             # which we save as undefined data in a single field. We don't
129             # dare to check the syntax of these data and go to the end.
130             $this->store_record('JPEGThumbnail',$UNDEF,$offset,$this->size()-$offset);
131 1         3 goto APP0_END;
132 1         5 APP0_ext_bytes:
133             # for the other two extensions, we first make sure that there
134             # are two other bytes, then we read the thumbnail size
135             $this->test_size($offset + 2, "no thumbnail dimensions");
136 1         4 $thumb_x_dim =$this->store_record('XThumbnail',$BYTE,$offset)->get_value();
137 1         4 $thumb_y_dim =$this->store_record('YThumbnail',$BYTE,$offset)->get_value();
138             # now calculate the number of pixels in the thumbnail data area.
139             # This is the product of the two previous dimensions.
140 1         2 my $thumb_pixels = $thumb_x_dim * $thumb_y_dim;
141             # now, the two extensions take different routes ...
142 1 50       6 goto APP0_ext_1byte if $ext_code eq $APP0_JFXX_1B;
143 0 0       0 goto APP0_ext_3bytes if $ext_code eq $APP0_JFXX_3B;
144 1         4 APP0_ext_1byte:
145             # in this case, there must be 768 bytes for the palette, followed
146             # by $thumb_pixels for the thumbnail. Issue an error otherwise
147             $this->test_size($offset + $APP0_JFXX_PAL + $thumb_pixels,
148             "Incorrect thumbnail data size in JFXX 0x10");
149             # store the colour palette and the thumbnail as
150             # undefined data and we have finished.
151 1         5 $this->store_record('ColorPalette' , $UNDEF, $offset, $APP0_JFXX_PAL);
152 1         3 $this->store_record('1ByteThumbnail', $UNDEF, $offset, $thumb_pixels);
153 1         4 goto APP0_END;
154 0         0 APP0_ext_3bytes:
155             # in this case, there must be 3 * $thumb_pixels
156             # for the thumbnail data. Issue an error otherwise
157             $this->test_size($offset + 3 * $thumb_pixels,
158             "Incorrect thumbnail data size in JFXX 0x13");
159             # store the thumbnail as undefined data and we have finished.
160 0         0 $this->store_record('3BytesThumbnail', $UNDEF, $offset, 3 * $thumb_pixels);
161 0         0 goto APP0_END;
162 39         161 APP0_END:
163             # check that there are no spurious data in the segment
164             $this->test_size(-$offset, "unknown data at segment end");
165             }
166              
167             # successful load
168             1;