Coverage Report

Created: 2025-11-16 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fontations/klippa/src/layout.rs
Line
Count
Source
1
//! impl subset() for layout common tables
2
3
use std::{cmp::Ordering, mem};
4
5
use crate::{
6
    offset::SerializeSubset,
7
    offset_array::SubsetOffsetArray,
8
    serialize::{OffsetWhence, SerializeErrorFlags, Serializer},
9
    CollectVariationIndices, NameIdClosure, Plan, Serialize, SubsetState, SubsetTable,
10
};
11
use fnv::FnvHashMap;
12
use write_fonts::{
13
    read::{
14
        collections::IntSet,
15
        tables::{
16
            gsub::Gsub,
17
            layout::{
18
                CharacterVariantParams, ClassDef, ClassDefFormat1, ClassDefFormat2,
19
                ClassRangeRecord, Condition, ConditionFormat1, ConditionSet, CoverageFormat1,
20
                CoverageFormat2, CoverageTable, DeltaFormat, Device, DeviceOrVariationIndex,
21
                ExtensionLookup, Feature, FeatureList, FeatureParams, FeatureRecord,
22
                FeatureTableSubstitution, FeatureTableSubstitutionRecord, FeatureVariationRecord,
23
                FeatureVariations, Intersect, LangSys, LangSysRecord, LookupList, RangeRecord,
24
                Script, ScriptList, ScriptRecord, SizeParams, StylisticSetParams, Subtables,
25
                VariationIndex,
26
            },
27
        },
28
        types::{GlyphId, GlyphId16, NameId},
29
        FontData, FontRead, FontRef, TopLevelTable,
30
    },
31
    types::{FixedSize, Offset16, Offset32, Tag},
32
};
33
34
const MAX_SCRIPTS: u16 = 500;
35
const MAX_LANGSYS: u16 = 2000;
36
const MAX_FEATURE_INDICES: u16 = 1500;
37
const MAX_LOOKUP_VISIT_COUNT: u16 = 35000;
38
const MAX_LANGSYS_FEATURE_COUNT: u16 = 5000;
39
40
impl NameIdClosure for StylisticSetParams<'_> {
41
0
    fn collect_name_ids(&self, plan: &mut Plan) {
42
0
        plan.name_ids.insert(self.ui_name_id());
43
0
    }
44
}
45
46
impl NameIdClosure for SizeParams<'_> {
47
0
    fn collect_name_ids(&self, plan: &mut Plan) {
48
0
        plan.name_ids.insert(NameId::new(self.name_entry()));
49
0
    }
50
}
51
52
impl NameIdClosure for CharacterVariantParams<'_> {
53
0
    fn collect_name_ids(&self, plan: &mut Plan) {
54
0
        plan.name_ids.insert(self.feat_ui_label_name_id());
55
0
        plan.name_ids.insert(self.feat_ui_tooltip_text_name_id());
56
0
        plan.name_ids.insert(self.sample_text_name_id());
57
58
0
        let first_name_id = self.first_param_ui_label_name_id();
59
0
        let num_named_params = self.num_named_parameters();
60
0
        if first_name_id == NameId::COPYRIGHT_NOTICE
61
0
            || num_named_params == 0
62
0
            || num_named_params >= 0x7FFF
63
        {
64
0
            return;
65
0
        }
66
67
0
        let last_name_id = first_name_id.to_u16() as u32 + num_named_params as u32 - 1;
68
0
        plan.name_ids
69
0
            .insert_range(first_name_id..=NameId::new(last_name_id as u16));
70
0
    }
71
}
72
73
impl NameIdClosure for Feature<'_> {
74
0
    fn collect_name_ids(&self, plan: &mut Plan) {
75
0
        let Some(Ok(feature_params)) = self.feature_params() else {
76
0
            return;
77
        };
78
0
        match feature_params {
79
0
            FeatureParams::StylisticSet(table) => table.collect_name_ids(plan),
80
0
            FeatureParams::Size(table) => table.collect_name_ids(plan),
81
0
            FeatureParams::CharacterVariant(table) => table.collect_name_ids(plan),
82
        }
83
0
    }
84
}
85
86
impl<'a> SubsetTable<'a> for DeviceOrVariationIndex<'a> {
87
    type ArgsForSubset = &'a FnvHashMap<u32, (u32, i32)>;
88
    type Output = ();
89
90
0
    fn subset(
91
0
        &self,
92
0
        plan: &Plan,
93
0
        s: &mut Serializer,
94
0
        args: &FnvHashMap<u32, (u32, i32)>,
95
0
    ) -> Result<(), SerializeErrorFlags> {
96
0
        match self {
97
0
            Self::Device(item) => item.subset(plan, s, ()),
98
0
            Self::VariationIndex(item) => item.subset(plan, s, args),
99
        }
100
0
    }
101
}
102
103
impl SubsetTable<'_> for Device<'_> {
104
    type ArgsForSubset = ();
105
    type Output = ();
106
0
    fn subset(
107
0
        &self,
108
0
        _plan: &Plan,
109
0
        s: &mut Serializer,
110
0
        _args: (),
111
0
    ) -> Result<(), SerializeErrorFlags> {
112
0
        s.embed_bytes(self.min_table_bytes()).map(|_| ())
113
0
    }
114
}
115
116
impl<'a> SubsetTable<'a> for VariationIndex<'a> {
117
    type ArgsForSubset = &'a FnvHashMap<u32, (u32, i32)>;
118
    type Output = ();
119
120
0
    fn subset(
121
0
        &self,
122
0
        _plan: &Plan,
123
0
        s: &mut Serializer,
124
0
        args: &FnvHashMap<u32, (u32, i32)>,
125
0
    ) -> Result<(), SerializeErrorFlags> {
126
0
        let var_idx =
127
0
            ((self.delta_set_outer_index() as u32) << 16) + self.delta_set_inner_index() as u32;
128
0
        let Some((new_idx, _)) = args.get(&var_idx) else {
129
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_OTHER);
130
        };
131
132
0
        s.embed(*new_idx)?;
133
0
        s.embed(self.delta_format()).map(|_| ())
134
0
    }
135
}
136
137
impl CollectVariationIndices for DeviceOrVariationIndex<'_> {
138
0
    fn collect_variation_indices(&self, plan: &Plan, varidx_set: &mut IntSet<u32>) {
139
0
        match self {
140
0
            Self::Device(_item) => (),
141
0
            Self::VariationIndex(item) => item.collect_variation_indices(plan, varidx_set),
142
        }
143
0
    }
144
}
145
146
impl CollectVariationIndices for VariationIndex<'_> {
147
0
    fn collect_variation_indices(&self, _plan: &Plan, varidx_set: &mut IntSet<u32>) {
148
0
        if self.delta_format() == DeltaFormat::VariationIndex {
149
0
            let var_idx =
150
0
                ((self.delta_set_outer_index() as u32) << 16) + self.delta_set_inner_index() as u32;
151
0
            varidx_set.insert(var_idx);
152
0
        }
153
0
    }
154
}
155
156
pub(crate) struct ClassDefSubsetStruct<'a> {
157
    pub(crate) remap_class: bool,
158
    pub(crate) keep_empty_table: bool,
159
    pub(crate) use_class_zero: bool,
160
    pub(crate) glyph_filter: Option<&'a CoverageTable<'a>>,
161
}
162
163
impl<'a> SubsetTable<'a> for ClassDef<'a> {
164
    type ArgsForSubset = &'a ClassDefSubsetStruct<'a>;
165
    // class_map: Option<FnvHashMap<u16, u16>>
166
    type Output = Option<FnvHashMap<u16, u16>>;
167
0
    fn subset(
168
0
        &self,
169
0
        plan: &Plan,
170
0
        s: &mut Serializer,
171
0
        args: Self::ArgsForSubset,
172
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
173
0
        match self {
174
0
            Self::Format1(item) => item.subset(plan, s, args),
175
0
            Self::Format2(item) => item.subset(plan, s, args),
176
        }
177
0
    }
178
}
179
180
impl<'a> SubsetTable<'a> for ClassDefFormat1<'a> {
181
    type ArgsForSubset = &'a ClassDefSubsetStruct<'a>;
182
    // class_map: Option<FnvHashMap<u16, u16>>
183
    type Output = Option<FnvHashMap<u16, u16>>;
184
0
    fn subset(
185
0
        &self,
186
0
        plan: &Plan,
187
0
        s: &mut Serializer,
188
0
        args: Self::ArgsForSubset,
189
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
190
0
        let glyph_map = &plan.glyph_map_gsub;
191
192
0
        let start = self.start_glyph_id().to_u32();
193
0
        let end = start + self.glyph_count() as u32 - 1;
194
0
        let end = plan.glyphset_gsub.last().unwrap().to_u32().min(end);
195
196
0
        let class_values = self.class_value_array();
197
0
        let mut retained_classes = IntSet::empty();
198
199
0
        let cap = glyph_map.len().min(self.glyph_count() as usize);
200
0
        let mut new_gid_classes = Vec::with_capacity(cap);
201
202
0
        for g in start..=end {
203
0
            let gid = GlyphId::from(g);
204
0
            let Some(new_gid) = glyph_map.get(&gid) else {
205
0
                continue;
206
            };
207
208
0
            if let Some(glyph_filter) = args.glyph_filter {
209
0
                if glyph_filter.get(gid).is_none() {
210
0
                    continue;
211
0
                }
212
0
            }
213
214
0
            let Some(class) = class_values.get((g - start) as usize) else {
215
0
                return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
216
            };
217
218
0
            let class = class.get();
219
0
            if class == 0 {
220
0
                continue;
221
0
            }
222
223
0
            retained_classes.insert(class);
224
0
            new_gid_classes.push((new_gid.to_u32() as u16, class));
225
        }
226
227
0
        let use_class_zero = if args.use_class_zero {
228
0
            let glyph_count = if let Some(glyph_filter) = args.glyph_filter {
229
0
                glyph_map
230
0
                    .keys()
231
0
                    .filter(|&g| glyph_filter.get(*g).is_some())
232
0
                    .count()
233
            } else {
234
0
                glyph_map.len()
235
            };
236
0
            glyph_count <= new_gid_classes.len()
237
        } else {
238
0
            false
239
        };
240
241
0
        if !args.keep_empty_table && new_gid_classes.is_empty() {
242
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
243
0
        }
244
245
0
        classdef_remap_and_serialize(
246
0
            args.remap_class,
247
0
            &retained_classes,
248
0
            use_class_zero,
249
0
            &mut new_gid_classes,
250
0
            s,
251
        )
252
0
    }
253
}
254
255
impl<'a> SubsetTable<'a> for ClassDefFormat2<'a> {
256
    type ArgsForSubset = &'a ClassDefSubsetStruct<'a>;
257
    // class_map: Option<FnvHashMap<u16, u16>>
258
    type Output = Option<FnvHashMap<u16, u16>>;
