File Coverage

blib/lib/PDF/API3/Compat/API2/Basic/PDF/Filter.pm
Criterion Covered Total %
statement 48 239 20.0
branch 0 74 0.0
condition 0 21 0.0
subroutine 16 31 51.6
pod 1 2 50.0
total 65 367 17.7


line stmt bran cond sub pod time code
1             #=======================================================================
2             # ____ ____ _____ _ ____ ___ ____
3             # | _ \| _ \| ___| _ _ / \ | _ \_ _| |___ \
4             # | |_) | | | | |_ (_) (_) / _ \ | |_) | | __) |
5             # | __/| |_| | _| _ _ / ___ \| __/| | / __/
6             # |_| |____/|_| (_) (_) /_/ \_\_| |___| |_____|
7             #
8             # A Perl Module Chain to faciliate the Creation and Modification
9             # of High-Quality "Portable Document Format (PDF)" Files.
10             #
11             #=======================================================================
12             #
13             # THIS IS A REUSED PERL MODULE, FOR PROPER LICENCING TERMS SEE BELOW:
14             #
15             #
16             # Copyright Martin Hosken
17             #
18             # No warranty or expression of effectiveness, least of all regarding
19             # anyone's safety, is implied in this software or documentation.
20             #
21             # This specific module is licensed under the Perl Artistic License.
22             #
23             #
24             # $Id: Filter.pm,v 2.0 2005/11/16 02:16:00 areibens Exp $
25             #
26             #=======================================================================
27             package PDF::API3::Compat::API2::Basic::PDF::Filter;
28            
29 1     1   5 no warnings qw[ deprecated recursion uninitialized ];
  1         2  
  1         284  
30            
31             =head1 NAME
32            
33             PDF::API3::Compat::API2::Basic::PDF::Filter - Abstract superclass for PDF stream filters
34            
35             =head1 SYNOPSIS
36            
37             $f = PDF::API3::Compat::API2::Basic::PDF::Filter->new;
38             $str = $f->outfilt($str, 1);
39             print OUTFILE $str;
40            
41             while (read(INFILE, $dat, 4096))
42             { $store .= $f->infilt($dat, 0); }
43             $store .= $f->infilt("", 1);
44            
45             =head1 DESCRIPTION
46            
47             A Filter object contains state information for the process of outputting
48             and inputting data through the filter. The precise state information stored
49             is up to the particular filter and may range from nothing to whole objects
50             created and destroyed.
51            
52             Each filter stores different state information for input and output and thus
53             may handle one input filtering process and one output filtering process at
54             the same time.
55            
56             =head1 METHODS
57            
58             =head2 PDF::API3::Compat::API2::Basic::PDF::Filter->new
59            
60             Creates a new filter object with empty state information ready for processing
61             data both input and output.
62            
63             =head2 $dat = $f->infilt($str, $isend)
64            
65             Filters from output to input the data. Notice that $isend == 0 implies that there
66             is more data to come and so following it $f may contain state information
67             (usually due to the break-off point of $str not being tidy). Subsequent calls
68             will incorporate this stored state information.
69            
70             $isend == 1 implies that there is no more data to follow. The
71             final state of $f will be that the state information is empty. Error messages
72             are most likely to occur here since if there is required state information to
73             be stored following this data, then that would imply an error in the data.
74            
75             =head2 $str = $f->outfilt($dat, $isend)
76            
77             Filter stored data ready for output. Parallels C.
78            
79             =cut
80            
81             sub new
82             {
83 0     0 1   my ($class) = @_;
84 0           my ($self) = {};
85            
86 0           bless $self, $class;
87             }
88            
89             sub release
90             {
91 0     0 0   my ($self) = @_;
92            
93 0 0         return($self) unless(ref $self);
94             # delete stuff that we know we can, here
95            
96 0           my @tofree = map { delete $self->{$_} } keys %{$self};
  0            
  0            
97            
98 0           while (my $item = shift @tofree)
99             {
100 0           my $ref = ref($item);
101 0 0         if (UNIVERSAL::can($item, 'release'))
    0          
    0          
102 0           { $item->release(); }
103             elsif ($ref eq 'ARRAY')
104 0           { push( @tofree, @{$item} ); }
  0            
105             elsif (UNIVERSAL::isa($ref, 'HASH'))
106 0           { release($item); }
107             }
108            
109             # check that everything has gone - it better had!
110 0           foreach my $key (keys %{$self})
  0            
111             { # warn ref($self) . " still has '$key' key left after release.\n";
112 0           $self->{$key}=undef;
113 0           delete($self->{$key});
114             }
115             }
116            
117            
118             package PDF::API3::Compat::API2::Basic::PDF::ASCII85Decode;
119            
120 1     1   6 use strict;
  1         13  
  1         34  
