Skip to main content

wuff/
lib.rs

1//! Pure Rust WOFF and WOFF2 decoder
2
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![allow(non_snake_case)]
5#![allow(non_upper_case_globals)]
6#![allow(unused_imports)]
7#![allow(dead_code)]
8#![allow(clippy::needless_range_loop)]
9#![allow(clippy::collapsible_if)]
10
11mod decompress_woff1;
12mod decompress_woff2;
13mod error;
14mod table_tags;
15mod variable_length;
16mod woff;
17
18use bytes::BufMut;
19pub use decompress_woff1::decompress_woff1_with_custom_z;
20pub use decompress_woff2::decompress_woff2_with_custom_brotli;
21pub use error::WuffErr;
22
23#[cfg(feature = "z")]
24#[cfg_attr(docsrs, doc(cfg(feature = "z")))]
25pub use decompress_woff1::decompress_woff1;
26
27#[cfg(feature = "brotli")]
28#[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
29pub use decompress_woff2::decompress_woff2;
30
31const HEAD: Tag = Tag::new(b"head");
32const HHEA: Tag = Tag::new(b"hhea");
33const HMTX: Tag = Tag::new(b"hmtx");
34const GLYF: Tag = Tag::new(b"glyf");
35const LOCA: Tag = Tag::new(b"loca");
36
37#[derive(Copy, Clone)]
38pub(crate) struct Point {
39    pub x: i32,
40    pub y: i32,
41    pub on_curve: bool,
42}
43
44// Round a value up to the nearest multiple of 4. Don't round the value in the
45// case that rounding up overflows.
46//
47// Implemented as a macro to make it generic over the type without horrible type bounds
48macro_rules! Round4 {
49    ($value:expr) => {
50        match $value.checked_add(3) {
51            Some(value_plus_3) => value_plus_3 & !3,
52            None => $value,
53        }
54    };
55}
56use Round4;
57use font_types::Tag;
58
59/// Compute checksum over size bytes of buf
60pub(crate) fn compute_checksum(buf: &[u8]) -> u32 {
61    let mut checksum: u32 = 0;
62    let mut iter = buf.chunks_exact(4);
63    for chunk in &mut iter {
64        let bytes: [u8; 4] = chunk.try_into().unwrap();
65        checksum = checksum.wrapping_add(u32::from_be_bytes(bytes));
66    }
67
68    // Treat sizes not aligned on 4 as if it were padded to 4 with 0's.
69    checksum = checksum.wrapping_add(match iter.remainder() {
70        &[a, b, c] => u32::from_be_bytes([a, b, c, 0]),
71        &[a, b] => u32::from_be_bytes([a, b, 0, 0]),
72        &[a] => u32::from_be_bytes([a, 0, 0, 0]),
73        [] => 0,
74        _ => unreachable!("chunk size was 4 so remainder will be a slice of length 3 or smaller"),
75    });
76
77    checksum
78}
79
80/// Writes an OpenType table directory
81///
82/// <https://learn.microsoft.com/en-us/typography/opentype/spec/otff#table-directory>
83fn write_table_directory_header(output: &mut impl BufMut, flavor: Tag, num_tables: u16) {
84    let mut max_pow2: u16 = 0;
85    while 1u32 << (max_pow2 + 1) <= (num_tables as u32) {
86        max_pow2 += 1;
87    }
88    let entry_selector = max_pow2;
89    let search_range: u16 = (1u16 << max_pow2) << 4;
90    let range_shift = (((num_tables as u32) << 4) - search_range as u32) as u16;
91
92    output.put_u32(u32::from_be_bytes(flavor.to_be_bytes())); // sfnt version
93    output.put_u16(num_tables); // num_tables
94    output.put_u16(search_range); // searchRange
95    output.put_u16(entry_selector); // entrySelector
96    output.put_u16(range_shift); // rangeShift
97}