259
0
    fn subset(
260
0
        &self,
261
0
        plan: &Plan,
262
0
        s: &mut Serializer,
263
0
        args: Self::ArgsForSubset,
264
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
265
0
        let glyph_map = &plan.glyph_map_gsub;
266
0
        let glyph_set = &plan.glyphset_gsub;
267
268
0
        let mut retained_classes = IntSet::empty();
269
270
0
        let population = self.population();
271
0
        let cap = glyph_map.len().min(population);
272
0
        let mut new_gid_classes = Vec::with_capacity(cap);
273
274
0
        let num_bits = 16 - self.class_range_count().leading_zeros() as u64;
275
0
        if population as u64 > glyph_set.len() * num_bits {
276
0
            for g in glyph_set.iter() {
277
0
                if g.to_u32() > 0xFFFF_u32 {
278
0
                    break;
279
0
                }
280
0
                let Some(new_gid) = glyph_map.get(&g) else {
281
0
                    continue;
282
                };
283
0
                if let Some(glyph_filter) = args.glyph_filter {
284
0
                    if glyph_filter.get(g).is_none() {
285
0
                        continue;
286
0
                    }
287
0
                }
288
289
0
                let class = self.get(GlyphId16::from(g.to_u32() as u16));
290
0
                if class == 0 {
291
0
                    continue;
292
0
                }
293
294
0
                retained_classes.insert(class);
295
0
                new_gid_classes.push((new_gid.to_u32() as u16, class));
296
            }
297
        } else {
298
0
            for record in self.class_range_records() {
299
0
                let class = record.class();
300
0
                if class == 0 {
301
0
                    continue;
302
0
                }
303
304
0
                let start = record.start_glyph_id().to_u32();
305
0
                let end = record
306
0
                    .end_glyph_id()
307
0
                    .to_u32()
308
0
                    .min(glyph_set.last().unwrap().to_u32());
309
0
                for g in start..=end {
310
0
                    let gid = GlyphId::from(g);
311
0
                    let Some(new_gid) = glyph_map.get(&gid) else {
312
0
                        continue;
313
                    };
314
0
                    if let Some(glyph_filter) = args.glyph_filter {
315
0
                        if glyph_filter.get(gid).is_none() {
316
0
                            continue;
317
0
                        }
318
0
                    }
319
320
0
                    retained_classes.insert(class);
321
0
                    new_gid_classes.push((new_gid.to_u32() as u16, class));
322
                }
323
            }
324
        }
325
326
0
        new_gid_classes.sort_by(|a, b| a.0.cmp(&b.0));
327
0
        let use_class_zero = if args.use_class_zero {
328
0
            let glyph_count = if let Some(glyph_filter) = args.glyph_filter {
329
0
                glyph_map
330
0
                    .keys()
331
0
                    .filter(|&g| glyph_filter.get(*g).is_some())
332
0
                    .count()
333
            } else {
334
0
                glyph_map.len()
335
            };
336
0
            glyph_count <= new_gid_classes.len()
337
        } else {
338
0
            false
339
        };
340
341
0
        if !args.keep_empty_table && new_gid_classes.is_empty() {
342
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
343
0
        }
344
345
0
        classdef_remap_and_serialize(
346
0
            args.remap_class,
347
0
            &retained_classes,
348
0
            use_class_zero,
349
0
            &mut new_gid_classes,
350
0
            s,
351
        )
352
0
    }
353
}
354
355
0
fn classdef_remap_and_serialize(
356
0
    remap_class: bool,
357
0
    retained_classes: &IntSet<u16>,
358
0
    use_class_zero: bool,
359
0
    new_gid_classes: &mut [(u16, u16)],
360
0
    s: &mut Serializer,
361
0
) -> Result<Option<FnvHashMap<u16, u16>>, SerializeErrorFlags> {
362
0
    if !remap_class {
363
0
        return ClassDef::serialize(s, new_gid_classes).map(|()| None);
364
0
    }
365
366
0
    let mut class_map = FnvHashMap::default();
367
0
    if !use_class_zero {
368
0
        class_map.insert(0_u16, 0_u16);
369
0
    }
370
371
0
    let mut new_idx = if use_class_zero { 0_u16 } else { 1 };
372
0
    for class in retained_classes.iter() {
373
0
        class_map.insert(class, new_idx);
374
0
        new_idx += 1;
375
0
    }
376
377
0
    for (_, class) in new_gid_classes.iter_mut() {
378
0
        let Some(new_class) = class_map.get(class) else {
379
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_OTHER);
380
        };
381
0
        *class = *new_class;
382
    }
383
0
    ClassDef::serialize(s, new_gid_classes).map(|()| Some(class_map))
384
0
}
385
386
impl<'a> Serialize<'a> for ClassDef<'a> {
387
    type Args = &'a [(u16, u16)];
388
0
    fn serialize(
389
0
        s: &mut Serializer,
390
0
        new_gid_classes: &[(u16, u16)],
391
0
    ) -> Result<(), SerializeErrorFlags> {
392
0
        let mut glyph_min = 0;
393
0
        let mut glyph_max = 0;
394
0
        let mut prev_g = 0;
395
0
        let mut prev_class = 0;
396
397
0
        let mut num_glyphs = 0_u16;
398
0
        let mut num_ranges = 1_u16;
399
0
        for (g, class) in new_gid_classes.iter().filter(|(_, class)| *class != 0) {
400
0
            num_glyphs += 1;
401
0
            if num_glyphs == 1 {
402
0
                glyph_min = *g;
403
0
                glyph_max = *g;
404
0
                prev_g = *g;
405
0
                prev_class = *class;
406
0
                continue;
407
0
            }
408
409
0
            glyph_max = glyph_max.max(*g);
410
0
            if *g != prev_g + 1 || *class != prev_class + 1 {
411
0
                num_ranges += 1;
412
0
            }
413
414
0
            prev_g = *g;
415
0
            prev_class = *class;
416
        }
417
418
0
        if num_glyphs > 0 && (glyph_max - glyph_min + 1) < num_ranges * 3 {
419
0
            ClassDefFormat1::serialize(s, new_gid_classes)
420
        } else {
421
0
            ClassDefFormat2::serialize(s, new_gid_classes)
422
        }
423
0
    }
424
}
425
426
impl<'a> Serialize<'a> for ClassDefFormat1<'a> {
427
    type Args = &'a [(u16, u16)];
428
0
    fn serialize(
429
0
        s: &mut Serializer,
430
0
        new_gid_classes: &[(u16, u16)],
431
0
    ) -> Result<(), SerializeErrorFlags> {
432
        // format 1
433
0
        s.embed(1_u16)?;
434
        // start_glyph
435
0
        let start_glyph_pos = s.embed(0_u16)?;
436
        // glyph count
437
0
        let glyph_count_pos = s.embed(0_u16)?;
438
439
0
        let mut num = 0;
440
0
        let mut glyph_min = 0;
441
0
        let mut glyph_max = 0;
442
0
        for (g, _) in new_gid_classes.iter().filter(|(_, class)| *class != 0) {
443
0
            if num == 0 {
444
0
                glyph_min = *g;
445
0
                glyph_max = *g;
446
0
            } else {
447
0
                glyph_max = *g.max(&glyph_max);
448
0
            }
449
0
            num += 1;
450
        }
451
452
0
        if num == 0 {
453
0
            return Ok(());
454
0
        }
455
456
0
        s.copy_assign(start_glyph_pos, glyph_min);
457
458
0
        let glyph_count = glyph_max - glyph_min + 1;
459
0
        s.copy_assign(glyph_count_pos, glyph_count);
460
461
0
        let pos = s.allocate_size((glyph_count as usize) * 2, true)?;
462
0
        for (g, class) in new_gid_classes.iter().filter(|(_, class)| *class != 0) {
463
0
            let idx = (*g - glyph_min) as usize;
464
0
            s.copy_assign(pos + idx * 2, *class);
465
0
        }
466
0
        Ok(())
467
0
    }
468
}
469
470
impl<'a> Serialize<'a> for ClassDefFormat2<'a> {
471
    type Args = &'a [(u16, u16)];
472
0
    fn serialize(
473
0
        s: &mut Serializer,
474
0
        new_gid_classes: &[(u16, u16)],
475
0
    ) -> Result<(), SerializeErrorFlags> {
476
        // format 2
477
0
        s.embed(2_u16)?;
478
        //classRange count
479
0
        let range_count_pos = s.embed(0_u16)?;
480
481
0
        let mut num = 0_u16;
482
0
        let mut prev_g = 0;
483
0
        let mut prev_class = 0;
484
485
0
        let mut num_ranges = 0_u16;
486
0
        let mut pos = 0;
487
0
        for (g, class) in new_gid_classes.iter().filter(|(_, class)| *class != 0) {
488
0
            num += 1;
489
0
            if num == 1 {
490
0
                prev_g = *g;
491
0
                prev_class = *class;
492
493
0
                pos = s.allocate_size(ClassRangeRecord::RAW_BYTE_LEN, true)?;
494
0
                s.copy_assign(pos, prev_g);
495
0
                s.copy_assign(pos + 2, prev_g);
496
0
                s.copy_assign(pos + 4, prev_class);
497
498
0
                num_ranges += 1;
499
0
                continue;
500
0
            }
501
502
0
            if *g != prev_g + 1 || *class != prev_class {
503
0
                num_ranges += 1;
504
                // update last_gid of previous record
505
0
                s.copy_assign(pos + 2, prev_g);
506
507
0
                pos = s.allocate_size(ClassRangeRecord::RAW_BYTE_LEN, true)?;
508
0
                s.copy_assign(pos, *g);
509
0
                s.copy_assign(pos + 2, *g);
510
0
                s.copy_assign(pos + 4, *class);
511
0
            }
512
513
0
            prev_class = *class;
514
0
            prev_g = *g;
515
        }
516
517
0
        if num == 0 {
518
0
            return Ok(());
519
0
        }
520
521
        // update end glyph of the last record
522
0
        s.copy_assign(pos + 2, prev_g);
523
        // update range count
524
0
        s.copy_assign(range_count_pos, num_ranges);
525
0
        Ok(())
526
0
    }
527
}
528
529
impl<'a> SubsetTable<'a> for CoverageTable<'a> {
530
    type ArgsForSubset = ();
531
    type Output = ();
532
0
    fn subset(
533
0
        &self,
534
0
        plan: &Plan,
535
0
        s: &mut Serializer,
536
0
        args: Self::ArgsForSubset,
537
0
    ) -> Result<(), SerializeErrorFlags> {
538
0
        match self {
539
0
            CoverageTable::Format1(sub) => sub.subset(plan, s, args),
540
0
            CoverageTable::Format2(sub) => sub.subset(plan, s, args),
541
        }
542
0
    }
543
}
544
545
impl<'a> SubsetTable<'a> for CoverageFormat1<'a> {
546
    type ArgsForSubset = ();
547
    type Output = ();
548
0
    fn subset(
549
0
        &self,
550
0
        plan: &Plan,
551
0
        s: &mut Serializer,
552
0
        _args: Self::ArgsForSubset,
553
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
554
0
        let glyph_count = (self.glyph_count() as usize).min(plan.font_num_glyphs);
555
0
        let Some(glyph_array) = self.glyph_array().get(0..glyph_count) else {
556
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR);
557
        };
558
559
0
        let num_bits = 16 - (glyph_count as u16).leading_zeros() as usize;
560
        // if/else branches return the same result, it's just an optimization that
561
        // we pick the faster approach depending on the number of glyphs
562
0
        let retained_glyphs: Vec<GlyphId> =
563
0
            if glyph_count > (plan.glyphset_gsub.len() as usize) * num_bits {
564
0
                plan.glyphset_gsub
565
0
                    .iter()
566
0
                    .filter_map(|old_gid| {
567
0
                        glyph_array
568
0
                            .binary_search_by(|g| g.get().to_u32().cmp(&old_gid.to_u32()))
569
0
                            .ok()
570
0
                            .and_then(|_| plan.glyph_map_gsub.get(&old_gid))
571
0
                            .copied()
572
0
                    })
573
0
                    .collect()
574
            } else {
575
0
                glyph_array
576
0
                    .iter()
577
0
                    .filter_map(|g| plan.glyph_map_gsub.get(&GlyphId::from(g.get())))
578
0
                    .copied()
579
0
                    .collect()
580
            };
581
582
0
        if retained_glyphs.is_empty() {
583
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
584
0
        }
585
0
        CoverageTable::serialize(s, &retained_glyphs)
586
0
    }
587
}
588
589
impl<'a> SubsetTable<'a> for CoverageFormat2<'a> {
590
    type ArgsForSubset = ();
591
    type Output = ();
