/src/fontations/skera/src/post.rs
Line | Count | Source |
1 | | //! impl subset() for post |
2 | | use crate::fnv::FnvHashMap; |
3 | | use crate::{ |
4 | | serialize::{SerializeErrorFlags, Serializer}, |
5 | | Plan, Subset, SubsetError, SubsetFlags, |
6 | | }; |
7 | | use write_fonts::{ |
8 | | read::{ |
9 | | tables::post::{Post, DEFAULT_GLYPH_NAMES}, |
10 | | FontRef, MinByteRange, TopLevelTable, |
11 | | }, |
12 | | types::{BigEndian, Version16Dot16}, |
13 | | FontBuilder, |
14 | | }; |
15 | | |
16 | | // reference: subset() for post in harfbuzz |
17 | | // https://github.com/harfbuzz/harfbuzz/blob/a070f9ebbe88dc71b248af9731dd49ec93f4e6e6/src/hb-ot-post-table.hh#L96 |
18 | | impl Subset for Post<'_> { |
19 | 0 | fn subset( |
20 | 0 | &self, |
21 | 0 | plan: &Plan, |
22 | 0 | _font: &FontRef, |
23 | 0 | s: &mut Serializer, |
24 | 0 | _builder: &mut FontBuilder, |
25 | 0 | ) -> Result<(), SubsetError> { |
26 | | // copy header |
27 | 0 | s.embed_bytes(self.min_table_bytes()) |
28 | 0 | .map_err(|_| SubsetError::SubsetTableError(Post::TAG))?; |
29 | | |
30 | 0 | let glyph_names = plan |
31 | 0 | .subset_flags |
32 | 0 | .contains(SubsetFlags::SUBSET_FLAGS_GLYPH_NAMES); |
33 | | //version 3 does not have any glyph names |
34 | 0 | if !glyph_names { |
35 | 0 | s.copy_assign(self.version_byte_range().start, Version16Dot16::VERSION_3_0); |
36 | 0 | } |
37 | | |
38 | 0 | if glyph_names && self.version() == Version16Dot16::VERSION_2_0 { |
39 | 0 | subset_post_v2tail(self, plan, s) |
40 | 0 | .map_err(|_| SubsetError::SubsetTableError(Post::TAG))?; |
41 | 0 | } |
42 | 0 | Ok(()) |
43 | 0 | } |
44 | | } |
45 | | |
46 | 0 | fn subset_post_v2tail( |
47 | 0 | post: &Post, |
48 | 0 | plan: &Plan, |
49 | 0 | s: &mut Serializer, |
50 | 0 | ) -> Result<(), SerializeErrorFlags> { |
51 | | // handle empty V2tail |
52 | 0 | let Some(glyph_name_indices) = post.glyph_name_index() else { |
53 | 0 | s.embed(0_u16)?; |
54 | 0 | return Ok(()); |
55 | | }; |
56 | | |
57 | | //copy numGlyphs |
58 | 0 | let num_output_glyphs = plan.num_output_glyphs; |
59 | 0 | s.embed(plan.num_output_glyphs as u16)?; |
60 | | |
61 | | // init all glyphNameIndex as 0, which refers to name .notdef |
62 | | // this handles retain-gid holes, so we don't need to loop all retained glyphs |
63 | 0 | let idx_start = s.allocate_size(num_output_glyphs * 2, false)?; |
64 | | |
65 | 0 | let string_data_byte_range = post.string_data_byte_range(); |
66 | 0 | let (string_data, index_to_offset) = if string_data_byte_range.is_empty() { |
67 | 0 | (None, Vec::new()) |
68 | | } else { |
69 | 0 | let str_data = post |
70 | 0 | .offset_data() |
71 | 0 | .as_bytes() |
72 | 0 | .get(string_data_byte_range.start..) |
73 | 0 | .ok_or_else(|| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?; |
74 | 0 | ( |
75 | 0 | Some(str_data), |
76 | 0 | index_to_offset(glyph_name_indices, num_output_glyphs, str_data), |
77 | 0 | ) |
78 | | }; |
79 | | |
80 | 0 | let default_name_idx_map: FnvHashMap<&[u8], u16> = if string_data_byte_range.is_empty() { |
81 | 0 | FnvHashMap::default() |
82 | | } else { |
83 | 0 | DEFAULT_GLYPH_NAMES |
84 | 0 | .iter() |
85 | 0 | .enumerate() |
86 | 0 | .map(|(i, s)| (s.as_bytes(), i as u16)) |
87 | 0 | .collect() |
88 | | }; |
89 | | |
90 | 0 | let mut new_name_idx = 258_u16; |
91 | 0 | let mut old_to_new_idx_map = FnvHashMap::default(); |
92 | 0 | let mut name_bytes_to_new_idx_map = FnvHashMap::default(); |
93 | | |
94 | 0 | for (new_gid, old_gid) in plan |
95 | 0 | .new_to_old_gid_list |
96 | 0 | .iter() |
97 | 0 | .map(|(new, old)| (new.to_u32() as usize, old.to_u32() as usize)) |
98 | | { |
99 | 0 | let old_name_idx = glyph_name_indices |
100 | 0 | .get(old_gid) |
101 | 0 | .ok_or_else(|| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_OTHER))? |
102 | 0 | .get(); |
103 | | |
104 | 0 | let idx_pos = idx_start + new_gid * 2; |
105 | 0 | if old_name_idx < 258 { |
106 | 0 | s.copy_assign(idx_pos, old_name_idx); |
107 | 0 | } else if let Some(idx) = old_to_new_idx_map.get(&old_name_idx) { |
108 | 0 | s.copy_assign(idx_pos, *idx); |
109 | 0 | } else { |
110 | 0 | let Some(string_data) = string_data else { |
111 | 0 | return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR)); |
112 | | }; |
113 | | |
114 | 0 | let name_bytes = find_glyph_name(old_name_idx as usize, string_data, &index_to_offset) |
115 | 0 | .ok_or_else(|| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?; |
116 | | |
117 | 0 | if let Some(idx) = name_bytes_to_new_idx_map.get(name_bytes) { |
118 | 0 | s.copy_assign(idx_pos, *idx); |
119 | 0 | old_to_new_idx_map.insert(old_name_idx, *idx); |
120 | 0 | } else { |
121 | 0 | let name_str = name_bytes |
122 | 0 | .get(1..) |
123 | 0 | .ok_or_else(|| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?; |
124 | | // check if duplicate with DEFAULT names first |
125 | 0 | if let Some(default_idx) = default_name_idx_map.get(name_str) { |
126 | 0 | s.copy_assign(idx_pos, *default_idx); |
127 | 0 | name_bytes_to_new_idx_map.insert(name_bytes, *default_idx); |
128 | 0 | old_to_new_idx_map.insert(old_name_idx, *default_idx); |
129 | 0 | } else { |
130 | 0 | s.copy_assign(idx_pos, new_name_idx); |
131 | 0 | name_bytes_to_new_idx_map.insert(name_bytes, new_name_idx); |
132 | 0 | old_to_new_idx_map.insert(old_name_idx, new_name_idx); |
133 | 0 | new_name_idx += 1; |
134 | | |
135 | | // copy name bytes |
136 | 0 | s.embed_bytes(name_bytes)?; |
137 | | } |
138 | | } |
139 | | } |
140 | | } |
141 | 0 | Ok(()) |
142 | 0 | } |
143 | | |
144 | 0 | fn find_glyph_name<'a>( |
145 | 0 | idx: usize, |
146 | 0 | string_data: &'a [u8], |
147 | 0 | index_to_offset: &[usize], |
148 | 0 | ) -> Option<&'a [u8]> { |
149 | 0 | let offset = index_to_offset.get(idx - 258)?; |
150 | 0 | let len = string_data.get(*offset)?; |
151 | 0 | if *len == 0 { |
152 | 0 | return None; |
153 | 0 | } |
154 | 0 | let start = *offset; |
155 | 0 | let end = start + *len as usize + 1; |
156 | 0 | string_data.get(start..end) |
157 | 0 | } |
158 | | |
159 | | // get() in PString is slow, we need to precompute offset into string_data for each index |
160 | | // why it's slow, ref: <https://github.com/googlefonts/fontations/blob/7c9d875992c42d0bda6d0c7f807c25222e863490/read-fonts/src/array.rs#L140> |
161 | 0 | fn index_to_offset( |
162 | 0 | glyph_name_indices: &[BigEndian<u16>], |
163 | 0 | num_output_glyphs: usize, |
164 | 0 | string_data: &[u8], |
165 | 0 | ) -> Vec<usize> { |
166 | 0 | let mut index_to_offset = Vec::with_capacity(glyph_name_indices.len().min(num_output_glyphs)); |
167 | 0 | let total_len = string_data.len(); |
168 | 0 | let mut pos = 0; |
169 | | |
170 | 0 | while pos < total_len && index_to_offset.len() < 65535 { |
171 | 0 | let cur_len = string_data[pos] as usize; |
172 | 0 | if pos + cur_len >= total_len { |
173 | 0 | break; |
174 | 0 | } |
175 | | |
176 | 0 | index_to_offset.push(pos); |
177 | 0 | pos += 1 + cur_len; |
178 | | } |
179 | | |
180 | 0 | index_to_offset |
181 | 0 | } |