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 45     45   294 use base 'PDF::Builder::Basic::PDF::Filter';
  45         96  
  45         5924  
4              
5 45     45   315 use strict;
  45         99  
  45         1353  
6 45     45   291 use warnings;
  45         86  
  45         4328  
7              
8             our $VERSION = '3.028'; # VERSION
9             our $LAST_UPDATE = '3.027'; # manually update whenever code is changed
10              
11 45     45   24954 use POSIX qw(ceil floor);
  45         358970  
  45         385  
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             Inherits from L<PDF::Builder::Basic::PDF::Filter>
20              
21             =cut
22              
23             # not specifying a minimum version
24             BEGIN {
25 45     45   84690 eval { require Compress::Zlib };
  45         31719  
26 45         3357950 $havezlib = !$@;
27             }
28              
29             sub new {
30 24 50   24 1 119 return unless $havezlib; # undef returned should prove fatal
31 24         79 my ($class, $decode_parms) = @_;
32              
33 24         99 my ($self) = { 'DecodeParms' => $decode_parms };
34              
35 24         142 $self->{'outfilt'} = Compress::Zlib::deflateInit(
36             -Level => 9,
37             -Bufsize => 32768,
38             );
39 24         11176 $self->{'infilt'} = Compress::Zlib::inflateInit();
40 24         3175 return bless $self, $class;
41             }
42              
43             sub outfilt {
44 17     17 1 54 my ($self, $str, $is_end) = @_;
45              
46 17         109 my $result = $self->{'outfilt'}->deflate($str);
47 17 50       75650 $result .= $self->{'outfilt'}->flush() if $is_end;
48 17         3632 return $result;
49             }
50              
51             sub infilt {
52 7     7 1 24 my ($self, $dat, $last) = @_;
53              
54 7         59 my ($result, $status) = $self->{'infilt'}->inflate("$dat");
55              
56 7 0 33     338 if ($self->{'DecodeParms'} and $self->{'DecodeParms'}->{'Predictor'}) {
57 0         0 my $predictor = $self->{'DecodeParms'}->{'Predictor'}->val();
58 0 0 0     0 if ($predictor == 2) {
    0          
59 0         0 die "The TIFF predictor logic has not been implemented";
60             } elsif ($predictor >= 10 and $predictor <= 15) {
61 0         0 $result = $self->_depredict_png($result);
62             } else {
63 0         0 die "Invalid predictor: $predictor";
64             }
65             }
66              
67 7         198 return $result;
68             }
69              
70             sub _depredict_png {
71 0     0     my ($self, $stream) = @_;
72 0           my $param = $self->{'DecodeParms'};
73              
74 0           my $prev = '';
75 0 0         $stream = $self->{'_depredict_next'} . $stream if defined $self->{'_depredict_next'};
76 0 0         $prev = $self->{'_depredict_prev'} if defined $self->{'_depredict_prev'};
77              
78 0 0         my $alpha = $param->{'Alpha'} ? $param->{'Alpha'}->val(): 0;
79 0 0         my $bpc = $param->{'BitsPerComponent'} ? $param->{'BitsPerComponent'}->val(): 8;
80 0 0         my $colors = $param->{'Colors'} ? $param->{'Colors'}->val(): 1;
81 0 0         my $columns = $param->{'Columns'} ? $param->{'Columns'}->val(): 1;
82 0 0         my $height = $param->{'Height'} ? $param->{'Height'}->val(): 0;
83              
84 0           my $comp = $colors + $alpha;
85 0           my $bpp = ceil($bpc * $comp / 8);
86 0           my $scanline = 1 + ceil($bpp * $columns);
87              
88 0           my $clearstream = '';
89 0   0       my $lastrow = ($height || int(length($stream) / $scanline)) - 1;
90 0           foreach my $n (0 .. $lastrow) {
91 0           my $line = substr($stream, $n * $scanline, $scanline);
92 0           my $filter = vec($line, 0, 8);
93 0           my $clear = '';
94 0           $line = substr($line, 1);
95 0 0         if ($filter == 0) {
    0          
    0          
    0          
    0          
96 0           $clear = $line;
97             } elsif ($filter == 1) {
98 0           foreach my $x (0 .. length($line) - 1) {
99 0           vec($clear, $x, 8) = (vec($line, $x, 8) + vec($clear, $x - $bpp, 8)) % 256;
100             }
101             } elsif ($filter == 2) {
102 0           foreach my $x (0 .. length($line) - 1) {
103 0           vec($clear, $x, 8) = (vec($line, $x, 8) + vec($prev, $x, 8)) % 256;
104             }
105             } elsif ($filter == 3) {
106 0           foreach my $x (0 .. length($line) - 1) {
107 0           vec($clear, $x, 8) = (vec($line, $x, 8) + floor((vec($clear, $x - $bpp, 8) + vec($prev, $x, 8)) / 2)) % 256;
108             }
109             } elsif ($filter == 4) {
110 0           foreach my $x (0 .. length($line) - 1) {
111 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;
112             }
113             } else {
114 0           die "Unexpected depredictor algorithm $filter requested on line $n (valid options are 0-4)";
115             }
116 0           $prev = $clear;
117 0           foreach my $x (0 .. ($columns * $comp) - 1) {
118 0           vec($clearstream, ($n * $columns * $comp) + $x, $bpc) = vec($clear, $x, $bpc);
119             }
120             }
121 0           $self->{'_depredict_next'} = substr($stream, ($lastrow + 1) * $scanline);
122 0           $self->{'_depredict_prev'} = $prev;
123              
124 0           return $clearstream;
125             }
126              
127             sub _paeth_predictor {
128 0     0     my ($a, $b, $c) = @_;
129              
130 0           my $p = $a + $b - $c;
131 0           my $pa = abs($p - $a);
132 0           my $pb = abs($p - $b);
133 0           my $pc = abs($p - $c);
134 0 0 0       if ($pa <= $pb && $pa <= $pc) {
    0          
135 0           return $a;
136             } elsif ($pb <= $pc) {
137 0           return $b;
138             } else {
139 0           return $c;
140             }
141             }
142              
143             1;