File Coverage

blib/lib/PDF/API2/Resource/XObject/Image/TIFF/File.pm
Criterion Covered Total %
statement 102 142 71.8
branch 69 104 66.3
condition 1 9 11.1
subroutine 7 7 100.0
pod 0 4 0.0
total 179 266 67.2


line stmt bran cond sub pod time code
1             package PDF::API2::Resource::XObject::Image::TIFF::File;
2              
3 2     2   15 use strict;
  2         5  
  2         62  
4 2     2   11 use warnings;
  2         4  
  2         98  
5              
6             our $VERSION = '2.043'; # VERSION
7              
8 2     2   12 use IO::File;
  2         5  
  2         3453  
9              
10             sub new {
11 4     4 0 10 my ($class, $file) = @_;
12              
13 4         8 my $self = {};
14 4         8 bless($self, $class);
15 4 100       13 if (ref($file)) {
16 1         3 $self->{'fh'} = $file;
17 1         9 seek($self->{'fh'}, 0, 0);
18             }
19             else {
20 3         14 $self->{'fh'} = IO::File->new();
21 3 100       246 open($self->{'fh'}, '<', $file) or die "$!: $file";
22             }
23 3         21 binmode($self->{'fh'}, ':raw');
24 3         8 my $fh = $self->{'fh'};
25              
26 3         6 $self->{'offset'} = 0;
27 3         21 $fh->seek($self->{'offset'}, 0);
28              
29             # checking byte order of data
30 3         94 $fh->read($self->{'byteOrder'}, 2);
31 3         153 $self->{'byte'} = 'C';
32 3 50       10 $self->{'short'} = (($self->{'byteOrder'} eq 'MM') ? 'n' : 'v' );
33 3 50       8 $self->{'long'} = (($self->{'byteOrder'} eq 'MM') ? 'N' : 'V' );
34 3 50       8 $self->{'rational'} = (($self->{'byteOrder'} eq 'MM') ? 'NN' : 'VV' );
35              
36             # get/check version id
37 3         14 $fh->read($self->{'version'}, 2);
38 3         37 $self->{'version'} = unpack($self->{'short'}, $self->{'version'});
39 3 50       10 die "Wrong TIFF Id '$self->{version}' (should be 42)." if $self->{'version'} != 42;
40              
41             # get the offset to the first tag directory.
42 3         11 $fh->read($self->{'ifdOffset'}, 4);
43 3         22 $self->{'ifdOffset'} = unpack($self->{'long'}, $self->{'ifdOffset'});
44              
45 3         11 $self->readTags();
46              
47 3         10 return $self;
48             }
49              
50             sub readTag {
51 61     61 0 91 my $self = shift();
52 61         87 my $fh = $self->{'fh'};
53 61         82 my $buf;
54 61         159 $fh->read($buf, 12);
55 61         420 my $tag = unpack($self->{'short'}, substr($buf, 0, 2));
56 61         120 my $type = unpack($self->{'short'}, substr($buf, 2, 2));
57 61         108 my $count = unpack($self->{'long'}, substr($buf, 4, 4));
58 61         89 my $len = 0;
59              
60 61 100       164 $len = ($type == 1 ? $count : # byte
    100          
    100          
    100          
    100          
61             $type == 2 ? $count : # char2
62             $type == 3 ? $count * 2 : # int16
63             $type == 4 ? $count * 4 : # int32
64             $type == 5 ? $count * 8 : # rational: 2 * int32
65             $count);
66              
67 61         98 my $off = substr($buf, 8, 4);
68              
69 61 100       103 if ($len > 4) {
70 22         40 $off = unpack($self->{'long'}, $off);
71             }
72             else {
73             $off = ($type == 1 ? unpack($self->{'byte'}, $off) :
74             $type == 2 ? unpack($self->{'long'}, $off) :
75             $type == 3 ? unpack($self->{'short'}, $off) :
76 39 50       106 $type == 4 ? unpack($self->{'long'}, $off) : unpack($self->{'short'}, $off));
    100          
    50          
    50          
77             }
78              
79 61         176 return ($tag, $type, $count, $len, $off);
80             }
81              
82             sub close { ## no critic
83 3     3 0 16 my $self = shift();
84 3         17 return $self->{'fh'}->close();
85             }
86              
87             sub readTags {
88 3     3 0 5 my $self = shift();
89 3         6 my $fh = $self->{'fh'};
90 3         5 $self->{'fillOrder'} = 1;
91 3         6 $self->{'ifd'} = $self->{'ifdOffset'};
92              
93 3         8 while ($self->{'ifd'} > 0) {
94 3         10 $fh->seek($self->{'ifd'}, 0);
95 3         60 $fh->read($self->{'ifdNum'}, 2);
96 3         60 $self->{'ifdNum'} = unpack($self->{'short'}, $self->{'ifdNum'});
97 3         8 $self->{'bitsPerSample'} = 1;
98 3         11 foreach (1 .. $self->{'ifdNum'}) {
99 61         147 my ($valTag, $valType, $valCount, $valLen, $valOffset) = $self->readTag();
100             # print "tag=$valTag type=$valType count=$valCount len=$valLen off=$valOffset\n";
101 61 50       327 if ($valTag == 0) {
    100          
    100          
    100          
    100          
    100          
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    50          
    50          
    50          
    50          
102             }
103             elsif ($valTag == 256) {
104 3         7 $self->{'imageWidth'} = $valOffset;
105             }
106             elsif ($valTag == 257) {
107 3         7 $self->{'imageHeight'} = $valOffset;
108             }
109             elsif ($valTag == 258) {
110             # bits per sample
111 3 100       6 if ($valCount > 1) {
112 2         12 my $here = $fh->tell();
113 2         13 my $val;
114 2         6 $fh->seek($valOffset, 0);
115 2         38 $fh->read($val, 2);
116 2         32 $self->{'bitsPerSample'} = unpack($self->{'short'}, $val);
117 2         8 $fh->seek($here, 0);
118             }
119             else {
120 1         3 $self->{'bitsPerSample'} = $valOffset;
121             }
122             }
123             elsif ($valTag == 259) {
124             # compression
125 3         13 $self->{'filter'} = $valOffset;
126 3 100 33     14 if ($valOffset == 1) {
    50 0        
    50 0        
    0          
    0          
    0          
127 2         5 delete $self->{'filter'};
128             }
129             elsif ($valOffset == 3 || $valOffset == 4) {
130 0         0 $self->{'filter'} = 'CCITTFaxDecode';
131 0         0 $self->{'ccitt'} = $valOffset;
132             }
133             elsif ($valOffset == 5) {
134 1         2 $self->{'filter'} = 'LZWDecode';
135             }
136             elsif ($valOffset == 6 || $valOffset == 7) {
137 0         0 $self->{'filter'} = 'DCTDecode';
138             }
139             elsif ($valOffset == 8 || $valOffset == 0x80b2) {
140 0         0 $self->{'filter'} = 'FlateDecode';
141             }
142             elsif ($valOffset == 32773) {
143 0         0 $self->{'filter'} = 'RunLengthDecode';
144             }
145             else {
146 0         0 die "unknown/unsupported TIFF compression method with id '$self->{filter}'.";
147             }
148             }
149             elsif ($valTag == 262) {
150             # photometric interpretation
151 3         6 $self->{'colorSpace'} = $valOffset;
152 3 100       9 if ($valOffset == 0) {
    50          
    50          
    0          
    0          
    0          
    0          
153 1         3 $self->{'colorSpace'} = 'DeviceGray';
154 1         2 $self->{'whiteIsZero'} = 1;
155             }
156             elsif ($valOffset == 1) {
157 0         0 $self->{'colorSpace'} = 'DeviceGray';
158 0         0 $self->{'blackIsZero'} = 1;
159             }
160             elsif ($valOffset == 2) {
161 2         5 $self->{'colorSpace'} = 'DeviceRGB';
162             }
163             elsif($valOffset == 3) {
164 0         0 $self->{'colorSpace'} = 'Indexed';
165             }
166             # elsif($valOffset == 4) {
167             # $self->{'colorSpace'} = 'TransMask';
168             # }
169             elsif ($valOffset == 5) {
170 0         0 $self->{'colorSpace'} = 'DeviceCMYK';
171             }
172             elsif($valOffset == 6) {
173 0         0 $self->{'colorSpace'} = 'DeviceRGB';
174             }
175             elsif ($valOffset == 8) {
176 0         0 $self->{'colorSpace'} = 'Lab';
177             }
178             else {
179 0         0 die "unknown/unsupported TIFF photometric interpretation with id '$self->{colorSpace}'.";
180             }
181             }
182             elsif ($valTag == 266) {
183 0         0 $self->{'fillOrder'} = $valOffset;
184             }
185             elsif ($valTag == 270) {
186             # ImageDescription
187 0         0 my $here = $fh->tell();
188 0         0 $fh->seek($valOffset, 0);
189 0         0 $fh->read($self->{'imageDescription'}, $valLen);
190 0         0 $fh->seek($here, 0);
191             }
192             elsif($valTag == 282) {
193             # xRes
194 3         10 my $here = $fh->tell();
195 3         25 $fh->seek($valOffset, 0);
196 3         62 $fh->read($self->{'xRes'}, $valLen);
197 3         62 $fh->seek($here, 0);
198 3         53 $self->{'xRes'} = [unpack($self->{'rational'}, $self->{'xRes'})];
199 3         15 $self->{'xRes'} = ($self->{'xRes'}->[0] / $self->{'xRes'}->[1]);
200             }
201             elsif($valTag == 283) {
202             # yRes
203 3         10 my $here = $fh->tell();
204 3         20 $fh->seek($valOffset, 0);
205 3         54 $fh->read($self->{'yRes'}, $valLen);
206 3         56 $fh->seek($here, 0);
207 3         52 $self->{'yRes'} = [unpack($self->{'rational'}, $self->{'yRes'})];
208 3         13 $self->{'yRes'} = ($self->{'yRes'}->[0] / $self->{'yRes'}->[1]);
209             }
210             elsif ($valTag == 296) {
211             # resolution Unit
212 3         6 $self->{'resUnit'} = $valOffset;
213             }
214             elsif ($valTag == 273) {
215             # image data offset/strip offsets
216 3 50       7 if ($valCount == 1) {
217 3         16 $self->{'imageOffset'} = $valOffset;
218             }
219             else {
220 0         0 my $here =$fh->tell();
221 0         0 my $val;
222 0         0 $fh->seek($valOffset, 0);
223 0         0 $fh->read($val, $valLen);
224 0         0 $fh->seek($here, 0);
225 0         0 $self->{'imageOffset'} = [unpack($self->{'long'} . '*', $val)];
226             }
227             }
228             elsif ($valTag == 277) {
229 3         7 $self->{'samplesPerPixel'} = $valOffset;
230             }
231             elsif ($valTag == 278) {
232 3         9 $self->{'RowsPerStrip'} = $valOffset;
233             }
234             elsif ($valTag == 279) {
235             # image data length/strip lengths
236 3 50       7 if ($valCount == 1) {
237 3         7 $self->{'imageLength'} = $valOffset;
238             }
239             else {
240 0         0 my $here = $fh->tell();
241 0         0 my $val;
242 0         0 $fh->seek($valOffset, 0);
243 0         0 $fh->read($val, $valLen);
244 0         0 $fh->seek($here, 0);
245 0         0 $self->{'imageLength'} = [unpack($self->{'long'} . '*', $val)];
246             }
247             }
248             elsif ($valTag == 292) {
249 0         0 $self->{'g3Options'} = $valOffset;
250             }
251             elsif ($valTag == 293) {
252 0         0 $self->{'g4Options'} = $valOffset;
253             }
254             elsif ($valTag == 320) {
255             # color map
256 0         0 $self->{'colorMapOffset'} = $valOffset;
257 0         0 $self->{'colorMapSamples'} = $valCount;
258 0         0 $self->{'colorMapLength'} = $valCount * 2; # shorts!
259             }
260             elsif ($valTag == 317) {
261 0         0 $self->{'lzwPredictor'} = $valOffset;
262             }
263             elsif ($valTag == 0x800d) {
264             # imageID
265 0         0 my $here = $fh->tell();
266 0         0 $fh->seek($valOffset, 0);
267 0         0 $fh->read($self->{'imageId'}, $valLen);
268 0         0 $fh->seek($here, 0);
269             }
270             # else {
271             # print "tag=$valTag, type=$valType, len=$valLen\n";
272             # }
273             }
274 3         10 $fh->read($self->{'ifd'}, 4);
275 3         23 $self->{'ifd'} = unpack($self->{'long'}, $self->{'ifd'});
276             }
277              
278 3         6 return $self;
279             }
280              
281             1;