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