121 1     1   5 use vars qw(@ISA);
  1         3  
  1         67  
122             @ISA = qw(PDF::API3::Compat::API2::Basic::PDF::Filter);
123 1     1   5 no warnings qw[ deprecated recursion uninitialized ];
  1         2  
  1         842  
124            
125             =head1 NAME
126            
127             PDF::API3::Compat::API2::Basic::PDF::ASCII85Decode - Ascii85 filter for PDF streams. Inherits from
128             L
129            
130             =cut
131            
132             sub outfilt
133             {
134 0     0     my ($self, $str, $isend) = @_;
135 0           my ($res, $i, $j, $b, @c);
136            
137 0 0         if ($self->{'outcache'} ne "")
138             {
139 0           $str = $self->{'outcache'} . $str;
140 0           $self->{'outcache'} = "";
141             }
142 0           for ($i = 0; $i < length($str); $i += 4)
143             {
144 0           $b = unpack("N", substr($str, $i, 4));
145 0 0         if ($b == 0)
146             {
147 0           $res .= "z";
148 0           next;
149             }
150 0           for ($j = 0; $j < 4; $j++)
151 0           { $c[$j] = $b - int($b / 85) * 85 + 33; $b /= 85; }
  0            
152 0           $res .= pack("C5", @c, $b + 33);
153 0 0         $res .= "\n" if ($i % 60 == 56);
154             }
155 0 0 0       if ($isend && $i > length($str))
    0          
156             {
157 0           $b = unpack("N", substr($str, $i - 4) . "\000\000\000");
158 0           for ($j = 0; $j < 4; $j++)
159 0           { $c[$j] = $b - int($b / 85) * 85 + 33; $b /= 85; }
  0            
160 0           $res .= substr(pack("C5", @c, $b), 0, $i - length($str) + 1) . "->";
161             } elsif ($i > length($str))
162 0           { $self->{'outcache'} = substr($str, $i - 4); }
163 0           $res;
164             }
165            
166             sub infilt
167             {
168 0     0     my ($self, $str, $isend) = @_;
169 0           my ($res, $i, $j, @c, $b, $num);
170 0           $num=0;
171 0 0 0       if (exists($self->{'incache'}) && $self->{'incache'} ne "")
172             {
173 0           $str = $self->{'incache'} . $str;
174 0           $self->{'incache'} = "";
175             }
176 0           $str =~ s/(\r|\n)\n?//og;
177 0           for ($i = 0; $i < length($str); $i += 5)
178             {
179 0           $b = 0;
180 0 0 0       if (substr($str, $i, 1) eq "z")
    0          
181             {
182 0           $i -= 4;
183 0           $res .= pack("N", 0);
184 0           next;
185             }
186             elsif ($isend && substr($str, $i, 6) =~ m/^(.{2,4})\~\>$/o)
187             {
188 0           $num = 5 - length($1);
189 0           @c = unpack("C5", $1 . ("u" x (4 - $num))); # pad with 84 to sort out rounding
190 0           $i = length($str);
191             } else
192 0           { @c = unpack("C5", substr($str, $i, 5)); }
193            
194 0           for ($j = 0; $j < 5; $j++)
195             {
196 0           $b *= 85;
197 0           $b += $c[$j] - 33;
198             }
199 0           $res .= substr(pack("N", $b), 0, 4 - $num);
200             }
201 0 0 0       if (!$isend && $i > length($str))
202 0           { $self->{'incache'} = substr($str, $i - 5); }
203 0           $res;
204             }
205            
206            
207            
208             package PDF::API3::Compat::API2::Basic::PDF::RunLengthDecode;
209            
210 1     1   7 use strict;
  1         3  
  1         32  
211 1     1   6 use vars qw(@ISA);
  1         2  
  1         61  
212             @ISA = qw(PDF::API3::Compat::API2::Basic::PDF::Filter);
213 1     1   6 no warnings qw[ deprecated recursion uninitialized ];
  1         1  
  1         552  