592
0
    fn subset(
593
0
        &self,
594
0
        plan: &Plan,
595
0
        s: &mut Serializer,
596
0
        _args: Self::ArgsForSubset,
597
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
598
0
        let range_count = self.range_count();
599
0
        if range_count as usize > plan.font_num_glyphs {
600
0
            return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
601
0
        }
602
0
        let num_bits = 16 - range_count.leading_zeros() as usize;
603
        // if/else branches return the same result, it's just an optimization that
604
        // we pick the faster approach depending on the number of glyphs
605
0
        let retained_glyphs: Vec<GlyphId> =
606
0
            if self.population() > plan.glyph_map_gsub.len() * num_bits {
607
0
                let range_records = self.range_records();
608
0
                plan.glyphset_gsub
609
0
                    .iter()
610
0
                    .filter_map(|g| {
611
0
                        range_records
612
0
                            .binary_search_by(|rec| {
613
0
                                if rec.end_glyph_id().to_u32() < g.to_u32() {
614
0
                                    Ordering::Less
615
0
                                } else if rec.start_glyph_id().to_u32() > g.to_u32() {
616
0
                                    Ordering::Greater
617
                                } else {
618
0
                                    Ordering::Equal
619
                                }
620
0
                            })
621
0
                            .ok()
622
0
                            .and_then(|_| plan.glyph_map_gsub.get(&g))
623
0
                    })
624
0
                    .copied()
625
0
                    .collect()
626
            } else {
627
0
                self.range_records()
628
0
                    .iter()
629
0
                    .flat_map(|r| {
630
0
                        r.iter()
631
0
                            .filter_map(|g| plan.glyph_map_gsub.get(&GlyphId::from(g)))
632
0
                    })
633
0
                    .copied()
634
0
                    .collect()
635
            };
636
637
0
        if retained_glyphs.is_empty() {
638
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
639
0
        }
640
641
0
        CoverageTable::serialize(s, &retained_glyphs)
642
0
    }
643
}
644
645
impl<'a> Serialize<'a> for CoverageTable<'a> {
646
    type Args = &'a [GlyphId];
647
0
    fn serialize(s: &mut Serializer, glyphs: &[GlyphId]) -> Result<(), SerializeErrorFlags> {
648
0
        if glyphs.is_empty() {
649
0
            return CoverageFormat1::serialize(s, glyphs);
650
0
        }
651
652
0
        let glyph_count = glyphs.len();
653
0
        let mut num_ranges = 1_u16;
654
0
        let mut last = glyphs[0].to_u32();
655
656
0
        for g in glyphs.iter().skip(1) {
657
0
            let gid = g.to_u32();
658
0
            if last + 1 != gid {
659
0
                num_ranges += 1;
660
0
            }
661
662
0
            last = gid;
663
        }
664
665
        // TODO: add support for unsorted glyph list??
666
        // ref: <https://github.com/harfbuzz/harfbuzz/blob/59001aa9527c056ad08626cfec9a079b65d8aec8/src/OT/Layout/Common/Coverage.hh#L143>
667
0
        if glyph_count <= num_ranges as usize * 3 {
668
0
            CoverageFormat1::serialize(s, glyphs)
669
        } else {
670
0
            CoverageFormat2::serialize(s, (glyphs, num_ranges))
671
        }
672
0
    }
673
}
674
675
impl<'a> Serialize<'a> for CoverageFormat1<'a> {
676
    type Args = &'a [GlyphId];
677
0
    fn serialize(s: &mut Serializer, glyphs: &[GlyphId]) -> Result<(), SerializeErrorFlags> {
678
        //format
679
0
        s.embed(1_u16)?;
680
681
        // count
682
0
        let count = glyphs.len();
683
0
        s.embed(count as u16)?;
684
685
0
        let pos = s.allocate_size(count * 2, true)?;
686
0
        for (idx, g) in glyphs.iter().enumerate() {
687
0
            s.copy_assign(pos + idx * 2, g.to_u32() as u16);
688
0
        }
689
0
        Ok(())
690
0
    }
691
}
692
693
impl<'a> Serialize<'a> for CoverageFormat2<'a> {
694
    type Args = (&'a [GlyphId], u16);
695
0
    fn serialize(s: &mut Serializer, args: Self::Args) -> Result<(), SerializeErrorFlags> {
696
0
        let (glyphs, range_count) = args;
697
        //format
698
0
        s.embed(2_u16)?;
699
700
        //range_count
701
0
        s.embed(range_count)?;
702
703
        // range records
704
0
        let pos = s.allocate_size((range_count as usize) * RangeRecord::RAW_BYTE_LEN, true)?;
705
0
        let mut last = glyphs[0].to_u32() as u16;
706
        // copy start glyph of first record
707
0
        s.copy_assign(pos, last);
708
        // copy coverage index of firstr ecord
709
0
        s.copy_assign(pos + 4, 0_u16);
710
711
0
        let mut range = 0;
712
0
        for (idx, g) in glyphs.iter().enumerate().skip(1) {
713
0
            let g = g.to_u32() as u16;
714
0
            let range_pos = pos + range * RangeRecord::RAW_BYTE_LEN;
715
0
            if last + 1 != g {
716
0
                //end glyph
717
0
                s.copy_assign(range_pos + 2, last);
718
0
                range += 1;
719
0
720
0
                let new_range_pos = range_pos + RangeRecord::RAW_BYTE_LEN;
721
0
                // start glyph of next record
722
0
                s.copy_assign(new_range_pos, g);
723
0
                // coverage index of next record
724
0
                s.copy_assign(new_range_pos + 4, idx as u16);
725
0
            }
726
0
            last = g;
727
        }
728
729
0
        let last_range_pos = pos + range * RangeRecord::RAW_BYTE_LEN;
730
        // end glyph
731
0
        s.copy_assign(last_range_pos + 2, last);
732
0
        Ok(())
733
0
    }
734
}
735
736
/// Return glyphs and their indices in the input Coverage table that intersect with the input glyph set
737
/// returned glyphs are mapped into new glyph ids
738
0
pub(crate) fn intersected_glyphs_and_indices(
739
0
    coverage: &CoverageTable,
740
0
    glyph_set: &IntSet<GlyphId>,
741
0
    glyph_map: &FnvHashMap<GlyphId, GlyphId>,
742
0
) -> (Vec<GlyphId>, IntSet<u16>) {
743
0
    let count = match coverage {
744
0
        CoverageTable::Format1(t) => t.glyph_count(),
745
0
        CoverageTable::Format2(t) => t.range_count(),
746
    };
747
0
    let num_bits = 32 - count.leading_zeros();
748
749
0
    let coverage_population = coverage.population();
750
0
    let glyph_set_len = glyph_set.len();
751
0
    let cap = coverage_population.min(glyph_set_len as usize);
752
0
    let mut glyphs = Vec::with_capacity(cap);
753
0
    let mut indices = IntSet::empty();
754
755
0
    if coverage_population as u32 > (glyph_set_len as u32) * num_bits {
756
0
        for (idx, g) in glyph_set
757
0
            .iter()
758
0
            .filter_map(|g| coverage.get(g).map(|idx| (idx, g)))
759
0
            .filter_map(|(idx, g)| glyph_map.get(&g).map(|new_g| (idx, *new_g)))
760
0
        {
761
0
            glyphs.push(g);
762
0
            indices.insert(idx);
763
0
        }
764
    } else {
765
0
        for (i, g) in coverage
766
0
            .iter()
767
0
            .enumerate()
768
0
            .filter_map(|(i, g)| glyph_map.get(&GlyphId::from(g)).map(|&new_g| (i, new_g)))
769
0
        {
770
0
            glyphs.push(g);
771
0
            indices.insert(i as u16);
772
0
        }
773
    }
774
0
    (glyphs, indices)
775
0
}
776
777
/// Return indices of glyphs in the input Coverage table that intersect with the input glyph set
778
0
pub(crate) fn intersected_coverage_indices(
779
0
    coverage: &CoverageTable,
780
0
    glyph_set: &IntSet<GlyphId>,
781
0
) -> IntSet<u16> {
782
0
    let count = match coverage {
783
0
        CoverageTable::Format1(t) => t.glyph_count(),
784
0
        CoverageTable::Format2(t) => t.range_count(),
785
    };
786
0
    let num_bits = 32 - count.leading_zeros();
787
788
0
    let coverage_population = coverage.population();
789
0
    let glyph_set_len = glyph_set.len();
790
791
0
    if coverage_population as u32 > (glyph_set_len as u32) * num_bits {
792
0
        glyph_set.iter().filter_map(|g| coverage.get(g)).collect()
793
    } else {
794
0
        coverage
795
0
            .iter()
796
0
            .enumerate()
797
0
            .filter_map(|(i, g)| glyph_set.contains(GlyphId::from(g)).then_some(i as u16))
798
0
            .collect()
799
    }
800
0
}
801
802
/// Return a set of feature indices that have alternate features defined in FeatureVariations table
803
/// and the alternate version(s) intersect the set of lookup indices
804
0
pub(crate) fn collect_features_with_retained_subs(
805
0
    feature_variations: &FeatureVariations,
806
0
    lookup_indices: &IntSet<u16>,
807
0
) -> IntSet<u16> {
808
0
    let font_data = feature_variations.offset_data();
809
0
    let mut out = IntSet::empty();
810
0
    for subs in feature_variations
811
0
        .feature_variation_records()
812
0
        .iter()
813
0
        .filter_map(|rec| rec.feature_table_substitution(font_data))
814
    {
815
0
        let Ok(subs) = subs else {
816
0
            return IntSet::empty();
817
        };
818
819
0
        for rec in subs.substitutions() {
820
0
            let Ok(sub_f) = rec.alternate_feature(subs.offset_data()) else {
821
0
                return IntSet::empty();
822
            };
823
0
            if !feature_intersects_lookups(&sub_f, lookup_indices) {
824
0
                continue;
825
0
            }
826
0
            out.insert(rec.feature_index());
827
        }
828
    }
829
0
    out
830
0
}
831
832
0
fn feature_intersects_lookups(f: &Feature, lookup_indices: &IntSet<u16>) -> bool {
833
0
    f.lookup_list_indices()
834
0
        .iter()
835
0
        .any(|i| lookup_indices.contains(i.get()))
836
0
}
837
838
0
pub(crate) fn prune_features(
839
0
    feature_list: &FeatureList,
840
0
    alternate_features: &IntSet<u16>,
841
0
    lookup_indices: &IntSet<u16>,
842
0
    feature_indices: IntSet<u16>,
843
0
) -> IntSet<u16> {
844
0
    let mut out = IntSet::empty();
845
0
    let feature_records = feature_list.feature_records();
846
0
    for i in feature_indices.iter() {
847
0
        let Some(feature_rec) = feature_records.get(i as usize) else {
848
0
            continue;
849
        };
850
0
        let feature_tag = feature_rec.feature_tag();
851
        // never drop feature "pref"
852
        // ref: https://github.com/harfbuzz/harfbuzz/blob/fc6231726e514f96bfbb098283aab332fc6b45fb/src/hb-ot-layout-gsubgpos.hh#L4822
853
0
        if feature_tag == Tag::new(b"pref") {
854
0
            out.insert(i);
855
0
            continue;
856
0
        }
857
858
0
        let Ok(feature) = feature_rec.feature(feature_list.offset_data()) else {
859
0
            return out;
860
        };
861
        // always keep "size" feature even if it's empty
862
        // ref: https://github.com/fonttools/fonttools/blob/e857fe5ef7b25e92fd829a445357e45cde16eb04/Lib/fontTools/subset/__init__.py#L1627
863
0
        if !feature.feature_params_offset().is_null() && feature_tag == Tag::new(b"size") {
864
0
            out.insert(i);
865
0
            continue;
866
0
        }
867
868
0
        if !feature_intersects_lookups(&feature, lookup_indices) && !alternate_features.contains(i)
869
        {
870
0
            continue;
871
0
        }
872
0
        out.insert(i);
873
    }
874
0
    out
875
0
}
876
877
0
pub(crate) fn find_duplicate_features(
878
0
    feature_list: &FeatureList,
879
0
    lookup_indices: &IntSet<u16>,
880
0
    feature_indices: IntSet<u16>,
881
0
) -> FnvHashMap<u16, u16> {
882
0
    let mut out = FnvHashMap::default();
883
0
    if lookup_indices.is_empty() {
884
0
        return out;
885
0
    }
886
887
0
    let feature_recs = feature_list.feature_records();
888
0
    let mut unique_features = FnvHashMap::default();
889
0
    for i in feature_indices.iter() {
890
0
        let Some(rec) = feature_recs.get(i as usize) else {
891
0
            continue;
892
        };
893
894
0
        let Ok(f) = rec.feature(feature_list.offset_data()) else {
895
0
            return out;
896
        };
897
898
0
        let t = u32::from_be_bytes(rec.feature_tag().to_be_bytes());
899
900
0
        let same_tag_features = unique_features.entry(t).or_insert(IntSet::empty());
901
0
        if same_tag_features.is_empty() {
902
0
            same_tag_features.insert(i);
903
0
            out.insert(i, i);
904
0
            continue;
905
0
        }
906
907
0
        for other_f_idx in same_tag_features.iter() {
908
0
            let Some(other_rec) = feature_recs.get(other_f_idx as usize) else {
909
0
                continue;
910
            };
911
912
0
            let Ok(other_f) = other_rec.feature(feature_list.offset_data()) else {
913
0
                return out;
914
            };
915
916
0
            let f_iter = f
917
0
                .lookup_list_indices()
918
0
                .iter()
919
0
                .filter_map(|i| lookup_indices.contains(i.get()).then_some(i.get()));
920
0
            let other_f_iter = other_f
921
0
                .lookup_list_indices()
922
0
                .iter()
923
0
                .filter_map(|i| lookup_indices.contains(i.get()).then_some(i.get()));
924
0
            if !f_iter.eq(other_f_iter) {
925
0
                continue;
926
            } else {
927
0
                out.insert(i, other_f_idx);
928
0
                break;
929
            }
930
        }
931
932
0
        let o = out.entry(i).or_insert(i);
933
        // no duplicate for this index
934
0
        if *o == i {
935
0
            same_tag_features.insert(i);
936
0
        }
937
    }
938
0
    out
939
0
}
940
941
pub(crate) struct PruneLangSysContext<'a> {
942
    script_count: u16,
