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 | |