File Coverage

blib/lib/Image/MetaData/JPEG/parsers/app0.pl
Criterion Covered Total %
statement 47 52 90.3
branch 9 14 64.2
condition 1 3 33.3
subroutine 5 5 100.0
pod 0 1 0.0
total 62 75 82.6


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   92 use Image::MetaData::JPEG::data::Tables qw(:TagsAPP0);
  15         34  
  15         3204  
7 15     15   146 no integer;
  15         31  
  15         95  
8 15     15   339 use strict;
  15         32  
  15         437  
9 15     15   81 use warnings;
  15         31  
  15         9243  
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 36     36 0 80 my ($this) = @_;
73 36         60 my $offset = 0;
74 36         72 my $thumb_x_dim = 0; my $thumb_y_dim = 0;
  36         57  
75             # first, decode the identifier. It can be simple
76             # (JFIF), or extended (JFXX). We need five bytes
77 36         218 my $identifier = $this->store_record
78             ('Identifier', $ASCII, $offset, length $APP0_JFIF_TAG)->get_value();
79             # go to the relevant decoding routine depending on it
80 36 100       182 goto APP0_simple if $identifier eq $APP0_JFIF_TAG;
81 3 100       20 goto APP0_extended if $identifier eq $APP0_JFXX_TAG;
82             # if we are still here, let us die of an unknown identifier
83 1         8 $this->die("Unknown identifier ($identifier)");
84 33         291 APP0_simple:
85             # as far as I know, in a JFIF APP0 there are always the following
86             # seven fields, even if the thumbnail is absent. This means that
87             # at least 14 bytes (including the initial identifier) must be there.
88             # Do a test size and then read the fields.
89             $this->test_size($offset + 9);
90 33         152 $this->store_record('MajorVersion', $BYTE , $offset);
91 33         122 $this->store_record('MinorVersion', $BYTE , $offset);
92 33         136 $this->store_record('Units' , $BYTE , $offset);
93 33         134 $this->store_record('XDensity' , $SHORT, $offset);
94 33         149 $this->store_record('YDensity' , $SHORT, $offset);
95 33         126 $thumb_x_dim =$this->store_record('XThumbnail',$BYTE,$offset)->get_value();
96 33         377 $thumb_y_dim =$this->store_record('YThumbnail',$BYTE,$offset)->get_value();
97             # now calculate the size of the thumbnail data area. This
98             # is three times the product of the two previous dimensions.
99 33         92 my $thumb_size = 3 * $thumb_x_dim * $thumb_y_dim;
100             # issue an error if the thumbnail data area is not there
101 33         397 $this->test_size($offset + $thumb_size, "corrupted thumbnail");
102             # if size is positive, get the packed thumbnail as unknown
103 33 50       110 $this->store_record('ThumbnailData', $UNDEF, $offset, $thumb_size)
104             if $thumb_size > 0;
105 33         210 goto APP0_END;
106 2         10 APP0_extended:
107             # so this is an extended JFIF (JFXX). Get the extension code
108             my $ext_code = $this->store_record
109             ('ExtensionCode', $BYTE, $offset)->get_value();
110             # now, depending on it, go to another parsing segment
111 2 100       13 goto APP0_ext_jpeg if $ext_code == $APP0_JFXX_JPG;
112 1 50 33     58 goto APP0_ext_bytes if ($ext_code == $APP0_JFXX_1B ||
113             $ext_code == $APP0_JFXX_3B);
114             # if we are still here, die of unknown extension code
115 0         0 $this->die("Unknown extension code ($ext_code)");
116 1         6 APP0_ext_jpeg:
117             # in this case, the rest of the data area is a jpeg image
118             # which we save as undefined data in a single field. We don't
119             # dare to check the syntax of these data and go to the end.
120             $this->store_record('JPEGThumbnail',$UNDEF,$offset,$this->size()-$offset);
121 1         5 goto APP0_END;
122 1         7 APP0_ext_bytes:
123             # for the other two extensions, we first make sure that there
124             # are two other bytes, then we read the thumbnail size
125             $this->test_size($offset + 2, "no thumbnail dimensions");
126 1         4 $thumb_x_dim =$this->store_record('XThumbnail',$BYTE,$offset)->get_value();
127 1         7 $thumb_y_dim =$this->store_record('YThumbnail',$BYTE,$offset)->get_value();
128             # now calculate the number of pixels in the thumbnail data area.
129             # This is the product of the two previous dimensions.
130 1         3 my $thumb_pixels = $thumb_x_dim * $thumb_y_dim;
131             # now, the two extensions take different routes ...
132 1 50       7 goto APP0_ext_1byte if $ext_code eq $APP0_JFXX_1B;
133 0 0       0 goto APP0_ext_3bytes if $ext_code eq $APP0_JFXX_3B;
134 1         8 APP0_ext_1byte:
135             # in this case, there must be 768 bytes for the palette, followed
136             # by $thumb_pixels for the thumbnail. Issue an error otherwise
137             $this->test_size($offset + $APP0_JFXX_PAL + $thumb_pixels,
138             "Incorrect thumbnail data size in JFXX 0x10");
139             # store the colour palette and the thumbnail as
140             # undefined data and we have finished.
141 1         5 $this->store_record('ColorPalette' , $UNDEF, $offset, $APP0_JFXX_PAL);
142 1         4 $this->store_record('1ByteThumbnail', $UNDEF, $offset, $thumb_pixels);
143 1         4 goto APP0_END;
144 0         0 APP0_ext_3bytes:
145             # in this case, there must be 3 * $thumb_pixels
146             # for the thumbnail data. Issue an error otherwise
147             $this->test_size($offset + 3 * $thumb_pixels,
148             "Incorrect thumbnail data size in JFXX 0x13");
149             # store the thumbnail as undefined data and we have finished.
150 0         0 $this->store_record('3BytesThumbnail', $UNDEF, $offset, 3 * $thumb_pixels);
151 0         0 goto APP0_END;
152 35         138 APP0_END:
153             # check that there are no spurious data in the segment
154             $this->test_size(-$offset, "unknown data at segment end");
155             }
156              
157             # successful load
158             1;