943
    langsys_feature_count: u16,
944
    // IN: retained feature indices map:
945
    // duplicate features will be mapped to the same value
946
    feature_index_map: &'a FnvHashMap<u16, u16>,
947
    // OUT: retained feature indices after pruning
948
    feature_indices: IntSet<u16>,
949
    // OUT: retained script->langsys map after pruning
950
    script_langsys_map: FnvHashMap<u16, IntSet<u16>>,
951
}
952
953
impl<'a> PruneLangSysContext<'a> {
954
0
    pub(crate) fn new(feature_index_map: &'a FnvHashMap<u16, u16>) -> Self {
955
0
        Self {
956
0
            script_count: 0,
957
0
            langsys_feature_count: 0,
958
0
            feature_index_map,
959
0
            feature_indices: IntSet::empty(),
960
0
            script_langsys_map: FnvHashMap::default(),
961
0
        }
962
0
    }
963
964
0
    fn visit_script(&mut self) -> bool {
965
0
        let ret = self.script_count < MAX_SCRIPTS;
966
0
        self.script_count += 1;
967
0
        ret
968
0
    }
969
970
0
    fn visit_langsys(&mut self, feature_count: u16) -> bool {
971
0
        self.langsys_feature_count += feature_count;
972
0
        self.langsys_feature_count < MAX_LANGSYS_FEATURE_COUNT
973
0
    }
974
975
0
    fn collect_langsys_features(&mut self, langsys: &LangSys) {
976
0
        let required_feature_index = langsys.required_feature_index();
977
0
        if required_feature_index == 0xFFFF_u16 && langsys.feature_index_count() == 0 {
978
0
            return;
979
0
        }
980
981
0
        if required_feature_index != 0xFFFF_u16
982
0
            && self.feature_index_map.contains_key(&required_feature_index)
983
0
        {
984
0
            self.feature_indices.insert(required_feature_index);
985
0
        }
986
987
0
        self.feature_indices
988
0
            .extend_unsorted(langsys.feature_indices().iter().filter_map(|i| {
989
0
                self.feature_index_map
990
0
                    .contains_key(&i.get())
991
0
                    .then_some(i.get())
992
0
            }));
993
0
    }
994
995
0
    fn check_equal(&self, la: &LangSys, lb: &LangSys) -> bool {
996
0
        if la.required_feature_index() != lb.required_feature_index() {
997
0
            return false;
998
0
        }
999
1000
0
        let iter_a = la
1001
0
            .feature_indices()
1002
0
            .iter()
1003
0
            .filter_map(|i| self.feature_index_map.get(&i.get()));
1004
0
        let iter_b = lb
1005
0
            .feature_indices()
1006
0
            .iter()
1007
0
            .filter_map(|i| self.feature_index_map.get(&i.get()));
1008
1009
0
        iter_a.eq(iter_b)
1010
0
    }
1011
1012
0
    fn add_script_langsys(&mut self, script_index: u16, langsys_index: u16) {
1013
0
        let langsys_indices = self
1014
0
            .script_langsys_map
1015
0
            .entry(script_index)
1016
0
            .or_insert(IntSet::empty());
1017
0
        langsys_indices.insert(langsys_index);
1018
0
    }
1019
1020
0
    pub(crate) fn prune_script_langsys(&mut self, script_index: u16, script: &Script) {
1021
0
        if script.lang_sys_count() == 0 && script.default_lang_sys_offset().is_null() {
1022
0
            return;
1023
0
        }
1024
1025
0
        if !self.visit_script() {
1026
0
            return;
1027
0
        }
1028
1029
0
        if let Some(Ok(default_langsys)) = script.default_lang_sys() {
1030
0
            if self.visit_langsys(default_langsys.feature_index_count()) {
1031
0
                self.collect_langsys_features(&default_langsys);
1032
0
            }
1033
1034
0
            for (i, langsys_rec) in script.lang_sys_records().iter().enumerate() {
1035
0
                let Ok(l) = langsys_rec.lang_sys(script.offset_data()) else {
1036
0
                    return;
1037
                };
1038
0
                if !self.visit_langsys(l.feature_index_count()) {
1039
0
                    return;
1040
0
                }
1041
1042
0
                if self.check_equal(&l, &default_langsys) {
1043
0
                    continue;
1044
0
                }
1045
0
                self.collect_langsys_features(&l);
1046
0
                self.add_script_langsys(script_index, i as u16);
1047
            }
1048
        } else {
1049
0
            for (i, langsys_rec) in script.lang_sys_records().iter().enumerate() {
1050
0
                let Ok(l) = langsys_rec.lang_sys(script.offset_data()) else {
1051
0
                    return;
1052
                };
1053
0
                if !self.visit_langsys(l.feature_index_count()) {
1054
0
                    return;
1055
0
                }
1056
0
                self.collect_langsys_features(&l);
1057
0
                self.add_script_langsys(script_index, i as u16);
1058
            }
1059
        }
1060
0
    }
1061
1062
0
    pub(crate) fn script_langsys_map(&mut self) -> FnvHashMap<u16, IntSet<u16>> {
1063
0
        mem::take(&mut self.script_langsys_map)
1064
0
    }
1065
1066
0
    pub(crate) fn feature_indices(&mut self) -> IntSet<u16> {
1067
0
        mem::take(&mut self.feature_indices)
1068
0
    }
1069
1070
0
    pub(crate) fn prune_langsys(
1071
0
        &mut self,
1072
0
        script_list: &ScriptList,
1073
0
        layout_scripts: &IntSet<Tag>,
1074
0
    ) -> (FnvHashMap<u16, IntSet<u16>>, IntSet<u16>) {
1075
0
        for (i, script_rec) in script_list.script_records().iter().enumerate() {
1076
0
            let script_tag = script_rec.script_tag();
1077
0
            if !layout_scripts.contains(script_tag) {
1078
0
                continue;
1079
0
            }
1080
1081
0
            let Ok(script) = script_rec.script(script_list.offset_data()) else {
1082
0
                return (self.script_langsys_map(), self.feature_indices());
1083
            };
1084
0
            self.prune_script_langsys(i as u16, &script);
1085
        }
1086
0
        (self.script_langsys_map(), self.feature_indices())
1087
0
    }
1088
}
1089
1090
// remap feature indices: old-> new
1091
// mapping contains unique old feature indices -> new indices mapping only, used by FeatureList subsetting
1092
// mapping_w_duplicate contains all retained feature indices in ScriptList/FeatureVariations subsetting
1093
0
pub(crate) fn remap_feature_indices(
1094
0
    feature_indices: &IntSet<u16>,
1095
0
    duplicate_feature_map: &FnvHashMap<u16, u16>,
1096
0
) -> (FnvHashMap<u16, u16>, FnvHashMap<u16, u16>) {
1097
0
    let mut mapping = FnvHashMap::default();
1098
0
    let mut mapping_w_duplicates = FnvHashMap::default();
1099
0
    let mut i = 0_u16;
1100
0
    for f_idx in feature_indices.iter() {
1101
0
        let unique_f_idx = duplicate_feature_map.get(&f_idx).unwrap_or(&f_idx);
1102
0
        if let Some(new_idx) = mapping.get(unique_f_idx) {
1103
0
            mapping_w_duplicates.insert(f_idx, *new_idx);
1104
0
        } else {
1105
0
            mapping.insert(f_idx, i);
1106
0
            mapping_w_duplicates.insert(f_idx, i);
1107
0
            i += 1;
1108
0
        }
1109
    }
1110
0
    (mapping, mapping_w_duplicates)
1111
0
}
1112
1113
pub(crate) struct SubsetLayoutContext {
1114
    script_count: u16,
1115
    langsys_count: u16,
1116
    feature_index_count: u16,
1117
    lookup_count: u16,
1118
    table_tag: Tag,
1119
}
1120
1121
impl SubsetLayoutContext {
1122
0
    pub(crate) fn new(table_tag: Tag) -> Self {
1123
0
        Self {
1124
0
            script_count: 0,
1125
0
            langsys_count: 0,
1126
0
            feature_index_count: 0,
1127
0
            lookup_count: 0,
1128
0
            table_tag,
1129
0
        }
1130
0
    }
1131
1132
0
    fn visit_script(&mut self) -> bool {
1133
0
        if self.script_count >= MAX_SCRIPTS {
1134
0
            return false;
1135
0
        }
1136
0
        self.script_count += 1;
1137
0
        true
1138
0
    }
1139
1140
0
    fn visit_langsys(&mut self) -> bool {
1141
0
        if self.langsys_count >= MAX_LANGSYS {
1142
0
            return false;
1143
0
        }
1144
0
        self.langsys_count += 1;
1145
0
        true
1146
0
    }
1147
1148
0
    fn visit_feature_index(&mut self, count: u16) -> bool {
1149
0
        let Some(sum) = self.feature_index_count.checked_add(count) else {
1150
0
            return false;
1151
        };
1152
0
        self.feature_index_count = sum;
1153
0
        self.feature_index_count < MAX_FEATURE_INDICES
1154
0
    }
1155
1156
0
    fn visit_lookup(&mut self) -> bool {
1157
0
        if self.lookup_count >= MAX_LOOKUP_VISIT_COUNT {
1158
0
            return false;
1159
0
        }
1160
0
        self.lookup_count += 1;
1161
0
        true
1162
0
    }
1163
}
1164
1165
impl<'a> SubsetTable<'a> for ScriptList<'_> {
1166
    type ArgsForSubset = &'a mut SubsetLayoutContext;
1167
    type Output = ();
