File Coverage

blib/lib/PDF/Builder/Resource/XObject/Image/TIFF.pm
Criterion Covered Total %
statement 79 176 44.8
branch 18 44 40.9
condition 15 39 38.4
subroutine 13 16 81.2
pod 3 8 37.5
total 128 283 45.2


line stmt bran cond sub pod time code
1             package PDF::Builder::Resource::XObject::Image::TIFF;
2              
3 2     2   1041 use base 'PDF::Builder::Resource::XObject::Image';
  2         5  
  2         1379  
4              
5 2     2   11 use strict;
  2         4  
  2         40  
6 2     2   9 use warnings;
  2         4  
  2         126  
7              
8             our $VERSION = '3.028'; # VERSION
9             our $LAST_UPDATE = '3.027'; # manually update whenever code is changed
10              
11 2     2   11 use Compress::Zlib;
  2         3  
  2         780  
12              
13 2     2   14 use PDF::Builder::Basic::PDF::Utils;
  2         4  
  2         179  
14 2     2   1807 use PDF::Builder::Resource::XObject::Image::TIFF::File;
  2         7  
  2         88  
15 2     2   14 use PDF::Builder::Util;
  2         5  
  2         317  
16 2     2   12 use Scalar::Util qw(weaken);
  2         4  
  2         4503  
