File Coverage

blib/lib/PDF/Image/GIF.pm
Criterion Covered Total %
statement 173 360 48.0
branch 43 88 48.8
condition 6 26 23.0
subroutine 11 14 78.5
pod 0 9 0.0
total 233 497 46.8


line stmt bran cond sub pod time code
1             #
2             # PDF::Image::GIF - GIF image support for PDF::Create
3             #
4             # Author: Michael Gross
5             #
6             # Copyright 1999-2001 Fabien Tassin
7             # Copyright 2007 Markus Baertschi
8             #
9             # Please see the CHANGES and Changes file for the detailed change log
10             #
11             # Please do not use any of the methods here directly. You will be
12             # punished with your application no longer working after an upgrade !
13             #
14              
15             package PDF::Image::GIF;
16              
17 18     18   251 use 5.006;
  18         40  
18 18     18   58 use strict;
  18         20  
  18         451  
19 18     18   57 use warnings;
  18         16  
  18         1041  
20 18     18   56 use FileHandle;
  18         14  
  18         81  
21              
22             our $VERSION = '1.41';
23             our $DEBUG = 0;
24              
25             sub new
26             {
27 1     1 0 2 my $self = {};
28              
29 1         3 $self->{private} = {};
30 1         2 $self->{colorspace} = 0;
31 1         2 $self->{width} = 0;
32 1         1 $self->{height} = 0;
33 1         1 $self->{colorspace} = "DeviceRGB";
34 1         1 $self->{colorspacedata} = "";
35 1         2 $self->{colorspacesize} = 0;
36 1         1 $self->{filename} = "";
37 1         2 $self->{error} = "";
38 1         1 $self->{imagesize} = 0;
39 1         2 $self->{transparent} = 0;
40 1         1 $self->{filter} = ["LZWDecode"];
41 1         2 $self->{decodeparms} = { 'EarlyChange' => 0 };
42 1         2 $self->{private}->{interlaced} = 0;
43              
44 1         1 bless($self);
45 1         2 return $self;
46             }
47              
48             sub LZW
49             {
50 0     0 0 0 my $self = shift;
51 0         0 my $data = shift;
52 0         0 my $result = "";
53 0         0 my $prefix = "";
54 0         0 my $c;
55             my %hash;
56 0         0 my $num;
57 0         0 my $codesize = 9;
58              
59             #init hash-table
60 0         0 for ( $num = 0 ; $num < 256 ; $num++ ) {
61 0         0 $hash{ chr($num) } = $num;
62             }
63              
64             #start with a clear
65 0         0 $num = 258;
66 0         0 my $currentvalue = 256;
67 0         0 my $bits = 9;
68              
69 0         0 my $pos = 0;
70 0         0 while ( $pos < length($data) ) {
71 0         0 $c = substr( $data, $pos, 1 );
72              
73 0 0       0 if ( exists( $hash{ $prefix . $c } ) ) {
74 0         0 $prefix .= $c;
75             } else {
76              
77             #save $hash{$prefix}
78 0         0 $currentvalue <<= $codesize;
79 0         0 $currentvalue |= $hash{$prefix};
80 0         0 $bits += $codesize;
81 0         0 while ( $bits >= 8 ) {
82 0         0 $result .= chr( ( $currentvalue >> ( $bits - 8 ) ) & 255 );
83 0         0 $bits -= 8;
84 0         0 $currentvalue &= ( 1 << $bits ) - 1;
85             }
86              
87 0         0 $hash{ $prefix . $c } = $num;
88 0         0 $prefix = $c;
89 0         0 $num++;
90              
91             #increase code size?
92 0 0 0     0 if ( $num == 513 || $num == 1025 || $num == 2049 ) {
      0        
93 0         0 $codesize++;
94             }
95              
96             #hash table overflow?
97 0 0       0 if ( $num == 4097 ) {
98              
99             #save clear
100 0         0 $currentvalue <<= $codesize;
101 0         0 $currentvalue |= 256;
102 0         0 $bits += $codesize;
103 0         0 while ( $bits >= 8 ) {
104 0         0 $result .= chr( ( $currentvalue >> ( $bits - 8 ) ) & 255 );
105 0         0 $bits -= 8;
106 0         0 $currentvalue &= ( 1 << $bits ) - 1;
107             }
108              
109             #reset hash table
110 0         0 $codesize = 9;
111 0         0 %hash = ();
112 0         0 for ( $num = 0 ; $num < 256 ; $num++ ) {
113 0         0 $hash{ chr($num) } = $num;
114             }
115 0         0 $num = 258;
116             }
117             }
118 0         0 $pos++;
119             }
120              
121             #save value for prefix
122 0         0 $currentvalue <<= $codesize;
123 0         0 $currentvalue |= $hash{$prefix};
124 0         0 $bits += $codesize;
125 0         0 while ( $bits >= 8 ) {
126 0         0 $result .= chr( ( $currentvalue >> ( $bits - 8 ) ) & 255 );
127 0         0 $bits -= 8;
128 0         0 $currentvalue &= ( 1 << $bits ) - 1;
129             }
130              
131             #save eoi
132 0         0 $currentvalue <<= $codesize;
133 0         0 $currentvalue |= 257;
134 0         0 $bits += $codesize;
135 0         0 while ( $bits >= 8 ) {
136 0         0 $result .= chr( ( $currentvalue >> ( $bits - 8 ) ) & 255 );
137 0         0 $bits -= 8;
138 0         0 $currentvalue &= ( 1 << $bits ) - 1;
139             }
140              
141             #save remainder in $currentvalue
142 0 0       0 if ( $bits > 0 ) {
143 0         0 $currentvalue = $currentvalue << ( 8 - $bits );
144 0         0 $result .= chr( $currentvalue & 255 );
145             }
146              
147 0         0 $result;
148             }
149              
150             sub UnLZW
151             {
152 0     0 0 0 my $self = shift;
153 0         0 my $data = shift;
154 0         0 my $result = "";
155              
156 0         0 my $bits = 0;
157 0         0 my $currentvalue = 0;
158 0         0 my $codesize = 9;
159 0         0 my $pos = 0;
160              
161 0         0 my $prefix = "";
162 0         0 my $suffix;
163             my @table;
164              
165             #initialize lookup-table
166 0         0 my $num;
167 0         0 for ( $num = 0 ; $num < 256 ; $num++ ) {
168 0         0 $table[$num] = chr($num);
169             }
170 0         0 $table[256] = "";
171              
172 0         0 $num = 257;
173              
174 0         0 my $c1;
175              
176             #get first word
177 0         0 while ( $bits < $codesize ) {
178 0         0 my $d = ord( substr( $data, $pos, 1 ) );
179 0         0 $currentvalue = ( $currentvalue << 8 ) + $d;
180 0         0 $bits += 8;
181 0         0 $pos++;
182             }
183 0         0 my $c2 = $currentvalue >> ( $bits - $codesize );
184 0         0 $bits -= $codesize;
185 0         0 my $mask = ( 1 << $bits ) - 1;
186 0         0 $currentvalue = $currentvalue & $mask;
187              
188 0         0 DECOMPRESS: while ( $pos < length($data) ) {
189 0         0 $c1 = $c2;
190              
191             #get next word
192 0         0 while ( $bits < $codesize ) {
193 0         0 my $d = ord( substr( $data, $pos, 1 ) );
194 0         0 $currentvalue = ( $currentvalue << 8 ) + $d;
195 0         0 $bits += 8;
196 0         0 $pos++;
197             }
198 0         0 $c2 = $currentvalue >> ( $bits - $codesize );
199 0         0 $bits -= $codesize;
200 0         0 $mask = ( 1 << $bits ) - 1;
201 0         0 $currentvalue = $currentvalue & $mask;
202              
203             #clear code?
204 0 0       0 if ( $c2 == 256 ) {
205 0         0 $result .= $table[$c1];
206 0         0 $#table = 256;
207 0         0 $codesize = 9;
208 0         0 $num = 257;
209 0         0 next DECOMPRESS;
210             }
211              
212             #End Of Image?
213 0 0       0 if ( $c2 == 257 ) {
214 0         0 last DECOMPRESS;
215             }
216              
217             #get prefix
218 0 0       0 if ( $c1 < $num ) {
219 0         0 $prefix = $table[$c1];
220             } else {
221 0         0 print "Compression Error ($c1>=$num)\n";
222             }
223              
224             #write prefix
225 0         0 $result .= $prefix;
226              
227             #get suffix
228 0 0       0 if ( $c2 < $num ) {
    0          
229 0         0 $suffix = substr( $table[$c2], 0, 1 );
230             } elsif ( $c2 == $num ) {
231 0         0 $suffix = substr( $prefix, 0, 1 );
232             } else {
233 0         0 print "Compression Error ($c2>$num)\n";
234             }
235              
236             #new table entry is prefix.suffix
237 0         0 $table[$num] = $prefix . $suffix;
238              
239             #next table entry
240 0         0 $num++;
241              
242             #increase code size?
243 0 0 0     0 if ( $num == 512 || $num == 1024 || $num == 2048 ) {
      0        
244 0         0 $codesize++;
245             }
246             }
247              
248 0 0       0 $result .= $table[$c1] if defined $table[$c1];
249              
250 0         0 $result;
251             }
252              
253             sub UnInterlace
254             {
255 0     0 0 0 my $self = shift;
256 0         0 my $data = shift;
257 0         0 my $row;
258             my @result;
259 0         0 my $width = $self->{width};
260 0         0 my $height = $self->{height};
261 0         0 my $idx = 0;
262              
263             #Pass 1 - every 8th row, starting with row 0
264 0         0 $row = 0;
265 0         0 while ( $row < $height ) {
266 0         0 $result[$row] = substr( $data, $idx * $width, $width );
267 0         0 $row += 8;
268 0         0 $idx++;
269             }
270              
271             #Pass 2 - every 8th row, starting with row 4
272 0         0 $row = 4;
273 0         0 while ( $row < $height ) {
274 0         0 $result[$row] = substr( $data, $idx * $width, $width );
275 0         0 $row += 8;
276 0         0 $idx++;
277             }
278              
279             #Pass 3 - every 4th row, starting with row 2
280 0         0 $row = 2;
281 0         0 while ( $row < $height ) {
282 0         0 $result[$row] = substr( $data, $idx * $width, $width );
283 0         0 $row += 4;
284 0         0 $idx++;
285             }
286              
287             #Pass 4 - every 2th row, starting with row 1
288 0         0 $row = 1;
289 0         0 while ( $row < $height ) {
290 0         0 $result[$row] = substr( $data, $idx * $width, $width );
291 0         0 $row += 2;
292 0         0 $idx++;
293             }
294              
295 0         0 join( '', @result );
296             }
297              
298             sub GetDataBlock
299             {
300 2     2 0 3 my $self = shift;
301 2         2 my $fh = shift;
302 2         1 my $s;
303             my $count;
304 0         0 my $buf;
305 2         3 read $fh, $s, 1;
306 2         2 $count = unpack( "C", $s );
307              
308 2 100       5 if ($count) {
309 1         1 read $fh, $buf, $count;
310             }
311              
312 2         4 ( $count, $buf );
313             }
314              
315             sub ReadColorMap
316             {
317 1     1 0 1 my $self = shift;
318 1         2 my $fh = shift;
319 1         2 read $fh, $self->{'colorspacedata'}, 3 * $self->{'colormapsize'};
320 1         3 1;
321             }
322              
323             sub DoExtension
324             {
325 1     1 0 4 my $self = shift;
326 1         1 my $label = shift;
327 1         1 my $fh = shift;
328 1         1 my $res;
329             my $buf;
330 0         0 my $c;
331 0         0 my $c2;
332 0         0 my $c3;
333              
334 1 50       6 if ( $label eq "\001" ) { #Plain Text Extension
    50          
    50          
    50          
335             } elsif ( ord($label) == 0xFF ) { #Application Extension
336             } elsif ( ord($label) == 0xFE ) { #Comment Extension
337             } elsif ( ord($label) == 0xF9 ) { #Grapgic Control Extension
338 1         2 ( $res, $buf ) = $self->GetDataBlock($fh); #(p, image, (unsigned char*) buf);
339 1         2 ( $c, $c2, $c2, $c3 ) = unpack( "CCCC", $buf );
340 1 50 50     8 if ( $c && 0x1 != 0 ) {
341 1         1 $self->{transparent} = 1;
342 1         4 $self->{mask} = $c3;
343             }
344             }
345              
346 1         1 BLOCK: while (1) {
347 1         2 ( $res, $buf ) = $self->GetDataBlock($fh);
348 1 50       3 if ( $res == 0 ) {
349 1         2 last BLOCK;
350             }
351             }
352              
353 1         1 1;
354             }
355              
356             sub Open
357             {
358 1     1 0 2 my $self = shift;
359 1         1 my $filename = shift;
360              
361 1         1 my $PDF_STRING_GIF = "\107\111\106";
362 1         1 my $PDF_STRING_87a = "\070\067\141";
363 1         1 my $PDF_STRING_89a = "\070\071\141";
364 1         1 my $LOCALCOLORMAP = 0x80;
365 1         1 my $INTERLACE = 0x40;
366              
367 1         1 my $s;
368             my $c;
369 0         0 my $ar;
370 0         0 my $flags;
371              
372 1         5 $self->{filename} = $filename;
373 1         5 my $fh = FileHandle->new("$filename");
374 1 50       49 if ( !defined $fh ) { $self->{error} = "PDF::Image::GIF.pm: $filename: $!"; return 0 }
  0         0  
  0         0  
375 1         2 binmode $fh;
376 1         14 read $fh, $s, 3;
377 1 50       9 if ( $s ne $PDF_STRING_GIF ) {
378 0         0 close $fh;
379 0         0 $self->{error} = "PDF::Image::GIF.pm: Not a gif file.";
380 0         0 return 0;
381             }
382              
383 1         2 read $fh, $s, 3;
384 1 50 33     5 if ( $s ne $PDF_STRING_87a && $s ne $PDF_STRING_89a ) {
385 0         0 close $fh;
386 0         0 $self->{error} = "PDF::Image::GIF.pm: GIF version $s not supported.";
387 0         0 return 0;
388             }
389              
390 1         1 read $fh, $s, 7;
391 1         6 ( $self->{width}, $self->{height}, $flags, $self->{private}->{background}, $ar ) = unpack( "vvCCC", $s );
392              
393 1         3 $self->{colormapsize} = 2 << ( $flags & 0x07 );
394 1         2 $self->{colorspacesize} = 3 * $self->{colormapsize};
395 1 50       3 if ( $flags & $LOCALCOLORMAP ) {
396 1 50       2 if ( !$self->ReadColorMap($fh) ) {
397 0         0 close $fh;
398 0         0 $self->{error} = "PDF::Image::GIF.pm: Cant read color map.";
399 0         0 return 0;
400             }
401             }
402              
403 1 50       3 if ( $ar != 0 ) {
404 0         0 $self->{private}->{dpi_x} = -( $ar + 15.0 ) / 64.0;
405 0         0 $self->{private}->{dpi_y} = -1.0;
406             }
407              
408 1         1 my $imageCount = 0;
409 1         1 IMAGES: while (1) {
410 2         3 read $fh, $c, 1;
411 2 50       4 if ( $c eq ";" ) { #GIF file terminator
412 0         0 close $fh;
413 0         0 $self->{error} = "PDF::Image::GIF.pm: Cant find image in gif file.";
414 0         0 return 0;
415             }
416              
417 2 100       4 if ( $c eq "!" ) { #Extension
418 1         1 read $fh, $c, 1;
419 1         2 $self->DoExtension( $c, $fh );
420 1         1 next;
421             }
422              
423 1 50       4 if ( $c ne "," ) { #must be comma
424 0         0 next; #ignore
425             }
426              
427 1         1 $imageCount++;
428              
429 1         2 read $fh, $s, 9;
430 1         1 my $x;
431 1         3 ( $x, $c, $self->{width}, $self->{height}, $flags ) = unpack( "vvvvC", $s );
432              
433 1 50 33     3 if ( $flags && $INTERLACE ) {
434 0         0 $self->{private}->{interlaced} = 1;
435             }
436              
437 1 50       2 if ( $flags & $LOCALCOLORMAP ) {
438 0 0       0 if ( !$self->ReadColorMap($fh) ) {
439 0         0 close $fh;
440 0         0 $self->{error} = "PDF::Image::GIF.pm: Cant read color map.";
441 0         0 return 0;
442             }
443             }
444              
445 1         2 read $fh, $s, 1; #read "LZW initial code size"
446 1         1 $self->{bpc} = unpack( "C", $s );
447 1 50       3 if ( $self->{bpc} != 8 ) {
448 0         0 close $fh;
449 0         0 $self->{error} = "PDF::Image::GIF.pm: LZW minimum code size is " . $self->{bpc} . ", must be 8 to be supported.";
450 0         0 return 0;
451             }
452              
453 1 50       2 if ( $imageCount == 1 ) {
454 1         1 last IMAGES;
455             }
456              
457             }
458              
459 1         2 $self->{private}->{datapos} = tell($fh);
460 1         6 close $fh;
461              
462 1         6 1;
463             }
464              
465             sub ReadData
466             {
467 1     1 0 2 my $self = shift;
468              
469             # init the LZW transformation vars
470 1         1 my $c_size = 9; # initial code size
471 1         1 my $t_size = 257; # initial "table" size
472 1         2 my $i_buff = 0; # input buffer
473 1         1 my $i_bits = 0; # input buffer empty
474 1         1 my $o_bits = 0; # output buffer empty
475 1         0 my $o_buff = 0;
476 1         1 my $c_mask;
477 1         1 my $bytes_available = 0;
478 1         2 my $n_bytes;
479             my $s;
480 0         0 my $c;
481 0         0 my $flag13;
482 0         0 my $code;
483 0         0 my $w_bits;
484              
485 1         1 my $result = "";
486              
487 1         5 my $fh = FileHandle->new($self->{filename});
488 1 50       43 if ( !defined $fh ) { $self->{error} = "PDF::Image::GIF.pm: $self->{filename}: $!"; return 0 }
  0         0  
  0         0  
489 1         3 binmode $fh;
490 1         3 seek( $fh, $self->{private}->{datapos}, 0 );
491 1         2 my $pos = 0;
492 1         1 my $data;
493 1         51 read $fh, $data, ( -s $self->{filename} );
494              
495 18     18   35813 use integer;
  18         144  
  18         69  
496              
497 1         3 $self->{imagesize} = 0;
498 1         1 BLOCKS: while (1) {
499 96         84 $s = substr( $data, $pos, 1 );
500 96         66 $pos++;
501 96         89 $n_bytes = unpack( "C", $s );
502 96 100       123 if ( !$n_bytes ) {
503 1         2 last BLOCKS;
504             }
505              
506 95         65 $c_mask = ( 1 << $c_size ) - 1;
507 95         68 $flag13 = 0;
508              
509 95         59 BLOCK: while (1) {
510 17319         9869 $w_bits = $c_size; # number of bits to write
511 17319         9661 $code = 0;
512              
513             #get at least c_size bits into i_buff
514 17319         17926 while ( $i_bits < $c_size ) {
515 24173 100       24410 if ( $n_bytes == 0 ) {
516 94         112 last BLOCK;
517             }
518 24079         12995 $n_bytes--;
519 24079         16677 $s = substr( $data, $pos, 1 );
520 24079         13134 $pos++;
521 24079         18045 $c = unpack( "C", $s );
522 24079         15261 $i_buff |= $c << $i_bits; #EOF will be caught later
523 24079         28199 $i_bits += 8;
524             }
525              
526 17225         10317 $code = $i_buff & $c_mask;
527              
528 17225         9407 $i_bits -= $c_size;
529 17225         9766 $i_buff >>= $c_size;
530              
531 17225 50 66     21481 if ( $flag13 && $code != 256 && $code != 257 ) {
      33        
532 0         0 $self->{error} = "PDF::Image::GIF.pm: LZW code size overflow.";
533 0         0 return 0;
534             }
535              
536 17225 100       18268 if ( $o_bits > 0 ) {
537 14980         10268 $o_buff |= $code >> ( $c_size - 8 + $o_bits );
538 14980         9009 $w_bits -= 8 - $o_bits;
539 14980         11422 $result .= chr( $o_buff & 255 );
540             }
541              
542 17225 100       18819 if ( $w_bits >= 8 ) {
543 9099         5041 $w_bits -= 8;
544 9099         6645 $result .= chr( ( $code >> $w_bits ) & 255 );
545             }
546 17225         9614 $o_bits = $w_bits;
547 17225 100       18310 if ( $o_bits > 0 ) {
548 14980         9074 $o_buff = $code << ( 8 - $o_bits );
549             }
550              
551 17225         9095 $t_size++;
552 17225 100       17531 if ( $code == 256 ) { #clear code
553 5         4 $c_size = 9;
554 5         6 $c_mask = ( 1 << $c_size ) - 1;
555 5         1 $t_size = 257;
556 5         5 $flag13 = 0;
557             }
558              
559 17225 100       17456 if ( $code == 257 ) { #end code
560 1         6 last BLOCK;
561             }
562              
563 17224 100       18249 if ( $t_size == ( 1 << $c_size ) ) {
564 19 100       29 if ( ++$c_size > 12 ) {
565 4         7 $c_size--;
566 4         6 $flag13 = 1;
567             } else {
568 15         12 $c_mask = ( 1 << $c_size ) - 1;
569             }
570             }
571             } # while () for block
572             } # while () for all blocks
573              
574             #interlaced?
575 1 50       7 if ( $self->{private}->{interlaced} ) {
576              
577             #when interlaced first uncompress image
578 0         0 $result = $self->UnLZW($result);
579              
580             #remove interlacing
581 0         0 $result = $self->UnInterlace($result);
582              
583             #compress image again
584 0         0 $result = $self->LZW($result);
585             }
586              
587 1         7 $self->{imagesize} = length($result);
588 1         77 $result;
589             }
590              
591             1;