214            
215             =head1 NAME
216            
217             PDF::API3::Compat::API2::Basic::PDF::RunLengthDecode - Run Length encoding filter for PDF streams. Inherits from
218             L
219            
220             =cut
221            
222             sub outfilt
223             {
224 0     0     my ($self, $str, $isend) = @_;
225 0           my ($res, $s, $r);
226            
227             # no state information, just slight inefficiency at block boundaries
228 0           while ($str ne "")
229             {
230 0 0         if ($str =~ m/^(.*?)((.)\2{2,127})(.*?)$/so)
231             {
232 0           $s = $1;
233 0           $r = $2;
234 0           $str = $3;
235             } else
236             {
237 0           $s = $str;
238 0           $r = '';
239 0           $str = '';
240             }
241 0           while (length($s) > 127)
242             {
243 0           $res .= pack("C", 127) . substr($s, 0, 127);
244 0           substr($s, 0, 127) = '';
245             }
246 0 0         $res .= pack("C", length($s)) . $s if length($s) > 0;
247 0           $res .= pack("C", 257 - length($r));
248             }
249 0 0         $res .= "\x80" if ($isend);
250 0           $res;
251             }
252            
253             sub infilt
254             {
255 0     0     my ($self, $str, $isend) = @_;
256 0           my ($res, $l, $d);
257            
258 0 0         if ($self->{'incache'} ne "")
259             {
260 0           $str = $self->{'incache'} . $str;
261 0           $self->{'incache'} = "";
262             }
263 0           while ($str ne "")
264             {
265 0           $l = unpack("C", $str);
266 0 0         if ($l == 128)
267             {
268 0           $isend = 1;
269 0           return $res;
270             }
271 0 0         if ($l > 128)
272             {
273 0 0         if (length($str) < 2)
274             {
275 0 0         warn "Premature end to data in RunLengthEncoded data" if $isend;
276 0           $self->{'incache'} = $str;
277 0           return $res;
278             }
279 0           $res .= substr($str, 1, 1) x (257 - $l);
280 0           substr($str, 0, 2) = "";
281             } else
282             {
283 0 0         if (length($str) < $l + 1)
284             {
285 0 0         warn "Premature end to data in RunLengthEncoded data" if $isend;
286 0           $self->{'incache'} = $str;
287 0           return $res;
288             }
289 0           $res .= substr($str, 1, $l);
290 0           substr($str, 0, $l + 1) = "";
291             }
292             }
293 0           $res;
294             }
295            
296            
297            
298             package PDF::API3::Compat::API2::Basic::PDF::ASCIIHexDecode;
299            
300 1     1   6 use strict;
  1         2  
  1         37  
301 1     1   12 use vars qw(@ISA);
  1         2  
  1         54  
302             @ISA = qw(PDF::API3::Compat::API2::Basic::PDF::Filter);
303 1     1   14 no warnings qw[ deprecated recursion uninitialized ];
  1         2  
  1         1343  