1168
0
    fn subset(
1169
0
        &self,
1170
0
        plan: &Plan,
1171
0
        s: &mut Serializer,
1172
0
        c: &mut SubsetLayoutContext,
1173
0
    ) -> Result<(), SerializeErrorFlags> {
1174
0
        let script_count_pos = s.embed(0_u16)?;
1175
0
        let mut num_records = 0_u16;
1176
0
        let font_data = self.offset_data();
1177
0
        for (i, script_record) in self.script_records().iter().enumerate() {
1178
0
            let tag = script_record.script_tag();
1179
0
            if !plan.layout_scripts.contains(tag) {
1180
0
                continue;
1181
0
            }
1182
1183
0
            if !c.visit_script() {
1184
0
                break;
1185
0
            }
1186
1187
0
            let snap = s.snapshot();
1188
0
            match script_record.subset(plan, s, (c, font_data, i)) {
1189
0
                Ok(()) => num_records += 1,
1190
0
                Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => s.revert_snapshot(snap),
1191
0
                Err(e) => return Err(e),
1192
            }
1193
        }
1194
0
        if num_records != 0 {
1195
0
            s.copy_assign(script_count_pos, num_records);
1196
0
        }
1197
0
        Ok(())
1198
0
    }
1199
}
1200
1201
impl<'a> SubsetTable<'a> for ScriptRecord {
1202
    type ArgsForSubset = (&'a mut SubsetLayoutContext, FontData<'a>, usize);
1203
    type Output = ();
1204
0
    fn subset(
1205
0
        &self,
1206
0
        plan: &Plan,
1207
0
        s: &mut Serializer,
1208
0
        args: Self::ArgsForSubset,
1209
0
    ) -> Result<(), SerializeErrorFlags> {
1210
0
        let tag = self.script_tag();
1211
0
        s.embed(tag)?;
1212
0
        let script_offset_pos = s.embed(0_u16)?;
1213
1214
0
        let (c, font_data, script_index) = args;
1215
0
        let Ok(script) = self.script(font_data) else {
1216
0
            return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
1217
        };
1218
1219
0
        Offset16::serialize_subset(&script, s, plan, (c, script_index, tag), script_offset_pos)
1220
0
    }
1221
}
1222
1223
impl<'a> SubsetTable<'a> for Script<'_> {
1224
    type ArgsForSubset = (&'a mut SubsetLayoutContext, usize, Tag);
1225
    type Output = ();
1226
0
    fn subset(
1227
0
        &self,
1228
0
        plan: &Plan,
1229
0
        s: &mut Serializer,
1230
0
        args: Self::ArgsForSubset,
1231
0
    ) -> Result<(), SerializeErrorFlags> {
1232
0
        let default_langsys_offset_pos = s.embed(0_u16)?;
1233
0
        let langsys_count_pos = s.embed(0_u16)?;
1234
0
        let mut langsys_count = 0_u16;
1235
1236
0
        let (c, script_index, script_tag) = args;
1237
0
        let has_default_langsys = if let Some(default_langsys) = self
1238
0
            .default_lang_sys()
1239
0
            .transpose()
1240
0
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?
1241
        {
1242
0
            s.push()?;
1243
0
            let ret = default_langsys.subset(plan, s, c);
1244
0
            if s.in_error() {
1245
0
                return Err(s.error());
1246
0
            }
1247
1248
            // harfbuzz ref: <https://github.com/harfbuzz/harfbuzz/blob/567a0307fa65db03d51a3bcf19d995e57ffc1d24/src/hb-ot-layout-common.hh#L1200>
1249
            // If there is a DFLT script table, it must have a default language system table
1250
0
            if ret.is_err() && script_tag != Tag::new(b"DFLT") {
1251
0
                s.pop_discard();
1252
0
                false
1253
            } else {
1254
0
                let Some(obj_idx) = s.pop_pack(true) else {
1255
0
                    return Err(s.error());
1256
                };
1257
0
                s.add_link(
1258
0
                    default_langsys_offset_pos..default_langsys_offset_pos + Offset16::RAW_BYTE_LEN,
1259
0
                    obj_idx,
1260
0
                    OffsetWhence::Head,
1261
                    0,
1262
                    false,
1263
0
                )?;
1264
0
                true
1265
            }
1266
        } else {
1267
0
            false
1268
        };
1269
1270
0
        let script_langsys_map = if c.table_tag == Gsub::TAG {
1271
0
            &plan.gsub_script_langsys
1272
        } else {
1273
0
            &plan.gpos_script_langsys
1274
        };
1275
1276
0
        if let Some(retained_langsys_idxes) = script_langsys_map.get(&(script_index as u16)) {
1277
0
            let langsys_records = self.lang_sys_records();
1278
0
            for i in retained_langsys_idxes.iter() {
1279
0
                let Some(langsys_rec) = langsys_records.get(i as usize) else {
1280
0
                    continue;
1281
                };
1282
1283
0
                if !c.visit_langsys() {
1284
0
                    break;
1285
0
                }
1286
1287
0
                let snap = s.snapshot();
1288
0
                match langsys_rec.subset(plan, s, (c, self.offset_data())) {
1289
0
                    Ok(()) => langsys_count += 1,
1290
0
                    Err(e) => {
1291
0
                        if s.in_error() {
1292
0
                            return Err(e);
1293
0
                        }
1294
0
                        s.revert_snapshot(snap);
1295
0
                        continue;
1296
                    }
1297
                };
1298
            }
1299
0
        }
1300
1301
0
        if has_default_langsys || langsys_count != 0 || c.table_tag == Gsub::TAG {
1302
0
            s.copy_assign(langsys_count_pos, langsys_count);
1303
0
            Ok(())
1304
        } else {
1305
0
            Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY)
1306
        }
1307
0
    }
1308
}
1309
1310
impl<'a> SubsetTable<'a> for LangSysRecord {
1311
    type ArgsForSubset = (&'a mut SubsetLayoutContext, FontData<'a>);
1312
    type Output = ();
1313
0
    fn subset(
1314
0
        &self,
1315
0
        plan: &Plan,
1316
0
        s: &mut Serializer,
1317
0
        args: Self::ArgsForSubset,
1318
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1319
0
        let tag = self.lang_sys_tag();
1320
0
        s.embed(tag)?;
1321
0
        let langsys_offset_pos = s.embed(0_u16)?;
1322
1323
0
        let (c, font_data) = args;
1324
0
        let Ok(langsys) = self.lang_sys(font_data) else {
1325
0
            return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
1326
        };
1327
1328
0
        Offset16::serialize_subset(&langsys, s, plan, c, langsys_offset_pos)
1329
0
    }
1330
}
1331
1332
impl<'a> SubsetTable<'a> for LangSys<'a> {
1333
    type ArgsForSubset = &'a mut SubsetLayoutContext;
1334
    type Output = ();
1335
0
    fn subset(
1336
0
        &self,
1337
0
        plan: &Plan,
1338
0
        s: &mut Serializer,
1339
0
        c: &mut SubsetLayoutContext,
1340
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1341
        // reserved field
1342
0
        s.embed(0_u16)?;
1343
1344
0
        let feature_index_map = if c.table_tag == Gsub::TAG {
1345
0
            &plan.gsub_features_w_duplicates
1346
        } else {
1347
0
            &plan.gpos_features_w_duplicates
1348
        };
1349
        // required feature index
1350
0
        let required_feature_idx = self.required_feature_index();
1351
0
        let new_required_idx = *feature_index_map
1352
0
            .get(&required_feature_idx)
1353
0
            .unwrap_or(&0xFFFF_u16);
1354
0
        s.embed(new_required_idx)?;
1355
1356
0
        let mut index_count = 0_u16;
1357
0
        let index_count_pos = s.embed(index_count)?;
1358
1359
0
        if !c.visit_feature_index(self.feature_index_count()) {
1360
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_OTHER);
1361
0
        }
1362
0
        for new_idx in self
1363
0
            .feature_indices()
1364
0
            .iter()
1365
0
            .filter_map(|i| feature_index_map.get(&i.get()))
1366
        {
1367
0
            s.embed(*new_idx)?;
1368
0
            index_count += 1;
1369
        }
1370
1371
0
        if index_count == 0 && new_required_idx == 0xFFFF {
1372
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
1373
0
        }
1374
0
        s.copy_assign(index_count_pos, index_count);
1375
0
        Ok(())
1376
0
    }
1377
}
1378
1379
impl<'a> SubsetTable<'a> for FeatureList<'_> {
1380
    type ArgsForSubset = &'a mut SubsetLayoutContext;
1381
    type Output = ();
1382
0
    fn subset(
1383
0
        &self,
1384
0
        plan: &Plan,
1385
0
        s: &mut Serializer,
1386
0
        c: &mut SubsetLayoutContext,
1387
0
    ) -> Result<(), SerializeErrorFlags> {
1388
0
        let feature_count_pos = s.embed(0_u16)?;
1389
0
        let mut num_records = 0_u16;
1390
0
        let font_data = self.offset_data();
1391
0
        let feature_index_map = if c.table_tag == Gsub::TAG {
1392
0
            &plan.gsub_features
1393
        } else {
1394
0
            &plan.gpos_features
1395
        };
1396
0
        for (_, feature_record) in self
1397
0
            .feature_records()
1398
0
            .iter()
1399
0
            .enumerate()
1400
0
            .filter(|&(i, _)| feature_index_map.contains_key(&(i as u16)))
1401
        {
1402
0
            feature_record.subset(plan, s, (c, font_data))?;
1403
0
            num_records += 1;
1404
        }
1405
0
        if num_records != 0 {
1406
0
            s.copy_assign(feature_count_pos, num_records);
1407
0
        }
1408
0
        Ok(())
1409
0
    }
1410
}
1411
1412
impl<'a> SubsetTable<'a> for FeatureRecord {
1413
    type ArgsForSubset = (&'a mut SubsetLayoutContext, FontData<'a>);
1414
    type Output = ();
1415
0
    fn subset(
1416
0
        &self,
1417
0
        plan: &Plan,
1418
0
        s: &mut Serializer,
1419
0
        args: Self::ArgsForSubset,
1420
0
    ) -> Result<(), SerializeErrorFlags> {
1421
0
        let tag = self.feature_tag();
1422
0
        s.embed(tag)?;
1423
0
        let feature_offset_pos = s.embed(0_u16)?;
1424
1425
0
        let (c, font_data) = args;
1426
0
        let Ok(feature) = self.feature(font_data) else {
1427
0
            return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
1428
        };
1429
1430
0
        Offset16::serialize_subset(&feature, s, plan, c, feature_offset_pos)
1431
0
    }
1432
}
1433
1434
impl<'a> SubsetTable<'a> for Feature<'_> {
1435
    type ArgsForSubset = &'a mut SubsetLayoutContext;
1436
    type Output = ();
1437
0
    fn subset(
1438
0
        &self,
1439
0
        plan: &Plan,
1440
0
        s: &mut Serializer,
1441
0
        c: &mut SubsetLayoutContext,
1442
0
    ) -> Result<(), SerializeErrorFlags> {
1443
        //FeatureParams offset
1444
0
        let feature_params_offset_pos = s.embed(0_u16)?;
1445
0
        let lookup_count_pos = s.embed(0_u16)?;
1446
0
        let mut lookup_count = 0_u16;
1447
0
        let lookup_index_map = if c.table_tag == Gsub::TAG {
1448
0
            &plan.gsub_lookups
1449
        } else {
1450
0
            &plan.gpos_lookups
1451
        };
1452
1453
0
        for idx in self
1454
0
            .lookup_list_indices()
1455
0
            .iter()
1456
0
            .filter_map(|i| lookup_index_map.get(&i.get()))
1457
        {
1458
0
            if !c.visit_lookup() {
1459
0
                break;
1460
0
            }
1461
0
            s.embed(*idx)?;
1462
0
            lookup_count += 1;
1463
        }
1464
1465
0
        if lookup_count != 0 {
1466
0
            s.copy_assign(lookup_count_pos, lookup_count);
1467
0
        }
1468
1469
0
        if let Some(feature_params) = self
1470
0
            .feature_params()
1471
0
            .transpose()
1472
0
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?
1473
        {
1474
0
            Offset16::serialize_subset(&feature_params, s, plan, (), feature_params_offset_pos)?;
1475
0
        }
1476
0
        Ok(())
1477
0
    }
