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 44     44   359 use strict;
  44         143  
  44         1569  
4 44     44   290 use warnings;
  44         132  
  44         1420  
5 44     44   284 use Carp;
  44         121  
  44         2539  
6 44     44   329 use POSIX;
  44         131  
  44         369  
7 44     44   96452 use base 'PDF::Builder::Basic::PDF::Filter::FlateDecode';
  44         140  
  44         97079  
8              
9             our $VERSION = '3.025'; # VERSION
10             our $LAST_UPDATE = '3.023'; # 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             =cut
17              
18             sub new {
19 10     10 1 32385 my ($class, $decode_parms) = @_;
20              
21 10         36 my $self = { DecodeParms => $decode_parms, };
22              
23 10         25 bless $self, $class;
24 10         34 $self->_reset_code();
25 10         991 return $self;
26             }
27              
28             sub infilt {
29 6     6 1 59 my ($self, $data, $is_last) = @_;
30              
31 6         13 my ($code, $result);
32 6         22 my $partial_code = $self->{'partial_code'};
33 6         14 my $partial_bits = $self->{'partial_bits'};
34              
35 6         11 my $early_change = 1;
36 6 50 66     24 if ($self->{'DecodeParms'} and $self->{'DecodeParms'}->{'EarlyChange'}) {
37 0         0 $early_change = $self->{'DecodeParms'}->{'EarlyChange'}->val();
38             }
39 6         47 $self->{'table'} = [ map { chr } 0 .. $self->{'clear_table'} - 1 ];
  1536         2885  
40              
41 6         157 while ( $data ne q{} ) {
42             ($code, $partial_code, $partial_bits) =
43             $self->read_dat(\$data, $partial_code, $partial_bits,
44 29570         60077 $self->{'code_length'});
45 29570 50       56500 last unless defined $code;
46              
47 29570 50       49109 unless ($early_change) {
48 0 0 0     0 if ($self->{next_code} == (1 << $self->{code_length})
49             and $self->{code_length} < 12) {
50 0         0 $self->{'code_length'}++;
51             }
52             }
53              
54 29570 100       70901 if ($code == $self->{'clear_table'}) {
    100          
    100          
55 13         30 $self->{'code_length'} = $self->{'initial_code_length'};
56 13         27 $self->{'next_code'} = $self->{'eod_marker'} + 1;
57 13         69 next;
58             } elsif ($code == $self->{'eod_marker'}) {
59 6         9 last;
60             } elsif ($code > $self->{'eod_marker'}) {
61 1037         2173 $self->{'table'}[$self->{'next_code'}] = $self->{'table'}[$code];
62             $self->{'table'}[$self->{'next_code'}] .=
63 1037         1994 substr($self->{'table'}[$code + 1], 0, 1);
64 1037         1657 $result .= $self->{'table'}[$self->{'next_code'}];
65 1037         1523 $self->{'next_code'}++;
66             } else {
67 28514         53748 $self->{'table'}[$self->{'next_code'}] = $self->{'table'}[$code];
68 28514         44405 $result .= $self->{'table'}[$self->{'next_code'}];
69 28514         40083 $self->{'next_code'}++;
70             }
71              
72 29551 50       51643 if ($early_change) {
73 29551 100 100     75868 if ($self->{'next_code'} == (1 << $self->{'code_length'})
74             and $self->{code_length} < 12) {
75 25         61 $self->{'code_length'}++;
76             }
77             }
78             }
79 6         20 $self->{'partial_code'} = $partial_code;
80 6         11 $self->{'partial_bits'} = $partial_bits;
81              
82 6 100       16 if ($self->_predictor_type() == 2) {
83 2         7 return $self->_depredict($result);
84             }
85 4         130 return $result;
86             }
87              
88             sub outfilt {
89 6     6 1 98 my ($self, $str, $is_end) = @_;
90 6         14 my $max_code = 32767;
91 6         11 my $bytes_in = 0;
92 6         9 my $checkpoint = 0;
93 6         10 my $last_ratio = 0;
94 6         11 my $seen = q{};
95 6         19 $self->{'buf'} = q{};
96 6         11 $self->{'buf_pos'} = 0;
97 6         30 $self->_write_code($self->{'clear_table'});
98              
99 6 100       17 if ($self->_predictor_type() == 2) {
100 2         50 $str = $self->_predict($str);
101             }
102              
103 6         20 for my $i (0 .. length($str)) {
104 31085         50495 my $char = substr($str, $i, 1);
105 31085         43411 $bytes_in += 1;
106              
107 31085 100       65381 if (exists $self->{'table'}{ $seen . $char }) {
108 1540         2165 $seen .= $char;
109 1540         2463 next;
110             }
111              
112 29545         70582 $self->_write_code($self->{'table'}{$seen});
113              
114 29545         74805 $self->_new_code($seen . $char);
115              
116 29545         48300 $seen = $char;
117              
118 29545 100       57682 if ($self->{'at_max_code'}) {
119 7         29 $self->_write_code($self->{'clear_table'});
120 7         57 $self->_reset_code();
121              
122 7         44 undef $checkpoint;
123 7         21 undef $last_ratio;
124             }
125             }
126 6         24 $self->_write_code($self->{'table'}{$seen}); #last bit of input
127 6         17 $self->_write_code($self->{'eod_marker'});
128 6         22 my $padding = length($self->{'buf'}) % 8;
129 6 100       20 if ($padding > 0) {
130 5         10 $padding = 8 - $padding;
131 5         22 $self->{'buf'} .= '0' x $padding;
132             }
133 6         1118 return pack 'B*', $self->{'buf'};
134             }
135              
136             sub _reset_code {
137 17     17   53 my $self = shift;
138              
139 17         55 $self->{'initial_code_length'} = 9;
140 17         37 $self->{'max_code_length'} = 12;
141 17         53 $self->{'code_length'} = $self->{'initial_code_length'};
142 17         38 $self->{'clear_table'} = 256;
143 17         37 $self->{'eod_marker'} = $self->{'clear_table'} + 1;
144 17         38 $self->{'next_code'} = $self->{'eod_marker'} + 1;
145 17         67 $self->{'next_increase'} = 2**$self->{'code_length'};
146 17         33 $self->{'at_max_code'} = 0;
147 17         240 $self->{'table'} = { map { chr $_ => $_ } 0 .. $self->{'clear_table'} - 1 };
  4352         14576  
148 17         479 return;
149             }
150              
151             sub _new_code {
152 29545     29545   48781 my ($self, $word) = @_;
153              
154 29545 50       57749 if ($self->{'at_max_code'} == 0) {
155 29545         74518 $self->{'table'}{$word} = $self->{'next_code'};
156 29545         43744 $self->{'next_code'} += 1;
157             }
158              
159 29545 100       54433 if ($self->{'next_code'} >= $self->{'next_increase'}) {
160 32 100       89 if ($self->{'code_length'} < $self->{'max_code_length'}) {
161 25         42 $self->{'code_length'} += 1;
162 25         83 $self->{'next_increase'} *= 2;
163             } else {
164 7         20 $self->{'at_max_code'} = 1;
165             }
166             }
167 29545         43258 return;
168             }
169              
170             sub _write_code {
171 29570     29570   48215 my ($self, $code) = @_;
172              
173 29570 50       51091 if (not defined $code) { return; }
  0         0  
174              
175 29570 50       58493 if ($code > (2**$self->{'code_length'})) {
176 0         0 croak
177             "Code $code too large for current code length $self->{'code_length'}";
178             }
179              
180 29570         59506 for my $bit (reverse 0 .. ($self->{'code_length'} - 1)) {
181 331291 100       519188 if (($code >> $bit) & 1) {
182 119927         182693 $self->{'buf'} .= '1';
183             } else {
184 211364         319669 $self->{'buf'} .= '0';
185             }
186             }
187              
188 29570         51869 $self->{'buf_pos'} += $self->{'code_length'};
189 29570         44778 return;
190             }
191              
192             sub read_dat {
193 29570     29570 0 50158 my ($self, $data_ref, $partial_code, $partial_bits, $code_length) = @_;
194              
195 29570 100       52734 if (not defined $partial_bits) { $partial_bits = 0; }
  6         11  
196 29570 100       49685 if (not defined $partial_code) { $partial_code = 0; }
  6         9  
197              
198 29570         51910 while ($partial_bits < $code_length ) {
199 41414 50       71467 return (undef, $partial_code, $partial_bits) unless length($$data_ref);
200 41414         69213 $partial_code = ($partial_code << 8 ) + unpack('C', $$data_ref);
201 41414         66071 substr($$data_ref, 0, 1, q{});
202 41414         76787 $partial_bits += 8;
203             }
204              
205 29570         43653 my $code = $partial_code >> ($partial_bits - $code_length);
206 29570         44221 $partial_code &= (1 << ($partial_bits - $code_length)) - 1;
207 29570         40007 $partial_bits -= $code_length;
208              
209 29570         53521 return ($code, $partial_code, $partial_bits);
210             }
211              
212             sub _predictor_type {
213 12     12   33 my ($self) = @_;
214 12 50 66     47 if ($self->{'DecodeParms'} and $self->{'DecodeParms'}->{'Predictor'}) {
215 4         20 my $predictor = $self->{'DecodeParms'}->{'Predictor'}->val();
216 4 50 33     21 if ($predictor == 1 or $predictor == 2) {
    0          
217 4         13 return $predictor;
218             } elsif ($predictor == 3) {
219 0         0 croak 'Floating point TIFF predictor not yet supported';
220             } else {
221 0         0 croak "Invalid predictor: $predictor";
222             }
223             }
224 8         23 return 1;
225             }
226              
227             sub _depredict {
228 2     2   8 my ($self, $data) = @_;
229 2         13 my $param = $self->{'DecodeParms'};
230 2 50       7 my $alpha = $param->{'Alpha'} ? $param->{'Alpha'}->val() : 0;
231             my $bpc =
232 2 50       7 $param->{'BitsPerComponent'} ? $param->{'BitsPerComponent'}->val() : 8;
233 2 100       9 my $colors = $param->{'Colors'} ? $param->{'Colors'}->val() : 1;
234 2 50       9 my $columns = $param->{'Columns'} ? $param->{'Columns'}->val() : 1;
235 2 50       19 my $rows = $param->{'Rows'} ? $param->{'Rows'}->val() : 0;
236              
237 2         5 my $comp = $colors + $alpha;
238 2         10 my $bpp = ceil($bpc * $comp / 8);
239 2         4 my $max = 256;
240 2 50       6 if ($bpc == 8) {
241 2         20 my @data = unpack('C*', $data);
242 2         6 for my $j (0 .. $rows - 1) {
243 4         9 my $count = $bpp * ($j * $columns + 1);
244 4         9 for my $i ($bpp .. $columns * $bpp - 1) {
245 72         104 $data[$count] =
246             ($data[$count] + $data[$count - $bpp]) % $max;
247 72         105 $count++;
248             }
249             }
250 2         12 $data = pack('C*', @data);
251 2         25 return $data;
252             }
253 0         0 return $data;
254             }
255              
256             sub _predict {
257 3     3   42 my ($self, $data) = @_;
258 3         7 my $param = $self->{'DecodeParms'};
259 3 50       9 my $alpha = $param->{'Alpha'} ? $param->{'Alpha'}->val() : 0;
260             my $bpc =
261 3 50       8 $param->{'BitsPerComponent'} ? $param->{'BitsPerComponent'}->val() : 8;
262 3 100       10 my $colors = $param->{'Colors'} ? $param->{'Colors'}->val() : 1;
263 3 50       12 my $columns = $param->{'Columns'} ? $param->{'Columns'}->val() : 1;
264 3 50       10 my $rows = $param->{'Rows'} ? $param->{'Rows'}->val() : 0;
265              
266 3         6 my $comp = $colors + $alpha;
267 3         16 my $bpp = ceil($bpc * $comp / 8);
268 3         5 my $max = 256;
269 3 50       11 if ($bpc == 8) {
270 3         16 my @data = unpack('C*', $data);
271 3         11 for my $j (0 .. $rows - 1) {
272 6         9 my $count = $bpp * $columns * ($j + 1) - 1;
273 6         14 for my $i ($bpp .. $columns * $bpp - 1) {
274 78         106 $data[$count] -= $data[$count - $bpp];
275 78 100       130 if ($data[$count] < 0) { $data[$count] += $max; }
  7         12  
276 78         111 $count--;
277             }
278             }
279 3         13 $data = pack('C*', @data);
280 3         13 return $data;
281             }
282 0           return $data;
283             }
284              
285             1;