304            
305             =head1 NAME
306            
307             PDF::API3::Compat::API2::Basic::PDF::ASCIIHexDecode - Ascii Hex encoding (very inefficient) for PDF streams.
308             Inherits from L
309            
310             =cut
311            
312             sub outfilt
313             {
314 0     0     my ($self, $str, $isend) = @_;
315            
316 0           $str =~ s/(.)/sprintf("%02x", ord($1))/oge;
  0            
317 0 0         $str .= ">" if $isend;
318 0           $str;
319             }
320            
321             sub infilt
322             {
323 0     0     my ($self, $str, $isend) = @_;
324            
325 0           $isend = ($str =~ s/>$//og);
326 0           $str =~ s/\s//oig;
327 0 0 0       $str =~ s/([0-9a-z])/pack("C", hex($1 . "0"))/oige if ($isend && length($str) & 1);
  0            
328 0           $str =~ s/([0-9a-z]{2})/pack("C", hex($1))/oige;
  0            
329 0           $str;
330             }
331            
332             package PDF::API3::Compat::API2::Basic::PDF::FlateDecode;
333            
334 1     1   7 use strict;
  1         2  
  1         38  
335 1     1   5 use vars qw(@ISA $havezlib);
  1         2  
  1         51  
336 1     1   11 no warnings qw[ deprecated recursion uninitialized ];
  1         1  
  1         59  
337             @ISA = qw(PDF::API3::Compat::API2::Basic::PDF::Filter);
338             BEGIN
339             {
340 1     1   1 eval {require "Compress/Zlib.pm";};
  1         2221  
341 1         125463 $havezlib = !$@;
342             }
343            
344             sub new
345             {
346 0 0   0     return undef unless $havezlib;
347 0           my ($class) = @_;
348 0           my ($self) = {};
349            
350 0           $self->{'outfilt'} = Compress::Zlib::deflateInit(
351             -Level=>9,
352             -Bufsize=>32768,
353             );
354 0           $self->{'infilt'} = Compress::Zlib::inflateInit();
355 0           bless $self, $class;
356             }
357            
358             sub outfilt
359             {
360 0     0     my ($self, $str, $isend) = @_;
361 0           my ($res);
362            
363 0           $res = $self->{'outfilt'}->deflate($str);
364 0 0         $res .= $self->{'outfilt'}->flush() if ($isend);
365 0           $res;
366             }
367            
368             sub infilt
369             {
370 0     0     my ($self, $dat, $last) = @_;
371 0           my ($res, $status) = $self->{'infilt'}->inflate("$dat");
372 0           $res;
373             }
374            
375             package PDF::API3::Compat::API2::Basic::PDF::LZWDecode;
376            
377 1     1   12 use vars qw(@ISA @basedict);
  1         2  
  1         203  
378             @ISA = qw(PDF::API3::Compat::API2::Basic::PDF::FlateDecode);
379             @basedict = map {pack("C", $_)} (0 .. 255, 0, 0);
380 1     1   7 no warnings qw[ deprecated recursion uninitialized ];
  1         2  
  1         899  
381            
382             sub new
383             {
384 0     0     my ($class) = @_;
385 0           my ($self) = {};
386            
387 0           $self->{indict} = [@basedict];
388 0           $self->{bits} = 9;
389 0           $self->{insize} = $self->{bits};
390 0           $self->{resetcode}=1<<($self->{insize}-1);
391 0           $self->{endcode}=$self->{resetcode}+1;
392 0           $self->{nextcode}=$self->{endcode}+1;
393            
394 0           bless $self, $class;
395             }
396            
397             sub infilt
398             {
399 0     0     my ($self, $dat, $last) = @_;
400 0           my ($num, $cache, $cache_size, $res);
401            
402 0   0       while ($dat ne '' || $cache_size > 0)
403             {
404 0           ($num, $cache, $cache_size) = $self->read_dat(\$dat, $cache, $cache_size, $self->{'insize'});
405            
406             # this was a little arkward to comprehand
407             # here is a better version -- fredo
408 0 0         $self->{'insize'}++ if($self->{nextcode} == (1<<$self->{'insize'}));
409 0 0         if($num==$self->{resetcode}) {
    0          
    0          
    0          
410 0           $self->{'insize'}=$self->{bits};
411 0           $self->{nextcode}=$self->{endcode}+1;
412 0           next;
413             } elsif($num==$self->{endcode}) {
414 0           last;
415             } elsif($num<$self->{resetcode}) {
416 0           $self->{'indict'}[$self->{nextcode}] = $self->{'indict'}[$num];
417 0           $res.=$self->{'indict'}[$self->{nextcode}];
418 0           $self->{nextcode}++;
419             } elsif($num>$self->{endcode}) {
420 0           $self->{'indict'}[$self->{nextcode}] = $self->{'indict'}[$num];
421 0           $self->{'indict'}[$self->{nextcode}].= substr($self->{'indict'}[$num+1],0,1);
422 0           $res.=$self->{'indict'}[$self->{nextcode}];
423 0           $self->{nextcode}++;
424             } else {
425 0           die "we shouldn't be here !";
426             }
427             }
428 0           return $res;
429             }
430            
431             sub infilt2
432             {
433 0     0     my ($self, $dat, $last) = @_;
434 0           my ($num, $cache, $cache_size, $res);
435            
436 0   0       while ($dat ne '' || $cache_size > 0)
437             {
438 0           ($num, $cache, $cache_size) = $self->read_dat(\$dat, $cache, $cache_size, $self->{'insize'});
439            
440             # this was a little arkward to comprehand
441             # here is a better version -- fredo
442 0 0         if($num==$self->{resetcode}) {
    0          
    0          
    0          
443 0           $self->{'insize'}=$self->{bits};
444 0           $self->{nextcode}=$self->{endcode}+1;
445 0           next;
446             } elsif($num==$self->{endcode}) {
447 0           last;
448             } elsif($num<$self->{resetcode}) {
449 0           $self->{'indict'}[$self->{nextcode}] = $self->{'indict'}[$num];
450 0           $res.=$self->{'indict'}[$self->{nextcode}];
451 0           $self->{nextcode}++;
452             } elsif($num>$self->{endcode}) {
453 0           $self->{'indict'}[$self->{nextcode}] = $self->{'indict'}[$num];
454 0           $self->{'indict'}[$self->{nextcode}].= substr($self->{'indict'}[$num+1],0,1);
455 0           $res.=$self->{'indict'}[$self->{nextcode}];
456 0           $self->{nextcode}++;
457             } else {
458 0           die "we shouldn't be here !";
459             }
460 0 0         $self->{'insize'}++ if($self->{nextcode} == (1<<$self->{'insize'}));
461             }
462 0           return $res;
463             }
464            
465             sub read_dat
466             {
467 0     0     my ($self, $rdat, $cache, $size, $len) = @_;
468 0           my ($res);
469            
470 0           while ($size < $len)
471             {
472 0           $cache = ($cache << 8) + unpack("C", $$rdat);
473 0           substr($$rdat, 0, 1) = '';
474 0           $size += 8;
475             }
476            
477 0           $res = $cache >> ($size - $len);
478 0           $cache &= (1 << ($size - $len)) - 1;
479 0           $size -= $len;
480 0           ($res, $cache, $size);
481             }
482            
483             1;
484