1use std::io::{Cursor, Read, Seek, SeekFrom};
8
9use byteorder::{LE, ReadBytesExt};
10
11use crate::core::*;
12
13#[derive(Clone)]
14#[repr(C)]
15pub struct Metadata {
16 pub total_size: u32,
17 pub version: EOTVersion,
18 pub flags: u32,
19 pub panose: [u8; 10],
20 pub charset: EOTCharset,
21 pub italic: bool,
22 pub weight: u32,
23 pub permissions: u16,
24 pub unicode_range: [u32; 4],
25 pub code_page_range: [u32; 2],
26 pub check_sum_adjustment: u32,
27 pub family_name: Vec<u16>,
28 pub style_name: Vec<u16>,
29 pub version_name: Vec<u16>,
30 pub full_name: Vec<u16>,
31 pub num_root_strings: ::core::ffi::c_uint,
32 pub font_data_size: u32,
33 pub font_data_offset: u32,
34 pub eudc_info: EUDCInfo,
35 pub do_not_use: Vec<u16>,
36}
37
38impl Metadata {
39 pub const ZERO: Metadata = Metadata {
40 total_size: 0,
41 version: 0 as EOTVersion,
42 flags: 0,
43 panose: [0; 10],
44 charset: ANSI_CHARSET,
45 italic: false,
46 weight: 0,
47 permissions: 0,
48 unicode_range: [0; 4],
49 code_page_range: [0; 2],
50 check_sum_adjustment: 0,
51 family_name: Vec::new(),
52 style_name: Vec::new(),
53 version_name: Vec::new(),
54 full_name: Vec::new(),
55 do_not_use: Vec::new(),
56 num_root_strings: 0,
57 font_data_size: 0,
58 font_data_offset: 0,
59 eudc_info: EUDCInfo {
60 exists: false,
61 code_page: 0,
62 flags: 0,
63 font_data: Vec::new(),
64 },
65 };
66}
67
68fn skip(c: &mut Cursor<&[u8]>, amount: u16) -> Result<(), Error> {
69 c.seek(SeekFrom::Current(amount as i64)).map_err(|_| Error::INSUFFICIENT_BYTES)?;
70 Ok(())
71}
72
73fn skip_padding(c: &mut Cursor<&[u8]>, amount: u16) -> Result<(), Error> {
74 for _ in 0..(amount as usize) {
75 if c.read_u8().map_err(|_| Error::INSUFFICIENT_BYTES)? != 0 {
76 return Err(Error::CORRUPT_FILE_PADDING_NOT_ZERO);
77 }
78 }
79 Ok(())
80}
81
82fn read_u32_le2(c: &mut Cursor<&[u8]>) -> Result<u32, Error> {
83 c.read_u32::<LE>().map_err(|_| Error::INSUFFICIENT_BYTES)
84}
85
86fn read_u16_le2(c: &mut Cursor<&[u8]>) -> Result<u16, Error> {
87 c.read_u16::<LE>().map_err(|_| Error::INSUFFICIENT_BYTES)
88}
89
90fn read_metadata_length(c: &mut Cursor<&[u8]>) -> Result<(u32, u32, u32), Error> {
92 let total_length = read_u32_le2(c)?;
93 let font_length = read_u32_le2(c)?;
94 if let Some(diff) = total_length.checked_sub(font_length) {
95 Ok((total_length, diff, font_length))
96 } else {
97 Err(Error::CORRUPT_FILE)
98 }
99}
100
101fn read_u16_array(c: &mut Cursor<&[u8]>) -> Result<Vec<u16>, Error> {
102 let size = read_u16_le2(c)? as usize;
103
104 if !size.is_multiple_of(2) {
105 return Err(Error::BOGUS_STRING_SIZE);
106 }
107
108 let mut buf = Vec::with_capacity(size / 2);
109 for _ in 0..size / 2 {
110 buf.push(read_u16_le2(c)?);
111 }
112
113 Ok(buf)
114}
115
116fn read_byte_array(c: &mut Cursor<&[u8]>) -> Result<Vec<u8>, Error> {
117 let size = read_u32_le2(c)? as usize;
118
119 let mut buf = Vec::with_capacity(size);
120 for _ in 0..size {
121 buf.push(c.read_u8().map_err(|_| Error::INSUFFICIENT_BYTES)?);
122 }
123
124 Ok(buf)
125}
126
127fn read_metadata_with_version(
128 c: &mut Cursor<&[u8]>, meta: &mut Metadata, version: EOTVersion,
129) -> Result<(), Error> {
130 meta.version = version;
131
132 meta.flags = read_u32_le2(c)?;
133 c.read_exact(&mut meta.panose).map_err(|_| Error::INSUFFICIENT_BYTES)?;
134 meta.charset = c.read_u8().map_err(|_| Error::INSUFFICIENT_BYTES)? as u32;
135 meta.italic = c.read_u8().map_err(|_| Error::INSUFFICIENT_BYTES)? != 0;
136 meta.weight = read_u32_le2(c)?;
137 meta.permissions = read_u16_le2(c)?;
138
139 if read_u16_le2(c)? != 0x504c {
140 return Err(Error::CORRUPT_FILE);
141 }
142
143 for i in 0..4 {
144 meta.unicode_range[i] = read_u32_le2(c)?;
145 }
146
147 for i in 0..2 {
148 meta.code_page_range[i] = read_u32_le2(c)?;
149 }
150
151 meta.check_sum_adjustment = read_u32_le2(c)?;
152 skip(c, 16)?; skip_padding(c, 2)?;
155 meta.family_name = read_u16_array(c)?;
156
157 skip_padding(c, 2)?;
158 meta.style_name = read_u16_array(c)?;
159
160 skip_padding(c, 2)?;
161 meta.version_name = read_u16_array(c)?;
162
163 skip_padding(c, 2)?;
164 meta.full_name = read_u16_array(c)?;
165
166 if meta.version > VERSION_1 {
167 skip_padding(c, 2)?;
168 meta.do_not_use = read_u16_array(c)?;
169
170 if meta.version == VERSION_3 {
171 skip(c, 4)?; meta.eudc_info.code_page = read_u32_le2(c)?;
173
174 skip_padding(c, 2)?;
175
176 let signature_size = read_u16_le2(c)?;
178 skip_padding(c, signature_size)?;
179
180 meta.eudc_info.flags = read_u32_le2(c)?;
181 meta.eudc_info.font_data = read_byte_array(c)?;
182 meta.eudc_info.exists = !meta.eudc_info.font_data.is_empty();
183 }
184 }
185
186 meta.font_data_offset = c.position() as u32;
189 let expected_header_size = meta.total_size.wrapping_sub(meta.font_data_size);
190 if meta.font_data_offset < expected_header_size {
191 return Err(Error::HEADER_TOO_BIG);
192 }
193
194 Ok(())
195}
196
197pub fn read_metadata(bytes: &[u8]) -> Result<Metadata, Error> {
198 let mut c = Cursor::new(bytes);
199 let (total_size, metadata_size, font_data_size) = read_metadata_length(&mut c)?;
200
201 if bytes.len() < metadata_size as usize {
202 return Err(Error::INSUFFICIENT_BYTES);
203 }
204
205 let coded_version = match read_u32_le2(&mut c)? {
206 0x00010000 => VERSION_1,
207 0x00020001 => VERSION_2,
208 0x00020002 => VERSION_3,
209 _ => return Err(Error::CORRUPT_FILE),
210 };
211
212 let mut try_version = coded_version;
213 let mut bumped_up = false;
214 let mut knocked_down = false;
215
216 loop {
217 let mut met = Metadata::ZERO;
218 met.total_size = total_size;
219 met.font_data_size = font_data_size;
220 let pos = c.position() as usize;
221
222 if bytes.len() < met.font_data_size as usize + pos {
223 return Err(Error::CORRUPT_FILE);
224 }
225
226 match read_metadata_with_version(&mut c, &mut met, try_version) {
227 Ok(()) =>
228 if try_version == coded_version {
229 return Ok(met);
230 } else {
231 return Err(Error::WARN_BAD_VERSION);
232 },
233 Err(Error::HEADER_TOO_BIG) => {
234 if knocked_down || try_version == VERSION_3 {
235 return Err(Error::CORRUPT_FILE);
236 }
237 knocked_down = false;
238 bumped_up = true;
239 try_version += 1;
240 }
241 Err(Error::INSUFFICIENT_BYTES) => {
242 if bumped_up || try_version == VERSION_1 {
243 return Err(Error::CORRUPT_FILE);
244 }
245 knocked_down = true;
246 bumped_up = false;
247 try_version -= 1;
248 }
249 Err(e) => return Err(e),
250 }
251 }
252}
253
254pub fn can_legally_edit(metadata: &Metadata) -> bool {
260 const EDITING_MASK: u16 = 0x8;
261 metadata.permissions == 0 || ((metadata.permissions & EDITING_MASK) != 0)
262}