| 1 | use crate::util::*; |
| 2 | use crate::{ImageResult, ImageSize}; |
| 3 | |
| 4 | use std::io::{self, BufRead, Seek, SeekFrom}; |
| 5 | |
| 6 | pub 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 | |
| 53 | pub 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 | } |
| 65 | |