File Coverage

blib/lib/PDF/Builder/Resource/XObject/Image/PNM.pm
Criterion Covered Total %
statement 145 341 42.5
branch 48 156 30.7
condition 8 45 17.7
subroutine 12 12 100.0
pod 1 5 20.0
total 214 559 38.2


line stmt bran cond sub pod time code
1             package PDF::Builder::Resource::XObject::Image::PNM;
2              
3             # For spec details, see man pages pam(5), pbm(5), pgm(5), pnm(5), ppm(5)
4              
5 2     2   1075 use base 'PDF::Builder::Resource::XObject::Image';
  2         4  
  2         629  
6              
7 2     2   9 use strict;
  2         3  
  2         29  
8 2     2   7 use warnings;
  2         4  
  2         97  
9              
10             our $VERSION = '3.028'; # VERSION
11             our $LAST_UPDATE = '3.027'; # manually update whenever code is changed
12              
13 2     2   7 use IO::File;
  2         4  
  2         282  
14 2     2   8 use PDF::Builder::Util;
  2         3  
  2         270  
15 2     2   24 use PDF::Builder::Basic::PDF::Utils;
  2         4  
  2         138  
16 2     2   9 use Scalar::Util qw(weaken);
  2         4  
  2         7147  
17              
18             # massively rewritten recently, so assuming nothing in PDF::API2 rewrite (2021)
19             # should make any functional difference
20              
21             =head1 NAME
22              
23             PDF::Builder::Resource::XObject::Image::PNM - Support routines for PNM (Portable aNy Map) image library
24              
25             Inherits from L<PDF::Builder::Resource::XObject::Image>
26              
27             =head2 METHODS
28              
29             =head2 new
30              
31             $res = PDF::Builder::Resource::XObject::Image::PNM->new($pdf, $file, %opts)
32              
33             =over
34              
35             Options:
36              
37             =over
38              
39             =item 'name' => 'string'
40              
41             This is the name you can give for the PNM image object. The default is Nxnnnn.
42              
43             =item 'compress' => 1
44              
45             This is the compression you can give for the PNM image object. Any value will
46             cause the use of I<Flate> compression, otherwise (C<compress> not given),
47             I<ASCIIHexDecode> is used.
48              
49             =back
50              
51             =back
52              
53             Returns an image in the PDF. PNM types 1 (ASCII/plain bi-level/PBM),
54             2 (ASCII/plain grayscale/PGM), 3 (ASCII/plain RGB/PPM),
55             4 (binary/raw bi-level/PBM), 5 (binary/raw grayscale/PGM), and
56             6 (binary/raw RGB/PPM) are supported.
57              
58             For bi-level, only values 0/1 (white/black) are supported. For grayscale, the
59             maximum sample (full white) may be anything from 1 to 65535, with 0 being full
60             black. If the maximum sample value is 255 or smaller, one byte of raw binary
61             data per pixel, otherwise two bytes. For RGB, each sample (full-on of that
62             color) may be anything from 1 to 65535 (the same maximum for all three colors),
63             with 0 being full black. If the maximum sample value is 255 or smaller, three
64             bytes of raw binary data per pixel, otherwise six bytes.
65              
66             Remember that you need to use Builder.pm's image_pnm method to use this
67             functionality.
68              
69             =cut
70              
71             # -------------------------------------------------------------------
72             sub new {
73 3     3 1 11 my ($class, $pdf, $file, %opts) = @_;
74             # copy dashed option names to preferred undashed names
75 3 50 33     9 if (defined $opts{'-name'} && !defined $opts{'name'}) { $opts{'name'} = delete($opts{'-name'}); }
  0         0  
76 3 50 33     7 if (defined $opts{'-compress'} && !defined $opts{'compress'}) { $opts{'compress'} = delete($opts{'-compress'}); }
  0         0  
77              
78 3         6 my ($name, $compress);
79 3 50       5 if (exists $opts{'name'}) { $name = $opts{'name'}; }
  0         0  
80 3 50       6 if (exists $opts{'compress'}) { $compress = $opts{'compress'}; }
  3         4  
81              
82 3         4 my $self;
83              
84 3 50       6 $class = ref($class) if ref($class);
85              
86 3   33     13 $self = $class->SUPER::new($pdf, $name || 'Nx'.pdfkey());
87 3 50       7 $pdf->new_obj($self) unless $self->is_obj($pdf);
88              
89 3         4 $self->{' apipdf'} = $pdf;
90 3         5 weaken $self->{' apipdf'};
91              
92 3         7 $self->read_pnm($pdf, $file);
93              
94 2 50       3 if (defined $compress) {
95 2         4 $self->filters('FlateDecode');
96             } else {
97 0         0 $self->filters('ASCIIHexDecode');
98             }
99              
100 2         5 return $self;
101             }
102              
103             # -------------------------------------------------------------------
104             # READPPMHEADER
105             # taken from Image::PBMLib
106             # Copyright by Benjamin Elijah Griffin (28 Feb 2003)
107             # extensively modified by Phil M Perry, copyright 2020
108             #
109             sub readppmheader {
110             # renamed to _read_header() in PDF::API2
111 2     2 0 4 my ($gr, $buffer) = @_; # already-opened input file's filehandle
112 2         3 my %info;
113 2         4 $info{'error'} = undef;
114 2         3 my ($width, $height, $max, $comment, $content);
115              
116             # extension: allow whitespace BEFORE the magic number (usually none)
117             # read Px magic number
118 2         5 ($buffer, $comment) = eat_whitespace($gr, $buffer, 0);
119 2         5 ($buffer, $content) = read_content($gr, $buffer);
120              
121 2 50       5 if (length($content) != 2) {
122 0         0 $info{'error'} = 'Read error or EOF';
123 0         0 return (\%info, $buffer);
124             }
125              
126 2 50       6 if ($content =~ /^P([1-6])/) {
127 2         5 $info{'type'} = $1;
128 2 50       5 if ($info{'type'} > 3) {
129 2         4 $info{'raw'} = 1; # P4-6 is raw (binary)
130             } else {
131 0         0 $info{'raw'} = 0; # P1-3 is plain (ASCII)
132             }
133             } else {
134 0         0 $info{'error'} = 'Unrecognized magic number, not 1..6';
135 0         0 return (\%info, $buffer);
136             }
137              
138 2 50 33     11 if ($info{'type'} == 1 or $info{'type'} == 4) {
    50 33        
139 0         0 $max = 1;
140 0         0 $info{'bgp'} = 'b';
141             } elsif ($info{'type'} == 2 or $info{'type'} == 5) {
142             # need to read and validate 'max'
143 0         0 $info{'bgp'} = 'g';
144             } else { # 3 or 6
145             # need to read and validate 'max'
146 2         4 $info{'bgp'} = 'p';
147             }
148              
149             # expect width as unsigned integer > 0
150 2         3 ($buffer, $comment) = eat_whitespace($gr, $buffer, 0);
151 2         3 ($buffer, $content) = read_content($gr, $buffer);
152 2 50       4 if (length($content) == 0) {
153 0         0 $info{'error'} = 'Read error or EOF on width';
154 0         0 return (\%info, $buffer);
155             }
156 2 50       6 if ($content =~ m/(^\d+)$/) {
157 2         4 $width = $1;
158             } else {
159 0         0 $info{'error'} = 'Invalid width value '.$1;
160 0         0 return (\%info, $buffer);
161             }
162 2 50       4 if ($width < 1) {
163 0         0 $info{'error'} = 'Invalid width value '.$width;
164 0         0 return (\%info, $buffer);
165             }
166            
167             # expect height as unsigned integer > 0
168 2         3 ($buffer, $comment) = eat_whitespace($gr, $buffer, 0);
169 2         2 ($buffer, $content) = read_content($gr, $buffer);
170 2 50       4 if (length($content) == 0) {
171 0         0 $info{'error'} = 'Read error or EOF on height';
172 0         0 return (\%info, $buffer);
173             }
174 2 50       17 if ($content =~ m/(^\d+)$/) {
175 2         4 $height = $1;
176             } else {
177 0         0 $info{'error'} = 'Invalid height value '.$1;
178 0         0 return (\%info, $buffer);
179             }
180 2 50       15 if ($height < 1) {
181 0         0 $info{'error'} = 'Invalid height value '.$height;
182 0         0 return (\%info, $buffer);
183             }
184            
185             # expect max sample value as unsigned integer > 0 & < 65536
186             # IF grayscale or pixmap (RGB). already set to 1 for bi-level
187 2 50       6 if ($info{'bgp'} =~ m/^[gp]$/) {
188 2         3 ($buffer, $comment) = eat_whitespace($gr, $buffer, 0);
189 2         4 ($buffer, $content) = read_content($gr, $buffer);
190 2 50       5 if (length($content) == 0) {
191 0         0 $info{'error'} = 'Read error or EOF on max';
192 0         0 return (\%info, $buffer);
193             }
194 2 50       15 if ($content =~ m/(^\d+)$/) {
195 2         5 $max = $1;
196             } else {
197 0         0 $info{'error'} = 'Invalid max value '.$1;
198 0         0 return (\%info, $buffer);
199             }
200 2 50 33     8 if ($max < 1 || $max > 65535) {
201 0         0 $info{'error'} = 'Invalid max value '.$max;
202 0         0 return (\%info, $buffer);
203             }
204             }
205            
206 2         4 $info{'width'} = $width;
207 2         3 $info{'height'} = $height;
208 2         5 $info{'max'} = $max;
209              
210             # for binary (raw) files, a single whitespace character should be seen.
211             # for ASCII (plain) files, extend to allow arbitrary whitespace
212 2 50       15 if ($info{'raw'}) {
213             # The buffer should have a single ws char in it already, left over from
214             # the previous content read. We don't want to read anything beyond that
215             # in case a byte value happens to be a valid whitespace character! If
216             # the file format is botched and there is additional whitespace, it
217             # will unfortunately be read as binary data.
218 2 50       4 if ($buffer =~ m/^\s/) {
219 2         5 $buffer = substr($buffer, 1); # discard first character
220             } else {
221 0         0 $info{'error'} = 'Expected single whitespace before raster data';
222 0         0 return (\%info, $buffer);
223             }
224             } else {
225             # As an extension, for plain (ASCII) format we allow arbitrary
226             # whitespace (including comments) after the max value and before the
227             # raster data, not just one whitespace.
228 0         0 ($buffer, $comment) = eat_whitespace($gr, $buffer, 0);
229             }
230              
231 2         5 return (\%info, $buffer);
232             } # end of readppmheader()
233              
234             # -------------------------------------------------------------------
235             # eat and discard whitespace stream, but return any comment(s) found
236             # within the header, cannot have an EOF during whitespace read
237             sub eat_whitespace {
238 8     8 0 15 my ($gr, $buffer, $qflag) = @_;
239             # qflag = 0 if OK to read more from file (don't expect an EOF)
240             # = 1 eating ws at end of image, might hit EOF here
241              
242 8         9 my ($count, $buf, @comment);
243             # first see if enough material is already in the buffer. if not, read some
244 8         7 my $in_comment = 0; # not currently processing a comment, just ws.
245 8         9 while (1) {
246             # is buffer empty? if so, read some content
247 8 100       11 if (length($buffer) == 0) {
248 2         89 $count = read($gr, $buffer, 50); # chunk of up to 50 bytes (could be 0)
249 2 0 0     7 if ($count == 0 && (!$qflag || $in_comment)) {
      33        
250             # EOF or read error, is bad thing here
251 0         0 print STDERR "EOF or read error reading whitespace.\n";
252 0         0 return ($buffer, '');
253             }
254             }
255             # if buffer is still empty (qflag == 1), will exit cleanly
256              
257 8 50       32 if (!$in_comment) { $buffer =~ s/^\s+//; }
  8         19  
258             # a bunch of whitespace may have been discarded. if buffer now starts
259             # with a #, it is a comment to be read to EOL. otherwise we're done.
260 8 50       13 if (length($buffer) > 0) {
261             # buffer still has stuff in it (starts with non-ws)
262 8 50       12 if ($buffer =~ m/^#/) {
263 0         0 $in_comment = 1;
264             # at start of comment. discard up through \n
265             # (\n might not yet be in buffer!)
266             # special case: #\n
267 0 0       0 if ($buffer =~ s/^#\n//) {
    0          
268             # special empty case
269 0         0 $in_comment = 0;
270             } elsif ($buffer =~ s/^#\s*([^\n]*)\n//) {
271 0         0 push @comment, $1; # has been removed from buffer
272 0         0 $in_comment = 0;
273             } else {
274             # haven't gotten to end of comment (\n) yet
275 0         0 $count = read($gr, $buf, 50);
276 0 0       0 if ($count == 0) {
277             # EOF or read error, is bad thing here
278 0         0 print STDERR "EOF or read error reading whitespace in pixel data\n";
279 0         0 return ($buffer, '');
280             }
281 0         0 $buffer .= $buf;
282 0         0 next;
283             }
284             } else {
285             # non-whitespace, not #. content to be left in buffer
286 8         8 $in_comment = 0;
287 8         9 last;
288             }
289             } else {
290             # empty buffer, need to read some more
291 0 0 0     0 if ($qflag && !$in_comment) { last; }
  0         0  
292 0         0 next;
293             }
294             } # while(1) until run out of whitespace
295              
296 8         9 my $comments = '';
297 8 50       13 if (scalar(@comment) > 0) { $comments = join("\n", @comment); }
  0         0  
298 8         17 return ($buffer, $comments);
299             } # end of eat_whitespace()
300              
301             # -------------------------------------------------------------------
302             # eat a non-whitespace stream, returning the content up until whitespace
303             # should not see an EOF during this (at least one ws after this stream)
304             sub read_content {
305 8     8 0 11 my ($gr, $buffer) = @_;
306              
307 8         7 my ($count, $content);
308 8         10 $content = '';
309             # first see if enough material is already in the buffer. if not, read some
310 8         9 while (1) {
311             # is buffer empty? if so, read some content
312 8 50       24 if (length($buffer) == 0) {
313 0         0 $count = read($gr, $buffer, 50); # chunk of up to 50 bytes (could be 0)
314 0 0       0 if ($count == 0) {
315             # EOF or read error, is bad thing here
316 0         0 print STDERR "EOF or read error reading content in pixel data\n";
317 0         0 return ($buffer, '');
318             }
319             }
320              
321             # should always be non-ws content here
322 8         18 $buffer =~ s/^([^\s]+)//;
323 8         14 $content .= $1; # has been removed from buffer (now possibly empty)
324             # if buffer now empty (didn't see ws char), need to read more
325 8 50       12 if (length($buffer) == 0) { next; }
  0         0  
326 8         8 last; # non-empty buffer means it starts with a ws char
327              
328             # this function is used for header fields and non-raw pixel data, so
329             # we don't expect to have an EOF immediately after a data item (must
330             # be a \n after it at the last data item).
331              
332             } # while(1) until run out of non-whitespace
333              
334 8         14 return ($buffer, $content);
335             } # end of read_content()
336              
337             # -------------------------------------------------------------------
338             sub read_pnm {
339 3     3 0 4 my $self = shift;
340 3         23 my $pdf = shift;
341 3         7 my $file = shift;
342              
343 3         3 my ($rc, $buf, $buf2, $s, $pix, $max);
344             # $s is a scale factor for sample not full 8 or 16 bits.
345             # it should scale the input to 0..255 or 0..65535, so final value
346             # will be a full 8 or 16 bits per channel (bpc)
347 3         7 my ($w,$h, $bpc, $cs, $img, @img) = (0,0, '', '', '');
348 3         8 my ($info, $buffer, $content, $comment, $sample, $gr);
349 3         0 my $inf;
350 3 100       5 if (ref($file)) {
351 1         2 $inf = $file;
352             } else {
353 2 100       110 open $inf, "<", $file or die "$!: $file";
354             }
355 2         9 binmode($inf,':raw');
356 2         18 $inf->seek(0, 0);
357 2         16 $buffer = ''; # initialize
358 2         4 ($info, $buffer) = readppmheader($inf, $buffer);
359             # info (hashref) fields:
360             # error undef or an error description
361             # type magic number 1-6
362             # raw 0 if plain/ASCII, 1 if raw/binary
363             # bgp b=bi-level (1,4) g=grayscale (2,5), p=pixmap/RGB (3,6)
364             # width width (row length/horizontal) in pixels
365             # height height (row count/vertical) in pixels
366             # max sample max value 1 for bi-level, 1-65535 for grayscale/RGB
367             # comments comment line(s), if any (else '')
368 2 50       5 if (defined $info->{'error'}) {
369 0         0 print STDERR "Error reported during PNM file header read:\n".($info->{'error'}).".\n";
370 0         0 return $self;
371             }
372              
373 2         13 $w = $info->{'width'};
374 2         3 $h = $info->{'height'};
375 2         2 $max = $info->{'max'};
376              
377 2         4 my $bytes_per_sample = 1;
378 2 50       3 if ($max > 255) { $bytes_per_sample = 2; }
  0         0  
379              
380             # ------------------------------
381 2 50       10 if ($info->{'type'} == 1) {
    50          
    50          
    50          
    50          
    50          
382             # plain (ASCII) PBM bi-level, each pixel 0..1, ws between is optional
383            
384 0         0 $bpc = 1; # one bit per channel/sample/pixel
385             # pack 8 pixels (possibly with don't-care at end of row) to a byte
386 0         0 my ($row, $col, $bits); # need to handle rows separately for d/c bits
387 0         0 my $qflag;
388 0         0 $content = '';
389 0         0 for ($row = 0; $row < $h; $row++) {
390 0         0 $bits = '';
391 0         0 for ($col = 0; $col < $w; $col++) {
392             # could be a single 0 or 1, or a whole bunch lumped together
393             # in one or more groups
394             # buffer has 0 or more entries. handle just one in this loop,
395             # reading in new buffer if necessary
396 0 0       0 if (length($content) == 0) {
397 0         0 $qflag = 0;
398 0 0 0     0 if ($row == $h-1 && $col == $w-1) { $qflag = 1; }
  0         0  
399 0         0 ($buffer, $comment) = eat_whitespace($inf, $buffer, $qflag);
400 0         0 ($buffer, $content) = read_content($inf, $buffer);
401 0 0       0 if (length($content) == 0) {
402 0         0 print STDERR "Unexpected EOF or read error reading pixel data.\n";
403 0         0 return $self;
404             }
405             }
406 0         0 $sample = substr($content, 0, 1);
407 0         0 $content = substr($content, 1);
408 0 0 0     0 if ($sample ne '0' && $sample ne '1') {
409 0         0 print STDERR "Invalid bit value '$sample' in pixel data.\n";
410 0         0 return $self;
411             }
412 0         0 $bits .= $sample;
413 0 0       0 if (length($bits) == 8) {
414 0         0 $self->{' stream'} .= pack('B8', $bits);
415 0         0 $bits = '';
416             }
417              
418             } # end of cols in row. partial $bits to finish?
419 0 0       0 if ($bits ne '') {
420 0         0 while (length($bits) < 8) {
421 0         0 $bits .= '0'; # don't care, but must be 0 or 1
422             }
423 0         0 $self->{' stream'} .= pack('B8', $bits);
424             }
425             } # end of rows
426              
427 0         0 $cs = 'DeviceGray'; # at 1 bit per pixel
428 0         0 $self->{'Decode'} = PDFArray(PDFNum(1), PDFNum(0));
429            
430             # ------------------------------
431             } elsif ($info->{'type'} == 2) {
432             # plain (ASCII) PGM grayscale, each pixel 0..max (1 or 2 bytes)
433            
434             # get scale factor $s to fully fill 8 or 16 bit sample (channel)
435 0 0 0     0 if ($max == 255 || $max == 65535) {
    0          
436 0         0 $s = 0; # flag: no scaling
437             } elsif ($max > 255) {
438 0         0 $s = 65535/$max;
439             } else {
440 0         0 $s = 255/$max;
441             }
442 0         0 $bpc = 8 * $bytes_per_sample;
443 0         0 my $format = 'C';
444 0 0       0 if ($bytes_per_sample == 2) { $format = 'S>'; }
  0         0  
445 0         0 my $sample;
446              
447 0         0 for ($pix=($w*$h); $pix>0; $pix--) {
448 0         0 ($buffer, $content) = read_content($inf, $buffer);
449 0 0       0 if (length($content) == 0) {
450 0         0 print STDERR "Unexpected EOF or read error reading pixel data.\n";
451 0         0 return $self;
452             }
453 0         0 ($buffer, $comment) = eat_whitespace($inf, $buffer, $pix==1);
454              
455 0 0       0 if ($content =~ m/^\d+$/) {
456 0 0       0 if ($content > $max) {
457 0         0 print STDERR "Pixel data entry '$content' higher than $max. Value changed to $max.\n";
458 0         0 $content = $max;
459             }
460             } else {
461 0         0 print STDERR "Invalid pixel data entry '$content'.\n";
462 0         0 return $self;
463             }
464 0         0 $sample = $content;
465              
466 0 0       0 if ($s > 0) {
467             # scaling needed
468 0         0 $sample = int($sample*$s + 0.5); # must not exceed 255/65535
469             }
470 0         0 $self->{' stream'} .= pack($format, $sample);
471             } # loop through all pixels
472 0         0 $cs = 'DeviceGray';
473            
474             # ------------------------------
475             } elsif ($info->{'type'} == 3) {
476             # plain (ASCII) PPM rgb, each pixel 0..max for R, G, B (1 or 2 bytes)
477            
478             # get scale factor $s to fully fill 8 or 16 bit sample (channel)
479 0 0 0     0 if ($max == 255 || $max == 65535) {
    0          
480 0         0 $s = 0; # flag: no scaling
481             } elsif ($max > 255) {
482 0         0 $s = 65535/$max;
483             } else {
484 0         0 $s = 255/$max;
485             }
486 0         0 $bpc = 8 * $bytes_per_sample;
487 0         0 my $format = 'C';
488 0 0       0 if ($bytes_per_sample == 2) { $format = 'S>'; }
  0         0  
489 0         0 my ($sample, $rgb);
490              
491 0         0 for ($pix=($w*$h); $pix>0; $pix--) {
492 0         0 for ($rgb = 0; $rgb < 3; $rgb++) { # R, G, and B values
493 0         0 ($buffer, $comment) = eat_whitespace($inf, $buffer, $pix==1);
494 0         0 ($buffer, $content) = read_content($inf, $buffer);
495 0 0       0 if (length($content) == 0) {
496 0         0 print STDERR "Unexpected EOF or read error reading pixel data.\n";
497 0         0 return $self;
498             }
499              
500 0 0       0 if ($content =~ m/^\d+$/) {
501 0 0       0 if ($content > $max) {
502             # remember, $pix counts DOWN from w x h
503 0         0 print STDERR "Pixel $pix data entry '$content' higher than $max. Value changed to $max.\n";
504 0         0 $content = $max;
505             }
506             } else {
507 0         0 print STDERR "Invalid pixel data entry '$content'.\n";
508 0         0 return $self;
509             }
510 0         0 $sample = $content;
511              
512 0 0       0 if ($s > 0) {
513             # scaling needed
514 0         0 $sample = int($sample*$s + 0.5); # must not exceed 255/65535
515             }
516 0         0 $self->{' stream'} .= pack($format, $sample);
517             } # R G B loop
518             } # loop through all pixels
519 0         0 $cs = 'DeviceRGB';
520            
521             # ------------------------------
522             } elsif ($info->{'type'} == 4) {
523             # raw (binary) PBM bi-level, each pixel 0..1, row packed 8 pixel/byte
524 0         0 $bpc = 1; # one bit per channel/sample/pixel
525             # round up for don't care bits at end of row
526 0         0 my $bytes = int(($w+7)/8) * $h;
527 0         0 $bytes -= length($buffer); # some already read from file!
528 0         0 $rc = read($inf, $buf2, $bytes);
529 0 0       0 if ($rc != $bytes) {
530 0         0 print STDERR "Unexpected EOF or read error while reading PNM binary pixel data.\n";
531 0         0 return $self;
532             }
533 0         0 $self->{' stream'} = $buffer.$buf2;
534 0         0 $cs = 'DeviceGray'; # at 1 bit per pixel
535 0         0 $self->{'Decode'} = PDFArray(PDFNum(1), PDFNum(0));
536              
537             # ------------------------------
538             } elsif ($info->{'type'} == 5) {
539             # raw (binary) PGM grayscale, each pixel 0..max (1 or 2 bytes)
540            
541             # get scale factor $s to fully fill 8 or 16 bit sample (channel)
542 0 0 0     0 if ($max == 255 || $max == 65535) {
    0          
543 0         0 $s = 0; # flag: no scaling
544             } elsif ($max > 255) {
545 0         0 $s = 65535/$max;
546             } else {
547 0         0 $s = 255/$max;
548             }
549 0         0 $bpc = 8 * $bytes_per_sample;
550 0         0 my $format = 'C';
551 0 0       0 if ($bytes_per_sample == 2) { $format = 'S>'; }
  0         0  
552 0         0 my ($buf, $sample);
553              
554 0         0 my $bytes = $w * $h * $bytes_per_sample;
555 0         0 $bytes -= length($buffer); # some already read from file!
556 0         0 $rc = read($inf, $buf, $bytes);
557 0 0       0 if ($rc != $bytes) {
558 0         0 print STDERR "Unexpected EOF or read error reading pixel data.\n";
559 0         0 return $self;
560             }
561 0         0 $buf = $buffer . $buf;
562 0 0       0 if ($s > 0) {
563             # scaling needed
564 0         0 for ($pix=($w*$h); $pix>0; $pix--) {
565 0         0 $buf2 = substr($buf, 0, $bytes_per_sample);
566 0         0 $buf = substr($buf, $bytes_per_sample);
567 0         0 $sample = unpack($format, $buf2);
568 0         0 $sample = int($sample*$s + 0.5); # must not exceed 255/65535
569 0         0 $self->{' stream'} .= pack($format, $sample);
570             }
571             } else {
572             # no scaling needed
573 0         0 $self->{' stream'} = $buf;
574             }
575 0         0 $cs = 'DeviceGray';
576            
577             # ------------------------------
578             } elsif ($info->{'type'} == 6) {
579             # raw (binary) PPM rgb, each pixel 0..max for R, G, B (3 or 6 bytes)
580            
581             # get scale factor $s to fully fill 8 or 16 bit sample (channel)
582 2 50 33     8 if ($max == 255 || $max == 65535) {
    0          
583 2         3 $s = 0; # flag: no scaling
584             } elsif ($max > 255) {
585 0         0 $s = 65535/$max;
586             } else {
587 0         0 $s = 255/$max;
588             }
589 2         4 $bpc = 8 * $bytes_per_sample;
590 2         2 my $format = 'C';
591 2 50       4 if ($bytes_per_sample == 2) { $format = 'S>'; }
  0         0  
592 2         3 my ($buf, $sample);
593              
594 2         4 my $bytes = $w * $h * $bytes_per_sample * 3;
595 2         2 $bytes -= length($buffer); # some already read from file!
596 2         186 $rc = read($inf, $buf, $bytes);
597 2 50       4 if ($rc != $bytes) {
598 0         0 print STDERR "Unexpected EOF or read error reading pixel data.\n";
599 0         0 return $self;
600             }
601 2         4 $buf = $buffer . $buf;
602 2 50       3 if ($s > 0) {
603             # scaling needed
604 0         0 for ($pix=($w*$h); $pix>0; $pix--) {
605             # Red
606 0         0 $buf2 = substr($buf, 0, $bytes_per_sample);
607 0         0 $sample = unpack($format, $buf2);
608 0         0 $sample = int($sample*$s + 0.5); # must not exceed 255/65535
609 0         0 $self->{' stream'} .= pack($format, $sample);
610             # Green
611 0         0 $buf2 = substr($buf, $bytes_per_sample, $bytes_per_sample);
612 0         0 $sample = unpack($format, $buf2);
613 0         0 $sample = int($sample*$s + 0.5); # must not exceed 255/65535
614 0         0 $self->{' stream'} .= pack($format, $sample);
615             # Blue
616 0         0 $buf2 = substr($buf, 2*$bytes_per_sample, $bytes_per_sample);
617 0         0 $sample = unpack($format, $buf2);
618 0         0 $sample = int($sample*$s + 0.5); # must not exceed 255/65535
619 0         0 $self->{' stream'} .= pack($format, $sample);
620              
621 0         0 $buf = substr($buf, $bytes_per_sample*3);
622             }
623             } else {
624             # no scaling needed
625 2         4 $self->{' stream'} = $buf;
626             }
627 2         4 $cs = 'DeviceRGB';
628             }
629 2         24 close($inf);
630              
631 2         15 $self->width($w);
632 2         6 $self->height($h);
633              
634 2         7 $self->bits_per_component($bpc);
635              
636 2         17 $self->filters('FlateDecode');
637              
638 2         6 $self->colorspace($cs);
639              
640 2         9 return $self;
641             } # end of read_pnm()
642              
643             1;