17              
18             =head1 NAME
19              
20             PDF::Builder::Resource::XObject::Image::TIFF - TIFF image support
21              
22             Inherits from L<PDF::Builder::Resource::XObject::Image>
23              
24             =head1 METHODS
25              
26             =head2 new
27              
28             $res = PDF::Builder::Resource::XObject::Image::TIFF->new($pdf, $file, %opts)
29              
30             =over
31              
32             Returns a TIFF-image object.
33              
34             If the Graphics::TIFF package is installed, the TIFF_GT library will be used
35             instead of the TIFF library. In such a case, use of the TIFF library may be
36             forced via the C<nouseGT> flag (see Builder documentation for C<image_tiff()>).
37              
38             Options:
39              
40             =over
41              
42             =item 'name' => 'string'
43              
44             This is the name you can give for the TIFF image object. The default is Ixnnnn.
45              
46             =back
47              
48             Remember that you need to use the Builder.pm method image_tiff in order to
49             display a TIFF file.
50              
51             =back
52              
53             =cut
54              
55             sub new {
56 4     4 1 22 my ($class, $pdf, $file, %opts) = @_;
57             # copy dashed option names to preferred undashed names
58 4 50 33     16 if (defined $opts{'-nouseGT'} && !defined $opts{'nouseGT'}) { $opts{'nouseGT'} = delete($opts{'-nouseGT'}); }
  0         0  
59 4 50 33     17 if (defined $opts{'-name'} && !defined $opts{'name'}) { $opts{'name'} = delete($opts{'-name'}); }
  0         0  
60 4 50 33     18 if (defined $opts{'-compress'} && !defined $opts{'compress'}) { $opts{'compress'} = delete($opts{'-compress'}); }
  0         0  
61              
62 4         9 my ($name, $compress);
63 4 50       14 if (exists $opts{'name'}) { $name = $opts{'name'}; }
  0         0  
64             #if (exists $opts{'compress'}) { $compress = $opts{'compress'}; }
65              
66 4         8 my $self;
67              
68 4         29 my $tif = PDF::Builder::Resource::XObject::Image::TIFF::File->new($file, %opts);
69              
70             # dump everything in tif except huge data streams
71             # foreach (sort keys %{ $tif }) {
72             # if ($_ eq ' stream') { next; }
73             # if ($_ eq ' apipdf') { next; }
74             # if ($_ eq ' realised') { next; }
75             # if ($_ eq ' uid') { next; }
76             # if (defined $tif->{$_}) {
77             # print "\$tif->{'$_'} = '".($tif->{$_})."'\n";
78             # } else {
79             # print "\$tif->{'$_'} = ?\n";
80             # }
81             # }
82              
83             # in case of problematic things
84             # proxy to other modules
85              
86 3 50       11 $class = ref($class) if ref($class);
87              
88 3   33     23 $self = $class->SUPER::new($pdf, $name || 'Ix'.pdfkey());
89 3 50       13 $pdf->new_obj($self) unless $self->is_obj($pdf);
90              
91 3         9 $self->{' apipdf'} = $pdf;
92 3         6 weaken $self->{' apipdf'};
93              
94 3         16 $self->read_tiff($pdf, $tif);
95              
96 3         13 $tif->close();
97              
98 3         77 return $self;
99             }
100              
101             =head2 usesLib
102              
103             $mode = $tif->usesLib()
104              
105             =over
106              
107             Returns 1 if Graphics::TIFF installed and used, 0 if not installed, or -1 if
108             installed but not used (nouseGT option given to C<image_tiff>).
109              
110             B<Caution:> this method can only be used I<after> the image object has been
111             created. It can't tell you whether Graphics::TIFF is available in
112             advance of actually using it, in case you want to use some functionality
113             available only in TIFF_GT. See the <PDF::Builder> LA_GT() call if you
114             need to know in advance.
115              
116             =back
117              
118             =cut
119              
120             sub usesLib {
121 2     2 1 11 my ($self) = shift;
122             # should be 0 for Graphics::TIFF not installed, or -1 for is installed,
123             # but not using it
124 2         7 return $self->{'usesGT'}->val();
125             }
126              
127             sub handle_generic {
128 2     2 0 4 my ($self, $pdf, $tif) = @_;
129              
130 2 50       6 if ($tif->{'filter'}) {
131             # should we die here ?
132             # die "unknown tiff-compression ";
133 0         0 $self->filters($tif->{'filter'});
134 0         0 $self->{' nofilt'} = 1;
135             } else {
136 2         14 $self->filters('FlateDecode');
137             }
138              
139 2 50       7 if (ref($tif->{'imageOffset'})) {
140 0         0 $self->{' stream'} = '';
141 0         0 my $d = scalar @{$tif->{'imageOffset'}};
  0         0  
142 0         0 foreach (1 .. $d) {
143 0         0 my $buf;
144 0         0 $tif->{'fh'}->seek(shift(@{$tif->{'imageOffset'}}), 0);
  0         0  
145 0         0 $tif->{'fh'}->read($buf, shift(@{$tif->{'imageLength'}}));
  0         0  
146 0         0 $self->{' stream'} .= $buf;
147             }
148             } else {
149 2         12 $tif->{'fh'}->seek($tif->{'imageOffset'}, 0);
150 2         42 $tif->{'fh'}->read($self->{' stream'}, $tif->{'imageLength'});
151             }
152              
153 2         55 return $self;
154             }
155              
156             sub handle_flate {
157 0     0 0 0 my ($self, $pdf, $tif) = @_;
158              
159 0         0 $self->filters('FlateDecode');
160              
161 0 0       0 if (ref($tif->{'imageOffset'})) {
162 0         0 $self->{' stream'} = '';
163 0         0 my $d = scalar @{$tif->{'imageOffset'}};
  0         0  
164 0         0 foreach (1 .. $d) {
165 0         0 my $buf;
166 0         0 $tif->{'fh'}->seek(shift(@{$tif->{'imageOffset'}}), 0);
  0         0  
167 0         0 $tif->{'fh'}->read($buf, shift(@{$tif->{'imageLength'}}));
  0         0  
168 0         0 $buf = uncompress($buf);
169 0         0 $self->{' stream'} .= $buf;
170             }
171             } else {
172 0         0 $tif->{'fh'}->seek($tif->{'imageOffset'}, 0);
173 0         0 $tif->{'fh'}->read($self->{' stream'}, $tif->{'imageLength'});
174 0         0 $self->{' stream'} = uncompress($self->{' stream'});
175             }
176              
177 0         0 return $self;
178             }
179              
180             sub handle_lzw {
181 1     1 0 4 my ($self, $pdf, $tif) = @_;
182              
183 1         3 $self->{' nofilt'} = 1;
184 1         4 $self->{'Filter'} = PDFArray(PDFName('LZWDecode'));
185 1         4 my $decode = PDFDict();
186 1         5 $self->{'DecodeParms'} = PDFArray($decode);
187 1         5 $decode->{'Columns'} = PDFNum($tif->{'imageWidth'});
188 1         3 $decode->{'Rows'} = PDFNum($tif->{'imageHeight'});
189 1         4 $decode->{'DamagedRowsBeforeError'} = PDFNum(100);
190 1         4 $decode->{'EndOfLine'} = PDFBool(1);
191 1         4 $decode->{'EncodedByteAlign'} = PDFBool(1);
192 1 50 33     20 if (defined $tif->{'Predictor'} and $tif->{'Predictor'} > 1) {
193 0         0 $decode->{'Predictor'} = PDFNum($tif->{'Predictor'});
194             }
195              
196 1 50       4 if (ref($tif->{'imageOffset'})) {
197 0         0 $self->{' stream'} = '';
198 0         0 my $d = scalar @{$tif->{'imageOffset'}};
  0         0  
199 0         0 foreach (1 .. $d) {
200 0         0 $tif->{'fh'}->seek(shift(@{$tif->{'imageOffset'}}), 0);
  0         0  
201 0         0 my $buf;
202 0         0 $tif->{'fh'}->read($buf, shift(@{$tif->{'imageLength'}}));
  0         0  
203 0         0 my $filter = PDF::Builder::Basic::PDF::Filter::LZWDecode->new();
204 0         0 $self->{' stream'} .= $filter->infilt($buf);
205             }
206 0         0 my $filter = PDF::Builder::Basic::PDF::Filter::LZWDecode->new();
207 0         0 $self->{' stream'} = $filter->outfilt($self->{' stream'});
208             } else {
209 1         6 $tif->{'fh'}->seek($tif->{'imageOffset'}, 0);
210 1         37 $tif->{'fh'}->read($self->{' stream'}, $tif->{'imageLength'});
211             }
212              
213 1         34 return $self;
214             }
215              
216             sub handle_ccitt {
217 0     0 0 0 my ($self, $pdf, $tif) = @_;
218              
219 0         0 $self->{' nofilt'} = 1;
220 0         0 $self->{'Filter'} = PDFName('CCITTFaxDecode');
221 0         0 $self->{'DecodeParms'} = PDFDict();
222             $self->{'DecodeParms'}->{'K'} = ($tif->{'ccitt'} == 4 ||
223 0 0 0     0 (($tif->{'g3Options'}||0) & 0x1))? PDFNum(-1): PDFNum(0);
224 0         0 $self->{'DecodeParms'}->{'Columns'} = PDFNum($tif->{'imageWidth'});
225 0         0 $self->{'DecodeParms'}->{'Rows'} = PDFNum($tif->{'imageHeight'});
226             $self->{'DecodeParms'}->{'BlackIs1'} =
227 0 0 0     0 PDFBool(($tif->{'whiteIsZero'}||0) == 0? 1: 0);
228 0 0 0     0 if (defined($tif->{'g3Options'}) && ($tif->{'g3Options'} & 0x4)) {
229 0         0 $self->{'DecodeParms'}->{'EndOfLine'} = PDFBool(1);
230 0         0 $self->{'DecodeParms'}->{'EncodedByteAlign'} = PDFBool(1);
231             }
232             # $self->{'DecodeParms'} = PDFArray($self->{'DecodeParms'});
233 0         0 $self->{'DecodeParms'}->{'DamagedRowsBeforeError'} = PDFNum(100);
234              
235 0 0       0 if (ref($tif->{'imageOffset'})) {
236 0         0 die "Chunked CCITT G4 TIFF not supported.";
237             } else {
238 0         0 $tif->{'fh'}->seek($tif->{'imageOffset'}, 0);
239 0         0 $tif->{'fh'}->read($self->{' stream'}, $tif->{'imageLength'});
240             }
241              
242 0         0 return $self;
243             }
244              
245             sub read_tiff {
246 3     3 0 7 my ($self, $pdf, $tif) = @_;
247              
248 3         15 $self->width($tif->{'imageWidth'});
249 3         15 $self->height($tif->{'imageHeight'});
250 3 50       11 if ($tif->{'colorSpace'} eq 'Indexed') {
251 0         0 my $dict = PDFDict();
252 0         0 $pdf->new_obj($dict);
253             $self->colorspace(PDFArray(PDFName($tif->{'colorSpace'}),
254 0         0 PDFName('DeviceRGB'), PDFNum(2**$tif->{'bitsPerSample'}-1), $dict));
255 0         0 $dict->{'Filter'} = PDFArray(PDFName('FlateDecode'));
256 0         0 $tif->{'fh'}->seek($tif->{'colorMapOffset'}, 0);
257 0         0 my $colormap;
258             my $straight;
259 0         0 $tif->{'fh'}->read($colormap, $tif->{'colorMapLength'});
260 0         0 $dict->{' stream'} = '';
261 0         0 $straight .= pack('C', ($_/256)) for
262             unpack($tif->{'short'} . '*', $colormap);
263 0         0 foreach my $c (0 .. (($tif->{'colorMapSamples'}/3)-1)) {
264 0         0 $dict->{' stream'} .= substr($straight, $c, 1);
265 0         0 $dict->{' stream'} .= substr($straight, $c + ($tif->{'colorMapSamples'}/3), 1);
266 0         0 $dict->{' stream'} .= substr($straight, $c + ($tif->{'colorMapSamples'}/3)*2, 1);
267             }
268             } else {
269 3         13 $self->colorspace($tif->{'colorSpace'});
270             }
271              
272 3         10 $self->{'Interpolate'} = PDFBool(1);
273 3         17 $self->bits_per_component($tif->{'bitsPerSample'});
274              
275             # swaps 0 and 1 ([0 1] -> [1 0]) in certain cases
276 3 100 100     24 if (($tif->{'whiteIsZero'}||0) == 1 &&
      50        
      66        
277             ($tif->{'filter'}||'') ne 'CCITTFaxDecode') {
278 1         5 $self->{'Decode'} = PDFArray(PDFNum(1), PDFNum(0));
279             }
280              
281             # check filters and handle separately
282 3 50 66     55 if (defined $tif->{'filter'} and $tif->{'filter'} eq 'CCITTFaxDecode') {
    100 66        
    50 33        
283 0         0 $self->handle_ccitt($pdf, $tif);
284             } elsif (defined $tif->{'filter'} and $tif->{'filter'} eq 'LZWDecode') {
285 1         6 $self->handle_lzw($pdf, $tif);
286             } elsif (defined $tif->{'filter'} and $tif->{'filter'} eq 'FlateDecode') {
287 0         0 $self->handle_flate($pdf, $tif);
288             } else {
289 2         10 $self->handle_generic($pdf, $tif);
290             }
291              
292             # # dump everything in self except huge data streams
293             # foreach (sort keys %{ $self }) {
294             # if ($_ eq ' stream') { next; }
295             # if ($_ eq ' apipdf') { next; }
296             # if ($_ eq ' realised') { next; }
297             # if ($_ eq ' uid') { next; }
298             # if (defined $self->{$_}) {
299             # print "\$self->{'$_'} = '".($self->{$_}->val())."'\n";
300             # } else {
301             # print "\$self->{'$_'} = ?\n";
302             # }
303             # }
304              
305 3 50       13 if ($tif->{'fillOrder'} == 2) {
306 0         0 my @bl = ();
307 0         0 foreach my $n (0 .. 255) {
308 0         0 my $b = $n;
309 0         0 my $f = 0;
310 0         0 foreach (0 .. 7) {
311 0         0 my $bit = 0;
312 0 0       0 if ($b & 0x1) {
313 0         0 $bit = 1;
314             }
315 0         0 $b >>= 1;
316 0         0 $f <<= 1;
317 0         0 $f |= $bit;
318             }
319 0         0 $bl[$n] = $f;
320             }
321 0         0 my $l = length($self->{' stream'}) - 1;
322 0         0 foreach my $n (0 .. $l) {
323 0         0 vec($self->{' stream'}, $n, 8) = $bl[vec($self->{' stream'}, $n, 8)];
324             }
325             }
326 3         9 $self->{' tiff'} = $tif;
327              
328 3         7 return $self;
329             }
330              
331             =head2 tiffTag
332              
333             $value = $tif->tiffTag($tag)
334              
335             =over
336              
337             returns the value of the internal tiff-tag.
338              
339             B<Useful Tags:>
340              
341             imageDescription, imageId (strings)
342             xRes, yRes (dpi; pixel/cm if resUnit==3)
343             resUnit
344              
345             =back
346              
347             =cut
348              
349             sub tiffTag {
350 0     0 1   my ($self, $tag) = @_;
351 0           return $self->{' tiff'}->{$tag};
352             }
353              
354             1;