File Coverage

blib/lib/PDF/Builder/Basic/PDF/Filter/FlateDecode.pm
Criterion Covered Total %
statement 29 80 36.2
branch 2 38 5.2
condition 1 12 8.3
subroutine 8 10 80.0
pod 3 3 100.0
total 43 143 30.0


line stmt bran cond sub pod time code
1             package PDF::Builder::Basic::PDF::Filter::FlateDecode;
2              
3 40     40   300 use base 'PDF::Builder::Basic::PDF::Filter';
  40         83  
  40         4037  
4              
5 40     40   276 use strict;
  40         87  
  40         813  
6 40     40   193 use warnings;
  40         99  
  40         2080  
7              
8             our $VERSION = '3.023'; # VERSION
9             our $LAST_UPDATE = '3.016'; # manually update whenever code is changed
10              
11 40     40   22143 use POSIX qw(ceil floor);
  40         268372  
  40         262  
12              
13             our $havezlib;
14              
15             =head1 NAME
16              
17             PDF::Builder::Basic::PDF::Filter::FlateDecode - compress and uncompress stream filters for Flate
18              
19             =cut
20              
21             BEGIN {
22 40     40   61287 eval { require Compress::Zlib };
  40         29082  
23 40         2390545 $havezlib = !$@;
24             }
25              
26             sub new {
27 20 50   20 1 79 return unless $havezlib;
28 20         53 my ($class, $decode_parms) = @_;
29              
30 20         71 my ($self) = {
31             DecodeParms => $decode_parms,
32             };
33              
34 20         128 $self->{'outfilt'} = Compress::Zlib::deflateInit(
35             -Level => 9,
36             -Bufsize => 32768,
37             );
38 20         9726 $self->{'infilt'} = Compress::Zlib::inflateInit();
39 20         2229 return bless $self, $class;
40             }
41              
42             sub outfilt {
43 13     13 1 42 my ($self, $str, $is_end) = @_;
44              
45 13         100 my $result = $self->{'outfilt'}->deflate($str);
46 13 50       385 $result .= $self->{'outfilt'}->flush() if $is_end;
47 13         881 return $result;
48             }
49              
50             sub infilt {
51 7     7 1 20 my ($self, $dat, $last) = @_;
52              
53 7         46 my ($result, $status) = $self->{'infilt'}->inflate("$dat");
54              
55 7 0 33     179 if ($self->{'DecodeParms'} and $self->{'DecodeParms'}->{'Predictor'}) {
56 0         0 my $predictor = $self->{'DecodeParms'}->{'Predictor'}->val();
57 0 0 0     0 if ($predictor == 2) {
    0          
58 0         0 die "The TIFF predictor logic has not been implemented";
59             } elsif ($predictor >= 10 and $predictor <= 15) {
60 0         0 $result = $self->_depredict_png($result);
61             } else {
62 0         0 die "Invalid predictor: $predictor";
63             }
64             }
65              
66 7         162 return $result;
67             }
68              
69             sub _depredict_png {
70 0     0     my ($self, $stream) = @_;
71 0           my $param = $self->{'DecodeParms'};
72              
73 0           my $prev = '';
74 0 0         $stream = $self->{'_depredict_next'} . $stream if defined $self->{'_depredict_next'};
75 0 0         $prev = $self->{'_depredict_prev'} if defined $self->{'_depredict_prev'};
76              
77 0 0         my $alpha = $param->{'Alpha'} ? $param->{'Alpha'}->val(): 0;
78 0 0         my $bpc = $param->{'BitsPerComponent'}? $param->{'BitsPerComponent'}->val(): 8;
79 0 0         my $colors = $param->{'Colors'} ? $param->{'Colors'}->val(): 1;
80 0 0         my $columns = $param->{'Columns'} ? $param->{'Columns'}->val(): 1;
81 0 0         my $height = $param->{'Height'} ? $param->{'Height'}->val(): 0;
82              
83 0           my $comp = $colors + $alpha;
84 0           my $bpp = ceil($bpc * $comp / 8);
85 0           my $scanline = 1 + ceil($bpp * $columns);
86              
87 0           my $clearstream = '';
88 0   0       my $lastrow = ($height || int(length($stream) / $scanline)) - 1;
89 0           foreach my $n (0 .. $lastrow) {
90 0           my $line = substr($stream, $n * $scanline, $scanline);
91 0           my $filter = vec($line, 0, 8);
92 0           my $clear = '';
93 0           $line = substr($line, 1);
94 0 0         if ($filter == 0) {
    0          
    0          
    0          
    0          
95 0           $clear = $line;
96             } elsif ($filter == 1) {
97 0           foreach my $x (0 .. length($line) - 1) {
98 0           vec($clear, $x, 8) = (vec($line, $x, 8) + vec($clear, $x - $bpp, 8)) % 256;
99             }
100             } elsif ($filter == 2) {
101 0           foreach my $x (0 .. length($line) - 1) {
102 0           vec($clear, $x, 8) = (vec($line, $x, 8) + vec($prev, $x, 8)) % 256;
103             }
104             } elsif ($filter == 3) {
105 0           foreach my $x (0 .. length($line) - 1) {
106 0           vec($clear, $x, 8) = (vec($line, $x, 8) + floor((vec($clear, $x - $bpp, 8) + vec($prev, $x, 8)) / 2)) % 256;
107             }
108             } elsif ($filter == 4) {
109 0           foreach my $x (0 .. length($line) - 1) {
110 0           vec($clear, $x, 8) = (vec($line, $x, 8) + _paeth_predictor(vec($clear, $x - $bpp, 8), vec($prev, $x, 8), vec($prev, $x - $bpp, 8))) % 256;
111             }
112             } else {
113 0           die "Unexpected depredictor algorithm $filter requested on line $n (valid options are 0-4)";
114             }
115 0           $prev = $clear;
116 0           foreach my $x (0 .. ($columns * $comp) - 1) {
117 0           vec($clearstream, ($n * $columns * $comp) + $x, $bpc) = vec($clear, $x, $bpc);
118             }
119             }
120 0           $self->{'_depredict_next'} = substr($stream, ($lastrow + 1) * $scanline);
121 0           $self->{'_depredict_prev'} = $prev;
122              
123 0           return $clearstream;
124             }
125              
126             sub _paeth_predictor {
127 0     0     my ($a, $b, $c) = @_;
128              
129 0           my $p = $a + $b - $c;
130 0           my $pa = abs($p - $a);
131 0           my $pb = abs($p - $b);
132 0           my $pc = abs($p - $c);
133 0 0 0       if ($pa <= $pb && $pa <= $pc) {
    0          
134 0           return $a;
135             } elsif ($pb <= $pc) {
136 0           return $b;
137             } else {
138 0           return $c;
139             }
140             }
141              
142             1;