1use std::{collections::BTreeMap, convert::TryFrom};
4
5use types::{FixedSize, GlyphId16, Offset16};
6
7use crate::{
8 tables::{
9 layout::{builders::Builder, CoverageTable},
10 variations::ivs_builder::VariationStoreBuilder,
11 },
12 FontWrite,
13};
14
15#[derive(Clone, Debug)]
19struct TableSplitter<T: SplitTable> {
20 finished: Vec<T>,
21 current_coverage: Vec<GlyphId16>,
22 current_items: Vec<T::Component>,
23 current_size: usize,
24}
25
26trait SplitTable {
28 type Component;
30
31 fn size_for_item(item: &Self::Component) -> usize;
39 fn initial_size_for_item(item: &Self::Component) -> usize;
44 fn instantiate(coverage: CoverageTable, items: Vec<Self::Component>) -> Self;
45}
46
47impl<T: SplitTable + FontWrite> TableSplitter<T> {
48 const MAX_TABLE_SIZE: usize = u16::MAX as usize;
49
50 fn new() -> Self {
51 Self {
52 finished: Vec::new(),
53 current_coverage: Vec::new(),
54 current_items: Vec::new(),
55 current_size: 0,
56 }
57 }
58
59 fn add(&mut self, gid: GlyphId16, item: T::Component) {
60 let item_size = T::size_for_item(&item);
61 if item_size + self.current_size > Self::MAX_TABLE_SIZE {
62 let current_len = self.current_coverage.len();
63 self.finish_current();
64 let type_ = self.finished.last().unwrap().table_type();
65 log::info!("adding split in {type_} at {current_len}");
66 }
67
68 if self.current_size == 0 {
69 self.current_size = T::initial_size_for_item(&item);
70 }
71 self.current_coverage.push(gid);
72 self.current_items.push(item);
73 self.current_size += item_size + GlyphId16::RAW_BYTE_LEN;
75 }
76
77 fn finish_current(&mut self) {
78 if !self.current_coverage.is_empty() {
79 let coverage = std::mem::take(&mut self.current_coverage).into();
80 self.finished.push(T::instantiate(
81 coverage,
82 std::mem::take(&mut self.current_items),
83 ));
84 self.current_size = 0;
85 }
86 }
87
88 fn finish(mut self) -> Vec<T> {
89 self.finish_current();
90 self.finished
91 }
92}
93#[derive(Clone, Debug, Default)]
95pub struct SingleSubBuilder {
96 items: BTreeMap<GlyphId16, GlyphId16>,
97}
98
99impl SingleSubBuilder {
100 pub fn insert(&mut self, target: GlyphId16, replacement: GlyphId16) {
105 self.items.insert(target, replacement);
106 }
107
108 pub fn can_add(&self, target: GlyphId16, replacement: GlyphId16) -> bool {
113 !matches!(self.items.get(&target), Some(x) if *x != replacement)
115 }
116
117 pub fn len(&self) -> usize {
119 self.items.len()
120 }
121
122 pub fn is_empty(&self) -> bool {
124 self.items.is_empty()
125 }
126
127 #[deprecated(since = "0.38.2", note = "use ::iter instead")]
131 pub fn iter_pairs(&self) -> impl Iterator<Item = (GlyphId16, GlyphId16)> + '_ {
132 self.iter()
133 }
134
135 pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, GlyphId16)> + '_ {
137 self.items.iter().map(|(target, alt)| (*target, *alt))
138 }
139
140 pub fn promote_to_multi_sub(self) -> MultipleSubBuilder {
145 MultipleSubBuilder {
146 items: self
147 .items
148 .into_iter()
149 .map(|(key, gid)| (key, vec![gid]))
150 .collect(),
151 }
152 }
153
154 pub fn promote_to_ligature_sub(self) -> LigatureSubBuilder {
159 let mut items = BTreeMap::new();
160 for (from, to) in self.items.into_iter() {
161 items
162 .entry(from)
163 .or_insert(Vec::new())
164 .push((Vec::new(), to));
165 }
166 LigatureSubBuilder { items }
167 }
168}
169
170impl Builder for SingleSubBuilder {
171 type Output = Vec<super::SingleSubst>;
172
173 fn build(self, _: &mut VariationStoreBuilder) -> Self::Output {
174 if self.items.is_empty() {
175 return Default::default();
176 }
177 let delta = self
180 .items
181 .iter()
182 .map(|(k, v)| v.to_u16() as i32 - k.to_u16() as i32)
183 .reduce(|acc, val| if acc == val { acc } else { i32::MAX })
184 .and_then(|delta| i16::try_from(delta).ok());
185
186 let coverage = self.items.keys().copied().collect();
187 if let Some(delta) = delta {
188 vec![super::SingleSubst::format_1(coverage, delta)]
189 } else {
190 let replacements = self.items.values().copied().collect();
191 vec![super::SingleSubst::format_2(coverage, replacements)]
192 }
193 }
194}
195
196#[derive(Clone, Debug, Default)]
198pub struct MultipleSubBuilder {
199 items: BTreeMap<GlyphId16, Vec<GlyphId16>>,
200}
201
202impl Builder for MultipleSubBuilder {
203 type Output = Vec<super::MultipleSubstFormat1>;
204
205 fn build(self, _: &mut VariationStoreBuilder) -> Self::Output {
206 let coverage = self.items.keys().copied().collect();
207 let seq_tables = self.items.into_values().map(super::Sequence::new).collect();
208 vec![super::MultipleSubstFormat1::new(coverage, seq_tables)]
209 }
210}
211
212impl MultipleSubBuilder {
213 pub fn len(&self) -> usize {
215 self.items.len()
216 }
217
218 pub fn is_empty(&self) -> bool {
220 self.items.is_empty()
221 }
222
223 pub fn insert(&mut self, target: GlyphId16, replacement: Vec<GlyphId16>) {
228 self.items.insert(target, replacement);
229 }
230
231 pub fn can_add(&self, target: GlyphId16, replacement: &[GlyphId16]) -> bool {
233 match self.items.get(&target) {
234 None => true,
235 Some(thing) => thing == replacement,
236 }
237 }
238
239 pub fn iter(&self) -> impl Iterator<Item = (&GlyphId16, &Vec<GlyphId16>)> {
241 self.items.iter()
242 }
243}
244
245#[derive(Clone, Debug, Default)]
247pub struct AlternateSubBuilder {
248 items: BTreeMap<GlyphId16, Vec<GlyphId16>>,
249}
250
251impl AlternateSubBuilder {
252 pub fn len(&self) -> usize {
254 self.items.len()
255 }
256
257 pub fn is_empty(&self) -> bool {
259 self.items.is_empty()
260 }
261
262 pub fn insert(&mut self, target: GlyphId16, replacement: Vec<GlyphId16>) {
264 self.items.insert(target, replacement);
265 }
266
267 pub fn iter(&self) -> impl Iterator<Item = (&GlyphId16, &Vec<GlyphId16>)> {
269 self.items.iter()
270 }
271
272 pub fn iter_pairs(&self) -> impl Iterator<Item = (GlyphId16, GlyphId16)> + '_ {
276 self.items
277 .iter()
278 .flat_map(|(target, alt)| alt.iter().map(|alt| (*target, *alt)))
279 }
280}
281
282impl Builder for AlternateSubBuilder {
283 type Output = Vec<super::AlternateSubstFormat1>;
284
285 fn build(self, _: &mut VariationStoreBuilder) -> Self::Output {
286 let coverage = self.items.keys().copied().collect();
287 let seq_tables = self
288 .items
289 .into_values()
290 .map(super::AlternateSet::new)
291 .collect();
292 vec![super::AlternateSubstFormat1::new(coverage, seq_tables)]
293 }
294}
295
296#[derive(Clone, Debug, Default)]
298pub struct LigatureSubBuilder {
299 items: BTreeMap<GlyphId16, Vec<(Vec<GlyphId16>, GlyphId16)>>,
300}
301
302impl LigatureSubBuilder {
303 pub fn len(&self) -> usize {
305 self.items.len()
306 }
307
308 pub fn is_empty(&self) -> bool {
310 self.items.is_empty()
311 }
312
313 pub fn insert(&mut self, target: Vec<GlyphId16>, replacement: GlyphId16) {
315 let (first, rest) = target.split_first().unwrap();
316 let entry = self.items.entry(*first).or_default();
317 if !entry
319 .iter()
320 .any(|existing| existing.0 == rest && existing.1 == replacement)
321 {
322 entry.push((rest.to_owned(), replacement))
323 }
324 }
325
326 pub fn can_add(&self, target: &[GlyphId16], replacement: GlyphId16) -> bool {
328 let Some((first, rest)) = target.split_first() else {
329 return false;
330 };
331 match self.items.get(first) {
332 Some(ligs) => !ligs
333 .iter()
334 .any(|(seq, target)| seq == rest && *target != replacement),
335 None => true,
336 }
337 }
338
339 pub fn iter(&self) -> impl Iterator<Item = (&GlyphId16, &Vec<(Vec<GlyphId16>, GlyphId16)>)> {
344 self.items.iter()
345 }
346}
347
348impl Builder for LigatureSubBuilder {
349 type Output = Vec<super::LigatureSubstFormat1>;
350
351 fn build(self, _: &mut VariationStoreBuilder) -> Self::Output {
352 let mut splitter = TableSplitter::<super::LigatureSubstFormat1>::new();
353 for (gid, mut ligs) in self.items.into_iter() {
354 ligs.sort_by_key(|(lig, _)| std::cmp::Reverse(lig.len()));
357 let lig_set = super::LigatureSet::new(
358 ligs.into_iter()
359 .map(|(components, replacement)| super::Ligature::new(replacement, components))
360 .collect(),
361 );
362 splitter.add(gid, lig_set);
363 }
364 splitter.finish()
365 }
366}
367
368impl SplitTable for super::LigatureSubstFormat1 {
369 type Component = super::LigatureSet;
370
371 fn size_for_item(item: &Self::Component) -> usize {
372 item.compute_size()
373 }
374
375 fn initial_size_for_item(_item: &Self::Component) -> usize {
376 u16::RAW_BYTE_LEN * 4
378 }
379
380 fn instantiate(coverage: CoverageTable, items: Vec<Self::Component>) -> Self {
381 Self::new(coverage, items)
382 }
383}
384
385impl super::LigatureSet {
386 fn compute_size(&self) -> usize {
387 u16::RAW_BYTE_LEN
389 + Offset16::RAW_BYTE_LEN * self.ligatures.len()
391 + self
393 .ligatures
394 .iter()
395 .map(|lig| lig.compute_size())
396 .sum::<usize>()
397 }
398}
399
400impl super::Ligature {
401 fn compute_size(&self) -> usize {
402 u16::RAW_BYTE_LEN
404 + u16::RAW_BYTE_LEN
406 + u16::RAW_BYTE_LEN * self.component_glyph_ids.len()
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use crate::tables::gsub::LigatureSubstFormat1;
414
415 use super::*;
416
417 fn make_lig_table(n_bytes: u16, first: u16) -> super::super::Ligature {
418 assert!(n_bytes >= 6, "minimum table size");
419 assert!(n_bytes % 2 == 0, "can only generate even sizes: {n_bytes}");
420 let n_glyphs = (n_bytes - 6) / 2;
422 let components = (first..=first + n_glyphs).map(GlyphId16::new).collect();
423 super::super::Ligature::new(GlyphId16::new(first), components)
424 }
425 fn make_2048_bytes_of_ligature() -> super::super::LigatureSet {
426 let lig1 = make_lig_table(1022, 1);
430 let lig2 = make_lig_table(1022, 3);
431 super::super::LigatureSet::new(vec![lig1, lig2])
432 }
433
434 #[test]
435 fn who_tests_the_testers1() {
436 for size in [6, 12, 144, 2046, u16::MAX - 1] {
437 let table = make_lig_table(size, 1);
438 let bytes = crate::dump_table(&table).unwrap();
439 assert_eq!(bytes.len(), size as usize);
440 }
441 }
442
443 #[test]
444 fn splitting_ligature_subs() {
445 let mut splitter = TableSplitter::<LigatureSubstFormat1>::new();
446 let ligset = make_2048_bytes_of_ligature();
447 for gid in 0u16..31 {
448 splitter.add(GlyphId16::new(gid), ligset.clone());
450 }
451
452 assert_eq!(splitter.clone().finish().len(), 1);
454 splitter.add(GlyphId16::new(32), ligset);
455 assert_eq!(splitter.finish().len(), 2)
457 }
458}