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::image::{ImageDecoder, ImageFormat};
15use crate::utils;
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(format:ImageFormat::Pnm.into(), err: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: &ParseIntError) => 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(data: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: &PnmHeaderLine) => l.fmt(f),
213 ErrorDataSource::Preamble => f.write_str(data:"number in preamble"),
214 ErrorDataSource::Sample => f.write_str(data:"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 GrayU8,
225 GrayU16,
226 RGBU8,
227 RGBU16,
228}
229
230trait Sample {
231 type Representation;
232
233 /// Representation size in bytes
234 fn sample_size() -> u32 {
235 size_of::<Self::Representation>() as u32
236 }
237 fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
238 Ok((width * height * samples * Self::sample_size()) as usize)
239 }
240 fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()>;
241 fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()>;
242}
243
244struct U8;
245struct U16;
246struct PbmBit;
247struct BWBit;
248
249trait DecodableImageHeader {
250 fn tuple_type(&self) -> ImageResult<TupleType>;
251}
252
253/// PNM decoder
254pub struct PnmDecoder<R> {
255 reader: R,
256 header: PnmHeader,
257 tuple: TupleType,
258}
259
260impl<R: Read> PnmDecoder<R> {
261 /// Create a new decoder that decodes from the stream ```read```
262 pub fn new(mut buffered_read: R) -> ImageResult<PnmDecoder<R>> {
263 let magic = buffered_read.read_magic_constant()?;
264
265 let subtype = match magic {
266 [b'P', b'1'] => PnmSubtype::Bitmap(SampleEncoding::Ascii),
267 [b'P', b'2'] => PnmSubtype::Graymap(SampleEncoding::Ascii),
268 [b'P', b'3'] => PnmSubtype::Pixmap(SampleEncoding::Ascii),
269 [b'P', b'4'] => PnmSubtype::Bitmap(SampleEncoding::Binary),
270 [b'P', b'5'] => PnmSubtype::Graymap(SampleEncoding::Binary),
271 [b'P', b'6'] => PnmSubtype::Pixmap(SampleEncoding::Binary),
272 [b'P', b'7'] => PnmSubtype::ArbitraryMap,
273 _ => return Err(DecoderError::PnmMagicInvalid(magic).into()),
274 };
275
276 let decoder = match subtype {
277 PnmSubtype::Bitmap(enc) => PnmDecoder::read_bitmap_header(buffered_read, enc),
278 PnmSubtype::Graymap(enc) => PnmDecoder::read_graymap_header(buffered_read, enc),
279 PnmSubtype::Pixmap(enc) => PnmDecoder::read_pixmap_header(buffered_read, enc),
280 PnmSubtype::ArbitraryMap => PnmDecoder::read_arbitrary_header(buffered_read),
281 }?;
282
283 if utils::check_dimension_overflow(
284 decoder.dimensions().0,
285 decoder.dimensions().1,
286 decoder.color_type().bytes_per_pixel(),
287 ) {
288 return Err(ImageError::Unsupported(
289 UnsupportedError::from_format_and_kind(
290 ImageFormat::Pnm.into(),
291 UnsupportedErrorKind::GenericFeature(format!(
292 "Image dimensions ({}x{}) are too large",
293 decoder.dimensions().0,
294 decoder.dimensions().1
295 )),
296 ),
297 ));
298 }
299
300 Ok(decoder)
301 }
302
303 /// Extract the reader and header after an image has been read.
304 pub fn into_inner(self) -> (R, PnmHeader) {
305 (self.reader, self.header)
306 }
307
308 fn read_bitmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
309 let header = reader.read_bitmap_header(encoding)?;
310 Ok(PnmDecoder {
311 reader,
312 tuple: TupleType::PbmBit,
313 header: PnmHeader {
314 decoded: HeaderRecord::Bitmap(header),
315 encoded: None,
316 },
317 })
318 }
319
320 fn read_graymap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
321 let header = reader.read_graymap_header(encoding)?;
322 let tuple_type = header.tuple_type()?;
323 Ok(PnmDecoder {
324 reader,
325 tuple: tuple_type,
326 header: PnmHeader {
327 decoded: HeaderRecord::Graymap(header),
328 encoded: None,
329 },
330 })
331 }
332
333 fn read_pixmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
334 let header = reader.read_pixmap_header(encoding)?;
335 let tuple_type = header.tuple_type()?;
336 Ok(PnmDecoder {
337 reader,
338 tuple: tuple_type,
339 header: PnmHeader {
340 decoded: HeaderRecord::Pixmap(header),
341 encoded: None,
342 },
343 })
344 }
345
346 fn read_arbitrary_header(mut reader: R) -> ImageResult<PnmDecoder<R>> {
347 let header = reader.read_arbitrary_header()?;
348 let tuple_type = header.tuple_type()?;
349 Ok(PnmDecoder {
350 reader,
351 tuple: tuple_type,
352 header: PnmHeader {
353 decoded: HeaderRecord::Arbitrary(header),
354 encoded: None,
355 },
356 })
357 }
358}
359
360trait HeaderReader: Read {
361 /// Reads the two magic constant bytes
362 fn read_magic_constant(&mut self) -> ImageResult<[u8; 2]> {
363 let mut magic: [u8; 2] = [0, 0];
364 self.read_exact(&mut magic)?;
365 Ok(magic)
366 }
367
368 /// Reads a string as well as a single whitespace after it, ignoring comments
369 fn read_next_string(&mut self) -> ImageResult<String> {
370 let mut bytes = Vec::new();
371
372 // pair input bytes with a bool mask to remove comments
373 #[allow(clippy::unbuffered_bytes)]
374 let mark_comments = self.bytes().scan(true, |partof, read| {
375 let byte = match read {
376 Err(err) => return Some((*partof, Err(err))),
377 Ok(byte) => byte,
378 };
379 let cur_enabled = *partof && byte != b'#';
380 let next_enabled = cur_enabled || (byte == b'\r' || byte == b'\n');
381 *partof = next_enabled;
382 Some((cur_enabled, Ok(byte)))
383 });
384
385 for (_, byte) in mark_comments.filter(|e| e.0) {
386 match byte {
387 Ok(b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ') => {
388 if !bytes.is_empty() {
389 break; // We're done as we already have some content
390 }
391 }
392 Ok(byte) if !byte.is_ascii() => {
393 return Err(DecoderError::NonAsciiByteInHeader(byte).into())
394 }
395 Ok(byte) => {
396 bytes.push(byte);
397 }
398 Err(_) => break,
399 }
400 }
401
402 if bytes.is_empty() {
403 return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into()));
404 }
405
406 if !bytes.as_slice().is_ascii() {
407 // We have only filled the buffer with characters for which `byte.is_ascii()` holds.
408 unreachable!("Non-ASCII character should have returned sooner")
409 }
410
411 let string = String::from_utf8(bytes)
412 // We checked the precondition ourselves a few lines before, `bytes.as_slice().is_ascii()`.
413 .unwrap_or_else(|_| unreachable!("Only ASCII characters should be decoded"));
414
415 Ok(string)
416 }
417
418 fn read_next_line(&mut self) -> ImageResult<String> {
419 let mut buffer = Vec::new();
420 loop {
421 let mut byte = [0];
422 if self.read(&mut byte)? == 0 || byte[0] == b'\n' {
423 break;
424 }
425 buffer.push(byte[0]);
426 }
427
428 String::from_utf8(buffer)
429 .map_err(|e| ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e)))
430 }
431
432 fn read_next_u32(&mut self) -> ImageResult<u32> {
433 let s = self.read_next_string()?;
434 s.parse::<u32>()
435 .map_err(|err| DecoderError::UnparsableValue(ErrorDataSource::Preamble, s, err).into())
436 }
437
438 fn read_bitmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<BitmapHeader> {
439 let width = self.read_next_u32()?;
440 let height = self.read_next_u32()?;
441 Ok(BitmapHeader {
442 encoding,
443 height,
444 width,
445 })
446 }
447
448 fn read_graymap_header(&mut self, encoding: SampleEncoding) -> ImageResult<GraymapHeader> {
449 self.read_pixmap_header(encoding).map(
450 |PixmapHeader {
451 encoding,
452 width,
453 height,
454 maxval,
455 }| GraymapHeader {
456 encoding,
457 width,
458 height,
459 maxwhite: maxval,
460 },
461 )
462 }
463
464 fn read_pixmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<PixmapHeader> {
465 let width = self.read_next_u32()?;
466 let height = self.read_next_u32()?;
467 let maxval = self.read_next_u32()?;
468 Ok(PixmapHeader {
469 encoding,
470 height,
471 width,
472 maxval,
473 })
474 }
475
476 fn read_arbitrary_header(&mut self) -> ImageResult<ArbitraryHeader> {
477 fn parse_single_value_line(
478 line_val: &mut Option<u32>,
479 rest: &str,
480 line: PnmHeaderLine,
481 ) -> ImageResult<()> {
482 if line_val.is_some() {
483 Err(DecoderError::HeaderLineDuplicated(line).into())
484 } else {
485 let v = rest.trim().parse().map_err(|err| {
486 DecoderError::UnparsableValue(ErrorDataSource::Line(line), rest.to_owned(), err)
487 })?;
488 *line_val = Some(v);
489 Ok(())
490 }
491 }
492
493 #[allow(clippy::unbuffered_bytes)]
494 match self.bytes().next() {
495 None => return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into())),
496 Some(Err(io)) => return Err(ImageError::IoError(io)),
497 Some(Ok(b'\n')) => (),
498 Some(Ok(c)) => return Err(DecoderError::NotNewlineAfterP7Magic(c).into()),
499 }
500
501 let mut line;
502 let mut height: Option<u32> = None;
503 let mut width: Option<u32> = None;
504 let mut depth: Option<u32> = None;
505 let mut maxval: Option<u32> = None;
506 let mut tupltype: Option<String> = None;
507 loop {
508 line = self.read_next_line()?;
509 if line.is_empty() {
510 return Err(DecoderError::UnexpectedPnmHeaderEnd.into());
511 }
512 if line.as_bytes()[0] == b'#' {
513 continue;
514 }
515 if !line.is_ascii() {
516 return Err(DecoderError::NonAsciiLineInPamHeader.into());
517 }
518 #[allow(deprecated)]
519 let (identifier, rest) = line
520 .trim_left()
521 .split_at(line.find(char::is_whitespace).unwrap_or(line.len()));
522 match identifier {
523 "ENDHDR" => break,
524 "HEIGHT" => parse_single_value_line(&mut height, rest, PnmHeaderLine::Height)?,
525 "WIDTH" => parse_single_value_line(&mut width, rest, PnmHeaderLine::Width)?,
526 "DEPTH" => parse_single_value_line(&mut depth, rest, PnmHeaderLine::Depth)?,
527 "MAXVAL" => parse_single_value_line(&mut maxval, rest, PnmHeaderLine::Maxval)?,
528 "TUPLTYPE" => {
529 let identifier = rest.trim();
530 if tupltype.is_some() {
531 let appended = tupltype.take().map(|mut v| {
532 v.push(' ');
533 v.push_str(identifier);
534 v
535 });
536 tupltype = appended;
537 } else {
538 tupltype = Some(identifier.to_string());
539 }
540 }
541 _ => return Err(DecoderError::HeaderLineUnknown(identifier.to_string()).into()),
542 }
543 }
544
545 let (Some(h), Some(w), Some(d), Some(m)) = (height, width, depth, maxval) else {
546 return Err(DecoderError::HeaderLineMissing {
547 height,
548 width,
549 depth,
550 maxval,
551 }
552 .into());
553 };
554
555 let tupltype = match tupltype {
556 None => None,
557 Some(ref t) if t == "BLACKANDWHITE" => Some(ArbitraryTuplType::BlackAndWhite),
558 Some(ref t) if t == "BLACKANDWHITE_ALPHA" => {
559 Some(ArbitraryTuplType::BlackAndWhiteAlpha)
560 }
561 Some(ref t) if t == "GRAYSCALE" => Some(ArbitraryTuplType::Grayscale),
562 Some(ref t) if t == "GRAYSCALE_ALPHA" => Some(ArbitraryTuplType::GrayscaleAlpha),
563 Some(ref t) if t == "RGB" => Some(ArbitraryTuplType::RGB),
564 Some(ref t) if t == "RGB_ALPHA" => Some(ArbitraryTuplType::RGBAlpha),
565 Some(other) => Some(ArbitraryTuplType::Custom(other)),
566 };
567
568 Ok(ArbitraryHeader {
569 height: h,
570 width: w,
571 depth: d,
572 maxval: m,
573 tupltype,
574 })
575 }
576}
577
578impl<R> HeaderReader for R where R: Read {}
579
580impl<R: Read> ImageDecoder for PnmDecoder<R> {
581 fn dimensions(&self) -> (u32, u32) {
582 (self.header.width(), self.header.height())
583 }
584
585 fn color_type(&self) -> ColorType {
586 match self.tuple {
587 TupleType::PbmBit => ColorType::L8,
588 TupleType::BWBit => ColorType::L8,
589 TupleType::GrayU8 => ColorType::L8,
590 TupleType::GrayU16 => ColorType::L16,
591 TupleType::RGBU8 => ColorType::Rgb8,
592 TupleType::RGBU16 => ColorType::Rgb16,
593 }
594 }
595
596 fn original_color_type(&self) -> ExtendedColorType {
597 match self.tuple {
598 TupleType::PbmBit => ExtendedColorType::L1,
599 TupleType::BWBit => ExtendedColorType::L1,
600 TupleType::GrayU8 => ExtendedColorType::L8,
601 TupleType::GrayU16 => ExtendedColorType::L16,
602 TupleType::RGBU8 => ExtendedColorType::Rgb8,
603 TupleType::RGBU16 => ExtendedColorType::Rgb16,
604 }
605 }
606
607 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
608 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
609 match self.tuple {
610 TupleType::PbmBit => self.read_samples::<PbmBit>(1, buf),
611 TupleType::BWBit => self.read_samples::<BWBit>(1, buf),
612 TupleType::RGBU8 => self.read_samples::<U8>(3, buf),
613 TupleType::RGBU16 => self.read_samples::<U16>(3, buf),
614 TupleType::GrayU8 => self.read_samples::<U8>(1, buf),
615 TupleType::GrayU16 => self.read_samples::<U16>(1, buf),
616 }
617 }
618
619 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
620 (*self).read_image(buf)
621 }
622}
623
624impl<R: Read> PnmDecoder<R> {
625 fn read_samples<S: Sample>(&mut self, components: u32, buf: &mut [u8]) -> ImageResult<()> {
626 match self.subtype().sample_encoding() {
627 SampleEncoding::Binary => {
628 let width = self.header.width();
629 let height = self.header.height();
630 let bytecount = S::bytelen(width, height, components)?;
631
632 let mut bytes = vec![];
633 self.reader
634 .by_ref()
635 // This conversion is potentially lossy but unlikely and in that case we error
636 // later anyways.
637 .take(bytecount as u64)
638 .read_to_end(&mut bytes)?;
639 if bytes.len() != bytecount {
640 return Err(DecoderError::InputTooShort.into());
641 }
642
643 let width: usize = width.try_into().map_err(|_| DecoderError::Overflow)?;
644 let components: usize =
645 components.try_into().map_err(|_| DecoderError::Overflow)?;
646 let row_size = width
647 .checked_mul(components)
648 .ok_or(DecoderError::Overflow)?;
649
650 S::from_bytes(&bytes, row_size, buf)?;
651 }
652 SampleEncoding::Ascii => {
653 self.read_ascii::<S>(buf)?;
654 }
655 };
656
657 // Scale samples if 8bit or 16bit is not saturated
658 let current_sample_max = self.header.maximal_sample();
659 let target_sample_max = 256_u32.pow(S::sample_size()) - 1;
660
661 if current_sample_max != target_sample_max {
662 let factor = target_sample_max as f32 / current_sample_max as f32;
663
664 if S::sample_size() == 1 {
665 for v in buf.iter_mut() {
666 *v = (f32::from(*v) * factor).round() as u8;
667 }
668 } else if S::sample_size() == 2 {
669 for chunk in buf.chunks_exact_mut(2) {
670 let v = NativeEndian::read_u16(chunk);
671 NativeEndian::write_u16(chunk, (f32::from(v) * factor).round() as u16);
672 }
673 }
674 }
675
676 Ok(())
677 }
678
679 fn read_ascii<Basic: Sample>(&mut self, output_buf: &mut [u8]) -> ImageResult<()> {
680 Basic::from_ascii(&mut self.reader, output_buf)
681 }
682
683 /// Get the pnm subtype, depending on the magic constant contained in the header
684 pub fn subtype(&self) -> PnmSubtype {
685 self.header.subtype()
686 }
687}
688
689fn read_separated_ascii<T: TryFrom<u16>>(reader: &mut dyn Read) -> ImageResult<T> {
690 let is_separator: impl Fn(&u8) -> bool = |v: &u8| matches!(*v, b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ');
691
692 let mut v: u16 = 0;
693 #[allow(clippy::unbuffered_bytes)]
694 for rc: Result in readerimpl Iterator>
695 .bytes()
696 .skip_while(|v: &Result| v.as_ref().ok().is_some_and(is_separator))
697 .take_while(|v: &Result| v.as_ref().ok().is_some_and(|c: &u8| !is_separator(c)))
698 {
699 let c: u8 = rc?;
700 let digit: u16 = match c {
701 b'0'..=b'9' => (c - b'0') as u16,
702 _ => return Err(DecoderError::InvalidDigit(ErrorDataSource::Sample).into()),
703 };
704 v = v.checked_mul(10).ok_or(err:DecoderError::Overflow)?;
705 v = v.checked_add(digit).ok_or(err:DecoderError::Overflow)?;
706 }
707 Ok(T::try_from(v).or(res:Err(DecoderError::Overflow))?)
708}
709
710impl Sample for U8 {
711 type Representation = u8;
712
713 fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
714 output_buf.copy_from_slice(src:bytes);
715 Ok(())
716 }
717
718 fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
719 for b: &mut u8 in output_buf {
720 *b = read_separated_ascii(reader)?;
721 }
722 Ok(())
723 }
724}
725
726impl Sample for U16 {
727 type Representation = u16;
728
729 fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
730 output_buf.copy_from_slice(src:bytes);
731 for chunk: &mut [u8] in output_buf.chunks_exact_mut(chunk_size:2) {
732 let v: u16 = BigEndian::read_u16(buf:chunk);
733 NativeEndian::write_u16(buf:chunk, n:v);
734 }
735 Ok(())
736 }
737
738 fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
739 for chunk: &mut [u8] in output_buf.chunks_exact_mut(chunk_size:2) {
740 let v: u16 = read_separated_ascii::<u16>(reader)?;
741 NativeEndian::write_u16(buf:chunk, n:v);
742 }
743 Ok(())
744 }
745}
746
747// The image is encoded in rows of bits, high order bits first. Any bits beyond the row bits should
748// be ignored. Also, contrary to rgb, black pixels are encoded as a 1 while white is 0. This will
749// need to be reversed for the grayscale output.
750impl Sample for PbmBit {
751 type Representation = u8;
752
753 fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
754 let count = width * samples;
755 let linelen = (count / 8) + u32::from((count % 8) != 0);
756 Ok((linelen * height) as usize)
757 }
758
759 fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
760 let mut expanded = utils::expand_bits(1, row_size.try_into().unwrap(), bytes);
761 for b in &mut expanded {
762 *b = !*b;
763 }
764 output_buf.copy_from_slice(&expanded);
765 Ok(())
766 }
767
768 fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
769 #[allow(clippy::unbuffered_bytes)]
770 let mut bytes = reader.bytes();
771 for b in output_buf {
772 loop {
773 let byte = bytes
774 .next()
775 .ok_or_else::<ImageError, _>(|| DecoderError::InputTooShort.into())??;
776 match byte {
777 b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' => continue,
778 b'0' => *b = 255,
779 b'1' => *b = 0,
780 c => return Err(DecoderError::UnexpectedByteInRaster(c).into()),
781 }
782 break;
783 }
784 }
785
786 Ok(())
787 }
788}
789
790// Encoded just like a normal U8 but we check the values.
791impl Sample for BWBit {
792 type Representation = u8;
793
794 fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
795 U8::from_bytes(bytes, row_size, output_buf)?;
796 if let Some(val: &u8) = output_buf.iter().find(|&val: &u8| *val > 1) {
797 return Err(DecoderError::SampleOutOfBounds(*val).into());
798 }
799 Ok(())
800 }
801
802 fn from_ascii(_reader: &mut dyn Read, _output_buf: &mut [u8]) -> ImageResult<()> {
803 unreachable!("BW bits from anymaps are never encoded as ASCII")
804 }
805}
806
807impl DecodableImageHeader for BitmapHeader {
808 fn tuple_type(&self) -> ImageResult<TupleType> {
809 Ok(TupleType::PbmBit)
810 }
811}
812
813impl DecodableImageHeader for GraymapHeader {
814 fn tuple_type(&self) -> ImageResult<TupleType> {
815 match self.maxwhite {
816 0 => Err(DecoderError::MaxvalZero.into()),
817 v: u32 if v <= 0xFF => Ok(TupleType::GrayU8),
818 v: u32 if v <= 0xFFFF => Ok(TupleType::GrayU16),
819 _ => Err(DecoderError::MaxvalTooBig(self.maxwhite).into()),
820 }
821 }
822}
823
824impl DecodableImageHeader for PixmapHeader {
825 fn tuple_type(&self) -> ImageResult<TupleType> {
826 match self.maxval {
827 0 => Err(DecoderError::MaxvalZero.into()),
828 v: u32 if v <= 0xFF => Ok(TupleType::RGBU8),
829 v: u32 if v <= 0xFFFF => Ok(TupleType::RGBU16),
830 _ => Err(DecoderError::MaxvalTooBig(self.maxval).into()),
831 }
832 }
833}
834
835impl DecodableImageHeader for ArbitraryHeader {
836 fn tuple_type(&self) -> ImageResult<TupleType> {
837 match self.tupltype {
838 _ if self.maxval == 0 => Err(DecoderError::MaxvalZero.into()),
839 None if self.depth == 1 => Ok(TupleType::GrayU8),
840 None if self.depth == 2 => Err(ImageError::Unsupported(
841 UnsupportedError::from_format_and_kind(
842 ImageFormat::Pnm.into(),
843 UnsupportedErrorKind::Color(ExtendedColorType::La8),
844 ),
845 )),
846 None if self.depth == 3 => Ok(TupleType::RGBU8),
847 None if self.depth == 4 => Err(ImageError::Unsupported(
848 UnsupportedError::from_format_and_kind(
849 ImageFormat::Pnm.into(),
850 UnsupportedErrorKind::Color(ExtendedColorType::Rgba8),
851 ),
852 )),
853
854 Some(ArbitraryTuplType::BlackAndWhite) if self.maxval == 1 && self.depth == 1 => {
855 Ok(TupleType::BWBit)
856 }
857 Some(ArbitraryTuplType::BlackAndWhite) => Err(DecoderError::InvalidDepthOrMaxval {
858 tuple_type: ArbitraryTuplType::BlackAndWhite,
859 maxval: self.maxval,
860 depth: self.depth,
861 }
862 .into()),
863
864 Some(ArbitraryTuplType::Grayscale) if self.depth == 1 && self.maxval <= 0xFF => {
865 Ok(TupleType::GrayU8)
866 }
867 Some(ArbitraryTuplType::Grayscale) if self.depth <= 1 && self.maxval <= 0xFFFF => {
868 Ok(TupleType::GrayU16)
869 }
870 Some(ArbitraryTuplType::Grayscale) => Err(DecoderError::InvalidDepthOrMaxval {
871 tuple_type: ArbitraryTuplType::Grayscale,
872 maxval: self.maxval,
873 depth: self.depth,
874 }
875 .into()),
876
877 Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFF => {
878 Ok(TupleType::RGBU8)
879 }
880 Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFFFF => {
881 Ok(TupleType::RGBU16)
882 }
883 Some(ArbitraryTuplType::RGB) => Err(DecoderError::InvalidDepth {
884 tuple_type: ArbitraryTuplType::RGB,
885 depth: self.depth,
886 }
887 .into()),
888
889 Some(ArbitraryTuplType::BlackAndWhiteAlpha) => Err(ImageError::Unsupported(
890 UnsupportedError::from_format_and_kind(
891 ImageFormat::Pnm.into(),
892 UnsupportedErrorKind::GenericFeature(format!(
893 "Color type {}",
894 ArbitraryTuplType::BlackAndWhiteAlpha.name()
895 )),
896 ),
897 )),
898 Some(ArbitraryTuplType::GrayscaleAlpha) => Err(ImageError::Unsupported(
899 UnsupportedError::from_format_and_kind(
900 ImageFormat::Pnm.into(),
901 UnsupportedErrorKind::Color(ExtendedColorType::La8),
902 ),
903 )),
904 Some(ArbitraryTuplType::RGBAlpha) => Err(ImageError::Unsupported(
905 UnsupportedError::from_format_and_kind(
906 ImageFormat::Pnm.into(),
907 UnsupportedErrorKind::Color(ExtendedColorType::Rgba8),
908 ),
909 )),
910 Some(ArbitraryTuplType::Custom(ref custom)) => Err(ImageError::Unsupported(
911 UnsupportedError::from_format_and_kind(
912 ImageFormat::Pnm.into(),
913 UnsupportedErrorKind::GenericFeature(format!("Tuple type {custom:?}")),
914 ),
915 )),
916 None => Err(DecoderError::TupleTypeUnrecognised.into()),
917 }
918 }
919}
920
921#[cfg(test)]
922mod tests {
923 use super::*;
924 /// Tests reading of a valid blackandwhite pam
925 #[test]
926 fn pam_blackandwhite() {
927 let pamdata = b"P7
928WIDTH 4
929HEIGHT 4
930DEPTH 1
931MAXVAL 1
932TUPLTYPE BLACKANDWHITE
933# Comment line
934ENDHDR
935\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01";
936 let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
937 assert_eq!(decoder.color_type(), ColorType::L8);
938 assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
939 assert_eq!(decoder.dimensions(), (4, 4));
940 assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
941
942 let mut image = vec![0; decoder.total_bytes() as usize];
943 decoder.read_image(&mut image).unwrap();
944 assert_eq!(
945 image,
946 vec![
947 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00,
948 0x00, 0xFF
949 ]
950 );
951 match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
952 (
953 _,
954 PnmHeader {
955 decoded:
956 HeaderRecord::Arbitrary(ArbitraryHeader {
957 width: 4,
958 height: 4,
959 maxval: 1,
960 depth: 1,
961 tupltype: Some(ArbitraryTuplType::BlackAndWhite),
962 }),
963 encoded: _,
964 },
965 ) => (),
966 _ => panic!("Decoded header is incorrect"),
967 }
968 }
969
970 /// Tests reading of a valid grayscale pam
971 #[test]
972 fn pam_grayscale() {
973 let pamdata = b"P7
974WIDTH 4
975HEIGHT 4
976DEPTH 1
977MAXVAL 255
978TUPLTYPE GRAYSCALE
979# Comment line
980ENDHDR
981\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
982 let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
983 assert_eq!(decoder.color_type(), ColorType::L8);
984 assert_eq!(decoder.dimensions(), (4, 4));
985 assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
986
987 let mut image = vec![0; decoder.total_bytes() as usize];
988 decoder.read_image(&mut image).unwrap();
989 assert_eq!(
990 image,
991 vec![
992 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
993 0xbe, 0xef
994 ]
995 );
996 match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
997 (
998 _,
999 PnmHeader {
1000 decoded:
1001 HeaderRecord::Arbitrary(ArbitraryHeader {
1002 width: 4,
1003 height: 4,
1004 depth: 1,
1005 maxval: 255,
1006 tupltype: Some(ArbitraryTuplType::Grayscale),
1007 }),
1008 encoded: _,
1009 },
1010 ) => (),
1011 _ => panic!("Decoded header is incorrect"),
1012 }
1013 }
1014
1015 /// Tests reading of a valid rgb pam
1016 #[test]
1017 fn pam_rgb() {
1018 let pamdata = b"P7
1019# Comment line
1020MAXVAL 255
1021TUPLTYPE RGB
1022DEPTH 3
1023WIDTH 2
1024HEIGHT 2
1025ENDHDR
1026\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1027 let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1028 assert_eq!(decoder.color_type(), ColorType::Rgb8);
1029 assert_eq!(decoder.dimensions(), (2, 2));
1030 assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1031
1032 let mut image = vec![0; decoder.total_bytes() as usize];
1033 decoder.read_image(&mut image).unwrap();
1034 assert_eq!(
1035 image,
1036 vec![0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef]
1037 );
1038 match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1039 (
1040 _,
1041 PnmHeader {
1042 decoded:
1043 HeaderRecord::Arbitrary(ArbitraryHeader {
1044 maxval: 255,
1045 tupltype: Some(ArbitraryTuplType::RGB),
1046 depth: 3,
1047 width: 2,
1048 height: 2,
1049 }),
1050 encoded: _,
1051 },
1052 ) => (),
1053 _ => panic!("Decoded header is incorrect"),
1054 }
1055 }
1056
1057 #[test]
1058 fn pbm_binary() {
1059 // The data contains two rows of the image (each line is padded to the full byte). For
1060 // comments on its format, see documentation of `impl SampleType for PbmBit`.
1061 let pbmbinary = [&b"P4 6 2\n"[..], &[0b0110_1100_u8, 0b1011_0111]].concat();
1062 let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1063 assert_eq!(decoder.color_type(), ColorType::L8);
1064 assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1065 assert_eq!(decoder.dimensions(), (6, 2));
1066 assert_eq!(
1067 decoder.subtype(),
1068 PnmSubtype::Bitmap(SampleEncoding::Binary)
1069 );
1070 let mut image = vec![0; decoder.total_bytes() as usize];
1071 decoder.read_image(&mut image).unwrap();
1072 assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1073 match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1074 (
1075 _,
1076 PnmHeader {
1077 decoded:
1078 HeaderRecord::Bitmap(BitmapHeader {
1079 encoding: SampleEncoding::Binary,
1080 width: 6,
1081 height: 2,
1082 }),
1083 encoded: _,
1084 },
1085 ) => (),
1086 _ => panic!("Decoded header is incorrect"),
1087 }
1088 }
1089
1090 /// A previous infinite loop.
1091 #[test]
1092 fn pbm_binary_ascii_termination() {
1093 use std::io::{BufReader, Cursor, Error, ErrorKind, Read, Result};
1094 struct FailRead(Cursor<&'static [u8]>);
1095
1096 impl Read for FailRead {
1097 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1098 match self.0.read(buf) {
1099 Ok(n) if n > 0 => Ok(n),
1100 _ => Err(Error::new(
1101 ErrorKind::BrokenPipe,
1102 "Simulated broken pipe error",
1103 )),
1104 }
1105 }
1106 }
1107
1108 let pbmbinary = BufReader::new(FailRead(Cursor::new(b"P1 1 1\n")));
1109
1110 let decoder = PnmDecoder::new(pbmbinary).unwrap();
1111 let mut image = vec![0; decoder.total_bytes() as usize];
1112 decoder
1113 .read_image(&mut image)
1114 .expect_err("Image is malformed");
1115 }
1116
1117 #[test]
1118 fn pbm_ascii() {
1119 // The data contains two rows of the image (each line is padded to the full byte). For
1120 // comments on its format, see documentation of `impl SampleType for PbmBit`. Tests all
1121 // whitespace characters that should be allowed (the 6 characters according to POSIX).
1122 let pbmbinary = b"P1 6 2\n 0 1 1 0 1 1\n1 0 1 1 0\t\n\x0b\x0c\r1";
1123 let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1124 assert_eq!(decoder.color_type(), ColorType::L8);
1125 assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1126 assert_eq!(decoder.dimensions(), (6, 2));
1127 assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1128
1129 let mut image = vec![0; decoder.total_bytes() as usize];
1130 decoder.read_image(&mut image).unwrap();
1131 assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1132 match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1133 (
1134 _,
1135 PnmHeader {
1136 decoded:
1137 HeaderRecord::Bitmap(BitmapHeader {
1138 encoding: SampleEncoding::Ascii,
1139 width: 6,
1140 height: 2,
1141 }),
1142 encoded: _,
1143 },
1144 ) => (),
1145 _ => panic!("Decoded header is incorrect"),
1146 }
1147 }
1148
1149 #[test]
1150 fn pbm_ascii_nospace() {
1151 // The data contains two rows of the image (each line is padded to the full byte). Notably,
1152 // it is completely within specification for the ascii data not to contain separating
1153 // whitespace for the pbm format or any mix.
1154 let pbmbinary = b"P1 6 2\n011011101101";
1155 let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1156 assert_eq!(decoder.color_type(), ColorType::L8);
1157 assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1158 assert_eq!(decoder.dimensions(), (6, 2));
1159 assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1160
1161 let mut image = vec![0; decoder.total_bytes() as usize];
1162 decoder.read_image(&mut image).unwrap();
1163 assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1164 match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1165 (
1166 _,
1167 PnmHeader {
1168 decoded:
1169 HeaderRecord::Bitmap(BitmapHeader {
1170 encoding: SampleEncoding::Ascii,
1171 width: 6,
1172 height: 2,
1173 }),
1174 encoded: _,
1175 },
1176 ) => (),
1177 _ => panic!("Decoded header is incorrect"),
1178 }
1179 }
1180
1181 #[test]
1182 fn pgm_binary() {
1183 // The data contains two rows of the image (each line is padded to the full byte). For
1184 // comments on its format, see documentation of `impl SampleType for PbmBit`.
1185 let elements = (0..16).collect::<Vec<_>>();
1186 let pbmbinary = [&b"P5 4 4 255\n"[..], &elements].concat();
1187 let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1188 assert_eq!(decoder.color_type(), ColorType::L8);
1189 assert_eq!(decoder.dimensions(), (4, 4));
1190 assert_eq!(
1191 decoder.subtype(),
1192 PnmSubtype::Graymap(SampleEncoding::Binary)
1193 );
1194 let mut image = vec![0; decoder.total_bytes() as usize];
1195 decoder.read_image(&mut image).unwrap();
1196 assert_eq!(image, elements);
1197 match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1198 (
1199 _,
1200 PnmHeader {
1201 decoded:
1202 HeaderRecord::Graymap(GraymapHeader {
1203 encoding: SampleEncoding::Binary,
1204 width: 4,
1205 height: 4,
1206 maxwhite: 255,
1207 }),
1208 encoded: _,
1209 },
1210 ) => (),
1211 _ => panic!("Decoded header is incorrect"),
1212 }
1213 }
1214
1215 #[test]
1216 fn pgm_ascii() {
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"P2 4 4 255\n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
1220 let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1221 assert_eq!(decoder.color_type(), ColorType::L8);
1222 assert_eq!(decoder.dimensions(), (4, 4));
1223 assert_eq!(
1224 decoder.subtype(),
1225 PnmSubtype::Graymap(SampleEncoding::Ascii)
1226 );
1227 let mut image = vec![0; decoder.total_bytes() as usize];
1228 decoder.read_image(&mut image).unwrap();
1229 assert_eq!(image, (0..16).collect::<Vec<_>>());
1230 match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1231 (
1232 _,
1233 PnmHeader {
1234 decoded:
1235 HeaderRecord::Graymap(GraymapHeader {
1236 encoding: SampleEncoding::Ascii,
1237 width: 4,
1238 height: 4,
1239 maxwhite: 255,
1240 }),
1241 encoded: _,
1242 },
1243 ) => (),
1244 _ => panic!("Decoded header is incorrect"),
1245 }
1246 }
1247
1248 #[test]
1249 fn ppm_ascii() {
1250 let ascii = b"P3 1 1 2000\n0 1000 2000";
1251 let decoder = PnmDecoder::new(&ascii[..]).unwrap();
1252 let mut image = vec![0; decoder.total_bytes() as usize];
1253 decoder.read_image(&mut image).unwrap();
1254 assert_eq!(
1255 image,
1256 [
1257 0_u16.to_ne_bytes(),
1258 (u16::MAX / 2 + 1).to_ne_bytes(),
1259 u16::MAX.to_ne_bytes()
1260 ]
1261 .into_iter()
1262 .flatten()
1263 .collect::<Vec<_>>()
1264 );
1265 }
1266
1267 #[test]
1268 fn dimension_overflow() {
1269 let pamdata = b"P7
1270# Comment line
1271MAXVAL 255
1272TUPLTYPE RGB
1273DEPTH 3
1274WIDTH 4294967295
1275HEIGHT 4294967295
1276ENDHDR
1277\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1278
1279 assert!(PnmDecoder::new(&pamdata[..]).is_err());
1280 }
1281
1282 #[test]
1283 fn issue_1508() {
1284 let _ = crate::load_from_memory(b"P391919 16999 1 1 9 919 16999 1 9999 999* 99999 N");
1285 }
1286
1287 #[test]
1288 fn issue_1616_overflow() {
1289 let data = [
1290 80, 54, 10, 52, 50, 57, 52, 56, 50, 57, 52, 56, 35, 56, 10, 52, 10, 48, 10, 12, 12, 56,
1291 ];
1292 // Validate: we have a header. Note: we might already calculate that this will fail but
1293 // then we could not return information about the header to the caller.
1294 let decoder = PnmDecoder::new(&data[..]).unwrap();
1295 let mut image = vec![0; decoder.total_bytes() as usize];
1296 let _ = decoder.read_image(&mut image);
1297 }
1298}
1299