1478
}
1479
1480
impl<'a> SubsetTable<'a> for FeatureParams<'_> {
1481
    type ArgsForSubset = ();
1482
    type Output = ();
1483
0
    fn subset(
1484
0
        &self,
1485
0
        _plan: &Plan,
1486
0
        s: &mut Serializer,
1487
0
        _args: (),
1488
0
    ) -> Result<(), SerializeErrorFlags> {
1489
0
        let ret = match self {
1490
0
            FeatureParams::StylisticSet(table) => s.embed_bytes(table.min_table_bytes()),
1491
0
            FeatureParams::Size(table) => s.embed_bytes(table.min_table_bytes()),
1492
0
            FeatureParams::CharacterVariant(table) => s.embed_bytes(table.min_table_bytes()),
1493
        };
1494
0
        ret.map(|_| ())
1495
0
    }
1496
}
1497
1498
impl<
1499
        'a,
1500
        T: FontRead<'a>
1501
            + SubsetTable<
1502
                'a,
1503
                ArgsForSubset = (&'a SubsetState, &'a FontRef<'a>, &'a FnvHashMap<u16, u16>),
1504
            >,
1505
    > SubsetTable<'a> for LookupList<'a, T>
1506
{
1507
    type ArgsForSubset = (&'a SubsetState, &'a FontRef<'a>, &'a FnvHashMap<u16, u16>);
1508
    type Output = ();
1509
0
    fn subset(
1510
0
        &self,
1511
0
        plan: &Plan,
1512
0
        s: &mut Serializer,
1513
0
        args: Self::ArgsForSubset,
1514
0
    ) -> Result<(), SerializeErrorFlags> {
1515
0
        let lookup_index_map = args.2;
1516
0
        let lookup_count = lookup_index_map.len() as u16;
1517
0
        s.embed(lookup_count)?;
1518
1519
0
        let lookup_offsets = self.lookups();
1520
0
        for i in (0..self.lookup_count()).filter(|idx| lookup_index_map.contains_key(idx)) {
Unexecuted instantiation: <read_fonts::table_ref::TableRef<read_fonts::tables::layout::LookupListMarker<read_fonts::tables::gpos::PositionLookup>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::table_ref::TableRef<read_fonts::tables::layout::LookupListMarker<read_fonts::tables::gsub::SubstitutionLookup>> as klippa::SubsetTable>::subset::{closure#0}
1521
0
            lookup_offsets.subset_offset(i as usize, s, plan, args)?;
1522
        }
1523
0
        Ok(())
1524
0
    }
Unexecuted instantiation: <read_fonts::table_ref::TableRef<read_fonts::tables::layout::LookupListMarker<read_fonts::tables::gpos::PositionLookup>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::table_ref::TableRef<read_fonts::tables::layout::LookupListMarker<read_fonts::tables::gsub::SubstitutionLookup>> as klippa::SubsetTable>::subset
1525
}
1526
1527
impl<'a> SubsetTable<'a> for FeatureVariations<'_> {
1528
    type ArgsForSubset = &'a mut SubsetLayoutContext;
1529
    type Output = ();
1530
0
    fn subset(
1531
0
        &self,
1532
0
        plan: &Plan,
1533
0
        s: &mut Serializer,
1534
0
        c: &mut SubsetLayoutContext,
1535
0
    ) -> Result<(), SerializeErrorFlags> {
1536
0
        let feature_index_map = if c.table_tag == Gsub::TAG {
1537
0
            &plan.gsub_features_w_duplicates
1538
        } else {
1539
0
            &plan.gpos_features_w_duplicates
1540
        };
1541
0
        let num_retained_records = num_variation_record_to_retain(self, feature_index_map, s)?;
1542
0
        if num_retained_records == 0 {
1543
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
1544
0
        }
1545
1546
0
        s.embed(self.version())?;
1547
0
        s.embed(num_retained_records)?;
1548
1549
0
        let font_data = self.offset_data();
1550
1551
0
        let variation_records = self.feature_variation_records();
1552
0
        for i in 0..num_retained_records {
1553
0
            variation_records[i as usize].subset(plan, s, (font_data, feature_index_map, c))?;
1554
        }
1555
0
        Ok(())
1556
0
    }
1557
}
1558
1559
// Prune empty records at the end only
1560
// ref: <https://github.com/fonttools/fonttools/blob/3c1822544d608f87c41fc8fb9ba41ea129257aa8/Lib/fontTools/subset/__init__.py#L1782>
1561
0
fn num_variation_record_to_retain(
1562
0
    feature_variations: &FeatureVariations,
1563
0
    feature_index_map: &FnvHashMap<u16, u16>,
1564
0
    s: &mut Serializer,
1565
0
) -> Result<u32, SerializeErrorFlags> {
1566
0
    let num_records = feature_variations.feature_variation_record_count();
1567
0
    let variation_records = feature_variations.feature_variation_records();
1568
0
    let font_data = feature_variations.offset_data();
1569
1570
0
    for i in (0..num_records).rev() {
1571
0
        let Some(feature_substitution) = variation_records[i as usize]
1572
0
            .feature_table_substitution(font_data)
1573
0
            .transpose()
1574
0
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?
1575
        else {
1576
0
            continue;
1577
        };
1578
1579
0
        if feature_substitution
1580
0
            .substitutions()
1581
0
            .iter()
1582
0
            .any(|subs| feature_index_map.contains_key(&subs.feature_index()))
1583
        {
1584
0
            return Ok(i + 1);
1585
0
        }
1586
    }
1587
0
    Ok(0)
1588
0
}
1589
1590
impl<'a> SubsetTable<'a> for FeatureVariationRecord {
1591
    type ArgsForSubset = (
1592
        FontData<'a>,
1593
        &'a FnvHashMap<u16, u16>,
1594
        &'a mut SubsetLayoutContext,
1595
    );
1596
    type Output = ();
1597
0
    fn subset(
1598
0
        &self,
1599
0
        plan: &Plan,
1600
0
        s: &mut Serializer,
1601
0
        args: Self::ArgsForSubset,
1602
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1603
0
        let (font_data, feature_index_map, c) = args;
1604
0
        let condition_set_offset_pos = s.embed(0_u32)?;
1605
0
        if let Some(condition_set) = self
1606
0
            .condition_set(font_data)
1607
0
            .transpose()
1608
0
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?
1609
        {
1610
0
            Offset32::serialize_subset(&condition_set, s, plan, (), condition_set_offset_pos)?;
1611
0
        }
1612
1613
0
        let feature_substitutions_offset_pos = s.embed(0_u32)?;
1614
0
        if let Some(feature_subs) = self
1615
0
            .feature_table_substitution(font_data)
1616
0
            .transpose()
1617
0
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?
1618
        {
1619
0
            Offset32::serialize_subset(
1620
0
                &feature_subs,
1621
0
                s,
1622
0
                plan,
1623
0
                (feature_index_map, c),
1624
0
                feature_substitutions_offset_pos,
1625
0
            )?;
1626
0
        }
1627
1628
0
        Ok(())
1629
0
    }
1630
}
1631
1632
impl SubsetTable<'_> for ConditionSet<'_> {
1633
    type ArgsForSubset = ();
1634
    type Output = ();
1635
0
    fn subset(
1636
0
        &self,
1637
0
        plan: &Plan,
1638
0
        s: &mut Serializer,
1639
0
        _args: Self::ArgsForSubset,
1640
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1641
0
        let count_pos = s.embed(0_u16)?;
1642
0
        let mut count = 0_u16;
1643
1644
0
        let conditions = self.conditions();
1645
0
        for i in 0..self.condition_count() {
1646
0
            match conditions.subset_offset(i as usize, s, plan, ()) {
1647
0
                Ok(()) => count += 1,
1648
0
                Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => continue,
1649
0
                Err(e) => return Err(e),
1650
            }
1651
        }
1652
1653
0
        if count != 0 {
1654
0
            s.copy_assign(count_pos, count);
1655
0
        }
1656
0
        Ok(())
1657
0
    }
1658
}
1659
1660
impl SubsetTable<'_> for Condition<'_> {
1661
    type ArgsForSubset = ();
1662
    type Output = ();
1663
0
    fn subset(
1664
0
        &self,
1665
0
        plan: &Plan,
1666
0
        s: &mut Serializer,
1667
0
        _args: Self::ArgsForSubset,
1668
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1669
0
        match self {
1670
0
            Self::Format1AxisRange(item) => item.subset(plan, s, ()),
1671
            // TODO: support other formats
1672
0
            _ => Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY),
1673
        }
1674
0
    }
1675
}
1676
1677
impl SubsetTable<'_> for ConditionFormat1<'_> {
1678
    type ArgsForSubset = ();
1679
    type Output = ();
1680
0
    fn subset(
1681
0
        &self,
1682
0
        _plan: &Plan,
1683
0
        s: &mut Serializer,
1684
0
        _args: Self::ArgsForSubset,
1685
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1686
0
        s.embed_bytes(self.min_table_bytes()).map(|_| ())
1687
0
    }
1688
}
1689
1690
impl<'a> SubsetTable<'a> for FeatureTableSubstitution<'_> {
1691
    type ArgsForSubset = (&'a FnvHashMap<u16, u16>, &'a mut SubsetLayoutContext);
1692
    type Output = ();
1693
0
    fn subset(
1694
0
        &self,
1695
0
        plan: &Plan,
1696
0
        s: &mut Serializer,
1697
0
        args: Self::ArgsForSubset,
1698
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1699
0
        s.embed(self.version())?;
1700
1701
        // substitution count
1702
0
        let subs_count_pos = s.embed(0_u16)?;
1703
0
        let mut subs_count = 0_u16;
1704
1705
0
        let (feature_index_map, c) = args;
1706
0
        let font_data = self.offset_data();
1707
0
        for sub in self.substitutions() {
1708
0
            match sub.subset(plan, s, (feature_index_map, c, font_data)) {
1709
0
                Ok(()) => subs_count += 1,
1710
0
                Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => continue,
1711
0
                Err(e) => return Err(e),
1712
            }
1713
        }
1714
1715
0
        if subs_count != 0 {
1716
0
            s.copy_assign(subs_count_pos, subs_count);
1717
0
        }
1718
0
        Ok(())
1719
0
    }
1720
}
1721
1722
impl<'a> SubsetTable<'a> for FeatureTableSubstitutionRecord {
1723
    type ArgsForSubset = (
1724
        &'a FnvHashMap<u16, u16>,
1725
        &'a mut SubsetLayoutContext,
1726
        FontData<'a>,
1727
    );
1728
    type Output = ();
1729
0
    fn subset(
1730
0
        &self,
1731
0
        plan: &Plan,
1732
0
        s: &mut Serializer,
1733
0
        args: Self::ArgsForSubset,
1734
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1735
0
        let (feature_index_map, c, font_data) = args;
1736
0
        let Some(new_feature_indx) = feature_index_map.get(&self.feature_index()) else {
1737
0
            return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
1738
        };
1739
1740
0
        let alternate_feature = self
1741
0
            .alternate_feature(font_data)
1742
0
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?;
1743
0
        s.embed(*new_feature_indx)?;
1744
1745
0
        let feature_offset_pos = s.embed(0_u32)?;
1746
0
        Offset32::serialize_subset(&alternate_feature, s, plan, c, feature_offset_pos)
1747
0
    }
1748
}
1749
1750
impl<'a, T, Ext> SubsetTable<'a> for Subtables<'a, T, Ext>
1751
where
1752
    T: SubsetTable<
1753
            'a,
1754
            ArgsForSubset = (&'a SubsetState, &'a FontRef<'a>, &'a FnvHashMap<u16, u16>),
1755
        > + Intersect
1756
        + FontRead<'a>
1757
        + 'a,
