1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use std::borrow::Cow;
use std::collections::BTreeMap;
use types::{Tag, TT_SFNT_VERSION};
include!("../generated/generated_font.rs");
const TABLE_RECORD_LEN: usize = 16;
#[derive(Debug, Clone, Default)]
pub struct FontBuilder<'a> {
tables: BTreeMap<Tag, Cow<'a, [u8]>>,
}
impl<'a> FontBuilder<'a> {
pub fn add_table(&mut self, tag: Tag, data: impl Into<Cow<'a, [u8]>>) -> &mut Self {
self.tables.insert(tag, data.into());
self
}
pub fn contains(&self, tag: Tag) -> bool {
self.tables.contains_key(&tag)
}
pub fn build(&mut self) -> Vec<u8> {
let header_len = std::mem::size_of::<u32>() + std::mem::size_of::<u16>() * 4 + self.tables.len() * TABLE_RECORD_LEN;
let mut position = header_len as u32;
let table_records = self
.tables
.iter_mut()
.map(|(tag, data)| {
let offset = position;
let length = data.len() as u32;
position += length;
let (checksum, padding) = checksum_and_padding(data);
position += padding;
TableRecord::new(*tag, checksum, offset, length)
})
.collect();
let directory = TableDirectory::new(TT_SFNT_VERSION, 0, 0, 0, table_records);
let mut writer = TableWriter::default();
directory.write_into(&mut writer);
let mut data = writer.into_data();
for table in self.tables.values() {
data.extend_from_slice(table);
let rem = table.len() % 4;
let padding = [0u8; 4];
data.extend_from_slice(&padding[..rem]);
}
data
}
}
fn checksum_and_padding(table: &[u8]) -> (u32, u32) {
let padding = table.len() % 4;
let mut sum = 0u32;
let mut iter = table.chunks_exact(4);
for quad in &mut iter {
let array: [u8; 4] = quad.try_into().unwrap_or_default();
sum = sum.wrapping_add(u32::from_be_bytes(array));
}
let rem = match *iter.remainder() {
[a] => u32::from_be_bytes([a, 0, 0, 0]),
[a, b] => u32::from_be_bytes([a, b, 0, 0]),
[a, b, c] => u32::from_be_bytes([a, b, c, 0]),
_ => 0,
};
(sum.wrapping_add(rem), padding as u32)
}
impl TTCHeader {
fn compute_version(&self) -> MajorMinor {
panic!("TTCHeader writing not supported (yet)")
}
}