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(ErrorDataSource),
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(src) => f.write_fmt(format_args!(
153                "Overflow when parsing integer in {src}"
154            ))
155        }
156    }
157}
158
159/// Note: should `pnm` be extracted into a separate crate,
160/// this will need to be hidden until that crate hits version `1.0`.
161impl From<DecoderError> for ImageError {
162    fn from(e: DecoderError) -> ImageError {
163        ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e))
164    }
165}
166
167impl error::Error for DecoderError {
168    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
169        match self {
170            DecoderError::UnparsableValue(_, _, err) => Some(err),
171            _ => None,
172        }
173    }
174}
175
176/// Single-value lines in a PNM header
177#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
178enum PnmHeaderLine {
179    /// "HEIGHT"
180    Height,
181    /// "WIDTH"
182    Width,
183    /// "DEPTH"
184    Depth,
185    /// "MAXVAL", a.k.a. `maxwhite`
186    Maxval,
187}
188
189impl Display for PnmHeaderLine {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        f.write_str(match self {
192            PnmHeaderLine::Height => "HEIGHT",
193            PnmHeaderLine::Width => "WIDTH",
194            PnmHeaderLine::Depth => "DEPTH",
195            PnmHeaderLine::Maxval => "MAXVAL",
196        })
197    }
198}
199
200/// Single-value lines in a PNM header
201#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
202enum ErrorDataSource {
203    /// One of the header lines
204    Line(PnmHeaderLine),
205    /// Value in the preamble
206    Preamble,
207    /// Sample/pixel data
208    Sample,
209}
210
211impl Display for ErrorDataSource {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        match self {
214            ErrorDataSource::Line(l) => l.fmt(f),
215            ErrorDataSource::Preamble => f.write_str("number in preamble"),
216            ErrorDataSource::Sample => f.write_str("sample"),
217        }
218    }
219}
220
221/// Dynamic representation, represents all decodable (sample, depth) combinations.
222#[derive(Clone, Copy)]
223enum TupleType {
224    PbmBit,
225    BWBit,
226    BWAlphaBit,
227    GrayU8,
228    GrayAlphaU8,
229    GrayU16,
230    GrayAlphaU16,
231    RGBU8,
232    RGBAlphaU8,
233    RGBU16,
234    RGBAlphaU16,
235}
236
237trait Sample {
238    type Representation;
239
240    /// Representation size in bytes
241    fn sample_size() -> u32 {
242        size_of::<Self::Representation>() as u32
243    }
244    fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
245        Ok((width * height * samples * Self::sample_size()) as usize)
246    }
247    fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> 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                let width = self.header.width();
657                let height = self.header.height();
658                let bytecount = S::bytelen(width, height, components)?;
659
660                let mut bytes = vec![];
661                self.reader.read_exact_vec(&mut bytes, bytecount)?;
662
663                let width: usize = width
664                    .try_into()
665                    .map_err(|_| DecoderError::Overflow(ErrorDataSource::Sample))?;
666                let components: usize = components
667                    .try_into()
668                    .map_err(|_| DecoderError::Overflow(ErrorDataSource::Sample))?;
669                let row_size = width
670                    .checked_mul(components)
671                    .ok_or(DecoderError::Overflow(ErrorDataSource::Sample))?;
672
673                S::from_bytes(&bytes, row_size, buf)?;
674            }
675            SampleEncoding::Ascii => {
676                self.read_ascii::<S>(buf)?;
677            }
678        }
679
680        // Scale samples if 8bit or 16bit is not saturated
681        let current_sample_max = self.header.maximal_sample();
682        let target_sample_max = 256_u32.pow(S::sample_size()) - 1;
683
684        if current_sample_max != target_sample_max {
685            let factor = target_sample_max as f32 / current_sample_max as f32;
686
687            if S::sample_size() == 1 {
688                for v in buf.iter_mut() {
689                    *v = (f32::from(*v) * factor).round() as u8;
690                }
691            } else if S::sample_size() == 2 {
692                for chunk in buf.chunks_exact_mut(2) {
693                    let v = NativeEndian::read_u16(chunk);
694                    NativeEndian::write_u16(chunk, (f32::from(v) * factor).round() as u16);
695                }
696            }
697        }
698
699        Ok(())
700    }
701
702    fn read_ascii<Basic: Sample>(&mut self, output_buf: &mut [u8]) -> ImageResult<()> {
703        Basic::from_ascii(&mut self.reader, output_buf)
704    }
705
706    /// Get the pnm subtype, depending on the magic constant contained in the header
707    pub fn subtype(&self) -> PnmSubtype {
708        self.header.subtype()
709    }
710}
711
712fn read_separated_ascii<T: TryFrom<u16>>(reader: &mut dyn Read) -> ImageResult<T> {
713    let is_separator = |v: &u8| matches!(*v, b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ');
714
715    let mut v: u16 = 0;
716    let mut had_any = false;
717    #[allow(clippy::unbuffered_bytes)]
718    for rc in reader
719        .bytes()
720        .skip_while(|v| v.as_ref().ok().is_some_and(is_separator))
721        .take_while(|v| v.as_ref().ok().is_some_and(|c| !is_separator(c)))
722    {
723        let c = rc?;
724        let digit = match c {
725            b'0'..=b'9' => u16::from(c - b'0'),
726            _ => return Err(DecoderError::InvalidDigit(ErrorDataSource::Sample).into()),
727        };
728        v = v
729            .checked_mul(10)
730            .ok_or(DecoderError::Overflow(ErrorDataSource::Sample))?;
731        v = v
732            .checked_add(digit)
733            .ok_or(DecoderError::Overflow(ErrorDataSource::Sample))?;
734        had_any = true;
735    }
736
737    if !had_any {
738        return Err(DecoderError::InputTooShort.into());
739    }
740
741    Ok(T::try_from(v).or(Err(DecoderError::Overflow(ErrorDataSource::Sample)))?)
742}
743
744impl Sample for U8 {
745    type Representation = u8;
746
747    fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
748        output_buf.copy_from_slice(bytes);
749        Ok(())
750    }
751
752    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
753        for b in output_buf {
754            *b = read_separated_ascii(reader)?;
755        }
756        Ok(())
757    }
758}
759
760impl Sample for U16 {
761    type Representation = u16;
762
763    fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
764        output_buf.copy_from_slice(bytes);
765        for chunk in output_buf.chunks_exact_mut(2) {
766            let v = BigEndian::read_u16(chunk);
767            NativeEndian::write_u16(chunk, v);
768        }
769        Ok(())
770    }
771
772    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
773        for chunk in output_buf.chunks_exact_mut(2) {
774            let v = read_separated_ascii::<u16>(reader)?;
775            NativeEndian::write_u16(chunk, v);
776        }
777        Ok(())
778    }
779}
780
781// The image is encoded in rows of bits, high order bits first. Any bits beyond the row bits should
782// be ignored. Also, contrary to rgb, black pixels are encoded as a 1 while white is 0. This will
783// need to be reversed for the grayscale output.
784impl Sample for PbmBit {
785    type Representation = u8;
786
787    fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
788        let count = width * samples;
789        let linelen = (count / 8) + u32::from((count % 8) != 0);
790        Ok((linelen * height) as usize)
791    }
792
793    fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
794        let mut expanded = utils::expand_bits(1, row_size.try_into().unwrap(), bytes);
795        for b in &mut expanded {
796            *b = !*b;
797        }
798        output_buf.copy_from_slice(&expanded);
799        Ok(())
800    }
801
802    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
803        #[allow(clippy::unbuffered_bytes)]
804        let mut bytes = reader.bytes();
805        for b in output_buf {
806            loop {
807                let byte = bytes
808                    .next()
809                    .ok_or_else::<ImageError, _>(|| DecoderError::InputTooShort.into())??;
810                match byte {
811                    b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' => continue,
812                    b'0' => *b = 255,
813                    b'1' => *b = 0,
814                    c => return Err(DecoderError::UnexpectedByteInRaster(c).into()),
815                }
816                break;
817            }
818        }
819
820        Ok(())
821    }
822}
823
824// Encoded just like a normal U8 but we check the values.
825impl Sample for BWBit {
826    type Representation = u8;
827
828    fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
829        U8::from_bytes(bytes, row_size, output_buf)?;
830        if let Some(val) = output_buf.iter().find(|&val| *val > 1) {
831            return Err(DecoderError::SampleOutOfBounds(*val).into());
832        }
833        Ok(())
834    }
835
836    fn from_ascii(_reader: &mut dyn Read, _output_buf: &mut [u8]) -> ImageResult<()> {
837        unreachable!("BW bits from anymaps are never encoded as ASCII")
838    }
839}
840
841impl DecodableImageHeader for BitmapHeader {
842    fn tuple_type(&self) -> ImageResult<TupleType> {
843        Ok(TupleType::PbmBit)
844    }
845}
846
847impl DecodableImageHeader for GraymapHeader {
848    fn tuple_type(&self) -> ImageResult<TupleType> {
849        match self.maxwhite {
850            0 => Err(DecoderError::MaxvalZero.into()),
851            v if v <= 0xFF => Ok(TupleType::GrayU8),
852            v if v <= 0xFFFF => Ok(TupleType::GrayU16),
853            _ => Err(DecoderError::MaxvalTooBig(self.maxwhite).into()),
854        }
855    }
856}
857
858impl DecodableImageHeader for PixmapHeader {
859    fn tuple_type(&self) -> ImageResult<TupleType> {
860        match self.maxval {
861            0 => Err(DecoderError::MaxvalZero.into()),
862            v if v <= 0xFF => Ok(TupleType::RGBU8),
863            v if v <= 0xFFFF => Ok(TupleType::RGBU16),
864            _ => Err(DecoderError::MaxvalTooBig(self.maxval).into()),
865        }
866    }
867}
868
869impl DecodableImageHeader for ArbitraryHeader {
870    fn tuple_type(&self) -> ImageResult<TupleType> {
871        match self.tupltype {
872            _ if self.maxval == 0 => Err(DecoderError::MaxvalZero.into()),
873            None if self.depth == 1 => Ok(TupleType::GrayU8),
874            None if self.depth == 2 => Ok(TupleType::GrayAlphaU8),
875            None if self.depth == 3 => Ok(TupleType::RGBU8),
876            None if self.depth == 4 => Ok(TupleType::RGBAlphaU8),
877
878            Some(ArbitraryTuplType::BlackAndWhite) if self.maxval == 1 && self.depth == 1 => {
879                Ok(TupleType::BWBit)
880            }
881            Some(ArbitraryTuplType::BlackAndWhite) => Err(DecoderError::InvalidDepthOrMaxval {
882                tuple_type: ArbitraryTuplType::BlackAndWhite,
883                maxval: self.maxval,
884                depth: self.depth,
885            }
886            .into()),
887
888            Some(ArbitraryTuplType::Grayscale) if self.depth == 1 && self.maxval <= 0xFF => {
889                Ok(TupleType::GrayU8)
890            }
891            Some(ArbitraryTuplType::Grayscale) if self.depth <= 1 && self.maxval <= 0xFFFF => {
892                Ok(TupleType::GrayU16)
893            }
894            Some(ArbitraryTuplType::Grayscale) => Err(DecoderError::InvalidDepthOrMaxval {
895                tuple_type: ArbitraryTuplType::Grayscale,
896                maxval: self.maxval,
897                depth: self.depth,
898            }
899            .into()),
900
901            Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFF => {
902                Ok(TupleType::RGBU8)
903            }
904            Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFFFF => {
905                Ok(TupleType::RGBU16)
906            }
907            Some(ArbitraryTuplType::RGB) => Err(DecoderError::InvalidDepth {
908                tuple_type: ArbitraryTuplType::RGB,
909                depth: self.depth,
910            }
911            .into()),
912
913            Some(ArbitraryTuplType::BlackAndWhiteAlpha) if self.depth == 2 && self.maxval == 1 => {
914                Ok(TupleType::BWAlphaBit)
915            }
916            Some(ArbitraryTuplType::BlackAndWhiteAlpha) => {
917                Err(DecoderError::InvalidDepthOrMaxval {
918                    tuple_type: ArbitraryTuplType::BlackAndWhiteAlpha,
919                    maxval: self.maxval,
920                    depth: self.depth,
921                }
922                .into())
923            }
924
925            Some(ArbitraryTuplType::GrayscaleAlpha) if self.depth == 2 && self.maxval <= 0xFF => {
926                Ok(TupleType::GrayAlphaU8)
927            }
928            Some(ArbitraryTuplType::GrayscaleAlpha) if self.depth == 2 && self.maxval <= 0xFFFF => {
929                Ok(TupleType::GrayAlphaU16)
930            }
931            Some(ArbitraryTuplType::GrayscaleAlpha) => Err(DecoderError::InvalidDepth {
932                tuple_type: ArbitraryTuplType::GrayscaleAlpha,
933                depth: self.depth,
934            }
935            .into()),
936
937            Some(ArbitraryTuplType::RGBAlpha) if self.depth == 4 && self.maxval <= 0xFF => {
938                Ok(TupleType::RGBAlphaU8)
939            }
940            Some(ArbitraryTuplType::RGBAlpha) if self.depth == 4 && self.maxval <= 0xFFFF => {
941                Ok(TupleType::RGBAlphaU16)
942            }
943            Some(ArbitraryTuplType::RGBAlpha) => Err(DecoderError::InvalidDepth {
944                tuple_type: ArbitraryTuplType::RGBAlpha,
945                depth: self.depth,
946            }
947            .into()),
948
949            Some(ArbitraryTuplType::Custom(ref custom)) => Err(ImageError::Unsupported(
950                UnsupportedError::from_format_and_kind(
951                    ImageFormat::Pnm.into(),
952                    UnsupportedErrorKind::GenericFeature(format!("Tuple type {custom:?}")),
953                ),
954            )),
955            None => Err(DecoderError::TupleTypeUnrecognised.into()),
956        }
957    }
958}
959
960#[cfg(test)]
961mod tests {
962    use super::*;
963    /// Tests reading of a valid blackandwhite pam
964    #[test]
965    fn pam_blackandwhite() {
966        let pamdata = b"P7
967WIDTH 4
968HEIGHT 4
969DEPTH 1
970MAXVAL 1
971TUPLTYPE BLACKANDWHITE
972# Comment line
973ENDHDR
974\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01";
975        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
976        assert_eq!(decoder.color_type(), ColorType::L8);
977        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
978        assert_eq!(decoder.dimensions(), (4, 4));
979        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
980
981        let mut image = vec![0; decoder.total_bytes() as usize];
982        decoder.read_image(&mut image).unwrap();
983        assert_eq!(
984            image,
985            vec![
986                0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00,
987                0x00, 0xFF
988            ]
989        );
990        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
991            (
992                _,
993                PnmHeader {
994                    decoded:
995                        HeaderRecord::Arbitrary(ArbitraryHeader {
996                            width: 4,
997                            height: 4,
998                            maxval: 1,
999                            depth: 1,
1000                            tupltype: Some(ArbitraryTuplType::BlackAndWhite),
1001                        }),
1002                    encoded: _,
1003                },
1004            ) => (),
1005            _ => panic!("Decoded header is incorrect"),
1006        }
1007    }
1008
1009    /// Tests reading of a valid blackandwhite_alpha pam
1010    #[test]
1011    fn pam_blackandwhite_alpha() {
1012        let pamdata = b"P7
1013WIDTH 2
1014HEIGHT 2
1015DEPTH 2
1016MAXVAL 1
1017TUPLTYPE BLACKANDWHITE_ALPHA
1018# Comment line
1019ENDHDR
1020\x01\x00\x00\x01\x01\x00\x00\x01";
1021        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1022        assert_eq!(decoder.color_type(), ColorType::La8);
1023        assert_eq!(decoder.original_color_type(), ExtendedColorType::La1);
1024        assert_eq!(decoder.dimensions(), (2, 2));
1025        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1026
1027        let mut image = vec![0; decoder.total_bytes() as usize];
1028        decoder.read_image(&mut image).unwrap();
1029        assert_eq!(image, vec![0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF,]);
1030        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1031            (
1032                _,
1033                PnmHeader {
1034                    decoded:
1035                        HeaderRecord::Arbitrary(ArbitraryHeader {
1036                            width: 2,
1037                            height: 2,
1038                            maxval: 1,
1039                            depth: 2,
1040                            tupltype: Some(ArbitraryTuplType::BlackAndWhiteAlpha),
1041                        }),
1042                    encoded: _,
1043                },
1044            ) => (),
1045            _ => panic!("Decoded header is incorrect"),
1046        }
1047    }
1048
1049    /// Tests reading of a valid grayscale pam
1050    #[test]
1051    fn pam_grayscale() {
1052        let pamdata = b"P7
1053WIDTH 4
1054HEIGHT 4
1055DEPTH 1
1056MAXVAL 255
1057TUPLTYPE GRAYSCALE
1058# Comment line
1059ENDHDR
1060\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1061        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1062        assert_eq!(decoder.color_type(), ColorType::L8);
1063        assert_eq!(decoder.dimensions(), (4, 4));
1064        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1065
1066        let mut image = vec![0; decoder.total_bytes() as usize];
1067        decoder.read_image(&mut image).unwrap();
1068        assert_eq!(
1069            image,
1070            vec![
1071                0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
1072                0xbe, 0xef
1073            ]
1074        );
1075        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1076            (
1077                _,
1078                PnmHeader {
1079                    decoded:
1080                        HeaderRecord::Arbitrary(ArbitraryHeader {
1081                            width: 4,
1082                            height: 4,
1083                            depth: 1,
1084                            maxval: 255,
1085                            tupltype: Some(ArbitraryTuplType::Grayscale),
1086                        }),
1087                    encoded: _,
1088                },
1089            ) => (),
1090            _ => panic!("Decoded header is incorrect"),
1091        }
1092    }
1093
1094    /// Tests reading of a valid grayscale_alpha pam
1095    #[test]
1096    fn pam_grayscale_alpha() {
1097        let pamdata = b"P7
1098HEIGHT 1
1099WIDTH 2
1100MAXVAL 65535
1101DEPTH 2
1102TUPLTYPE GRAYSCALE_ALPHA
1103# Comment line
1104ENDHDR
1105\xdc\xba\x32\x10\xdc\xba\x32\x10";
1106        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1107        assert_eq!(decoder.color_type(), ColorType::La16);
1108        assert_eq!(decoder.original_color_type(), ExtendedColorType::La16);
1109        assert_eq!(decoder.dimensions(), (2, 1));
1110        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1111
1112        let mut image = vec![0; decoder.total_bytes() as usize];
1113        decoder.read_image(&mut image).unwrap();
1114        assert_eq!(
1115            image,
1116            [
1117                u16::to_ne_bytes(0xdcba),
1118                u16::to_ne_bytes(0x3210),
1119                u16::to_ne_bytes(0xdcba),
1120                u16::to_ne_bytes(0x3210)
1121            ]
1122            .concat()
1123        );
1124        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1125            (
1126                _,
1127                PnmHeader {
1128                    decoded:
1129                        HeaderRecord::Arbitrary(ArbitraryHeader {
1130                            width: 2,
1131                            height: 1,
1132                            maxval: 65535,
1133                            depth: 2,
1134                            tupltype: Some(ArbitraryTuplType::GrayscaleAlpha),
1135                        }),
1136                    encoded: _,
1137                },
1138            ) => (),
1139            _ => panic!("Decoded header is incorrect"),
1140        }
1141    }
1142
1143    /// Tests reading of a valid rgb pam
1144    #[test]
1145    fn pam_rgb() {
1146        let pamdata = b"P7
1147# Comment line
1148MAXVAL 255
1149TUPLTYPE RGB
1150DEPTH 3
1151WIDTH 2
1152HEIGHT 2
1153ENDHDR
1154\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1155        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1156        assert_eq!(decoder.color_type(), ColorType::Rgb8);
1157        assert_eq!(decoder.dimensions(), (2, 2));
1158        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1159
1160        let mut image = vec![0; decoder.total_bytes() as usize];
1161        decoder.read_image(&mut image).unwrap();
1162        assert_eq!(
1163            image,
1164            vec![0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef]
1165        );
1166        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1167            (
1168                _,
1169                PnmHeader {
1170                    decoded:
1171                        HeaderRecord::Arbitrary(ArbitraryHeader {
1172                            maxval: 255,
1173                            tupltype: Some(ArbitraryTuplType::RGB),
1174                            depth: 3,
1175                            width: 2,
1176                            height: 2,
1177                        }),
1178                    encoded: _,
1179                },
1180            ) => (),
1181            _ => panic!("Decoded header is incorrect"),
1182        }
1183    }
1184
1185    /// Tests reading of a valid rgb_alpha pam
1186    #[test]
1187    fn pam_rgb_alpha() {
1188        let pamdata = b"P7
1189WIDTH 1
1190HEIGHT 3
1191DEPTH 4
1192MAXVAL 15
1193TUPLTYPE RGB_ALPHA
1194# Comment line
1195ENDHDR
1196\x00\x01\x02\x03\x0a\x0b\x0c\x0d\x05\x06\x07\x08";
1197        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1198        assert_eq!(decoder.color_type(), ColorType::Rgba8);
1199        assert_eq!(decoder.original_color_type(), ExtendedColorType::Rgba8);
1200        assert_eq!(decoder.dimensions(), (1, 3));
1201        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1202
1203        let mut image = vec![0; decoder.total_bytes() as usize];
1204        decoder.read_image(&mut image).unwrap();
1205        assert_eq!(image, b"\x00\x11\x22\x33\xaa\xbb\xcc\xdd\x55\x66\x77\x88",);
1206        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1207            (
1208                _,
1209                PnmHeader {
1210                    decoded:
1211                        HeaderRecord::Arbitrary(ArbitraryHeader {
1212                            width: 1,
1213                            height: 3,
1214                            maxval: 15,
1215                            depth: 4,
1216                            tupltype: Some(ArbitraryTuplType::RGBAlpha),
1217                        }),
1218                    encoded: _,
1219                },
1220            ) => (),
1221            _ => panic!("Decoded header is incorrect"),
1222        }
1223    }
1224
1225    #[test]
1226    fn pbm_binary() {
1227        // The data contains two rows of the image (each line is padded to the full byte). For
1228        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1229        let pbmbinary = [&b"P4 6 2\n"[..], &[0b0110_1100_u8, 0b1011_0111]].concat();
1230        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1231        assert_eq!(decoder.color_type(), ColorType::L8);
1232        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1233        assert_eq!(decoder.dimensions(), (6, 2));
1234        assert_eq!(
1235            decoder.subtype(),
1236            PnmSubtype::Bitmap(SampleEncoding::Binary)
1237        );
1238        let mut image = vec![0; decoder.total_bytes() as usize];
1239        decoder.read_image(&mut image).unwrap();
1240        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1241        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1242            (
1243                _,
1244                PnmHeader {
1245                    decoded:
1246                        HeaderRecord::Bitmap(BitmapHeader {
1247                            encoding: SampleEncoding::Binary,
1248                            width: 6,
1249                            height: 2,
1250                        }),
1251                    encoded: _,
1252                },
1253            ) => (),
1254            _ => panic!("Decoded header is incorrect"),
1255        }
1256    }
1257
1258    /// A previous infinite loop.
1259    #[test]
1260    fn pbm_binary_ascii_termination() {
1261        use std::io::{BufReader, Cursor, Error, ErrorKind, Read, Result};
1262        struct FailRead(Cursor<&'static [u8]>);
1263
1264        impl Read for FailRead {
1265            fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1266                match self.0.read(buf) {
1267                    Ok(n) if n > 0 => Ok(n),
1268                    _ => Err(Error::new(
1269                        ErrorKind::BrokenPipe,
1270                        "Simulated broken pipe error",
1271                    )),
1272                }
1273            }
1274        }
1275
1276        let pbmbinary = BufReader::new(FailRead(Cursor::new(b"P1 1 1\n")));
1277
1278        let decoder = PnmDecoder::new(pbmbinary).unwrap();
1279        let mut image = vec![0; decoder.total_bytes() as usize];
1280        decoder
1281            .read_image(&mut image)
1282            .expect_err("Image is malformed");
1283    }
1284
1285    #[test]
1286    fn pbm_ascii() {
1287        // The data contains two rows of the image (each line is padded to the full byte). For
1288        // comments on its format, see documentation of `impl SampleType for PbmBit`.  Tests all
1289        // whitespace characters that should be allowed (the 6 characters according to POSIX).
1290        let pbmbinary = b"P1 6 2\n 0 1 1 0 1 1\n1 0 1 1 0\t\n\x0b\x0c\r1";
1291        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1292        assert_eq!(decoder.color_type(), ColorType::L8);
1293        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1294        assert_eq!(decoder.dimensions(), (6, 2));
1295        assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1296
1297        let mut image = vec![0; decoder.total_bytes() as usize];
1298        decoder.read_image(&mut image).unwrap();
1299        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1300        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1301            (
1302                _,
1303                PnmHeader {
1304                    decoded:
1305                        HeaderRecord::Bitmap(BitmapHeader {
1306                            encoding: SampleEncoding::Ascii,
1307                            width: 6,
1308                            height: 2,
1309                        }),
1310                    encoded: _,
1311                },
1312            ) => (),
1313            _ => panic!("Decoded header is incorrect"),
1314        }
1315    }
1316
1317    #[test]
1318    fn pbm_ascii_nospace() {
1319        // The data contains two rows of the image (each line is padded to the full byte). Notably,
1320        // it is completely within specification for the ascii data not to contain separating
1321        // whitespace for the pbm format or any mix.
1322        let pbmbinary = b"P1 6 2\n011011101101";
1323        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1324        assert_eq!(decoder.color_type(), ColorType::L8);
1325        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1326        assert_eq!(decoder.dimensions(), (6, 2));
1327        assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1328
1329        let mut image = vec![0; decoder.total_bytes() as usize];
1330        decoder.read_image(&mut image).unwrap();
1331        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1332        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1333            (
1334                _,
1335                PnmHeader {
1336                    decoded:
1337                        HeaderRecord::Bitmap(BitmapHeader {
1338                            encoding: SampleEncoding::Ascii,
1339                            width: 6,
1340                            height: 2,
1341                        }),
1342                    encoded: _,
1343                },
1344            ) => (),
1345            _ => panic!("Decoded header is incorrect"),
1346        }
1347    }
1348
1349    #[test]
1350    fn pgm_binary() {
1351        // The data contains two rows of the image (each line is padded to the full byte). For
1352        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1353        let elements = (0..16).collect::<Vec<_>>();
1354        let pbmbinary = [&b"P5 4 4 255\n"[..], &elements].concat();
1355        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1356        assert_eq!(decoder.color_type(), ColorType::L8);
1357        assert_eq!(decoder.dimensions(), (4, 4));
1358        assert_eq!(
1359            decoder.subtype(),
1360            PnmSubtype::Graymap(SampleEncoding::Binary)
1361        );
1362        let mut image = vec![0; decoder.total_bytes() as usize];
1363        decoder.read_image(&mut image).unwrap();
1364        assert_eq!(image, elements);
1365        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1366            (
1367                _,
1368                PnmHeader {
1369                    decoded:
1370                        HeaderRecord::Graymap(GraymapHeader {
1371                            encoding: SampleEncoding::Binary,
1372                            width: 4,
1373                            height: 4,
1374                            maxwhite: 255,
1375                        }),
1376                    encoded: _,
1377                },
1378            ) => (),
1379            _ => panic!("Decoded header is incorrect"),
1380        }
1381    }
1382
1383    #[test]
1384    fn pgm_ascii() {
1385        // The data contains two rows of the image (each line is padded to the full byte). For
1386        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1387        let pbmbinary = b"P2 4 4 255\n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
1388        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1389        assert_eq!(decoder.color_type(), ColorType::L8);
1390        assert_eq!(decoder.dimensions(), (4, 4));
1391        assert_eq!(
1392            decoder.subtype(),
1393            PnmSubtype::Graymap(SampleEncoding::Ascii)
1394        );
1395        let mut image = vec![0; decoder.total_bytes() as usize];
1396        decoder.read_image(&mut image).unwrap();
1397        assert_eq!(image, (0..16).collect::<Vec<_>>());
1398        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1399            (
1400                _,
1401                PnmHeader {
1402                    decoded:
1403                        HeaderRecord::Graymap(GraymapHeader {
1404                            encoding: SampleEncoding::Ascii,
1405                            width: 4,
1406                            height: 4,
1407                            maxwhite: 255,
1408                        }),
1409                    encoded: _,
1410                },
1411            ) => (),
1412            _ => panic!("Decoded header is incorrect"),
1413        }
1414    }
1415
1416    #[test]
1417    fn ppm_ascii() {
1418        let ascii = b"P3 1 1 2000\n0 1000 2000";
1419        let decoder = PnmDecoder::new(&ascii[..]).unwrap();
1420        let mut image = vec![0; decoder.total_bytes() as usize];
1421        decoder.read_image(&mut image).unwrap();
1422        assert_eq!(
1423            image,
1424            [
1425                0_u16.to_ne_bytes(),
1426                (u16::MAX / 2 + 1).to_ne_bytes(),
1427                u16::MAX.to_ne_bytes()
1428            ]
1429            .into_iter()
1430            .flatten()
1431            .collect::<Vec<_>>()
1432        );
1433    }
1434
1435    #[test]
1436    fn dimension_overflow() {
1437        let pamdata = b"P7
1438# Comment line
1439MAXVAL 255
1440TUPLTYPE RGB
1441DEPTH 3
1442WIDTH 4294967295
1443HEIGHT 4294967295
1444ENDHDR
1445\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1446
1447        assert!(PnmDecoder::new(&pamdata[..]).is_err());
1448    }
1449
1450    #[test]
1451    fn issue_1508() {
1452        let _ = crate::load_from_memory(b"P391919 16999 1 1 9 919 16999 1 9999 999* 99999 N");
1453    }
1454
1455    #[test]
1456    fn issue_1616_overflow() {
1457        let data = [
1458            80, 54, 10, 52, 50, 57, 52, 56, 50, 57, 52, 56, 35, 56, 10, 52, 10, 48, 10, 12, 12, 56,
1459        ];
1460        // Validate: we have a header. Note: we might already calculate that this will fail but
1461        // then we could not return information about the header to the caller.
1462        let decoder = PnmDecoder::new(&data[..]).unwrap();
1463        let mut image = vec![0; decoder.total_bytes() as usize];
1464        let _ = decoder.read_image(&mut image);
1465    }
1466
1467    #[test]
1468    fn data_too_short() {
1469        let data = b"P3 16 16 1\n";
1470        let decoder = PnmDecoder::new(&data[..]).unwrap();
1471        let mut image = vec![0; decoder.total_bytes() as usize];
1472
1473        let _ = decoder.read_image(&mut image).unwrap_err();
1474    }
1475
1476    #[test]
1477    fn no_integers_with_plus() {
1478        let data = b"P3 +1 1 1\n";
1479        assert!(PnmDecoder::new(&data[..]).is_err());
1480    }
1481
1482    #[test]
1483    fn incomplete_pnm_header() {
1484        let data = b"P5 2 3 \n";
1485        assert!(PnmDecoder::new(&data[..]).is_err());
1486    }
1487
1488    #[test]
1489    fn leading_zeros() {
1490        let data = b"P2 03 00000000000002 00100\n011 22 033\n44 055 66\n";
1491        let decoder = PnmDecoder::new(&data[..]).unwrap();
1492        let mut image = vec![0; decoder.total_bytes() as usize];
1493        assert!(decoder.read_image(&mut image).is_ok());
1494    }
1495
1496    #[test]
1497    fn header_overflow() {
1498        let data = b"P1 4294967295 4294967297\n";
1499        assert!(PnmDecoder::new(&data[..]).is_err());
1500    }
1501
1502    #[test]
1503    fn header_large_dimension() {
1504        let data = b"P4 1 01234567890\n";
1505        let decoder = PnmDecoder::new(&data[..]).unwrap();
1506        assert!(decoder.dimensions() == (1, 1234567890));
1507    }
1508}