File Coverage

blib/lib/PDF/Builder/Basic/PDF/Filter/FlateDecode.pm
Criterion Covered Total %
statement 25 80 31.2
branch 2 38 5.2
condition 0 12 0.0
subroutine 7 10 70.0
pod 3 3 100.0
total 37 143 25.8


line stmt bran cond sub pod time code
1             package PDF::Builder::Basic::PDF::Filter::FlateDecode;
2              
3 44     44   266 use base 'PDF::Builder::Basic::PDF::Filter';
  44         83  
  44         3771  
4              
5 44     44   269 use strict;
  44         87  
  44         718  
6 44     44   196 use warnings;
  44         79  
  44         2014  
7              
8             our $VERSION = '3.024'; # VERSION
9             our $LAST_UPDATE = '3.016'; # manually update whenever code is changed
10              
11 44     44   20227 use POSIX qw(ceil floor);
  44         247185  
  44         255  
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 44     44   57261 eval { require Compress::Zlib };
  44         26473  
23 44         2201365 $havezlib = !$@;
24             }
25              
26             sub new {
27 13 50   13 1 45 return unless $havezlib;
28 13         141 my ($class, $decode_parms) = @_;
29              
30 13         64 my ($self) = {
31             DecodeParms => $decode_parms,
32             };
33              
34 13         88 $self->{'outfilt'} = Compress::Zlib::deflateInit(
35             -Level => 9,
36             -Bufsize => 32768,
37             );
38 13         5651 $self->{'infilt'} = Compress::Zlib::inflateInit();
39 13         1294 return bless $self, $class;
40             }
41              
42             sub outfilt {
43 13     13 1 46 my ($self, $str, $is_end) = @_;
44              
45 13         95 my $result = $self->{'outfilt'}->deflate($str);
46 13 50       47615 $result .= $self->{'outfilt'}->flush() if $is_end;
47 13         1907 return $result;
48             }
49              
50             sub infilt {
51 0     0 1   my ($self, $dat, $last) = @_;
52              
53 0           my ($result, $status) = $self->{'infilt'}->inflate("$dat");
54              
55 0 0 0       if ($self->{'DecodeParms'} and $self->{'DecodeParms'}->{'Predictor'}) {
56 0           my $predictor = $self->{'DecodeParms'}->{'Predictor'}->val();
57 0 0 0       if ($predictor == 2) {
    0          
58 0           die "The TIFF predictor logic has not been implemented";
59             } elsif ($predictor >= 10 and $predictor <= 15) {
60 0           $result = $self->_depredict_png($result);
61             } else {
62 0           die "Invalid predictor: $predictor";
63             }
64             }
65              
66 0           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;