1758
    Ext: ExtensionLookup<'a, T> + 'a,
1759
{
1760
    type ArgsForSubset = T::ArgsForSubset;
1761
    type Output = u16;
1762
0
    fn subset(
1763
0
        &self,
1764
0
        plan: &Plan,
1765
0
        s: &mut Serializer,
1766
0
        args: Self::ArgsForSubset,
1767
0
    ) -> Result<Self::Output, SerializeErrorFlags> {
1768
0
        let mut count = 0_u16;
1769
0
        for sub in self.iter() {
1770
0
            let sub =
1771
0
                sub.map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?;
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::CursivePosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::CursivePosFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkLigPosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkLigPosFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkBasePosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkBasePosFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkMarkPosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkMarkPosFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::LigatureSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::LigatureSubstFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::MultipleSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::MultipleSubstFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::AlternateSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::AlternateSubstFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::SequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::layout::SequenceContext>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::SequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::layout::SequenceContext>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::ChainedSequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::layout::ChainedSequenceContext>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::ChainedSequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::layout::ChainedSequenceContext>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gpos::PairPos, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::gpos::PairPos>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gpos::SinglePos, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::gpos::SinglePos>>> as klippa::SubsetTable>::subset::{closure#0}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gsub::SingleSubst, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::gsub::SingleSubst>>> as klippa::SubsetTable>::subset::{closure#0}
1772
1773
0
            if !sub
1774
0
                .intersects(&plan.glyphset_gsub)
1775
0
                .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::CursivePosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::CursivePosFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkLigPosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkLigPosFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkBasePosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkBasePosFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkMarkPosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkMarkPosFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::LigatureSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::LigatureSubstFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::MultipleSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::MultipleSubstFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::AlternateSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::AlternateSubstFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1Marker>>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::SequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::layout::SequenceContext>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::SequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::layout::SequenceContext>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::ChainedSequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::layout::ChainedSequenceContext>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::ChainedSequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::layout::ChainedSequenceContext>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gpos::PairPos, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::gpos::PairPos>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gpos::SinglePos, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::gpos::SinglePos>>> as klippa::SubsetTable>::subset::{closure#1}
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gsub::SingleSubst, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::gsub::SingleSubst>>> as klippa::SubsetTable>::subset::{closure#1}
1776
            {
1777
0
                continue;
1778
0
            }
1779
0
            let snap = s.snapshot();
1780
0
            let offset_pos = s.embed(0_u16)?;
1781
0
            match Offset16::serialize_subset(&sub, s, plan, args, offset_pos) {
1782
0
                Ok(_) => count += 1,
1783
0
                Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => s.revert_snapshot(snap),
1784
0
                Err(e) => {
1785
0
                    s.revert_snapshot(snap);
1786
0
                    return Err(e);
1787
                }
1788
            }
1789
        }
1790
0
        Ok(count)
1791
0
    }
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::CursivePosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::CursivePosFormat1Marker>>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkLigPosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkLigPosFormat1Marker>>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkBasePosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkBasePosFormat1Marker>>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkMarkPosFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gpos::MarkMarkPosFormat1Marker>>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::LigatureSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::LigatureSubstFormat1Marker>>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::MultipleSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::MultipleSubstFormat1Marker>>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::AlternateSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::AlternateSubstFormat1Marker>>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1Marker>, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ReverseChainSingleSubstFormat1Marker>>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::SequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::layout::SequenceContext>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::SequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::layout::SequenceContext>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::ChainedSequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::layout::ChainedSequenceContext>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::layout::ChainedSequenceContext, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::layout::ChainedSequenceContext>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gpos::PairPos, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::gpos::PairPos>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gpos::SinglePos, read_fonts::table_ref::TableRef<read_fonts::tables::gpos::ExtensionPosFormat1Marker<read_fonts::tables::gpos::SinglePos>>> as klippa::SubsetTable>::subset
Unexecuted instantiation: <read_fonts::tables::layout::Subtables<read_fonts::tables::gsub::SingleSubst, read_fonts::table_ref::TableRef<read_fonts::tables::gsub::ExtensionSubstFormat1Marker<read_fonts::tables::gsub::SingleSubst>>> as klippa::SubsetTable>::subset
1792
}
1793
1794
#[cfg(test)]
1795
mod test {
1796
    use super::*;
1797
    use write_fonts::{
1798
        read::{
1799
            tables::gpos::{PairPos, PositionSubtables},
1800
            FontRef, TableProvider,
1801
        },
1802
        types::GlyphId,
1803
    };
1804
1805
    #[test]
1806
    fn test_subset_gpos_classdefs() {
1807
        let font = FontRef::new(include_bytes!("../test-data/fonts/AdobeVFPrototype.otf")).unwrap();
1808
        let gpos = font.gpos().unwrap();
1809
        let gpos_lookup = gpos.lookup_list().unwrap().lookups().get(0).unwrap();
1810
1811
        let Ok(PositionSubtables::Pair(subtables)) = gpos_lookup.subtables() else {
1812
            panic!("invalid lookup!")
1813
        };
1814
        let Ok(PairPos::Format2(pair_pos2)) = subtables.get(1) else {
1815
            panic!("invalid lookup!")
1816
        };
1817
1818
        let class_def1 = pair_pos2.class_def1().unwrap();
1819
        let class_def2 = pair_pos2.class_def2().unwrap();
1820
        let coverage = pair_pos2.coverage().unwrap();
1821
1822
        let mut plan = Plan::default();
1823
        plan.glyphset_gsub.insert(GlyphId::NOTDEF);
1824
        plan.glyphset_gsub.insert(GlyphId::from(34_u32));
1825
        plan.glyphset_gsub.insert(GlyphId::from(35_u32));
1826
1827
        plan.glyph_map_gsub.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
1828
        plan.glyph_map_gsub
1829
            .insert(GlyphId::from(34_u32), GlyphId::from(1_u32));
1830
        plan.glyph_map_gsub
1831
            .insert(GlyphId::from(35_u32), GlyphId::from(2_u32));
1832
1833
        let mut s = Serializer::new(1024);
1834
        assert_eq!(s.start_serialize(), Ok(()));
1835
        //test ClassDef1: remap_class: true, keep_empty_table: true, use_class_zero: true, use glyph_filter:Some(&Coverage)
1836
        let ret = class_def1.subset(
1837
            &plan,
1838
            &mut s,
1839
            &ClassDefSubsetStruct {
1840
                remap_class: true,
1841
                keep_empty_table: true,
1842
                use_class_zero: true,
1843
                glyph_filter: Some(&coverage),
1844
            },
1845
        );
1846
        assert!(ret.is_ok());
1847
        assert!(!s.in_error());
1848
        s.end_serialize();
1849
1850
        let subsetted_data = s.copy_bytes();
1851
        let expected_bytes: [u8; 8] = [0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01];
1852
        assert_eq!(subsetted_data, expected_bytes);
1853
1854
        let ret_hashmap = ret.unwrap().unwrap();
1855
        assert_eq!(ret_hashmap.len(), 2);
1856
        assert_eq!(ret_hashmap.get(&2), Some(&0));
1857
        assert_eq!(ret_hashmap.get(&44), Some(&1));
1858
1859
        // test subset ClassDef2:
1860
        // remap_class: true, keep_empty_table: true, use_class_zero: false, no glyph_filter: None
1861
        let mut s = Serializer::new(1024);
1862
        assert_eq!(s.start_serialize(), Ok(()));
1863
        let ret = class_def2.subset(
1864
            &plan,
1865
            &mut s,
1866
            &ClassDefSubsetStruct {
1867
                remap_class: true,
1868
                keep_empty_table: true,
1869
                use_class_zero: false,
1870
                glyph_filter: None,
1871
            },
1872
        );
1873
        assert!(ret.is_ok());
1874
        assert!(!s.in_error());
1875
        s.end_serialize();
1876
1877
        let subsetted_data = s.copy_bytes();
1878
        let expected_bytes: [u8; 10] = [0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01];
1879
        assert_eq!(subsetted_data, expected_bytes);
1880
1881
        let ret_hashmap = ret.unwrap().unwrap();
1882
        assert_eq!(ret_hashmap.len(), 3);
1883
        assert_eq!(ret_hashmap.get(&0), Some(&0));
1884
        assert_eq!(ret_hashmap.get(&1), Some(&1));
1885
        assert_eq!(ret_hashmap.get(&5), Some(&2));
1886
    }
1887
1888
    #[test]
1889
    fn test_subset_gdef_glyph_classdef() {
1890
        let font = FontRef::new(include_bytes!(
1891
            "../test-data/fonts/IndicTestHowrah-Regular.ttf"
1892
        ))
1893
        .unwrap();
1894
        let gdef = font.gdef().unwrap();
1895
        let glyph_class_def = gdef.glyph_class_def().unwrap().unwrap();
1896
1897
        let mut plan = Plan::default();
1898
        plan.glyphset_gsub.insert(GlyphId::NOTDEF);
1899
        plan.glyphset_gsub.insert(GlyphId::from(68_u32));
1900
1901
        plan.glyph_map_gsub.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
1902
        plan.glyph_map_gsub
1903
            .insert(GlyphId::from(68_u32), GlyphId::from(1_u32));
1904
1905
        let mut s = Serializer::new(1024);
1906
        assert_eq!(s.start_serialize(), Ok(()));
1907
        //remap_class: false, keep_empty_table: false, use_class_zero: true, no glyph_filter:None
1908
        let ret = glyph_class_def.subset(
1909
            &plan,
1910
            &mut s,
1911
            &ClassDefSubsetStruct {
1912
                remap_class: false,
1913
                keep_empty_table: false,
1914
                use_class_zero: true,
1915
                glyph_filter: None,
1916
            },
1917
        );
1918
        assert!(ret.is_ok());
1919
        assert!(ret.unwrap().is_none());
1920
1921
        assert!(!s.in_error());
1922
        s.end_serialize();
1923
1924
        let subsetted_data = s.copy_bytes();
1925
        let expected_bytes: [u8; 8] = [0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01];
1926
        assert_eq!(subsetted_data, expected_bytes);
1927
    }
1928
1929
    #[test]
1930
    fn test_subset_coverage_format2_to_format1() {
1931
        let font = FontRef::new(include_bytes!(
1932
            "../test-data/fonts/IndicTestHowrah-Regular.ttf"
1933
        ))
1934
        .unwrap();
1935
        let gdef = font.gdef().unwrap();
1936
        let attach_list = gdef.attach_list().unwrap().unwrap();
1937
        let coverage = attach_list.coverage().unwrap();
1938
1939
        let mut plan = Plan {
1940
            font_num_glyphs: 611,
1941
            ..Default::default()
1942
        };
1943
        plan.glyphset_gsub.insert(GlyphId::NOTDEF);
1944
        plan.glyphset_gsub.insert(GlyphId::from(68_u32));
1945
1946
        plan.glyph_map_gsub.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
1947
        plan.glyph_map_gsub
1948
            .insert(GlyphId::from(68_u32), GlyphId::from(3_u32));
1949
1950
        let mut s = Serializer::new(1024);
1951
        assert_eq!(s.start_serialize(), Ok(()));
1952
        let ret = coverage.subset(&plan, &mut s, ());
1953
        assert!(ret.is_ok());
1954
1955
        assert!(!s.in_error());
1956
        s.end_serialize();
1957
1958
        let subsetted_data = s.copy_bytes();
1959
        let expected_bytes: [u8; 6] = [0x00, 0x01, 0x00, 0x01, 0x00, 0x03];
1960
        assert_eq!(subsetted_data, expected_bytes);
1961
    }
1962
1963
    #[test]
1964
    fn test_subset_coverage_format1() {
1965
        let font = FontRef::new(include_bytes!("../test-data/fonts/AdobeVFPrototype.otf")).unwrap();
1966
        let gpos = font.gpos().unwrap();
1967
        let gpos_lookup = gpos.lookup_list().unwrap().lookups().get(0).unwrap();
1968
1969
        let Ok(PositionSubtables::Pair(subtables)) = gpos_lookup.subtables() else {
1970
            panic!("invalid lookup!")
1971
        };
1972
        let Ok(PairPos::Format1(pair_pos1)) = subtables.get(0) else {
1973
            panic!("invalid lookup!")
1974
        };
1975
1976
        let coverage = pair_pos1.coverage().unwrap();
1977
1978
        let mut plan = Plan {
1979
            font_num_glyphs: 313,
1980
            ..Default::default()
1981
        };
1982
        plan.glyphset_gsub.insert(GlyphId::NOTDEF);
1983
        plan.glyphset_gsub.insert(GlyphId::from(34_u32));
1984
        plan.glyphset_gsub.insert(GlyphId::from(35_u32));
1985
        plan.glyphset_gsub.insert(GlyphId::from(36_u32));
1986
        plan.glyphset_gsub.insert(GlyphId::from(56_u32));
1987
1988
        plan.glyph_map_gsub.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
1989
        plan.glyph_map_gsub
1990
            .insert(GlyphId::from(34_u32), GlyphId::from(1_u32));
1991
        plan.glyph_map_gsub
1992
            .insert(GlyphId::from(35_u32), GlyphId::from(2_u32));
1993
        plan.glyph_map_gsub
1994
            .insert(GlyphId::from(36_u32), GlyphId::from(3_u32));
1995
        plan.glyph_map_gsub
1996
            .insert(GlyphId::from(56_u32), GlyphId::from(4_u32));
1997
1998
        let mut s = Serializer::new(1024);
1999
        assert_eq!(s.start_serialize(), Ok(()));
2000
        let ret = coverage.subset(&plan, &mut s, ());
2001
        assert!(ret.is_ok());
2002
        assert!(!s.in_error());
2003
        s.end_serialize();
2004
2005
        let subsetted_data = s.copy_bytes();
2006
        let expected_bytes: [u8; 8] = [0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04];
2007
        assert_eq!(subsetted_data, expected_bytes);
2008
    }
2009
2010
    #[test]
2011
    fn test_singlepos_closure_lookups() {
2012
        let font = FontRef::new(include_bytes!(
2013
            "../test-data/fonts/gpos_chaining1_multiple_subrules_f1.otf"
2014
        ))
2015
        .unwrap();
2016
        let gpos_lookup_list = font.gpos().unwrap().lookup_list().unwrap();
2017
        let mut lookup_indices = IntSet::empty();
2018
        lookup_indices.insert(1_u16);
2019
2020
        let mut glyphs = IntSet::empty();
2021
        // glyphs set doesn't intersect with any subtable in lookup index=1, lookup_indices set will be emptied
2022
        glyphs.insert(GlyphId::from(3_u32));
2023
        assert!(gpos_lookup_list
2024
            .closure_lookups(&glyphs, &mut lookup_indices)
2025
            .is_ok());
2026
        assert!(lookup_indices.is_empty());
2027
2028
        //reset
2029
        lookup_indices.insert(1);
2030
        glyphs.clear();
2031
2032
        // make glyphs intersect with lookup index=1
2033
        glyphs.insert(GlyphId::from(48_u32));
2034
2035
        // no new lookup indices are added, lookup_indices set remains the same
2036
        assert!(gpos_lookup_list
2037
            .closure_lookups(&glyphs, &mut lookup_indices)
2038
            .is_ok());
2039
        assert_eq!(lookup_indices.len(), 1);
2040
        assert!(lookup_indices.contains(1_u16));
2041
    }
2042
2043
    #[test]
2044
    fn test_context_format1_closure_lookups() {
2045
        let font = FontRef::new(include_bytes!(
2046
            "../test-data/fonts/gpos_chaining1_multiple_subrules_f1.otf"
2047
        ))
2048
        .unwrap();
2049
        let gpos_lookup_list = font.gpos().unwrap().lookup_list().unwrap();
2050
        let mut lookup_indices = IntSet::empty();
2051
        lookup_indices.insert(4_u16);
2052
2053
        let mut glyphs = IntSet::empty();
2054
        // glyphs set doesn't intersect with any subtable in lookup index=4, lookup_indices set will be emptied
2055
        glyphs.insert(GlyphId::from(3_u32));
2056
        assert!(gpos_lookup_list
2057
            .closure_lookups(&glyphs, &mut lookup_indices)
2058
            .is_ok());
2059
        assert!(lookup_indices.is_empty());
2060
2061
        //reset
2062
        lookup_indices.insert(4);
2063
        glyphs.clear();
2064
2065
        // make glyphs intersect with subtable index=1
2066
        // input coverage glyph
2067
        glyphs.insert(GlyphId::from(49_u32));
2068
        // backtrack glyph
2069
        glyphs.insert(GlyphId::from(48_u32));
2070
        // input glyph
2071
        glyphs.insert(GlyphId::from(50_u32));
2072
        // lookahead glyph
2073
        glyphs.insert(GlyphId::from(51_u32));
2074
2075
        assert!(gpos_lookup_list
2076
            .closure_lookups(&glyphs, &mut lookup_indices)
2077
            .is_ok());
2078
        assert_eq!(lookup_indices.len(), 2);
2079
        assert!(lookup_indices.contains(4_u16));
2080
        assert!(lookup_indices.contains(1_u16));
2081
    }
2082
2083
    #[test]
2084
    fn test_context_format2_closure_lookups() {
2085
        let font = FontRef::new(include_bytes!(
2086
            "../test-data/fonts/gpos_chaining2_multiple_subrules_f1.otf"
2087
        ))
2088
        .unwrap();
2089
        let gpos_lookup_list = font.gpos().unwrap().lookup_list().unwrap();
2090
        let mut lookup_indices = IntSet::empty();
2091
        lookup_indices.insert(4_u16);
2092
2093
        let mut glyphs = IntSet::empty();
2094
        // glyphs set doesn't intersect with any subtable in lookup index=4, lookup_indices set will be emptied
2095
        glyphs.insert(GlyphId::from(47_u32));
2096
        assert!(gpos_lookup_list
2097
            .closure_lookups(&glyphs, &mut lookup_indices)
2098
            .is_ok());
2099
        assert!(lookup_indices.is_empty());
2100
2101
        //reset
2102
        lookup_indices.insert(4);
2103
        glyphs.clear();
2104
2105
        // make glyphs intersect with subtable index=1
2106
        // input coverage glyph
2107
        glyphs.insert(GlyphId::from(49_u32));
2108
        glyphs.insert(GlyphId::from(50_u32));
2109
        // backtrack glyph
2110
        glyphs.insert(GlyphId::from(48_u32));
2111
        // lookahead glyph
2112
        glyphs.insert(GlyphId::from(51_u32));
2113
        glyphs.insert(GlyphId::from(52_u32));
2114
2115
        assert!(gpos_lookup_list
2116
            .closure_lookups(&glyphs, &mut lookup_indices)
2117
            .is_ok());
2118
        assert_eq!(lookup_indices.len(), 2);
2119
        assert!(lookup_indices.contains(4_u16));
2120
        assert!(lookup_indices.contains(1_u16));
2121
    }
2122
2123
    #[test]
2124
    fn test_context_format3_closure_lookups() {
2125
        let font = FontRef::new(include_bytes!("../test-data/fonts/Amiri-Regular.ttf")).unwrap();
2126
        let gpos_lookup_list = font.gpos().unwrap().lookup_list().unwrap();
2127
        let mut lookup_indices = IntSet::empty();
2128
        lookup_indices.insert(2_u16);
2129
2130
        let mut glyphs = IntSet::empty();
2131
        // glyphs set doesn't intersect with any subtable in lookup index=2, lookup_indices set will be emptied
2132
        glyphs.insert(GlyphId::from(3_u32));
2133
        assert!(gpos_lookup_list
2134
            .closure_lookups(&glyphs, &mut lookup_indices)
2135
            .is_ok());
2136
        assert!(lookup_indices.is_empty());
2137
2138
        //reset
2139
        lookup_indices.insert(2);
2140
        glyphs.clear();
2141
2142
        // make glyphs intersect with subtable index=0
2143
        // input glyph
2144
        glyphs.insert(GlyphId::from(6053_u32));
2145
        // lookahead glyph
2146
        glyphs.insert(GlyphId::from(580_u32));
2147
2148
        // make glyphs intersect with subtable index=2
2149
        // input glyph
2150
        glyphs.insert(GlyphId::from(2033_u32));
2151
        // lookahead glyph
2152
        glyphs.insert(GlyphId::from(435_u32));
2153
2154
        assert!(gpos_lookup_list
2155
            .closure_lookups(&glyphs, &mut lookup_indices)
2156
            .is_ok());
2157
        assert_eq!(lookup_indices.len(), 3);
2158
        assert!(lookup_indices.contains(3_u16));
2159
        assert!(lookup_indices.contains(4_u16));
2160
    }
2161
2162
    #[test]
2163
    fn test_subset_script_list() {
2164
        use write_fonts::read::tables::gpos::Gpos;
2165
        let font = FontRef::new(include_bytes!("../test-data/fonts/Amiri-Regular.ttf")).unwrap();
2166
        let gpos_script_list = font.gpos().unwrap().script_list().unwrap();
2167
2168
        let mut plan = Plan::default();
2169
        plan.gpos_features_w_duplicates.insert(0_u16, 0_u16);
2170
        plan.gpos_features_w_duplicates.insert(2_u16, 1_u16);
2171
2172
        plan.layout_scripts.invert();
2173
2174
        let mut s = Serializer::new(1024);
2175
        assert_eq!(s.start_serialize(), Ok(()));
2176
2177
        let mut c = SubsetLayoutContext::new(Gpos::TAG);
2178
        gpos_script_list.subset(&plan, &mut s, &mut c).unwrap();
2179
        assert!(!s.in_error());
2180
        s.end_serialize();
2181
2182
        let subsetted_data = s.copy_bytes();
2183
        let expected_data: [u8; 58] = [
2184
            0x00, 0x03, 0x44, 0x46, 0x4c, 0x54, 0x00, 0x2c, 0x61, 0x72, 0x61, 0x62, 0x00, 0x20,
2185
            0x6c, 0x61, 0x74, 0x6e, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
2186
            0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01,
2187
            0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x02, 0x00, 0x00,
2188
            0x00, 0x01,
2189
        ];
2190
2191
        assert_eq!(subsetted_data, expected_data);
2192
    }
2193
2194
    #[test]
2195
    fn test_subset_feature_list() {
2196
        use write_fonts::read::tables::gpos::Gpos;
2197
        let font = FontRef::new(include_bytes!("../test-data/fonts/Amiri-Regular.ttf")).unwrap();
2198
        let gpos_feature_list = font.gpos().unwrap().feature_list().unwrap();
2199
2200
        let mut plan = Plan::default();
2201
        plan.gpos_features.insert(0_u16, 0_u16);
2202
        plan.gpos_features.insert(2_u16, 1_u16);
2203
2204
        plan.gpos_lookups.insert(82, 1);
2205
        plan.gpos_lookups.insert(57, 0);
2206
2207
        let mut s = Serializer::new(1024);
2208
        assert_eq!(s.start_serialize(), Ok(()));
2209
2210
        let mut c = SubsetLayoutContext::new(Gpos::TAG);
2211
        gpos_feature_list.subset(&plan, &mut s, &mut c).unwrap();
2212
        assert!(!s.in_error());
2213
        s.end_serialize();
2214
2215
        let subsetted_data = s.copy_bytes();
2216
        let expected_data: [u8; 26] = [
2217
            0x00, 0x02, 0x63, 0x75, 0x72, 0x73, 0x00, 0x14, 0x6b, 0x65, 0x72, 0x6e, 0x00, 0x0e,
2218
            0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
2219
        ];
2220
2221
        assert_eq!(subsetted_data, expected_data);
2222
    }
2223
}