File Coverage

blib/lib/PDF/Builder/Basic/PDF/Filter/LZWDecode.pm
Criterion Covered Total %
statement 175 184 95.1
branch 54 78 69.2
condition 8 15 53.3
subroutine 15 15 100.0
pod 3 4 75.0
total 255 296 86.1


line stmt bran cond sub pod time code
1             package PDF::Builder::Basic::PDF::Filter::LZWDecode;
2              
3 45     45   714 use strict;
  45         207  
  45         2187  
4 45     45   271 use warnings;
  45         105  
  45         2781  
5 45     45   285 use Carp;
  45         91  
  45         3395  
6 45     45   293 use POSIX;
  45         90  
  45         407  
7 45     45   118142 use base 'PDF::Builder::Basic::PDF::Filter::FlateDecode';
  45         131  
  45         116350  
8              
9             our $VERSION = '3.028'; # VERSION
10             our $LAST_UPDATE = '3.027'; # manually update whenever code is changed
11              
12             =head1 NAME
13              
14             PDF::Builder::Basic::PDF::Filter::LZWDecode - Compress and uncompress stream filters for Lempel-Ziv-Welch
15              
16             Inherits from L<PDF::Builder::Basic::PDF::Filter::FlateDecode>
17              
18             =cut
19              
20             # extensively extended from PDF::API2 version, for TIFF support
21              
22             sub new {
23 10     10 1 433495 my ($class, $decode_parms) = @_;
24              
25 10         48 my $self = { 'DecodeParms' => $decode_parms };
26              
27 10         31 bless $self, $class;
28 10         50 $self->_reset_code();
29 10         1427 return $self;
30             }
31              
32             sub infilt {
33 6     6 1 57 my ($self, $data, $is_last) = @_;
34              
35 6         15 my ($code, $result);
36 6         16 my $partial_code = $self->{'partial_code'};
37 6         15 my $partial_bits = $self->{'partial_bits'};
38              
39 6         14 my $early_change = 1;
40 6 50 66     35 if ($self->{'DecodeParms'} and $self->{'DecodeParms'}->{'EarlyChange'}) {
41 0         0 $early_change = $self->{'DecodeParms'}->{'EarlyChange'}->val();
42             }
43 6         64 $self->{'table'} = [ map { chr } 0 .. $self->{'clear_table'} - 1 ];
  1536         4283  
44              
45 6         231 while ($data ne q{}) {
46             ($code, $partial_code, $partial_bits) =
47             $self->read_dat(\$data, $partial_code, $partial_bits,
48 29520         76899 $self->{'code_length'});
49 29520 50       67843 last unless defined $code;
50              
51 29520 50       64716 unless ($early_change) {
52 0 0 0     0 if ($self->{'next_code'} == (1 << $self->{'code_length'})
53             and $self->{'code_length'} < 12) {
54 0         0 $self->{'code_length'}++;
55             }
56             }
57              
58 29520 100       92090 if ($code == $self->{'clear_table'}) {
    100          
    100          
59 13         38 $self->{'code_length'} = $self->{'initial_code_length'};
60 13         31 $self->{'next_code'} = $self->{'eod_marker'} + 1;
61 13         43 next;
62             } elsif ($code == $self->{'eod_marker'}) {
63 6         18 last;
64             } elsif ($code > $self->{'eod_marker'}) {
65 1087         3079 $self->{'table'}[$self->{'next_code'}] = $self->{'table'}[$code];
66             $self->{'table'}[$self->{'next_code'}] .=
67 1087         3513 substr($self->{'table'}[$code + 1], 0, 1);
68 1087         2396 $result .= $self->{'table'}[$self->{'next_code'}];
69 1087         1994 $self->{'next_code'}++;
70             } else {
71 28414         73373 $self->{'table'}[$self->{'next_code'}] = $self->{'table'}[$code];
72 28414         57975 $result .= $self->{'table'}[$self->{'next_code'}];
73 28414         50548 $self->{'next_code'}++;
74             }
75              
76 29501 50       65200 if ($early_change) {
77 29501 100 100     99325 if ($self->{'next_code'} == (1 << $self->{'code_length'})
78             and $self->{'code_length'} < 12) {
79 25         85 $self->{'code_length'}++;
80             }
81             }
82             }
83 6         17 $self->{'partial_code'} = $partial_code;
84 6         15 $self->{'partial_bits'} = $partial_bits;
85              
86 6 100       20 if ($self->_predictor_type() == 2) {
87 2         10 return $self->_depredict($result);
88             }
89              
90 4         91 return $result;
91             }
92              
93             sub outfilt {
94 6     6 1 127 my ($self, $str, $is_end) = @_;
95 6         15 my $max_code = 32767;
96 6         73 my $bytes_in = 0;
97 6         12 my $checkpoint = 0;
98 6         13 my $last_ratio = 0;
99 6         16 my $seen = q{};
100 6         20 $self->{'buf'} = q{};
101 6         15 $self->{'buf_pos'} = 0;
102 6         32 $self->_write_code($self->{'clear_table'});
103            
104 6 100       20 if ($self->_predictor_type() == 2) {
105 2         8 $str = $self->_predict($str);
106             }
107            
108 6         22 for my $i (0 .. length($str)) {
109 31085         66905 my $char = substr($str, $i, 1);
110 31085         52193 $bytes_in += 1;
111            
112 31085 100       87650 if (exists $self->{'table'}{ $seen . $char }) {
113 1590         3206 $seen .= $char;
114 1590         3457 next;
115             }
116            
117 29495         99506 $self->_write_code($self->{'table'}{$seen});
118            
119 29495         106607 $self->_new_code($seen . $char);
120            
121 29495         57447 $seen = $char;
122            
123 29495 100       78654 if ($self->{'at_max_code'}) {
124 7         27 $self->_write_code($self->{'clear_table'});
125 7         33 $self->_reset_code();
126            
127 7         20 undef $checkpoint;
128 7         27 undef $last_ratio;
129             }
130             }
131 6         75 $self->_write_code($self->{'table'}{$seen}); #last bit of input
132 6         28 $self->_write_code($self->{'eod_marker'});
133 6         19 my $padding = length($self->{'buf'}) % 8;
134 6 100       20 if ($padding > 0) {
135 5         11 $padding = 8 - $padding;
136 5         38 $self->{'buf'} .= '0' x $padding;
137             }
138 6         1217 return pack 'B*', $self->{'buf'};
139             }
140            
141             sub _reset_code {
142 17     17   45 my $self = shift;
143            
144 17         59 $self->{'initial_code_length'} = 9;
145 17         52 $self->{'max_code_length'} = 12;
146 17         57 $self->{'code_length'} = $self->{'initial_code_length'};
147 17         41 $self->{'clear_table'} = 256;
148 17         53 $self->{'eod_marker'} = $self->{'clear_table'} + 1;
149 17         53 $self->{'next_code'} = $self->{'eod_marker'} + 1;
150 17         52 $self->{'next_increase'} = 2**$self->{'code_length'};
151 17         54 $self->{'at_max_code'} = 0;
152 17         365 $self->{'table'} = { map { chr $_ => $_ } 0 .. $self->{'clear_table'} - 1 };
  4352         21685  
153 17         848 return;
154             }
155            
156             sub _new_code {
157 29495     29495   61756 my ($self, $word) = @_;
158            
159 29495 50       75090 if ($self->{'at_max_code'} == 0) {
160 29495         91214 $self->{'table'}{$word} = $self->{'next_code'};
161 29495         54333 $self->{'next_code'} += 1;
162             }
163            
164 29495 100       72733 if ($self->{'next_code'} >= $self->{'next_increase'}) {
165 32 100       132 if ($self->{'code_length'} < $self->{'max_code_length'}) {
166 25         90 $self->{'code_length'} += 1;
167 25         92 $self->{'next_increase'} *= 2;
168             } else {
169 7         17 $self->{'at_max_code'} = 1;
170             }
171             }
172 29495         57056 return;
173             }
174            
175             sub _write_code {
176 29520     29520   61490 my ($self, $code) = @_;
177            
178 29520 50       68264 if (not defined $code) { return; }
  0         0  
179            
180 29520 50       76346 if ($code > (2**$self->{'code_length'})) {
181 0         0 croak
182             "Code $code too large for current code length $self->{'code_length'}";
183             }
184            
185 29520         78111 for my $bit (reverse 0 .. ($self->{'code_length'} - 1)) {
186 330691 100       686066 if (($code >> $bit) & 1) {
187 119717         231753 $self->{'buf'} .= '1';
188             } else {
189 210974         411026 $self->{'buf'} .= '0';
190             }
191             }
192            
193 29520         69691 $self->{'buf_pos'} += $self->{'code_length'};
194 29520         58935 return;
195             }
196            
197             sub read_dat {
198 29520     29520 0 63201 my ($self, $data_ref, $partial_code, $partial_bits, $code_length) = @_;
199              
200 29520 100       67549 if (not defined $partial_bits) { $partial_bits = 0; }
  6         14  
201 29520 100       62627 if (not defined $partial_code) { $partial_code = 0; }
  6         14  
202              
203 29520         64584 while ($partial_bits < $code_length) {
204 41339 50       93014 return (undef, $partial_code, $partial_bits) unless length($$data_ref);
205 41339         88209 $partial_code = ($partial_code << 8) + unpack('C', $$data_ref);
206 41339         82439 substr($$data_ref, 0, 1, q{});
207 41339         91984 $partial_bits += 8;
208             }
209              
210 29520         54395 my $code = $partial_code >> ($partial_bits - $code_length);
211 29520         54142 $partial_code &= (1 << ($partial_bits - $code_length)) - 1;
212 29520         48361 $partial_bits -= $code_length;
213              
214 29520         69171 return ($code, $partial_code, $partial_bits);
215             }
216              
217             sub _predictor_type {
218 12     12   31 my ($self) = @_;
219 12 50 66     70 if ($self->{'DecodeParms'} and $self->{'DecodeParms'}->{'Predictor'}) {
220 4         25 my $predictor = $self->{'DecodeParms'}->{'Predictor'}->val();
221 4 50 33     20 if ($predictor == 1 or $predictor == 2) {
    0          
222 4         16 return $predictor;
223             } elsif ($predictor == 3) {
224 0         0 croak 'Floating point TIFF predictor not yet supported';
225             } else {
226 0         0 croak "Invalid predictor: $predictor";
227             }
228             }
229 8         40 return 1;
230             }
231            
232             sub _depredict {
233 2     2   7 my ($self, $data) = @_;
234 2         6 my $param = $self->{'DecodeParms'};
235 2 50       7 my $alpha = $param->{'Alpha'} ? $param->{'Alpha'}->val() : 0;
236             my $bpc =
237 2 50       8 $param->{'BitsPerComponent'} ? $param->{'BitsPerComponent'}->val() : 8;
238 2 100       9 my $colors = $param->{'Colors'} ? $param->{'Colors'}->val() : 1;
239 2 50       10 my $columns = $param->{'Columns'} ? $param->{'Columns'}->val() : 1;
240 2 50       9 my $rows = $param->{'Rows'} ? $param->{'Rows'}->val() : 0;
241            
242 2         7 my $comp = $colors + $alpha;
243 2         10 my $bpp = ceil($bpc * $comp / 8);
244 2         6 my $max = 256;
245 2 50       7 if ($bpc == 8) {
246 2         12 my @data = unpack('C*', $data);
247 2         9 for my $j (0 .. $rows - 1) {
248 4         11 my $count = $bpp * ($j * $columns + 1);
249 4         10 for my $i ($bpp .. $columns * $bpp - 1) {
250 72         143 $data[$count] =
251             ($data[$count] + $data[$count - $bpp]) % $max;
252 72         141 $count++;
253             }
254             }
255 2         30 $data = pack('C*', @data);
256 2         21 return $data;
257             }
258 0         0 return $data;
259             }
260            
261             sub _predict {
262 3     3   13 my ($self, $data) = @_;
263 3         7 my $param = $self->{'DecodeParms'};
264 3 50       10 my $alpha = $param->{'Alpha'} ? $param->{'Alpha'}->val() : 0;
265             my $bpc =
266 3 50       12 $param->{'BitsPerComponent'} ? $param->{'BitsPerComponent'}->val() : 8;
267 3 100       11 my $colors = $param->{'Colors'} ? $param->{'Colors'}->val() : 1;
268 3 50       15 my $columns = $param->{'Columns'} ? $param->{'Columns'}->val() : 1;
269 3 50       14 my $rows = $param->{'Rows'} ? $param->{'Rows'}->val() : 0;
270            
271 3         8 my $comp = $colors + $alpha;
272 3         16 my $bpp = ceil($bpc * $comp / 8);
273 3         9 my $max = 256;
274 3 50       11 if ($bpc == 8) {
275 3         18 my @data = unpack('C*', $data);
276 3         11 for my $j (0 .. $rows - 1) {
277 6         16 my $count = $bpp * $columns * ($j + 1) - 1;
278 6         16 for my $i ($bpp .. $columns * $bpp - 1) {
279 78         136 $data[$count] -= $data[$count - $bpp];
280 78 100       168 if ($data[$count] < 0) { $data[$count] += $max; }
  7         13  
281 78         161 $count--;
282             }
283             }
284 3         17 $data = pack('C*', @data);
285 3         14 return $data;
286             }
287 0           return $data;
288             }
289            
290             1;