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