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