File Coverage

blib/lib/PDF/Builder/Resource/XObject/Image/TIFF.pm
Criterion Covered Total %
statement 74 167 44.3
branch 14 36 38.8
condition 12 30 40.0
subroutine 13 16 81.2
pod 3 8 37.5
total 116 257 45.1


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