imagesize/formats/
pnm.rs

1use crate::util::*;
2use crate::{ImageResult, ImageSize};
3
4use std::io::{self, BufRead, Seek, SeekFrom};
5
6pub fn size<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> {
7    reader.seek(SeekFrom::Start(2))?;
8
9    // We try to loop until we find a line that does not start with a comment
10    // or is empty. After that, we should expect width and height back to back
11    // separated by an arbitrary amount of whitespace.
12    loop {
13        // Lines can be arbitrarily long, but 1k is a good enough cap I think.
14        // Anything higher and I blame whoever made the file.
15        let line = read_until_whitespace(reader, 1024)?;
16        let trimmed_line = line.trim();
17
18        // If it's a comment, skip until newline
19        if trimmed_line.starts_with('#') {
20            read_until_capped(reader, b'\n', 1024)?;
21            continue
22        }
23
24        // If it's just empty skip
25        if trimmed_line.is_empty() {
26            continue;
27        }
28
29        // The first thing we read that isn't empty or a comment should be the width
30        let raw_width = line;
31
32        // Read in the next non-whitespace section as the height
33        let line = read_until_whitespace(reader, 1024)?;
34        let raw_height = line.trim();
35
36        // Try to parse the width and height
37        let width_parsed = raw_width.parse::<usize>().ok();
38        let height_parsed = raw_height.parse::<usize>().ok();
39
40        // If successful return it
41        if let (Some(width), Some(height)) = (width_parsed, height_parsed) {
42            return Ok(ImageSize { width, height });
43        }
44
45        // If no successful then assume that it cannot be read
46        // If this happens we need to gather test files for those cases
47        break;
48    }
49
50    Err(io::Error::new(io::ErrorKind::InvalidData, "PNM dimensions not found").into())
51}
52
53pub fn matches(header: &[u8]) -> bool {
54    if header[0] != b'P' {
55        return false;
56    }
57
58    // We only support P1 to P6. Currently ignoring P7, PF, PFM
59    if header[1] < b'1' && header[1] > b'6' {
60        return false;
61    }
62
63    true
64}