Skip to main content

image/codecs/pnm/
decoder.rs

1use std::error;
2use std::fmt::{self, Display};
3use std::io::{self, Read};
4use std::mem::size_of;
5use std::num::ParseIntError;
6use std::str;
7
8use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};
9use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding};
10use crate::color::{ColorType, ExtendedColorType};
11use crate::error::{
12    DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
13};
14use crate::{utils, ImageDecoder, ImageFormat};
15
16/// All errors that can occur when attempting to parse a PNM
17#[derive(Debug, Clone)]
18enum DecoderError {
19    /// PNM's "P[123456]" signature wrong or missing
20    PnmMagicInvalid([u8; 2]),
21    /// Couldn't parse the specified string as an integer from the specified source
22    UnparsableValue(ErrorDataSource, String, ParseIntError),
23
24    /// More than the exactly one allowed plane specified by the format
25    NonAsciiByteInHeader(u8),
26    /// The PAM header contained a non-ASCII byte
27    NonAsciiLineInPamHeader,
28    /// Couldn't parse an integer: expected but did not get an ASCII digit
29    InvalidDigit(ErrorDataSource),
30
31    /// The byte after the P7 magic was not 0x0A NEWLINE
32    NotNewlineAfterP7Magic(u8),
33    /// The PNM header had too few lines
34    UnexpectedPnmHeaderEnd,
35
36    /// The specified line was specified twice
37    HeaderLineDuplicated(PnmHeaderLine),
38    /// The line with the specified ID was not understood
39    HeaderLineUnknown(String),
40    /// At least one of the required lines were missing from the header (are `None` here)
41    ///
42    /// Same names as [`PnmHeaderLine`](enum.PnmHeaderLine.html)
43    #[allow(missing_docs)]
44    HeaderLineMissing {
45        height: Option<u32>,
46        width: Option<u32>,
47        depth: Option<u32>,
48        maxval: Option<u32>,
49    },
50
51    /// Not enough data was provided to the Decoder to decode the image
52    InputTooShort,
53    /// Sample raster contained unexpected byte
54    UnexpectedByteInRaster(u8),
55    /// Specified sample was out of bounds (e.g. >1 in B&W)
56    SampleOutOfBounds(u8),
57    /// The image's maxval is zero
58    MaxvalZero,
59    /// The image's maxval exceeds 0xFFFF
60    MaxvalTooBig(u32),
61
62    /// The specified tuple type supports restricted depths and maxvals, those restrictions were not met
63    InvalidDepthOrMaxval {
64        tuple_type: ArbitraryTuplType,
65        depth: u32,
66        maxval: u32,
67    },
68    /// The specified tuple type supports restricted depths, those restrictions were not met
69    InvalidDepth {
70        tuple_type: ArbitraryTuplType,
71        depth: u32,
72    },
73    /// The tuple type was not recognised by the parser
74    TupleTypeUnrecognised,
75
76    /// Overflowed the specified value when parsing
77    Overflow(ErrorDataSource),
78}
79
80impl Display for DecoderError {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        match self {
83            DecoderError::PnmMagicInvalid(magic) => f.write_fmt(format_args!(
84                "Expected magic constant for PNM: P1..P7, got [{:#04X?}, {:#04X?}]",
85                magic[0], magic[1]
86            )),
87            DecoderError::UnparsableValue(src, data, err) => {
88                f.write_fmt(format_args!("Error parsing {data:?} as {src}: {err}"))
89            }
90
91            DecoderError::NonAsciiByteInHeader(c) => {
92                f.write_fmt(format_args!("Non-ASCII character {c:#04X?} in header"))
93            }
94            DecoderError::NonAsciiLineInPamHeader => f.write_str("Non-ASCII line in PAM header"),
95            DecoderError::InvalidDigit(src) => {
96                f.write_fmt(format_args!("Non-ASCII-digit character when parsing number in {src}"))
97            }
98
99            DecoderError::NotNewlineAfterP7Magic(c) => f.write_fmt(format_args!(
100                "Expected newline after P7 magic, got {c:#04X?}"
101            )),
102            DecoderError::UnexpectedPnmHeaderEnd => f.write_str("Unexpected end of PNM header"),
103
104            DecoderError::HeaderLineDuplicated(line) => {
105                f.write_fmt(format_args!("Duplicate {line} line"))
106            }
107            DecoderError::HeaderLineUnknown(identifier) => f.write_fmt(format_args!(
108                "Unknown header line with identifier {identifier:?}"
109            )),
110            DecoderError::HeaderLineMissing {
111                height,
112                width,
113                depth,
114                maxval,
115            } => f.write_fmt(format_args!(
116                "Missing header line: have height={height:?}, width={width:?}, depth={depth:?}, maxval={maxval:?}"
117            )),
118
119            DecoderError::InputTooShort => {
120                f.write_str("Not enough data was provided to the Decoder to decode the image")
121            }
122            DecoderError::UnexpectedByteInRaster(c) => f.write_fmt(format_args!(
123                "Unexpected character {c:#04X?} within sample raster"
124            )),
125            DecoderError::SampleOutOfBounds(val) => {
126                f.write_fmt(format_args!("Sample value {val} outside of bounds"))
127            }
128            DecoderError::MaxvalZero => f.write_str("Image MAXVAL is zero"),
129            DecoderError::MaxvalTooBig(maxval) => {
130                f.write_fmt(format_args!("Image MAXVAL exceeds {}: {}", 0xFFFF, maxval))
131            }
132
133            DecoderError::InvalidDepthOrMaxval {
134                tuple_type,
135                depth,
136                maxval,
137            } => f.write_fmt(format_args!(
138                "Invalid depth ({}) or maxval ({}) for tuple type {}",
139                depth,
140                maxval,
141                tuple_type.name()
142            )),
143            DecoderError::InvalidDepth { tuple_type, depth } => f.write_fmt(format_args!(
144                "Invalid depth ({}) for tuple type {}",
145                depth,
146                tuple_type.name()
147            )),
148            DecoderError::TupleTypeUnrecognised => f.write_str("Tuple type not recognized"),
149            DecoderError::Overflow(src) => f.write_fmt(format_args!(
150                "Overflow when parsing integer in {src}"
151            ))
152        }
153    }
154}
155
156/// Note: should `pnm` be extracted into a separate crate,
157/// this will need to be hidden until that crate hits version `1.0`.
158impl From<DecoderError> for ImageError {
159    fn from(e: DecoderError) -> ImageError {
160        ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e))
161    }
162}
163
164impl error::Error for DecoderError {
165    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
166        match self {
167            DecoderError::UnparsableValue(_, _, err) => Some(err),
168            _ => None,
169        }
170    }
171}
172
173/// Single-value lines in a PNM header
174#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
175enum PnmHeaderLine {
176    /// "HEIGHT"
177    Height,
178    /// "WIDTH"
179    Width,
180    /// "DEPTH"
181    Depth,
182    /// "MAXVAL", a.k.a. `maxwhite`
183    Maxval,
184}
185
186impl Display for PnmHeaderLine {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        f.write_str(match self {
189            PnmHeaderLine::Height => "HEIGHT",
190            PnmHeaderLine::Width => "WIDTH",
191            PnmHeaderLine::Depth => "DEPTH",
192            PnmHeaderLine::Maxval => "MAXVAL",
193        })
194    }
195}
196
197/// Single-value lines in a PNM header
198#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
199enum ErrorDataSource {
200    /// One of the header lines
201    Line(PnmHeaderLine),
202    /// Value in the preamble
203    Preamble,
204    /// Sample/pixel data
205    Sample,
206}
207
208impl Display for ErrorDataSource {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        match self {
211            ErrorDataSource::Line(l) => l.fmt(f),
212            ErrorDataSource::Preamble => f.write_str("number in preamble"),
213            ErrorDataSource::Sample => f.write_str("sample"),
214        }
215    }
216}
217
218/// Dynamic representation, represents all decodable (sample, depth) combinations.
219#[derive(Clone, Copy)]
220enum TupleType {
221    PbmBit,
222    BWBit,
223    BWAlphaBit,
224    GrayU8,
225    GrayAlphaU8,
226    GrayU16,
227    GrayAlphaU16,
228    RGBU8,
229    RGBAlphaU8,
230    RGBU16,
231    RGBAlphaU16,
232}
233
234trait Sample {
235    type Representation;
236
237    /// Representation size in bytes
238    fn sample_size() -> u32 {
239        size_of::<Self::Representation>() as u32
240    }
241    fn from_bytes(
242        reader: &mut dyn Read,
243        output_buf: &mut [u8],
244        width: u32,
245        height: u32,
246        components: u32,
247    ) -> ImageResult<()>;
248    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()>;
249}
250
251struct U8;
252struct U16;
253struct PbmBit;
254struct BWBit;
255
256trait DecodableImageHeader {
257    fn tuple_type(&self) -> ImageResult<TupleType>;
258}
259
260/// PNM decoder
261pub struct PnmDecoder<R> {
262    reader: R,
263    header: PnmHeader,
264    tuple: TupleType,
265}
266
267impl<R: Read> PnmDecoder<R> {
268    /// Create a new decoder that decodes from the stream ```read```
269    pub fn new(mut buffered_read: R) -> ImageResult<PnmDecoder<R>> {
270        let magic = buffered_read.read_magic_constant()?;
271
272        let subtype = match magic {
273            [b'P', b'1'] => PnmSubtype::Bitmap(SampleEncoding::Ascii),
274            [b'P', b'2'] => PnmSubtype::Graymap(SampleEncoding::Ascii),
275            [b'P', b'3'] => PnmSubtype::Pixmap(SampleEncoding::Ascii),
276            [b'P', b'4'] => PnmSubtype::Bitmap(SampleEncoding::Binary),
277            [b'P', b'5'] => PnmSubtype::Graymap(SampleEncoding::Binary),
278            [b'P', b'6'] => PnmSubtype::Pixmap(SampleEncoding::Binary),
279            [b'P', b'7'] => PnmSubtype::ArbitraryMap,
280            _ => return Err(DecoderError::PnmMagicInvalid(magic).into()),
281        };
282
283        let decoder = match subtype {
284            PnmSubtype::Bitmap(enc) => PnmDecoder::read_bitmap_header(buffered_read, enc),
285            PnmSubtype::Graymap(enc) => PnmDecoder::read_graymap_header(buffered_read, enc),
286            PnmSubtype::Pixmap(enc) => PnmDecoder::read_pixmap_header(buffered_read, enc),
287            PnmSubtype::ArbitraryMap => PnmDecoder::read_arbitrary_header(buffered_read),
288        }?;
289
290        if utils::check_dimension_overflow(
291            decoder.dimensions().0,
292            decoder.dimensions().1,
293            decoder.color_type().bytes_per_pixel(),
294        ) {
295            return Err(ImageError::Unsupported(
296                UnsupportedError::from_format_and_kind(
297                    ImageFormat::Pnm.into(),
298                    UnsupportedErrorKind::GenericFeature(format!(
299                        "Image dimensions ({}x{}) are too large",
300                        decoder.dimensions().0,
301                        decoder.dimensions().1
302                    )),
303                ),
304            ));
305        }
306
307        Ok(decoder)
308    }
309
310    /// Get the header of the decoded image.
311    pub fn header(&self) -> &PnmHeader {
312        &self.header
313    }
314
315    /// Extract the reader and header after an image has been read.
316    pub fn into_inner(self) -> (R, PnmHeader) {
317        (self.reader, self.header)
318    }
319
320    fn read_bitmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
321        let header = reader.read_bitmap_header(encoding)?;
322        Ok(PnmDecoder {
323            reader,
324            tuple: TupleType::PbmBit,
325            header: PnmHeader {
326                decoded: HeaderRecord::Bitmap(header),
327                encoded: None,
328            },
329        })
330    }
331
332    fn read_graymap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
333        let header = reader.read_graymap_header(encoding)?;
334        let tuple_type = header.tuple_type()?;
335        Ok(PnmDecoder {
336            reader,
337            tuple: tuple_type,
338            header: PnmHeader {
339                decoded: HeaderRecord::Graymap(header),
340                encoded: None,
341            },
342        })
343    }
344
345    fn read_pixmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
346        let header = reader.read_pixmap_header(encoding)?;
347        let tuple_type = header.tuple_type()?;
348        Ok(PnmDecoder {
349            reader,
350            tuple: tuple_type,
351            header: PnmHeader {
352                decoded: HeaderRecord::Pixmap(header),
353                encoded: None,
354            },
355        })
356    }
357
358    fn read_arbitrary_header(mut reader: R) -> ImageResult<PnmDecoder<R>> {
359        let header = reader.read_arbitrary_header()?;
360        let tuple_type = header.tuple_type()?;
361        Ok(PnmDecoder {
362            reader,
363            tuple: tuple_type,
364            header: PnmHeader {
365                decoded: HeaderRecord::Arbitrary(header),
366                encoded: None,
367            },
368        })
369    }
370}
371
372trait HeaderReader: Read {
373    /// Reads the two magic constant bytes
374    fn read_magic_constant(&mut self) -> ImageResult<[u8; 2]> {
375        let mut magic: [u8; 2] = [0, 0];
376        self.read_exact(&mut magic)?;
377        Ok(magic)
378    }
379
380    /// Reads an integer as well as a single whitespace after it, ignoring comments
381    /// and leading whitespace
382    fn read_next_u32(&mut self) -> ImageResult<u32> {
383        // pair input bytes with a bool mask to remove comments
384        #[allow(clippy::unbuffered_bytes)]
385        let mark_comments = self.bytes().scan(true, |partof, read| {
386            let byte = match read {
387                Err(err) => return Some((*partof, Err(err))),
388                Ok(byte) => byte,
389            };
390            let cur_enabled = *partof && byte != b'#';
391            let next_enabled = cur_enabled || (byte == b'\r' || byte == b'\n');
392            *partof = next_enabled;
393            Some((cur_enabled, Ok(byte)))
394        });
395
396        // Streaming parse of the integer. To match Netpbm, this accepts values
397        // with leading zeros, like 000005, but no leading + or -
398        let mut value: u32 = 0;
399        let mut found_digit = false;
400
401        for (_, byte) in mark_comments.filter(|e| e.0) {
402            match byte {
403                Ok(b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ') => {
404                    if found_digit {
405                        break; // We're done as we already have some content
406                    }
407                }
408                Ok(byte) if !byte.is_ascii() => {
409                    return Err(DecoderError::NonAsciiByteInHeader(byte).into())
410                }
411                Ok(byte) => {
412                    let digit = match byte {
413                        b'0'..=b'9' => u32::from(byte - b'0'),
414                        _ => {
415                            return Err(DecoderError::InvalidDigit(ErrorDataSource::Preamble).into())
416                        }
417                    };
418                    value = value
419                        .checked_mul(10)
420                        .ok_or(DecoderError::Overflow(ErrorDataSource::Preamble))?;
421                    value = value
422                        .checked_add(digit)
423                        .ok_or(DecoderError::Overflow(ErrorDataSource::Preamble))?;
424                    found_digit = true;
425                }
426                Err(_) => break,
427            }
428        }
429
430        if !found_digit {
431            return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into()));
432        }
433
434        Ok(value)
435    }
436
437    fn read_next_line(&mut self) -> ImageResult<String> {
438        let mut buffer = Vec::new();
439        loop {
440            let mut byte = [0];
441            if self.read(&mut byte)? == 0 || byte[0] == b'\n' {
442                break;
443            }
444            buffer.push(byte[0]);
445        }
446
447        String::from_utf8(buffer)
448            .map_err(|e| ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e)))
449    }
450
451    fn read_bitmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<BitmapHeader> {
452        let width = self.read_next_u32()?;
453        let height = self.read_next_u32()?;
454        Ok(BitmapHeader {
455            encoding,
456            height,
457            width,
458        })
459    }
460
461    fn read_graymap_header(&mut self, encoding: SampleEncoding) -> ImageResult<GraymapHeader> {
462        self.read_pixmap_header(encoding).map(
463            |PixmapHeader {
464                 encoding,
465                 width,
466                 height,
467                 maxval,
468             }| GraymapHeader {
469                encoding,
470                width,
471                height,
472                maxwhite: maxval,
473            },
474        )
475    }
476
477    fn read_pixmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<PixmapHeader> {
478        let width = self.read_next_u32()?;
479        let height = self.read_next_u32()?;
480        let maxval = self.read_next_u32()?;
481        Ok(PixmapHeader {
482            encoding,
483            height,
484            width,
485            maxval,
486        })
487    }
488
489    fn read_arbitrary_header(&mut self) -> ImageResult<ArbitraryHeader> {
490        fn parse_single_value_line(
491            line_val: &mut Option<u32>,
492            rest: &str,
493            line: PnmHeaderLine,
494        ) -> ImageResult<()> {
495            if line_val.is_some() {
496                Err(DecoderError::HeaderLineDuplicated(line).into())
497            } else {
498                let v = rest.trim().parse().map_err(|err| {
499                    DecoderError::UnparsableValue(ErrorDataSource::Line(line), rest.to_owned(), err)
500                })?;
501                *line_val = Some(v);
502                Ok(())
503            }
504        }
505
506        #[allow(clippy::unbuffered_bytes)]
507        match self.bytes().next() {
508            None => return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into())),
509            Some(Err(io)) => return Err(ImageError::IoError(io)),
510            Some(Ok(b'\n')) => (),
511            Some(Ok(c)) => return Err(DecoderError::NotNewlineAfterP7Magic(c).into()),
512        }
513
514        let mut line;
515        let mut height: Option<u32> = None;
516        let mut width: Option<u32> = None;
517        let mut depth: Option<u32> = None;
518        let mut maxval: Option<u32> = None;
519        let mut tupltype: Option<String> = None;
520        loop {
521            line = self.read_next_line()?;
522            if line.is_empty() {
523                return Err(DecoderError::UnexpectedPnmHeaderEnd.into());
524            }
525            if line.as_bytes()[0] == b'#' {
526                continue;
527            }
528            if !line.is_ascii() {
529                return Err(DecoderError::NonAsciiLineInPamHeader.into());
530            }
531            #[allow(deprecated)]
532            let (identifier, rest) = line
533                .trim_left()
534                .split_at(line.find(char::is_whitespace).unwrap_or(line.len()));
535            match identifier {
536                "ENDHDR" => break,
537                "HEIGHT" => parse_single_value_line(&mut height, rest, PnmHeaderLine::Height)?,
538                "WIDTH" => parse_single_value_line(&mut width, rest, PnmHeaderLine::Width)?,
539                "DEPTH" => parse_single_value_line(&mut depth, rest, PnmHeaderLine::Depth)?,
540                "MAXVAL" => parse_single_value_line(&mut maxval, rest, PnmHeaderLine::Maxval)?,
541                "TUPLTYPE" => {
542                    let identifier = rest.trim();
543                    if tupltype.is_some() {
544                        let appended = tupltype.take().map(|mut v| {
545                            v.push(' ');
546                            v.push_str(identifier);
547                            v
548                        });
549                        tupltype = appended;
550                    } else {
551                        tupltype = Some(identifier.to_string());
552                    }
553                }
554                _ => return Err(DecoderError::HeaderLineUnknown(identifier.to_string()).into()),
555            }
556        }
557
558        let (Some(h), Some(w), Some(d), Some(m)) = (height, width, depth, maxval) else {
559            return Err(DecoderError::HeaderLineMissing {
560                height,
561                width,
562                depth,
563                maxval,
564            }
565            .into());
566        };
567
568        let tupltype = match tupltype {
569            None => None,
570            Some(ref t) if t == "BLACKANDWHITE" => Some(ArbitraryTuplType::BlackAndWhite),
571            Some(ref t) if t == "BLACKANDWHITE_ALPHA" => {
572                Some(ArbitraryTuplType::BlackAndWhiteAlpha)
573            }
574            Some(ref t) if t == "GRAYSCALE" => Some(ArbitraryTuplType::Grayscale),
575            Some(ref t) if t == "GRAYSCALE_ALPHA" => Some(ArbitraryTuplType::GrayscaleAlpha),
576            Some(ref t) if t == "RGB" => Some(ArbitraryTuplType::RGB),
577            Some(ref t) if t == "RGB_ALPHA" => Some(ArbitraryTuplType::RGBAlpha),
578            Some(other) => Some(ArbitraryTuplType::Custom(other)),
579        };
580
581        Ok(ArbitraryHeader {
582            height: h,
583            width: w,
584            depth: d,
585            maxval: m,
586            tupltype,
587        })
588    }
589}
590
591impl<R> HeaderReader for R where R: Read {}
592
593impl<R: Read> ImageDecoder for PnmDecoder<R> {
594    fn dimensions(&self) -> (u32, u32) {
595        (self.header.width(), self.header.height())
596    }
597
598    fn color_type(&self) -> ColorType {
599        match self.tuple {
600            TupleType::PbmBit => ColorType::L8,
601            TupleType::BWBit => ColorType::L8,
602            TupleType::BWAlphaBit => ColorType::La8,
603            TupleType::GrayU8 => ColorType::L8,
604            TupleType::GrayAlphaU8 => ColorType::La8,
605            TupleType::GrayU16 => ColorType::L16,
606            TupleType::GrayAlphaU16 => ColorType::La16,
607            TupleType::RGBU8 => ColorType::Rgb8,
608            TupleType::RGBAlphaU8 => ColorType::Rgba8,
609            TupleType::RGBU16 => ColorType::Rgb16,
610            TupleType::RGBAlphaU16 => ColorType::Rgba16,
611        }
612    }
613
614    fn original_color_type(&self) -> ExtendedColorType {
615        match self.tuple {
616            TupleType::PbmBit => ExtendedColorType::L1,
617            TupleType::BWBit => ExtendedColorType::L1,
618            TupleType::BWAlphaBit => ExtendedColorType::La1,
619            TupleType::GrayU8 => ExtendedColorType::L8,
620            TupleType::GrayAlphaU8 => ExtendedColorType::La8,
621            TupleType::GrayU16 => ExtendedColorType::L16,
622            TupleType::GrayAlphaU16 => ExtendedColorType::La16,
623            TupleType::RGBU8 => ExtendedColorType::Rgb8,
624            TupleType::RGBAlphaU8 => ExtendedColorType::Rgba8,
625            TupleType::RGBU16 => ExtendedColorType::Rgb16,
626            TupleType::RGBAlphaU16 => ExtendedColorType::Rgba16,
627        }
628    }
629
630    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
631        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
632        match self.tuple {
633            TupleType::PbmBit => self.read_samples::<PbmBit>(1, buf),
634            TupleType::BWBit => self.read_samples::<BWBit>(1, buf),
635            TupleType::BWAlphaBit => self.read_samples::<BWBit>(2, buf),
636            TupleType::RGBU8 => self.read_samples::<U8>(3, buf),
637            TupleType::RGBAlphaU8 => self.read_samples::<U8>(4, buf),
638            TupleType::RGBU16 => self.read_samples::<U16>(3, buf),
639            TupleType::RGBAlphaU16 => self.read_samples::<U16>(4, buf),
640            TupleType::GrayU8 => self.read_samples::<U8>(1, buf),
641            TupleType::GrayAlphaU8 => self.read_samples::<U8>(2, buf),
642            TupleType::GrayU16 => self.read_samples::<U16>(1, buf),
643            TupleType::GrayAlphaU16 => self.read_samples::<U16>(2, buf),
644        }
645    }
646
647    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
648        (*self).read_image(buf)
649    }
650}
651
652impl<R: Read> PnmDecoder<R> {
653    fn read_samples<S: Sample>(&mut self, components: u32, buf: &mut [u8]) -> ImageResult<()> {
654        match self.subtype().sample_encoding() {
655            SampleEncoding::Binary => {
656                S::from_bytes(
657                    &mut self.reader,
658                    buf,
659                    self.header.width(),
660                    self.header.height(),
661                    components,
662                )?;
663            }
664            SampleEncoding::Ascii => {
665                S::from_ascii(&mut self.reader, buf)?;
666            }
667        }
668
669        // Scale samples if 8bit or 16bit is not saturated
670        let current_sample_max = self.header.maximal_sample();
671        let target_sample_max = 256_u32.pow(S::sample_size()) - 1;
672
673        if current_sample_max != target_sample_max {
674            let factor = target_sample_max as f32 / current_sample_max as f32;
675
676            if S::sample_size() == 1 {
677                for v in buf.iter_mut() {
678                    *v = (f32::from(*v) * factor).round() as u8;
679                }
680            } else if S::sample_size() == 2 {
681                for chunk in buf.as_chunks_mut::<2>().0.iter_mut() {
682                    let v = (f32::from(u16::from_ne_bytes(*chunk)) * factor).round() as u16;
683                    chunk.copy_from_slice(&v.to_ne_bytes());
684                }
685            }
686        }
687
688        Ok(())
689    }
690
691    /// Get the pnm subtype, depending on the magic constant contained in the header
692    pub fn subtype(&self) -> PnmSubtype {
693        self.header.subtype()
694    }
695}
696
697fn read_separated_ascii<T: TryFrom<u16>>(reader: &mut dyn Read) -> ImageResult<T> {
698    let is_separator = |v: &u8| matches!(*v, b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ');
699
700    let mut v: u16 = 0;
701    let mut had_any = false;
702    #[allow(clippy::unbuffered_bytes)]
703    for rc in reader
704        .bytes()
705        .skip_while(|v| v.as_ref().ok().is_some_and(is_separator))
706        .take_while(|v| v.as_ref().ok().is_some_and(|c| !is_separator(c)))
707    {
708        let c = rc?;
709        let digit = match c {
710            b'0'..=b'9' => u16::from(c - b'0'),
711            _ => return Err(DecoderError::InvalidDigit(ErrorDataSource::Sample).into()),
712        };
713        v = v
714            .checked_mul(10)
715            .ok_or(DecoderError::Overflow(ErrorDataSource::Sample))?;
716        v = v
717            .checked_add(digit)
718            .ok_or(DecoderError::Overflow(ErrorDataSource::Sample))?;
719        had_any = true;
720    }
721
722    if !had_any {
723        return Err(DecoderError::InputTooShort.into());
724    }
725
726    Ok(T::try_from(v).or(Err(DecoderError::Overflow(ErrorDataSource::Sample)))?)
727}
728
729impl Sample for U8 {
730    type Representation = u8;
731    fn from_bytes(
732        reader: &mut dyn Read,
733        output_buf: &mut [u8],
734        _width: u32,
735        _height: u32,
736        _components: u32,
737    ) -> ImageResult<()> {
738        reader.read_exact(output_buf)?;
739        Ok(())
740    }
741
742    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
743        for b in output_buf {
744            *b = read_separated_ascii(reader)?;
745        }
746        Ok(())
747    }
748}
749
750impl Sample for U16 {
751    type Representation = u16;
752
753    fn from_bytes(
754        reader: &mut dyn Read,
755        output_buf: &mut [u8],
756        _width: u32,
757        _height: u32,
758        _components: u32,
759    ) -> ImageResult<()> {
760        reader.read_exact(output_buf)?;
761        for chunk in output_buf.as_chunks_mut::<2>().0.iter_mut() {
762            let v = u16::from_be_bytes(*chunk);
763            chunk.copy_from_slice(&v.to_ne_bytes());
764        }
765        Ok(())
766    }
767
768    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
769        for chunk in output_buf.as_chunks_mut::<2>().0.iter_mut() {
770            let v = read_separated_ascii::<u16>(reader)?;
771            chunk.copy_from_slice(&v.to_ne_bytes());
772        }
773        Ok(())
774    }
775}
776
777// The image is encoded in rows of bits, high order bits first. Any bits beyond the row bits should
778// be ignored. Also, contrary to rgb, black pixels are encoded as a 1 while white is 0. This will
779// need to be reversed for the grayscale output.
780impl Sample for PbmBit {
781    type Representation = u8;
782
783    fn from_bytes(
784        reader: &mut dyn Read,
785        output_buf: &mut [u8],
786        width: u32,
787        height: u32,
788        components: u32,
789    ) -> ImageResult<()> {
790        assert!(components == 1);
791
792        let width: usize = width
793            .try_into()
794            .map_err(|_| DecoderError::Overflow(ErrorDataSource::Sample))?;
795        let height: usize = height
796            .try_into()
797            .map_err(|_| DecoderError::Overflow(ErrorDataSource::Sample))?;
798        assert!(width.checked_mul(height) == Some(output_buf.len()));
799
800        let linelen = width.div_ceil(8);
801        let bytecount = height
802            .checked_mul(linelen)
803            .filter(|l| *l <= output_buf.len())
804            .expect("PBM packed data is never longer than unpacked");
805
806        reader.read_exact(&mut output_buf[..bytecount])?;
807
808        // Expand the PBM data in place. This can be done byte by byte with a single
809        // backwards pass over the image. At all times, the position being read
810        // from will not be after the position being written to, so no data is lost.
811
812        for y in (0..height).rev() {
813            for x in (0..width).rev() {
814                let shift = 7 - (x % 8);
815                let v = (output_buf[y * linelen + x / 8] >> shift) & 0x1;
816                output_buf[y * width + x] = 1 - v;
817            }
818        }
819        Ok(())
820    }
821
822    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
823        #[allow(clippy::unbuffered_bytes)]
824        let mut bytes = reader.bytes();
825        for b in output_buf {
826            loop {
827                let byte = bytes
828                    .next()
829                    .ok_or_else::<ImageError, _>(|| DecoderError::InputTooShort.into())??;
830                match byte {
831                    b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' => continue,
832                    b'0' => *b = 255,
833                    b'1' => *b = 0,
834                    c => return Err(DecoderError::UnexpectedByteInRaster(c).into()),
835                }
836                break;
837            }
838        }
839
840        Ok(())
841    }
842}
843
844// Encoded just like a normal U8 but we check the values.
845impl Sample for BWBit {
846    type Representation = u8;
847
848    fn from_bytes(
849        reader: &mut dyn Read,
850        output_buf: &mut [u8],
851        width: u32,
852        height: u32,
853        components: u32,
854    ) -> ImageResult<()> {
855        U8::from_bytes(reader, output_buf, width, height, components)?;
856        if let Some(val) = output_buf.iter().find(|&val| *val > 1) {
857            return Err(DecoderError::SampleOutOfBounds(*val).into());
858        }
859        Ok(())
860    }
861
862    fn from_ascii(_reader: &mut dyn Read, _output_buf: &mut [u8]) -> ImageResult<()> {
863        unreachable!("BW bits from anymaps are never encoded as ASCII")
864    }
865}
866
867impl DecodableImageHeader for BitmapHeader {
868    fn tuple_type(&self) -> ImageResult<TupleType> {
869        Ok(TupleType::PbmBit)
870    }
871}
872
873impl DecodableImageHeader for GraymapHeader {
874    fn tuple_type(&self) -> ImageResult<TupleType> {
875        match self.maxwhite {
876            0 => Err(DecoderError::MaxvalZero.into()),
877            v if v <= 0xFF => Ok(TupleType::GrayU8),
878            v if v <= 0xFFFF => Ok(TupleType::GrayU16),
879            _ => Err(DecoderError::MaxvalTooBig(self.maxwhite).into()),
880        }
881    }
882}
883
884impl DecodableImageHeader for PixmapHeader {
885    fn tuple_type(&self) -> ImageResult<TupleType> {
886        match self.maxval {
887            0 => Err(DecoderError::MaxvalZero.into()),
888            v if v <= 0xFF => Ok(TupleType::RGBU8),
889            v if v <= 0xFFFF => Ok(TupleType::RGBU16),
890            _ => Err(DecoderError::MaxvalTooBig(self.maxval).into()),
891        }
892    }
893}
894
895impl DecodableImageHeader for ArbitraryHeader {
896    fn tuple_type(&self) -> ImageResult<TupleType> {
897        match self.tupltype {
898            _ if self.maxval == 0 => Err(DecoderError::MaxvalZero.into()),
899            None if self.depth == 1 => Ok(TupleType::GrayU8),
900            None if self.depth == 2 => Ok(TupleType::GrayAlphaU8),
901            None if self.depth == 3 => Ok(TupleType::RGBU8),
902            None if self.depth == 4 => Ok(TupleType::RGBAlphaU8),
903
904            Some(ArbitraryTuplType::BlackAndWhite) if self.maxval == 1 && self.depth == 1 => {
905                Ok(TupleType::BWBit)
906            }
907            Some(ArbitraryTuplType::BlackAndWhite) => Err(DecoderError::InvalidDepthOrMaxval {
908                tuple_type: ArbitraryTuplType::BlackAndWhite,
909                maxval: self.maxval,
910                depth: self.depth,
911            }
912            .into()),
913
914            Some(ArbitraryTuplType::Grayscale) if self.depth == 1 && self.maxval <= 0xFF => {
915                Ok(TupleType::GrayU8)
916            }
917            Some(ArbitraryTuplType::Grayscale) if self.depth <= 1 && self.maxval <= 0xFFFF => {
918                Ok(TupleType::GrayU16)
919            }
920            Some(ArbitraryTuplType::Grayscale) => Err(DecoderError::InvalidDepthOrMaxval {
921                tuple_type: ArbitraryTuplType::Grayscale,
922                maxval: self.maxval,
923                depth: self.depth,
924            }
925            .into()),
926
927            Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFF => {
928                Ok(TupleType::RGBU8)
929            }
930            Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFFFF => {
931                Ok(TupleType::RGBU16)
932            }
933            Some(ArbitraryTuplType::RGB) => Err(DecoderError::InvalidDepth {
934                tuple_type: ArbitraryTuplType::RGB,
935                depth: self.depth,
936            }
937            .into()),
938
939            Some(ArbitraryTuplType::BlackAndWhiteAlpha) if self.depth == 2 && self.maxval == 1 => {
940                Ok(TupleType::BWAlphaBit)
941            }
942            Some(ArbitraryTuplType::BlackAndWhiteAlpha) => {
943                Err(DecoderError::InvalidDepthOrMaxval {
944                    tuple_type: ArbitraryTuplType::BlackAndWhiteAlpha,
945                    maxval: self.maxval,
946                    depth: self.depth,
947                }
948                .into())
949            }
950
951            Some(ArbitraryTuplType::GrayscaleAlpha) if self.depth == 2 && self.maxval <= 0xFF => {
952                Ok(TupleType::GrayAlphaU8)
953            }
954            Some(ArbitraryTuplType::GrayscaleAlpha) if self.depth == 2 && self.maxval <= 0xFFFF => {
955                Ok(TupleType::GrayAlphaU16)
956            }
957            Some(ArbitraryTuplType::GrayscaleAlpha) => Err(DecoderError::InvalidDepth {
958                tuple_type: ArbitraryTuplType::GrayscaleAlpha,
959                depth: self.depth,
960            }
961            .into()),
962
963            Some(ArbitraryTuplType::RGBAlpha) if self.depth == 4 && self.maxval <= 0xFF => {
964                Ok(TupleType::RGBAlphaU8)
965            }
966            Some(ArbitraryTuplType::RGBAlpha) if self.depth == 4 && self.maxval <= 0xFFFF => {
967                Ok(TupleType::RGBAlphaU16)
968            }
969            Some(ArbitraryTuplType::RGBAlpha) => Err(DecoderError::InvalidDepth {
970                tuple_type: ArbitraryTuplType::RGBAlpha,
971                depth: self.depth,
972            }
973            .into()),
974
975            Some(ArbitraryTuplType::Custom(ref custom)) => Err(ImageError::Unsupported(
976                UnsupportedError::from_format_and_kind(
977                    ImageFormat::Pnm.into(),
978                    UnsupportedErrorKind::GenericFeature(format!("Tuple type {custom:?}")),
979                ),
980            )),
981            None => Err(DecoderError::TupleTypeUnrecognised.into()),
982        }
983    }
984}
985
986#[cfg(test)]
987mod tests {
988    use super::*;
989    /// Tests reading of a valid blackandwhite pam
990    #[test]
991    fn pam_blackandwhite() {
992        let pamdata = b"P7
993WIDTH 4
994HEIGHT 4
995DEPTH 1
996MAXVAL 1
997TUPLTYPE BLACKANDWHITE
998# Comment line
999ENDHDR
1000\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01";
1001        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1002        assert_eq!(decoder.color_type(), ColorType::L8);
1003        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1004        assert_eq!(decoder.dimensions(), (4, 4));
1005        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1006
1007        let mut image = vec![0; decoder.total_bytes() as usize];
1008        decoder.read_image(&mut image).unwrap();
1009        assert_eq!(
1010            image,
1011            vec![
1012                0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00,
1013                0x00, 0xFF
1014            ]
1015        );
1016        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1017            (
1018                _,
1019                PnmHeader {
1020                    decoded:
1021                        HeaderRecord::Arbitrary(ArbitraryHeader {
1022                            width: 4,
1023                            height: 4,
1024                            maxval: 1,
1025                            depth: 1,
1026                            tupltype: Some(ArbitraryTuplType::BlackAndWhite),
1027                        }),
1028                    encoded: _,
1029                },
1030            ) => (),
1031            _ => panic!("Decoded header is incorrect"),
1032        }
1033    }
1034
1035    /// Tests reading of a valid blackandwhite_alpha pam
1036    #[test]
1037    fn pam_blackandwhite_alpha() {
1038        let pamdata = b"P7
1039WIDTH 2
1040HEIGHT 2
1041DEPTH 2
1042MAXVAL 1
1043TUPLTYPE BLACKANDWHITE_ALPHA
1044# Comment line
1045ENDHDR
1046\x01\x00\x00\x01\x01\x00\x00\x01";
1047        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1048        assert_eq!(decoder.color_type(), ColorType::La8);
1049        assert_eq!(decoder.original_color_type(), ExtendedColorType::La1);
1050        assert_eq!(decoder.dimensions(), (2, 2));
1051        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1052
1053        let mut image = vec![0; decoder.total_bytes() as usize];
1054        decoder.read_image(&mut image).unwrap();
1055        assert_eq!(image, vec![0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF,]);
1056        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1057            (
1058                _,
1059                PnmHeader {
1060                    decoded:
1061                        HeaderRecord::Arbitrary(ArbitraryHeader {
1062                            width: 2,
1063                            height: 2,
1064                            maxval: 1,
1065                            depth: 2,
1066                            tupltype: Some(ArbitraryTuplType::BlackAndWhiteAlpha),
1067                        }),
1068                    encoded: _,
1069                },
1070            ) => (),
1071            _ => panic!("Decoded header is incorrect"),
1072        }
1073    }
1074
1075    /// Tests reading of a valid grayscale pam
1076    #[test]
1077    fn pam_grayscale() {
1078        let pamdata = b"P7
1079WIDTH 4
1080HEIGHT 4
1081DEPTH 1
1082MAXVAL 255
1083TUPLTYPE GRAYSCALE
1084# Comment line
1085ENDHDR
1086\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1087        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1088        assert_eq!(decoder.color_type(), ColorType::L8);
1089        assert_eq!(decoder.dimensions(), (4, 4));
1090        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1091
1092        let mut image = vec![0; decoder.total_bytes() as usize];
1093        decoder.read_image(&mut image).unwrap();
1094        assert_eq!(
1095            image,
1096            vec![
1097                0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
1098                0xbe, 0xef
1099            ]
1100        );
1101        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1102            (
1103                _,
1104                PnmHeader {
1105                    decoded:
1106                        HeaderRecord::Arbitrary(ArbitraryHeader {
1107                            width: 4,
1108                            height: 4,
1109                            depth: 1,
1110                            maxval: 255,
1111                            tupltype: Some(ArbitraryTuplType::Grayscale),
1112                        }),
1113                    encoded: _,
1114                },
1115            ) => (),
1116            _ => panic!("Decoded header is incorrect"),
1117        }
1118    }
1119
1120    /// Tests reading of a valid grayscale_alpha pam
1121    #[test]
1122    fn pam_grayscale_alpha() {
1123        let pamdata = b"P7
1124HEIGHT 1
1125WIDTH 2
1126MAXVAL 65535
1127DEPTH 2
1128TUPLTYPE GRAYSCALE_ALPHA
1129# Comment line
1130ENDHDR
1131\xdc\xba\x32\x10\xdc\xba\x32\x10";
1132        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1133        assert_eq!(decoder.color_type(), ColorType::La16);
1134        assert_eq!(decoder.original_color_type(), ExtendedColorType::La16);
1135        assert_eq!(decoder.dimensions(), (2, 1));
1136        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1137
1138        let mut image = vec![0; decoder.total_bytes() as usize];
1139        decoder.read_image(&mut image).unwrap();
1140        assert_eq!(
1141            image,
1142            [
1143                u16::to_ne_bytes(0xdcba),
1144                u16::to_ne_bytes(0x3210),
1145                u16::to_ne_bytes(0xdcba),
1146                u16::to_ne_bytes(0x3210)
1147            ]
1148            .concat()
1149        );
1150        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1151            (
1152                _,
1153                PnmHeader {
1154                    decoded:
1155                        HeaderRecord::Arbitrary(ArbitraryHeader {
1156                            width: 2,
1157                            height: 1,
1158                            maxval: 65535,
1159                            depth: 2,
1160                            tupltype: Some(ArbitraryTuplType::GrayscaleAlpha),
1161                        }),
1162                    encoded: _,
1163                },
1164            ) => (),
1165            _ => panic!("Decoded header is incorrect"),
1166        }
1167    }
1168
1169    /// Tests reading of a valid rgb pam
1170    #[test]
1171    fn pam_rgb() {
1172        let pamdata = b"P7
1173# Comment line
1174MAXVAL 255
1175TUPLTYPE RGB
1176DEPTH 3
1177WIDTH 2
1178HEIGHT 2
1179ENDHDR
1180\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1181        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1182        assert_eq!(decoder.color_type(), ColorType::Rgb8);
1183        assert_eq!(decoder.dimensions(), (2, 2));
1184        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1185
1186        let mut image = vec![0; decoder.total_bytes() as usize];
1187        decoder.read_image(&mut image).unwrap();
1188        assert_eq!(
1189            image,
1190            vec![0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef]
1191        );
1192        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1193            (
1194                _,
1195                PnmHeader {
1196                    decoded:
1197                        HeaderRecord::Arbitrary(ArbitraryHeader {
1198                            maxval: 255,
1199                            tupltype: Some(ArbitraryTuplType::RGB),
1200                            depth: 3,
1201                            width: 2,
1202                            height: 2,
1203                        }),
1204                    encoded: _,
1205                },
1206            ) => (),
1207            _ => panic!("Decoded header is incorrect"),
1208        }
1209    }
1210
1211    /// Tests reading of a valid rgb_alpha pam
1212    #[test]
1213    fn pam_rgb_alpha() {
1214        let pamdata = b"P7
1215WIDTH 1
1216HEIGHT 3
1217DEPTH 4
1218MAXVAL 15
1219TUPLTYPE RGB_ALPHA
1220# Comment line
1221ENDHDR
1222\x00\x01\x02\x03\x0a\x0b\x0c\x0d\x05\x06\x07\x08";
1223        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1224        assert_eq!(decoder.color_type(), ColorType::Rgba8);
1225        assert_eq!(decoder.original_color_type(), ExtendedColorType::Rgba8);
1226        assert_eq!(decoder.dimensions(), (1, 3));
1227        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1228
1229        let mut image = vec![0; decoder.total_bytes() as usize];
1230        decoder.read_image(&mut image).unwrap();
1231        assert_eq!(image, b"\x00\x11\x22\x33\xaa\xbb\xcc\xdd\x55\x66\x77\x88",);
1232        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1233            (
1234                _,
1235                PnmHeader {
1236                    decoded:
1237                        HeaderRecord::Arbitrary(ArbitraryHeader {
1238                            width: 1,
1239                            height: 3,
1240                            maxval: 15,
1241                            depth: 4,
1242                            tupltype: Some(ArbitraryTuplType::RGBAlpha),
1243                        }),
1244                    encoded: _,
1245                },
1246            ) => (),
1247            _ => panic!("Decoded header is incorrect"),
1248        }
1249    }
1250
1251    #[test]
1252    fn pbm_binary() {
1253        // The data contains two rows of the image (each line is padded to the full byte). For
1254        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1255        let pbmbinary = [&b"P4 6 2\n"[..], &[0b0110_1100_u8, 0b1011_0111]].concat();
1256        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1257        assert_eq!(decoder.color_type(), ColorType::L8);
1258        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1259        assert_eq!(decoder.dimensions(), (6, 2));
1260        assert_eq!(
1261            decoder.subtype(),
1262            PnmSubtype::Bitmap(SampleEncoding::Binary)
1263        );
1264        let mut image = vec![0; decoder.total_bytes() as usize];
1265        decoder.read_image(&mut image).unwrap();
1266        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1267        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1268            (
1269                _,
1270                PnmHeader {
1271                    decoded:
1272                        HeaderRecord::Bitmap(BitmapHeader {
1273                            encoding: SampleEncoding::Binary,
1274                            width: 6,
1275                            height: 2,
1276                        }),
1277                    encoded: _,
1278                },
1279            ) => (),
1280            _ => panic!("Decoded header is incorrect"),
1281        }
1282    }
1283
1284    /// A previous infinite loop.
1285    #[test]
1286    fn pbm_binary_ascii_termination() {
1287        use std::io::{BufReader, Cursor, Error, ErrorKind, Read, Result};
1288        struct FailRead(Cursor<&'static [u8]>);
1289
1290        impl Read for FailRead {
1291            fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1292                match self.0.read(buf) {
1293                    Ok(n) if n > 0 => Ok(n),
1294                    _ => Err(Error::new(
1295                        ErrorKind::BrokenPipe,
1296                        "Simulated broken pipe error",
1297                    )),
1298                }
1299            }
1300        }
1301
1302        let pbmbinary = BufReader::new(FailRead(Cursor::new(b"P1 1 1\n")));
1303
1304        let decoder = PnmDecoder::new(pbmbinary).unwrap();
1305        let mut image = vec![0; decoder.total_bytes() as usize];
1306        decoder
1307            .read_image(&mut image)
1308            .expect_err("Image is malformed");
1309    }
1310
1311    #[test]
1312    fn pbm_ascii() {
1313        // The data contains two rows of the image (each line is padded to the full byte). For
1314        // comments on its format, see documentation of `impl SampleType for PbmBit`.  Tests all
1315        // whitespace characters that should be allowed (the 6 characters according to POSIX).
1316        let pbmbinary = b"P1 6 2\n 0 1 1 0 1 1\n1 0 1 1 0\t\n\x0b\x0c\r1";
1317        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1318        assert_eq!(decoder.color_type(), ColorType::L8);
1319        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1320        assert_eq!(decoder.dimensions(), (6, 2));
1321        assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1322
1323        let mut image = vec![0; decoder.total_bytes() as usize];
1324        decoder.read_image(&mut image).unwrap();
1325        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1326        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1327            (
1328                _,
1329                PnmHeader {
1330                    decoded:
1331                        HeaderRecord::Bitmap(BitmapHeader {
1332                            encoding: SampleEncoding::Ascii,
1333                            width: 6,
1334                            height: 2,
1335                        }),
1336                    encoded: _,
1337                },
1338            ) => (),
1339            _ => panic!("Decoded header is incorrect"),
1340        }
1341    }
1342
1343    #[test]
1344    fn pbm_ascii_nospace() {
1345        // The data contains two rows of the image (each line is padded to the full byte). Notably,
1346        // it is completely within specification for the ascii data not to contain separating
1347        // whitespace for the pbm format or any mix.
1348        let pbmbinary = b"P1 6 2\n011011101101";
1349        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1350        assert_eq!(decoder.color_type(), ColorType::L8);
1351        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1352        assert_eq!(decoder.dimensions(), (6, 2));
1353        assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1354
1355        let mut image = vec![0; decoder.total_bytes() as usize];
1356        decoder.read_image(&mut image).unwrap();
1357        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1358        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1359            (
1360                _,
1361                PnmHeader {
1362                    decoded:
1363                        HeaderRecord::Bitmap(BitmapHeader {
1364                            encoding: SampleEncoding::Ascii,
1365                            width: 6,
1366                            height: 2,
1367                        }),
1368                    encoded: _,
1369                },
1370            ) => (),
1371            _ => panic!("Decoded header is incorrect"),
1372        }
1373    }
1374
1375    #[test]
1376    fn pgm_binary() {
1377        // The data contains two rows of the image (each line is padded to the full byte). For
1378        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1379        let elements = (0..16).collect::<Vec<_>>();
1380        let pbmbinary = [&b"P5 4 4 255\n"[..], &elements].concat();
1381        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1382        assert_eq!(decoder.color_type(), ColorType::L8);
1383        assert_eq!(decoder.dimensions(), (4, 4));
1384        assert_eq!(
1385            decoder.subtype(),
1386            PnmSubtype::Graymap(SampleEncoding::Binary)
1387        );
1388        let mut image = vec![0; decoder.total_bytes() as usize];
1389        decoder.read_image(&mut image).unwrap();
1390        assert_eq!(image, elements);
1391        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1392            (
1393                _,
1394                PnmHeader {
1395                    decoded:
1396                        HeaderRecord::Graymap(GraymapHeader {
1397                            encoding: SampleEncoding::Binary,
1398                            width: 4,
1399                            height: 4,
1400                            maxwhite: 255,
1401                        }),
1402                    encoded: _,
1403                },
1404            ) => (),
1405            _ => panic!("Decoded header is incorrect"),
1406        }
1407    }
1408
1409    #[test]
1410    fn pgm_ascii() {
1411        // The data contains two rows of the image (each line is padded to the full byte). For
1412        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1413        let pbmbinary = b"P2 4 4 255\n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
1414        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1415        assert_eq!(decoder.color_type(), ColorType::L8);
1416        assert_eq!(decoder.dimensions(), (4, 4));
1417        assert_eq!(
1418            decoder.subtype(),
1419            PnmSubtype::Graymap(SampleEncoding::Ascii)
1420        );
1421        let mut image = vec![0; decoder.total_bytes() as usize];
1422        decoder.read_image(&mut image).unwrap();
1423        assert_eq!(image, (0..16).collect::<Vec<_>>());
1424        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1425            (
1426                _,
1427                PnmHeader {
1428                    decoded:
1429                        HeaderRecord::Graymap(GraymapHeader {
1430                            encoding: SampleEncoding::Ascii,
1431                            width: 4,
1432                            height: 4,
1433                            maxwhite: 255,
1434                        }),
1435                    encoded: _,
1436                },
1437            ) => (),
1438            _ => panic!("Decoded header is incorrect"),
1439        }
1440    }
1441
1442    #[test]
1443    fn ppm_ascii() {
1444        let ascii = b"P3 1 1 2000\n0 1000 2000";
1445        let decoder = PnmDecoder::new(&ascii[..]).unwrap();
1446        let mut image = vec![0; decoder.total_bytes() as usize];
1447        decoder.read_image(&mut image).unwrap();
1448        assert_eq!(
1449            image,
1450            [
1451                0_u16.to_ne_bytes(),
1452                (u16::MAX / 2 + 1).to_ne_bytes(),
1453                u16::MAX.to_ne_bytes()
1454            ]
1455            .into_iter()
1456            .flatten()
1457            .collect::<Vec<_>>()
1458        );
1459    }
1460
1461    #[test]
1462    fn dimension_overflow() {
1463        let pamdata = b"P7
1464# Comment line
1465MAXVAL 255
1466TUPLTYPE RGB
1467DEPTH 3
1468WIDTH 4294967295
1469HEIGHT 4294967295
1470ENDHDR
1471\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1472
1473        assert!(PnmDecoder::new(&pamdata[..]).is_err());
1474    }
1475
1476    #[test]
1477    fn issue_1508() {
1478        let _ = crate::load_from_memory(b"P391919 16999 1 1 9 919 16999 1 9999 999* 99999 N");
1479    }
1480
1481    #[test]
1482    fn issue_1616_overflow() {
1483        let data = [
1484            80, 54, 10, 52, 50, 57, 52, 56, 50, 57, 52, 56, 35, 56, 10, 52, 10, 48, 10, 12, 12, 56,
1485        ];
1486        // Validate: we have a header. Note: we might already calculate that this will fail but
1487        // then we could not return information about the header to the caller.
1488        let decoder = PnmDecoder::new(&data[..]).unwrap();
1489        let mut image = vec![0; decoder.total_bytes() as usize];
1490        let _ = decoder.read_image(&mut image);
1491    }
1492
1493    #[test]
1494    fn data_too_short() {
1495        let data = b"P3 16 16 1\n";
1496        let decoder = PnmDecoder::new(&data[..]).unwrap();
1497        let mut image = vec![0; decoder.total_bytes() as usize];
1498
1499        let _ = decoder.read_image(&mut image).unwrap_err();
1500    }
1501
1502    #[test]
1503    fn no_integers_with_plus() {
1504        let data = b"P3 +1 1 1\n";
1505        assert!(PnmDecoder::new(&data[..]).is_err());
1506    }
1507
1508    #[test]
1509    fn incomplete_pnm_header() {
1510        let data = b"P5 2 3 \n";
1511        assert!(PnmDecoder::new(&data[..]).is_err());
1512    }
1513
1514    #[test]
1515    fn leading_zeros() {
1516        let data = b"P2 03 00000000000002 00100\n011 22 033\n44 055 66\n";
1517        let decoder = PnmDecoder::new(&data[..]).unwrap();
1518        let mut image = vec![0; decoder.total_bytes() as usize];
1519        assert!(decoder.read_image(&mut image).is_ok());
1520    }
1521
1522    #[test]
1523    fn header_overflow() {
1524        let data = b"P1 4294967295 4294967297\n";
1525        assert!(PnmDecoder::new(&data[..]).is_err());
1526    }
1527
1528    #[test]
1529    fn header_large_dimension() {
1530        let data = b"P4 1 01234567890\n";
1531        let decoder = PnmDecoder::new(&data[..]).unwrap();
1532        assert!(decoder.dimensions() == (1, 1234567890));
1533    }
1534}