1use std::convert::TryInto;
8
9use bitflags::bitflags;
10
11use crate::binary::read::{ReadBinaryDep, ReadCtxt};
12use crate::binary::write::{WriteBinary, WriteContext};
13use crate::binary::{I16Be, U16Be, U32Be};
14use crate::error::{ParseError, WriteError};
15use crate::tables::Fixed;
16
17#[derive(Clone)]
21pub struct Os2 {
22 pub version: u16,
23 pub x_avg_char_width: i16,
24 pub us_weight_class: u16,
25 pub us_width_class: u16,
26 pub fs_type: u16,
27 pub y_subscript_x_size: i16,
28 pub y_subscript_y_size: i16,
29 pub y_subscript_x_offset: i16,
30 pub y_subscript_y_offset: i16,
31 pub y_superscript_x_size: i16,
32 pub y_superscript_y_size: i16,
33 pub y_superscript_x_offset: i16,
34 pub y_superscript_y_offset: i16,
35 pub y_strikeout_size: i16,
36 pub y_strikeout_position: i16,
37 pub s_family_class: i16,
38 pub panose: [u8; 10],
39 pub ul_unicode_range1: u32,
40 pub ul_unicode_range2: u32,
41 pub ul_unicode_range3: u32,
42 pub ul_unicode_range4: u32,
43 pub ach_vend_id: u32, pub fs_selection: FsSelection,
45 pub us_first_char_index: u16,
46 pub us_last_char_index: u16,
47 pub version0: Option<Version0>,
53 pub version1: Option<Version1>,
54 pub version2to4: Option<Version2to4>,
55 pub version5: Option<Version5>,
56}
57
58#[derive(Clone)]
59pub struct Version0 {
60 pub s_typo_ascender: i16,
61 pub s_typo_descender: i16,
62 pub s_typo_line_gap: i16,
63 pub us_win_ascent: u16,
64 pub us_win_descent: u16,
65}
66
67#[derive(Clone)]
68pub struct Version1 {
69 pub ul_code_page_range1: u32,
70 pub ul_code_page_range2: u32,
71}
72
73#[derive(Clone)]
74pub struct Version2to4 {
75 pub sx_height: i16,
76 pub s_cap_height: i16,
77 pub us_default_char: u16,
78 pub us_break_char: u16,
79 pub us_max_context: u16,
80}
81
82#[derive(Clone)]
83pub struct Version5 {
84 pub us_lower_optical_point_size: u16,
85 pub us_upper_optical_point_size: u16,
86}
87
88bitflags! {
89 pub struct FsSelection: u16 {
112 const ITALIC = 1 << 0;
113 const UNDERSCORE = 1 << 1;
114 const NEGATIVE = 1 << 2;
115 const OUTLINED = 1 << 3;
116 const STRIKEOUT = 1 << 4;
117 const BOLD = 1 << 5;
118 const REGULAR = 1 << 6;
119 const USE_TYPO_METRICS = 1 << 7;
120 const WWS = 1 << 8;
121 const OBLIQUE = 1 << 9;
122 }
124}
125
126impl Os2 {
127 pub(crate) fn value_to_width_class(value: Fixed) -> u16 {
129 const WIDTH_CLASS_MAP: &[(Fixed, u16)] = &[
130 (Fixed::from_raw(3276800), 1), (Fixed::from_raw(4096000), 2), (Fixed::from_raw(4915200), 3), (Fixed::from_raw(5734400), 4), (Fixed::from_raw(6553600), 5), (Fixed::from_raw(7372800), 6), (Fixed::from_raw(8192000), 7), (Fixed::from_raw(9830400), 8), (Fixed::from_raw(13107200), 9), ];
141
142 match WIDTH_CLASS_MAP.binary_search_by_key(&value, |&(val, _cls)| val) {
145 Ok(i) => WIDTH_CLASS_MAP[i].1,
146 Err(i) => {
147 if i < 1 {
149 return WIDTH_CLASS_MAP[0].1;
150 }
151 if i >= WIDTH_CLASS_MAP.len() {
152 return WIDTH_CLASS_MAP.last().unwrap().1;
153 }
154 let (a, clsa) = WIDTH_CLASS_MAP[i - 1];
155 let (b, clsb) = WIDTH_CLASS_MAP[i];
156 if (value - a) > (b - value) {
157 clsb
158 } else {
159 clsa
160 }
161 }
162 }
163 }
164}
165
166impl ReadBinaryDep for Os2 {
167 type HostType<'a> = Self;
168 type Args<'a> = usize;
169
170 fn read_dep<'a>(ctxt: &mut ReadCtxt<'a>, table_size: usize) -> Result<Self, ParseError> {
175 let version = ctxt.read::<U16Be>()?;
176 let x_avg_char_width = ctxt.read::<I16Be>()?;
177 let us_weight_class = ctxt.read::<U16Be>()?;
178 let us_width_class = ctxt.read::<U16Be>()?;
179 let fs_type = ctxt.read::<U16Be>()?;
180 let y_subscript_x_size = ctxt.read::<I16Be>()?;
181 let y_subscript_y_size = ctxt.read::<I16Be>()?;
182 let y_subscript_x_offset = ctxt.read::<I16Be>()?;
183 let y_subscript_y_offset = ctxt.read::<I16Be>()?;
184 let y_superscript_x_size = ctxt.read::<I16Be>()?;
185 let y_superscript_y_size = ctxt.read::<I16Be>()?;
186 let y_superscript_x_offset = ctxt.read::<I16Be>()?;
187 let y_superscript_y_offset = ctxt.read::<I16Be>()?;
188 let y_strikeout_size = ctxt.read::<I16Be>()?;
189 let y_strikeout_position = ctxt.read::<I16Be>()?;
190 let s_family_class = ctxt.read::<I16Be>()?;
191 let panose: [u8; 10] = ctxt.read_slice(10)?.try_into().unwrap();
193 let ul_unicode_range1 = ctxt.read::<U32Be>()?;
194 let ul_unicode_range2 = ctxt.read::<U32Be>()?;
195 let ul_unicode_range3 = ctxt.read::<U32Be>()?;
196 let ul_unicode_range4 = ctxt.read::<U32Be>()?;
197 let ach_vend_id = ctxt.read::<U32Be>()?;
198 let fs_selection = ctxt.read::<U16Be>().map(FsSelection::from_bits_truncate)?;
199 let us_first_char_index = ctxt.read::<U16Be>()?;
200 let us_last_char_index = ctxt.read::<U16Be>()?;
201
202 let version0 = if table_size >= 78 {
204 let s_typo_ascender = ctxt.read::<I16Be>()?;
205 let s_typo_descender = ctxt.read::<I16Be>()?;
206 let s_typo_line_gap = ctxt.read::<I16Be>()?;
207 let us_win_ascent = ctxt.read::<U16Be>()?;
208 let us_win_descent = ctxt.read::<U16Be>()?;
209 Some(Version0 {
210 s_typo_ascender,
211 s_typo_descender,
212 s_typo_line_gap,
213 us_win_ascent,
214 us_win_descent,
215 })
216 } else {
217 None
218 };
219
220 let version1 = if version >= 1 {
221 let ul_code_page_range1 = ctxt.read::<U32Be>()?;
222 let ul_code_page_range2 = ctxt.read::<U32Be>()?;
223 Some(Version1 {
224 ul_code_page_range1,
225 ul_code_page_range2,
226 })
227 } else {
228 None
229 };
230
231 let version2to4 = if version >= 2 {
232 let sx_height = ctxt.read::<I16Be>()?;
233 let s_cap_height = ctxt.read::<I16Be>()?;
234 let us_default_char = ctxt.read::<U16Be>()?;
235 let us_break_char = ctxt.read::<U16Be>()?;
236 let us_max_context = ctxt.read::<U16Be>()?;
237 Some(Version2to4 {
238 sx_height,
239 s_cap_height,
240 us_default_char,
241 us_break_char,
242 us_max_context,
243 })
244 } else {
245 None
246 };
247
248 let version5 = if version >= 5 {
249 let us_lower_optical_point_size = ctxt.read::<U16Be>()?;
250 let us_upper_optical_point_size = ctxt.read::<U16Be>()?;
251 Some(Version5 {
252 us_lower_optical_point_size,
253 us_upper_optical_point_size,
254 })
255 } else {
256 None
257 };
258
259 Ok(Os2 {
260 version,
261 x_avg_char_width,
262 us_weight_class,
263 us_width_class,
264 fs_type,
265 y_subscript_x_size,
266 y_subscript_y_size,
267 y_subscript_x_offset,
268 y_subscript_y_offset,
269 y_superscript_x_size,
270 y_superscript_y_size,
271 y_superscript_x_offset,
272 y_superscript_y_offset,
273 y_strikeout_size,
274 y_strikeout_position,
275 s_family_class,
276 panose,
277 ul_unicode_range1,
278 ul_unicode_range2,
279 ul_unicode_range3,
280 ul_unicode_range4,
281 ach_vend_id,
282 fs_selection,
283 us_first_char_index,
284 us_last_char_index,
285 version0,
286 version1,
287 version2to4,
288 version5,
289 })
290 }
291}
292
293impl WriteBinary<&Self> for Os2 {
294 type Output = ();
295
296 fn write<C: WriteContext>(ctxt: &mut C, table: &Self) -> Result<Self::Output, WriteError> {
297 let version = if table.version5.is_some() {
301 5_u16
302 } else if table.version2to4.is_some() {
303 4
304 } else if table.version1.is_some() {
305 1
306 } else {
307 0
308 };
309
310 U16Be::write(ctxt, version)?;
311 I16Be::write(ctxt, table.x_avg_char_width)?;
312 U16Be::write(ctxt, table.us_weight_class)?;
313 U16Be::write(ctxt, table.us_width_class)?;
314 U16Be::write(ctxt, table.fs_type)?;
315 I16Be::write(ctxt, table.y_subscript_x_size)?;
316 I16Be::write(ctxt, table.y_subscript_y_size)?;
317 I16Be::write(ctxt, table.y_subscript_x_offset)?;
318 I16Be::write(ctxt, table.y_subscript_y_offset)?;
319 I16Be::write(ctxt, table.y_superscript_x_size)?;
320 I16Be::write(ctxt, table.y_superscript_y_size)?;
321 I16Be::write(ctxt, table.y_superscript_x_offset)?;
322 I16Be::write(ctxt, table.y_superscript_y_offset)?;
323 I16Be::write(ctxt, table.y_strikeout_size)?;
324 I16Be::write(ctxt, table.y_strikeout_position)?;
325 I16Be::write(ctxt, table.s_family_class)?;
326 ctxt.write_bytes(&table.panose)?;
327 U32Be::write(ctxt, table.ul_unicode_range1)?;
328 U32Be::write(ctxt, table.ul_unicode_range2)?;
329 U32Be::write(ctxt, table.ul_unicode_range3)?;
330 U32Be::write(ctxt, table.ul_unicode_range4)?;
331 U32Be::write(ctxt, table.ach_vend_id)?;
332 U16Be::write(ctxt, table.fs_selection.bits())?;
333 U16Be::write(ctxt, table.us_first_char_index)?;
334 U16Be::write(ctxt, table.us_last_char_index)?;
335
336 if let Some(v0) = &table.version0 {
337 Version0::write(ctxt, v0)?;
338 }
339 if let Some(v1) = &table.version1 {
340 Version1::write(ctxt, v1)?;
341 }
342 if let Some(v2) = &table.version2to4 {
343 Version2to4::write(ctxt, v2)?;
344 }
345 if let Some(v5) = &table.version5 {
346 Version5::write(ctxt, v5)?;
347 }
348 Ok(())
349 }
350}
351
352impl WriteBinary<&Self> for Version0 {
353 type Output = ();
354
355 fn write<C: WriteContext>(ctxt: &mut C, table: &Self) -> Result<Self::Output, WriteError> {
356 I16Be::write(ctxt, table.s_typo_ascender)?;
357 I16Be::write(ctxt, table.s_typo_descender)?;
358 I16Be::write(ctxt, table.s_typo_line_gap)?;
359 U16Be::write(ctxt, table.us_win_ascent)?;
360 U16Be::write(ctxt, table.us_win_descent)?;
361 Ok(())
362 }
363}
364
365impl WriteBinary<&Self> for Version1 {
366 type Output = ();
367
368 fn write<C: WriteContext>(ctxt: &mut C, table: &Self) -> Result<Self::Output, WriteError> {
369 U32Be::write(ctxt, table.ul_code_page_range1)?;
370 U32Be::write(ctxt, table.ul_code_page_range2)?;
371 Ok(())
372 }
373}
374
375impl WriteBinary<&Self> for Version2to4 {
376 type Output = ();
377
378 fn write<C: WriteContext>(ctxt: &mut C, table: &Self) -> Result<Self::Output, WriteError> {
379 I16Be::write(ctxt, table.sx_height)?;
380 I16Be::write(ctxt, table.s_cap_height)?;
381 U16Be::write(ctxt, table.us_default_char)?;
382 U16Be::write(ctxt, table.us_break_char)?;
383 U16Be::write(ctxt, table.us_max_context)?;
384 Ok(())
385 }
386}
387
388impl WriteBinary<&Self> for Version5 {
389 type Output = ();
390
391 fn write<C: WriteContext>(ctxt: &mut C, table: &Self) -> Result<Self::Output, WriteError> {
392 U16Be::write(ctxt, table.us_lower_optical_point_size)?;
393 U16Be::write(ctxt, table.us_upper_optical_point_size)?;
394 Ok(())
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401
402 #[test]
403 #[cfg(feature = "prince")]
404 fn test_read() {
405 use crate::binary::read::ReadScope;
407 use crate::tables::{FontTableProvider, OpenTypeFont};
408 use crate::tag;
409 use crate::tests::read_fixture;
410
411 let buffer = read_fixture("../../../tests/data/fonts/HardGothicNormal.ttf");
412 let opentype_file = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
413 let provider = opentype_file.table_provider(0).unwrap();
414 let os_2_data = provider.read_table_data(tag::OS_2).unwrap();
415
416 let os_2 = ReadScope::new(&os_2_data)
417 .read_dep::<Os2>(os_2_data.len())
418 .expect("unable to parse OS/2 table");
419 assert_eq!(os_2.version, 1);
420 assert!(os_2.version0.is_some());
421 assert!(os_2.version1.is_some());
422 assert!(os_2.version2to4.is_none());
423 assert!(os_2.version5.is_none());
424 }
425
426 #[test]
427 #[cfg(feature = "prince")]
428 fn test_write() {
429 use crate::binary::read::ReadScope;
431 use crate::binary::write::WriteBuffer;
432 use crate::tables::{FontTableProvider, OpenTypeFont};
433 use crate::tag;
434 use crate::tests::read_fixture;
435
436 let buffer = read_fixture("../../../tests/data/fonts/HardGothicNormal.ttf");
437 let opentype_file = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
438 let provider = opentype_file.table_provider(0).unwrap();
439 let os_2_data = provider.read_table_data(tag::OS_2).unwrap();
440
441 let os_2 = ReadScope::new(&os_2_data)
442 .read_dep::<Os2>(os_2_data.len())
443 .expect("unable to parse OS/2 table");
444
445 let mut out = WriteBuffer::new();
446 Os2::write(&mut out, &os_2).unwrap();
447 let written = out.into_inner();
448 assert_eq!(written.as_slice(), &*os_2_data);
449 }
450
451 #[test]
452 fn map_weight_class() {
453 assert_eq!(Os2::value_to_width_class(Fixed::from(0.)), 1);
454 assert_eq!(Os2::value_to_width_class(Fixed::from(1.)), 1);
455 assert_eq!(Os2::value_to_width_class(Fixed::from(50.)), 1);
456 assert_eq!(Os2::value_to_width_class(Fixed::from(51.)), 1);
457 assert_eq!(Os2::value_to_width_class(Fixed::from(60.)), 2);
458 assert_eq!(Os2::value_to_width_class(Fixed::from(150.)), 8);
459 assert_eq!(Os2::value_to_width_class(Fixed::from(300.)), 9);
460 }
461}