File Coverage

blib/lib/PDF/API2/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::API2::Basic::PDF::Filter::FlateDecode;
2              
3 43     43   328 use base 'PDF::API2::Basic::PDF::Filter';
  43         103  
  43         4489  
4              
5 43     43   292 use strict;
  43         115  
  43         851  
6 43     43   208 use warnings;
  43         91  
  43         1903  
7              
8             our $VERSION = '2.043'; # VERSION
9              
10 43     43   24329 use POSIX qw(ceil floor);
  43         297467  
  43         294  
11              
12             our $havezlib;
13              
14             BEGIN {
15 43     43   68674 eval { require Compress::Zlib };
  43         29926  
16 43         2564904 $havezlib = !$@;
17             }
18              
19             sub new {
20 22 50   22 1 83 return unless $havezlib;
21 22         60 my ($class, $decode_parms) = @_;
22 22         88 my $self = { DecodeParms => $decode_parms };
23              
24 22         133 $self->{'outfilt'} = Compress::Zlib::deflateInit(
25             -Level => 9,
26             -Bufsize => 32768,
27             );
28 22         10155 $self->{'infilt'} = Compress::Zlib::inflateInit();
29 22         2630 bless $self, $class;
30             }
31              
32             sub outfilt {
33 16     16 1 54 my ($self, $str, $is_end) = @_;
34              
35 16         92 my $result = $self->{'outfilt'}->deflate($str);
36 16 50       57689 $result .= $self->{'outfilt'}->flush() if $is_end;
37 16         2215 return $result;
38             }
39              
40             sub infilt {
41 6     6 1 28 my ($self, $dat, $last) = @_;
42 6         54 my ($result, $status) = $self->{'infilt'}->inflate("$dat");
43              
44 6 0 33     182 if ($self->{'DecodeParms'} and $self->{'DecodeParms'}->{'Predictor'}) {
45 0         0 my $predictor = $self->{'DecodeParms'}->{'Predictor'}->val();
46 0 0 0     0 if ($predictor == 2) {
    0          
47 0         0 die "The TIFF predictor logic has not been implemented";
48             }
49             elsif ($predictor >= 10 and $predictor <= 15) {
50 0         0 $result = $self->_depredict_png($result);
51             }
52             else {
53 0         0 die "Invalid predictor: $predictor";
54             }
55             }
56              
57 6         184 return $result;
58             }
59              
60             sub _depredict_png {
61 0     0     my ($self, $stream) = @_;
62 0           my $param = $self->{'DecodeParms'};
63              
64 0           my $prev = '';
65 0 0         $stream = $self->{'_depredict_next'} . $stream if defined $self->{'_depredict_next'};
66 0 0         $prev = $self->{'_depredict_prev'} if defined $self->{'_depredict_prev'};
67              
68 0 0         my $alpha = $param->{'Alpha'} ? $param->{'Alpha'}->val() : 0;
69 0 0         my $bpc = $param->{'BitsPerComponent'} ? $param->{'BitsPerComponent'}->val() : 8;
70 0 0         my $colors = $param->{'Colors'} ? $param->{'Colors'}->val() : 1;
71 0 0         my $columns = $param->{'Columns'} ? $param->{'Columns'}->val() : 1;
72 0 0         my $height = $param->{'Height'} ? $param->{'Height'}->val() : 0;
73              
74 0           my $comp = $colors + $alpha;
75 0           my $bpp = ceil($bpc * $comp / 8);
76 0           my $scanline = 1 + ceil($bpp * $columns);
77              
78 0           my $clearstream = '';
79 0   0       my $lastrow = ($height || int(length($stream) / $scanline)) - 1;
80 0           foreach my $n (0 .. $lastrow) {
81 0           my $line = substr($stream, $n * $scanline, $scanline);
82 0           my $filter = vec($line, 0, 8);
83 0           my $clear = '';
84 0           $line = substr($line, 1);
85 0 0         if ($filter == 0) {
    0          
    0          
    0          
    0          
86 0           $clear = $line;
87             }
88             elsif ($filter == 1) {
89 0           foreach my $x (0 .. length($line) - 1) {
90 0           vec($clear, $x, 8) = (vec($line, $x, 8) + vec($clear, $x - $bpp, 8)) % 256;
91             }
92             }
93             elsif ($filter == 2) {
94 0           foreach my $x (0 .. length($line) - 1) {
95 0           vec($clear, $x, 8) = (vec($line, $x, 8) + vec($prev, $x, 8)) % 256;
96             }
97             }
98             elsif ($filter == 3) {
99 0           foreach my $x (0 .. length($line) - 1) {
100 0           vec($clear, $x, 8) = (vec($line, $x, 8) + floor((vec($clear, $x - $bpp, 8) + vec($prev, $x, 8)) / 2)) % 256;
101             }
102             }
103             elsif ($filter == 4) {
104 0           foreach my $x (0 .. length($line) - 1) {
105 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;
106             }
107             }
108             else {
109 0           die "Unexpected depredictor algorithm $filter requested on line $n (valid options are 0-4)";
110             }
111 0           $prev = $clear;
112 0           foreach my $x (0 .. ($columns * $comp) - 1) {
113 0           vec($clearstream, ($n * $columns * $comp) + $x, $bpc) = vec($clear, $x, $bpc);
114             }
115             }
116 0           $self->{'_depredict_next'} = substr($stream, ($lastrow + 1) * $scanline);
117 0           $self->{'_depredict_prev'} = $prev;
118              
119 0           return $clearstream;
120             }
121              
122             sub _paeth_predictor {
123 0     0     my ($a, $b, $c) = @_;
124 0           my $p = $a + $b - $c;
125 0           my $pa = abs($p - $a);
126 0           my $pb = abs($p - $b);
127 0           my $pc = abs($p - $c);
128 0 0 0       if ($pa <= $pb && $pa <= $pc) {
    0          
129 0           return $a;
130             }
131             elsif ($pb <= $pc) {
132 0           return $b;
133             }
134             else {
135 0           return $c;
136             }
137             }
138              
139             1;