read_fonts/tables/
cmap.rs

1//! The [cmap](https://docs.microsoft.com/en-us/typography/opentype/spec/cmap) table
2
3include!("../../generated/generated_cmap.rs");
4
5#[cfg(feature = "std")]
6use crate::collections::IntSet;
7use crate::{FontRef, TableProvider};
8use std::ops::Range;
9
10// See <https://docs.microsoft.com/en-us/typography/opentype/spec/cmap#windows-platform-platform-id--3>
11const WINDOWS_SYMBOL_ENCODING: u16 = 0;
12const WINDOWS_UNICODE_BMP_ENCODING: u16 = 1;
13const WINDOWS_UNICODE_FULL_ENCODING: u16 = 10;
14
15// See <https://docs.microsoft.com/en-us/typography/opentype/spec/name#platform-specific-encoding-and-language-ids-unicode-platform-platform-id--0>
16const UNICODE_1_0_ENCODING: u16 = 0;
17const UNICODE_1_1_ENCODING: u16 = 1;
18const UNICODE_ISO_ENCODING: u16 = 2;
19const UNICODE_2_0_BMP_ENCODING: u16 = 3;
20const UNICODE_2_0_FULL_ENCODING: u16 = 4;
21const UNICODE_FULL_ENCODING: u16 = 6;
22
23/// Result of mapping a codepoint with a variation selector.
24#[derive(Copy, Clone, PartialEq, Eq, Debug)]
25pub enum MapVariant {
26    /// The variation selector should be ignored and the default mapping
27    /// of the character should be used.
28    UseDefault,
29    /// The variant glyph mapped by a codepoint and associated variation
30    /// selector.
31    Variant(GlyphId),
32}
33
34impl<'a> Cmap<'a> {
35    /// Map a codepoint to a nominal glyph identifier
36    ///
37    /// This uses the first available subtable that provides a valid mapping.
38    ///
39    /// # Note:
40    ///
41    /// Mapping logic is currently only implemented for the most common subtable
42    /// formats.
43    pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
44        let codepoint = codepoint.into();
45        for record in self.encoding_records() {
46            if let Ok(subtable) = record.subtable(self.offset_data()) {
47                if let Some(gid) = match subtable {
48                    CmapSubtable::Format0(format0) => format0.map_codepoint(codepoint),
49                    CmapSubtable::Format4(format4) => format4.map_codepoint(codepoint),
50                    CmapSubtable::Format6(format6) => format6.map_codepoint(codepoint),
51                    CmapSubtable::Format12(format12) => format12.map_codepoint(codepoint),
52                    CmapSubtable::Format13(format13) => format13.map_codepoint(codepoint),
53                    _ => None,
54                } {
55                    return Some(gid);
56                }
57            }
58        }
59        None
60    }
61
62    /// Returns the index, encoding record and subtable for the most
63    /// comprehensive mapping available.
64    ///
65    /// Comprehensive means that tables capable of mapping the Unicode full
66    /// repertoire are chosen over those that only support the basic
67    /// multilingual plane. The exception is that symbol mappings are
68    /// preferred above all others
69    /// (see <https://github.com/harfbuzz/harfbuzz/issues/1918>).
70    pub fn best_subtable(&self) -> Option<(u16, EncodingRecord, CmapSubtable<'a>)> {
71        // Follows the HarfBuzz approach
72        // See <https://github.com/harfbuzz/harfbuzz/blob/a9a78e1bff9d4a62429d22277fea4e0e76e9ac7e/src/hb-ot-cmap-table.hh#L1962>
73        let offset_data = self.offset_data();
74        let records = self.encoding_records();
75        let find = |platform_id, encoding_id| {
76            for (index, record) in records.iter().enumerate() {
77                if record.platform_id() != platform_id || record.encoding_id() != encoding_id {
78                    continue;
79                }
80                if let Ok(subtable) = record.subtable(offset_data) {
81                    match subtable {
82                        CmapSubtable::Format0(_)
83                        | CmapSubtable::Format4(_)
84                        | CmapSubtable::Format6(_)
85                        | CmapSubtable::Format10(_)
86                        | CmapSubtable::Format12(_)
87                        | CmapSubtable::Format13(_) => {
88                            return Some((index as u16, *record, subtable))
89                        }
90                        _ => {}
91                    }
92                }
93            }
94            None
95        };
96        // Symbol subtable.
97        // Prefer symbol if available.
98        // https://github.com/harfbuzz/harfbuzz/issues/1918
99        find(PlatformId::Windows, WINDOWS_SYMBOL_ENCODING)
100            // 32-bit subtables:
101            .or_else(|| find(PlatformId::Windows, WINDOWS_UNICODE_FULL_ENCODING))
102            .or_else(|| find(PlatformId::Unicode, UNICODE_FULL_ENCODING))
103            .or_else(|| find(PlatformId::Unicode, UNICODE_2_0_FULL_ENCODING))
104            // 16-bit subtables:
105            .or_else(|| find(PlatformId::Windows, WINDOWS_UNICODE_BMP_ENCODING))
106            .or_else(|| find(PlatformId::Unicode, UNICODE_2_0_BMP_ENCODING))
107            .or_else(|| find(PlatformId::Unicode, UNICODE_ISO_ENCODING))
108            .or_else(|| find(PlatformId::Unicode, UNICODE_1_1_ENCODING))
109            .or_else(|| find(PlatformId::Unicode, UNICODE_1_0_ENCODING))
110            // MacRoman subtable:
111            .or_else(|| find(PlatformId::Macintosh, 0))
112    }
113
114    /// Returns the index and subtable for the first mapping capable of
115    /// handling Unicode variation sequences.
116    ///
117    /// This is always a [format 14](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences)
118    /// subtable.
119    pub fn uvs_subtable(&self) -> Option<(u16, Cmap14<'a>)> {
120        let offset_data = self.offset_data();
121        for (index, record) in self.encoding_records().iter().enumerate() {
122            if let Ok(CmapSubtable::Format14(cmap14)) = record.subtable(offset_data) {
123                return Some((index as u16, cmap14));
124            };
125        }
126        None
127    }
128
129    /// Returns the subtable at the given index.
130    pub fn subtable(&self, index: u16) -> Result<CmapSubtable<'a>, ReadError> {
131        self.encoding_records()
132            .get(index as usize)
133            .ok_or(ReadError::OutOfBounds)
134            .and_then(|encoding| encoding.subtable(self.offset_data()))
135    }
136
137    #[cfg(feature = "std")]
138    pub fn closure_glyphs(&self, unicodes: &IntSet<u32>, glyph_set: &mut IntSet<GlyphId>) {
139        for record in self.encoding_records() {
140            if let Ok(subtable) = record.subtable(self.offset_data()) {
141                match subtable {
142                    CmapSubtable::Format14(format14) => {
143                        format14.closure_glyphs(unicodes, glyph_set);
144                        return;
145                    }
146                    _ => {
147                        continue;
148                    }
149                }
150            }
151        }
152    }
153}
154
155impl EncodingRecord {
156    pub fn is_symbol(&self) -> bool {
157        self.platform_id() == PlatformId::Windows && self.encoding_id() == WINDOWS_SYMBOL_ENCODING
158    }
159
160    pub fn is_mac_roman(&self) -> bool {
161        self.platform_id() == PlatformId::Macintosh && self.encoding_id() == 0
162    }
163}
164
165impl CmapSubtable<'_> {
166    pub fn language(&self) -> u32 {
167        match self {
168            Self::Format0(item) => item.language() as u32,
169            Self::Format2(item) => item.language() as u32,
170            Self::Format4(item) => item.language() as u32,
171            Self::Format6(item) => item.language() as u32,
172            Self::Format10(item) => item.language(),
173            Self::Format12(item) => item.language(),
174            Self::Format13(item) => item.language(),
175            _ => 0,
176        }
177    }
178}
179
180impl Cmap0<'_> {
181    pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
182        let codepoint = codepoint.into();
183
184        self.glyph_id_array()
185            .get(codepoint as usize)
186            .map(|g| GlyphId::new(*g as u32))
187    }
188}
189
190impl<'a> Cmap4<'a> {
191    /// Maps a codepoint to a nominal glyph identifier.
192    pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
193        let codepoint = codepoint.into();
194        if codepoint > 0xFFFF {
195            return None;
196        }
197        let codepoint = codepoint as u16;
198        let mut lo = 0;
199        let mut hi = self.seg_count_x2() as usize / 2;
200        let start_codes = self.start_code();
201        let end_codes = self.end_code();
202        while lo < hi {
203            let i = (lo + hi) / 2;
204            let start_code = start_codes.get(i)?.get();
205            if codepoint < start_code {
206                hi = i;
207            } else if codepoint > end_codes.get(i)?.get() {
208                lo = i + 1;
209            } else {
210                return self.lookup_glyph_id(codepoint, i, start_code);
211            }
212        }
213        None
214    }
215
216    /// Returns an iterator over all (codepoint, glyph identifier) pairs
217    /// in the subtable.
218    pub fn iter(&self) -> Cmap4Iter<'a> {
219        Cmap4Iter::new(self.clone())
220    }
221
222    /// Does the final phase of glyph id lookup.
223    ///
224    /// Shared between Self::map and Cmap4Iter.
225    fn lookup_glyph_id(&self, codepoint: u16, index: usize, start_code: u16) -> Option<GlyphId> {
226        let deltas = self.id_delta();
227        let range_offsets = self.id_range_offsets();
228        let delta = deltas.get(index)?.get() as i32;
229        let range_offset = range_offsets.get(index)?.get() as usize;
230        if range_offset == 0 {
231            return Some(GlyphId::from((codepoint as i32 + delta) as u16));
232        }
233        let mut offset = range_offset / 2 + (codepoint - start_code) as usize;
234        offset = offset.saturating_sub(range_offsets.len() - index);
235        let gid = self.glyph_id_array().get(offset)?.get();
236        (gid != 0).then_some(GlyphId::from((gid as i32 + delta) as u16))
237    }
238
239    /// Returns the [start_code, end_code] range at the given index.
240    fn code_range(&self, index: usize) -> Option<Range<u32>> {
241        // Extend to u32 to ensure we don't overflow on the end + 1 bound
242        // below.
243        let start = self.start_code().get(index)?.get() as u32;
244        let end = self.end_code().get(index)?.get() as u32;
245        // Use end + 1 here because the range in the table is inclusive
246        Some(start..end + 1)
247    }
248}
249
250/// Iterator over all (codepoint, glyph identifier) pairs in
251/// the subtable.
252#[derive(Clone)]
253pub struct Cmap4Iter<'a> {
254    subtable: Cmap4<'a>,
255    cur_range: Range<u32>,
256    cur_start_code: u16,
257    cur_range_ix: usize,
258}
259
260impl<'a> Cmap4Iter<'a> {
261    fn new(subtable: Cmap4<'a>) -> Self {
262        let cur_range = subtable.code_range(0).unwrap_or_default();
263        let cur_start_code = cur_range.start as u16;
264        Self {
265            subtable,
266            cur_range,
267            cur_start_code,
268            cur_range_ix: 0,
269        }
270    }
271}
272
273impl Iterator for Cmap4Iter<'_> {
274    type Item = (u32, GlyphId);
275
276    fn next(&mut self) -> Option<Self::Item> {
277        loop {
278            if let Some(codepoint) = self.cur_range.next() {
279                let Some(glyph_id) = self.subtable.lookup_glyph_id(
280                    codepoint as u16,
281                    self.cur_range_ix,
282                    self.cur_start_code,
283                ) else {
284                    continue;
285                };
286                return Some((codepoint, glyph_id));
287            } else {
288                self.cur_range_ix += 1;
289                let next_range = self.subtable.code_range(self.cur_range_ix)?;
290                // Groups should be in order and non-overlapping so make sure
291                // that the start code of next group is at least current_end + 1.
292                // Also avoid start sliding backwards if we see data where end < start by taking the max
293                // of next.end and curr.end as the new end.
294                // This prevents timeout and bizarre results in the face of numerous overlapping ranges
295                // https://github.com/googlefonts/fontations/issues/1100
296                // cmap4 ranges are u16 so no need to stress about values past char::MAX
297                self.cur_range = next_range.start.max(self.cur_range.end)
298                    ..next_range.end.max(self.cur_range.end);
299                self.cur_start_code = self.cur_range.start as u16;
300            }
301        }
302    }
303}
304
305impl Cmap6<'_> {
306    pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
307        let codepoint = codepoint.into();
308
309        let first = self.first_code() as u32;
310        let idx = codepoint.checked_sub(first)?;
311        self.glyph_id_array()
312            .get(idx as usize)
313            .map(|g| GlyphId::new(g.get() as u32))
314    }
315}
316
317/// Trait to unify constant and sequential map groups.
318trait AnyMapGroup {
319    const IS_CONSTANT: bool;
320
321    fn start_char_code(&self) -> u32;
322    fn end_char_code(&self) -> u32;
323    /// Either start glyph id for a sequential group or just glyph id
324    /// for a constant group.
325    fn ref_glyph_id(&self) -> u32;
326
327    fn compute_glyph_id(codepoint: u32, start_char_code: u32, ref_glyph_id: u32) -> GlyphId {
328        if Self::IS_CONSTANT {
329            GlyphId::new(ref_glyph_id)
330        } else {
331            GlyphId::new(ref_glyph_id.wrapping_add(codepoint.wrapping_sub(start_char_code)))
332        }
333    }
334}
335
336impl AnyMapGroup for ConstantMapGroup {
337    const IS_CONSTANT: bool = true;
338
339    fn start_char_code(&self) -> u32 {
340        self.start_char_code()
341    }
342
343    fn end_char_code(&self) -> u32 {
344        self.end_char_code()
345    }
346
347    fn ref_glyph_id(&self) -> u32 {
348        self.glyph_id()
349    }
350}
351
352impl AnyMapGroup for SequentialMapGroup {
353    const IS_CONSTANT: bool = false;
354
355    fn start_char_code(&self) -> u32 {
356        self.start_char_code()
357    }
358
359    fn end_char_code(&self) -> u32 {
360        self.end_char_code()
361    }
362
363    fn ref_glyph_id(&self) -> u32 {
364        self.start_glyph_id()
365    }
366}
367
368/// Shared codepoint mapping code for cmap 12/13.
369fn cmap1213_map_codepoint<T: AnyMapGroup>(
370    groups: &[T],
371    codepoint: impl Into<u32>,
372) -> Option<GlyphId> {
373    let codepoint = codepoint.into();
374    let mut lo = 0;
375    let mut hi = groups.len();
376    while lo < hi {
377        let i = (lo + hi) / 2;
378        let group = groups.get(i)?;
379        if codepoint < group.start_char_code() {
380            hi = i;
381        } else if codepoint > group.end_char_code() {
382            lo = i + 1;
383        } else {
384            return Some(T::compute_glyph_id(
385                codepoint,
386                group.start_char_code(),
387                group.ref_glyph_id(),
388            ));
389        }
390    }
391    None
392}
393
394/// Character and glyph limits for iterating format 12 and 13 subtables.
395#[derive(Copy, Clone, Debug)]
396pub struct CmapIterLimits {
397    /// The maximum valid character.
398    pub max_char: u32,
399    /// The number of glyphs in the font.
400    pub glyph_count: u32,
401}
402
403impl CmapIterLimits {
404    /// Returns the default limits for the given font.
405    ///
406    /// This will limit pairs to `char::MAX` and the number of glyphs contained
407    /// in the font. If the font is missing a `maxp` table, the number of
408    /// glyphs will be limited to `u16::MAX`.
409    pub fn default_for_font(font: &FontRef) -> Self {
410        let glyph_count = font
411            .maxp()
412            .map(|maxp| maxp.num_glyphs())
413            .unwrap_or(u16::MAX) as u32;
414        Self {
415            // Limit to the valid range of Unicode characters
416            // per https://github.com/googlefonts/fontations/issues/952#issuecomment-2161510184
417            max_char: char::MAX as u32,
418            glyph_count,
419        }
420    }
421}
422
423impl Default for CmapIterLimits {
424    fn default() -> Self {
425        Self {
426            max_char: char::MAX as u32,
427            // Revisit this when we actually support big glyph ids
428            glyph_count: u16::MAX as u32,
429        }
430    }
431}
432
433/// Remapped groups for iterating cmap12/13.
434#[derive(Clone, Debug)]
435struct Cmap1213IterGroup {
436    range: Range<u64>,
437    start_code: u32,
438    ref_glyph_id: u32,
439}
440
441/// Shared group resolution code for cmap 12/13.
442fn cmap1213_iter_group<T: AnyMapGroup>(
443    groups: &[T],
444    index: usize,
445    limits: &Option<CmapIterLimits>,
446) -> Option<Cmap1213IterGroup> {
447    let group = groups.get(index)?;
448    let start_code = group.start_char_code();
449    // Change to exclusive range. This can never overflow since the source
450    // is a 32-bit value
451    let end_code = group.end_char_code() as u64 + 1;
452    let start_glyph_id = group.ref_glyph_id();
453    let end_code = if let Some(limits) = limits {
454        // Set our end code to the minimum of our character and glyph
455        // count limit
456        if T::IS_CONSTANT {
457            end_code.min(limits.max_char as u64)
458        } else {
459            (limits.glyph_count as u64)
460                .saturating_sub(start_glyph_id as u64)
461                .saturating_add(start_code as u64)
462                .min(end_code.min(limits.max_char as u64))
463        }
464    } else {
465        end_code
466    };
467    Some(Cmap1213IterGroup {
468        range: start_code as u64..end_code,
469        start_code,
470        ref_glyph_id: start_glyph_id,
471    })
472}
473
474/// Shared iterator for cmap 12/13.
475#[derive(Clone)]
476struct Cmap1213Iter<'a, T> {
477    groups: &'a [T],
478    cur_group: Option<Cmap1213IterGroup>,
479    cur_group_ix: usize,
480    limits: Option<CmapIterLimits>,
481}
482
483impl<'a, T> Cmap1213Iter<'a, T>
484where
485    T: AnyMapGroup,
486{
487    fn new(groups: &'a [T], limits: Option<CmapIterLimits>) -> Self {
488        let cur_group = cmap1213_iter_group(groups, 0, &limits);
489        Self {
490            groups,
491            cur_group,
492            cur_group_ix: 0,
493            limits,
494        }
495    }
496}
497
498impl<T> Iterator for Cmap1213Iter<'_, T>
499where
500    T: AnyMapGroup,
501{
502    type Item = (u32, GlyphId);
503
504    fn next(&mut self) -> Option<Self::Item> {
505        loop {
506            let group = self.cur_group.as_mut()?;
507            if let Some(codepoint) = group.range.next() {
508                let codepoint = codepoint as u32;
509                let glyph_id = T::compute_glyph_id(codepoint, group.start_code, group.ref_glyph_id);
510                return Some((codepoint, glyph_id));
511            } else {
512                self.cur_group_ix += 1;
513                let mut next_group =
514                    cmap1213_iter_group(self.groups, self.cur_group_ix, &self.limits)?;
515                // Groups should be in order and non-overlapping so make sure
516                // that the start code of next group is at least
517                // current_end.
518                if next_group.range.start < group.range.end {
519                    next_group.range = group.range.end..next_group.range.end;
520                }
521                self.cur_group = Some(next_group);
522            }
523        }
524    }
525}
526
527impl<'a> Cmap12<'a> {
528    /// Maps a codepoint to a nominal glyph identifier.
529    pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
530        cmap1213_map_codepoint(self.groups(), codepoint)
531    }
532
533    /// Returns an iterator over all (codepoint, glyph identifier) pairs
534    /// in the subtable.
535    ///
536    /// Malicious and malformed fonts can produce a large number of invalid
537    /// pairs. Use [`Self::iter_with_limits`] to generate a pruned sequence
538    /// that is limited to reasonable values.
539    pub fn iter(&self) -> Cmap12Iter<'a> {
540        Cmap12Iter::new(self.clone(), None)
541    }
542
543    /// Returns an iterator over all (codepoint, glyph identifier) pairs
544    /// in the subtable within the given limits.
545    pub fn iter_with_limits(&self, limits: CmapIterLimits) -> Cmap12Iter<'a> {
546        Cmap12Iter::new(self.clone(), Some(limits))
547    }
548}
549
550/// Iterator over all (codepoint, glyph identifier) pairs in
551/// the subtable.
552#[derive(Clone)]
553pub struct Cmap12Iter<'a>(Cmap1213Iter<'a, SequentialMapGroup>);
554
555impl<'a> Cmap12Iter<'a> {
556    fn new(subtable: Cmap12<'a>, limits: Option<CmapIterLimits>) -> Self {
557        Self(Cmap1213Iter::new(subtable.groups(), limits))
558    }
559}
560
561impl Iterator for Cmap12Iter<'_> {
562    type Item = (u32, GlyphId);
563
564    fn next(&mut self) -> Option<Self::Item> {
565        self.0.next()
566    }
567}
568
569impl<'a> Cmap13<'a> {
570    /// Maps a codepoint to a nominal glyph identifier.
571    pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
572        cmap1213_map_codepoint(self.groups(), codepoint)
573    }
574
575    /// Returns an iterator over all (codepoint, glyph identifier) pairs
576    /// in the subtable.
577    ///
578    /// Malicious and malformed fonts can produce a large number of invalid
579    /// pairs. Use [`Self::iter_with_limits`] to generate a pruned sequence
580    /// that is limited to reasonable values.
581    pub fn iter(&self) -> Cmap13Iter<'a> {
582        Cmap13Iter::new(self.clone(), None)
583    }
584
585    /// Returns an iterator over all (codepoint, glyph identifier) pairs
586    /// in the subtable within the given limits.
587    pub fn iter_with_limits(&self, limits: CmapIterLimits) -> Cmap13Iter<'a> {
588        Cmap13Iter::new(self.clone(), Some(limits))
589    }
590}
591
592/// Iterator over all (codepoint, glyph identifier) pairs in
593/// the subtable.
594#[derive(Clone)]
595pub struct Cmap13Iter<'a>(Cmap1213Iter<'a, ConstantMapGroup>);
596
597impl<'a> Cmap13Iter<'a> {
598    fn new(subtable: Cmap13<'a>, limits: Option<CmapIterLimits>) -> Self {
599        Self(Cmap1213Iter::new(subtable.groups(), limits))
600    }
601}
602
603impl Iterator for Cmap13Iter<'_> {
604    type Item = (u32, GlyphId);
605
606    fn next(&mut self) -> Option<Self::Item> {
607        self.0.next()
608    }
609}
610
611impl<'a> Cmap14<'a> {
612    /// Maps a codepoint and variation selector to a nominal glyph identifier.
613    pub fn map_variant(
614        &self,
615        codepoint: impl Into<u32>,
616        selector: impl Into<u32>,
617    ) -> Option<MapVariant> {
618        let codepoint = codepoint.into();
619        let selector = selector.into();
620        let selector_records = self.var_selector();
621        // Variation selector records are sorted in order of var_selector. Binary search to find
622        // the appropriate record.
623        let selector_record = selector_records
624            .binary_search_by(|rec| {
625                let rec_selector: u32 = rec.var_selector().into();
626                rec_selector.cmp(&selector)
627            })
628            .ok()
629            .and_then(|idx| selector_records.get(idx))?;
630        // If a default UVS table is present in this selector record, binary search on the ranges
631        // (start_unicode_value, start_unicode_value + additional_count) to find the requested codepoint.
632        // If found, ignore the selector and return a value indicating that the default cmap mapping
633        // should be used.
634        if let Some(Ok(default_uvs)) = selector_record.default_uvs(self.offset_data()) {
635            use core::cmp::Ordering;
636            let found_default_uvs = default_uvs
637                .ranges()
638                .binary_search_by(|range| {
639                    let start = range.start_unicode_value().into();
640                    if codepoint < start {
641                        Ordering::Greater
642                    } else if codepoint > (start + range.additional_count() as u32) {
643                        Ordering::Less
644                    } else {
645                        Ordering::Equal
646                    }
647                })
648                .is_ok();
649            if found_default_uvs {
650                return Some(MapVariant::UseDefault);
651            }
652        }
653        // Binary search the non-default UVS table if present. This maps codepoint+selector to a variant glyph.
654        let non_default_uvs = selector_record.non_default_uvs(self.offset_data())?.ok()?;
655        let mapping = non_default_uvs.uvs_mapping();
656        let ix = mapping
657            .binary_search_by(|map| {
658                let map_codepoint: u32 = map.unicode_value().into();
659                map_codepoint.cmp(&codepoint)
660            })
661            .ok()?;
662        Some(MapVariant::Variant(GlyphId::from(
663            mapping.get(ix)?.glyph_id(),
664        )))
665    }
666
667    /// Returns an iterator over all (codepoint, selector, mapping variant)
668    /// triples in the subtable.
669    pub fn iter(&self) -> Cmap14Iter<'a> {
670        Cmap14Iter::new(self.clone())
671    }
672
673    fn selector(
674        &self,
675        index: usize,
676    ) -> (
677        Option<VariationSelector>,
678        Option<DefaultUvs<'a>>,
679        Option<NonDefaultUvs<'a>>,
680    ) {
681        let selector = self.var_selector().get(index).cloned();
682        let default_uvs = selector.as_ref().and_then(|selector| {
683            selector
684                .default_uvs(self.offset_data())
685                .transpose()
686                .ok()
687                .flatten()
688        });
689        let non_default_uvs = selector.as_ref().and_then(|selector| {
690            selector
691                .non_default_uvs(self.offset_data())
692                .transpose()
693                .ok()
694                .flatten()
695        });
696        (selector, default_uvs, non_default_uvs)
697    }
698
699    #[cfg(feature = "std")]
700    pub fn closure_glyphs(&self, unicodes: &IntSet<u32>, glyph_set: &mut IntSet<GlyphId>) {
701        for selector in self.var_selector() {
702            if !unicodes.contains(selector.var_selector().to_u32()) {
703                continue;
704            }
705            if let Some(non_default_uvs) = selector
706                .non_default_uvs(self.offset_data())
707                .transpose()
708                .ok()
709                .flatten()
710            {
711                glyph_set.extend(
712                    non_default_uvs
713                        .uvs_mapping()
714                        .iter()
715                        .filter(|m| unicodes.contains(m.unicode_value().to_u32()))
716                        .map(|m| m.glyph_id().into()),
717                );
718            }
719        }
720    }
721}
722
723/// Iterator over all (codepoint, selector, mapping variant) triples
724/// in the subtable.
725#[derive(Clone)]
726pub struct Cmap14Iter<'a> {
727    subtable: Cmap14<'a>,
728    selector_record: Option<VariationSelector>,
729    default_uvs: Option<DefaultUvsIter<'a>>,
730    non_default_uvs: Option<NonDefaultUvsIter<'a>>,
731    cur_selector_ix: usize,
732}
733
734impl<'a> Cmap14Iter<'a> {
735    fn new(subtable: Cmap14<'a>) -> Self {
736        let (selector_record, default_uvs, non_default_uvs) = subtable.selector(0);
737        Self {
738            subtable,
739            selector_record,
740            default_uvs: default_uvs.map(DefaultUvsIter::new),
741            non_default_uvs: non_default_uvs.map(NonDefaultUvsIter::new),
742            cur_selector_ix: 0,
743        }
744    }
745}
746
747impl Iterator for Cmap14Iter<'_> {
748    type Item = (u32, u32, MapVariant);
749
750    fn next(&mut self) -> Option<Self::Item> {
751        loop {
752            let selector_record = self.selector_record.as_ref()?;
753            let selector: u32 = selector_record.var_selector().into();
754            if let Some(default_uvs) = self.default_uvs.as_mut() {
755                if let Some(codepoint) = default_uvs.next() {
756                    return Some((codepoint, selector, MapVariant::UseDefault));
757                }
758            }
759            if let Some(non_default_uvs) = self.non_default_uvs.as_mut() {
760                if let Some((codepoint, variant)) = non_default_uvs.next() {
761                    return Some((codepoint, selector, MapVariant::Variant(variant.into())));
762                }
763            }
764            self.cur_selector_ix += 1;
765            let (selector_record, default_uvs, non_default_uvs) =
766                self.subtable.selector(self.cur_selector_ix);
767            self.selector_record = selector_record;
768            self.default_uvs = default_uvs.map(DefaultUvsIter::new);
769            self.non_default_uvs = non_default_uvs.map(NonDefaultUvsIter::new);
770        }
771    }
772}
773
774#[derive(Clone)]
775struct DefaultUvsIter<'a> {
776    ranges: std::slice::Iter<'a, UnicodeRange>,
777    cur_range: Range<u32>,
778}
779
780impl<'a> DefaultUvsIter<'a> {
781    fn new(ranges: DefaultUvs<'a>) -> Self {
782        let mut ranges = ranges.ranges().iter();
783        let cur_range = if let Some(range) = ranges.next() {
784            let start: u32 = range.start_unicode_value().into();
785            let end = start + range.additional_count() as u32 + 1;
786            start..end
787        } else {
788            0..0
789        };
790        Self { ranges, cur_range }
791    }
792}
793
794impl Iterator for DefaultUvsIter<'_> {
795    type Item = u32;
796
797    fn next(&mut self) -> Option<Self::Item> {
798        loop {
799            if let Some(codepoint) = self.cur_range.next() {
800                return Some(codepoint);
801            }
802            let range = self.ranges.next()?;
803            let start: u32 = range.start_unicode_value().into();
804            let end = start + range.additional_count() as u32 + 1;
805            self.cur_range = start..end;
806        }
807    }
808}
809
810#[derive(Clone)]
811struct NonDefaultUvsIter<'a> {
812    iter: std::slice::Iter<'a, UvsMapping>,
813}
814
815impl<'a> NonDefaultUvsIter<'a> {
816    fn new(uvs: NonDefaultUvs<'a>) -> Self {
817        Self {
818            iter: uvs.uvs_mapping().iter(),
819        }
820    }
821}
822
823impl Iterator for NonDefaultUvsIter<'_> {
824    type Item = (u32, GlyphId16);
825
826    fn next(&mut self) -> Option<Self::Item> {
827        let mapping = self.iter.next()?;
828        let codepoint: u32 = mapping.unicode_value().into();
829        let glyph_id = GlyphId16::new(mapping.glyph_id());
830        Some((codepoint, glyph_id))
831    }
832}
833
834#[cfg(test)]
835mod tests {
836    use font_test_data::{be_buffer, bebuffer::BeBuffer};
837
838    use super::*;
839    use crate::{FontRef, GlyphId, TableProvider};
840
841    #[test]
842    fn map_codepoints() {
843        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
844        let cmap = font.cmap().unwrap();
845        assert_eq!(cmap.map_codepoint('A'), Some(GlyphId::new(1)));
846        assert_eq!(cmap.map_codepoint('À'), Some(GlyphId::new(2)));
847        assert_eq!(cmap.map_codepoint('`'), Some(GlyphId::new(3)));
848        assert_eq!(cmap.map_codepoint('B'), None);
849
850        let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
851        let cmap = font.cmap().unwrap();
852        assert_eq!(cmap.map_codepoint(' '), Some(GlyphId::new(1)));
853        assert_eq!(cmap.map_codepoint(0xE_u32), Some(GlyphId::new(2)));
854        assert_eq!(cmap.map_codepoint('B'), None);
855
856        let cmap0_data = cmap0_data();
857        let cmap = Cmap::read(FontData::new(cmap0_data.data())).unwrap();
858
859        assert_eq!(cmap.map_codepoint(0u8), Some(GlyphId::new(0)));
860        assert_eq!(cmap.map_codepoint(b' '), Some(GlyphId::new(178)));
861        assert_eq!(cmap.map_codepoint(b'r'), Some(GlyphId::new(193)));
862        assert_eq!(cmap.map_codepoint(b'X'), Some(GlyphId::new(13)));
863        assert_eq!(cmap.map_codepoint(255u8), Some(GlyphId::new(3)));
864
865        let cmap6_data = be_buffer! {
866            // version
867            0u16,
868            // numTables
869            1u16,
870            // platformID
871            1u16,
872            // encodingID
873            0u16,
874            // subtableOffset
875            12u32,
876            // format
877            6u16,
878            // length
879            32u16,
880            // language
881            0u16,
882            // firstCode
883            32u16,
884            // entryCount
885            5u16,
886            // glyphIDArray
887            [10u16, 15, 7, 20, 4]
888        };
889
890        let cmap = Cmap::read(FontData::new(cmap6_data.data())).unwrap();
891
892        assert_eq!(cmap.map_codepoint(0u8), None);
893        assert_eq!(cmap.map_codepoint(31u8), None);
894        assert_eq!(cmap.map_codepoint(33u8), Some(GlyphId::new(15)));
895        assert_eq!(cmap.map_codepoint(35u8), Some(GlyphId::new(20)));
896        assert_eq!(cmap.map_codepoint(36u8), Some(GlyphId::new(4)));
897        assert_eq!(cmap.map_codepoint(50u8), None);
898    }
899
900    #[test]
901    fn map_variants() {
902        use super::MapVariant::*;
903        let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
904        let cmap = font.cmap().unwrap();
905        let cmap14 = find_cmap14(&cmap).unwrap();
906        let selector = '\u{e0100}';
907        assert_eq!(cmap14.map_variant('a', selector), None);
908        assert_eq!(cmap14.map_variant('\u{4e00}', selector), Some(UseDefault));
909        assert_eq!(cmap14.map_variant('\u{4e06}', selector), Some(UseDefault));
910        assert_eq!(
911            cmap14.map_variant('\u{4e08}', selector),
912            Some(Variant(GlyphId::new(25)))
913        );
914        assert_eq!(
915            cmap14.map_variant('\u{4e09}', selector),
916            Some(Variant(GlyphId::new(26)))
917        );
918    }
919
920    #[test]
921    #[cfg(feature = "std")]
922    fn cmap14_closure_glyphs() {
923        let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
924        let cmap = font.cmap().unwrap();
925        let mut unicodes = IntSet::empty();
926        unicodes.insert(0x4e08_u32);
927        unicodes.insert(0xe0100_u32);
928
929        let mut glyph_set = IntSet::empty();
930        glyph_set.insert(GlyphId::new(18));
931        cmap.closure_glyphs(&unicodes, &mut glyph_set);
932
933        assert_eq!(glyph_set.len(), 2);
934        assert!(glyph_set.contains(GlyphId::new(18)));
935        assert!(glyph_set.contains(GlyphId::new(25)));
936    }
937
938    #[test]
939    fn cmap4_iter() {
940        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
941        let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
942        let mut count = 0;
943        for (codepoint, glyph_id) in cmap4.iter() {
944            assert_eq!(cmap4.map_codepoint(codepoint), Some(glyph_id));
945            count += 1;
946        }
947        assert_eq!(count, 4);
948        let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
949        let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
950        let mut count = 0;
951        for (codepoint, glyph_id) in cmap4.iter() {
952            assert_eq!(cmap4.map_codepoint(codepoint), Some(glyph_id));
953            count += 1;
954        }
955        assert_eq!(count, 3);
956    }
957
958    #[test]
959    fn cmap4_iter_explicit_notdef() {
960        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
961        let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
962        let mut notdef_count = 0;
963        for (_, glyph_id) in cmap4.iter() {
964            notdef_count += (glyph_id == GlyphId::NOTDEF) as i32;
965        }
966        assert!(notdef_count > 0);
967        assert_eq!(cmap4.map_codepoint(0xFFFF_u32), Some(GlyphId::NOTDEF));
968    }
969
970    // Make sure we don't bail early when iterating ranges with holes.
971    // Encountered with Gentium Basic and Gentium Basic Book.
972    // See <https://github.com/googlefonts/fontations/issues/897>
973    #[test]
974    fn cmap4_iter_sparse_range() {
975        #[rustfmt::skip]
976        let cmap4_data: &[u16] = &[
977            // format, length, lang
978            4, 0, 0,
979            // segCountX2
980            4,
981            // bin search data
982            0, 0, 0,
983            // end code
984            262, 0xFFFF, 
985            // reserved pad
986            0,
987            // start code
988            259, 0xFFFF,
989            // id delta
990            0, 1, 
991            // id range offset
992            4, 0,
993            // glyph ids
994            236, 0, 0, 326,
995        ];
996        let mut buf = BeBuffer::new();
997        for &word in cmap4_data {
998            buf = buf.push(word);
999        }
1000        let cmap4 = Cmap4::read(FontData::new(&buf)).unwrap();
1001        let mappings = cmap4
1002            .iter()
1003            .map(|(ch, gid)| (ch, gid.to_u32()))
1004            .collect::<Vec<_>>();
1005        assert_eq!(mappings, &[(259, 236), (262, 326), (65535, 0)]);
1006    }
1007
1008    #[test]
1009    fn cmap12_iter() {
1010        let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
1011        let cmap12 = find_cmap12(&font.cmap().unwrap()).unwrap();
1012        let mut count = 0;
1013        for (codepoint, glyph_id) in cmap12.iter() {
1014            assert_eq!(cmap12.map_codepoint(codepoint), Some(glyph_id));
1015            count += 1;
1016        }
1017        assert_eq!(count, 10);
1018    }
1019
1020    // oss-fuzz: detected integer addition overflow in Cmap12::group()
1021    // ref: https://oss-fuzz.com/testcase-detail/5141969742397440
1022    // and https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=69547
1023    #[test]
1024    fn cmap12_iter_avoid_overflow() {
1025        // reconstructed cmap from <https://oss-fuzz.com/testcase-detail/5141969742397440>
1026        let data = be_buffer! {
1027            12u16,      // format
1028            0u16,       // reserved, set to 0
1029            0u32,       // length, ignored
1030            0u32,       // language, ignored
1031            2u32,       // numGroups
1032            // groups: [startCode, endCode, startGlyphID]
1033            [0xFFFFFFFA_u32, 0xFFFFFFFC, 0], // group 0
1034            [0xFFFFFFFB_u32, 0xFFFFFFFF, 0] // group 1
1035        };
1036        let cmap12 = Cmap12::read(data.data().into()).unwrap();
1037        let _ = cmap12.iter().count();
1038    }
1039
1040    // oss-fuzz: timeout in Cmap12Iter
1041    // ref: https://oss-fuzz.com/testcase-detail/4628971063934976
1042    // and https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=69540
1043    #[test]
1044    fn cmap12_iter_avoid_timeout() {
1045        // ranges: [SequentialMapGroup { start_char_code: 170, end_char_code: 1330926671, start_glyph_id: 328960 }]
1046        let cmap12_data = be_buffer! {
1047            12u16,      // format
1048            0u16,       // reserved, set to 0
1049            0u32,       // length, ignored
1050            0u32,       // language, ignored
1051            1u32,       // numGroups
1052            // groups: [startCode, endCode, startGlyphID]
1053            [170u32, 1330926671, 328960] // group 0
1054        };
1055        let cmap12 = Cmap12::read(cmap12_data.data().into()).unwrap();
1056        assert!(
1057            cmap12.iter_with_limits(CmapIterLimits::default()).count() <= char::MAX as usize + 1
1058        );
1059    }
1060
1061    // oss-fuzz: timeout in outlines, caused by cmap 12 iter
1062    // ref: <https://issues.oss-fuzz.com/issues/394638728>
1063    #[test]
1064    fn cmap12_iter_avoid_timeout2() {
1065        let cmap12_data = be_buffer! {
1066            12u16,      // format
1067            0u16,       // reserved, set to 0
1068            0u32,       // length, ignored
1069            0u32,       // language, ignored
1070            3u32,       // numGroups
1071            // groups: [startCode, endCode, startGlyphID]
1072            [199u32, 16777271, 2],
1073            [262u32, 262, 3],
1074            [268u32, 268, 4]
1075        };
1076        let cmap12 = Cmap12::read(cmap12_data.data().into()).unwrap();
1077        // In the test case, maxp.numGlyphs = 8
1078        const MAX_GLYPHS: u32 = 8;
1079        let limits = CmapIterLimits {
1080            glyph_count: MAX_GLYPHS,
1081            ..Default::default()
1082        };
1083        assert_eq!(cmap12.iter_with_limits(limits).count(), MAX_GLYPHS as usize);
1084    }
1085
1086    #[test]
1087    fn cmap12_iter_glyph_limit() {
1088        let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
1089        let cmap12 = find_cmap12(&font.cmap().unwrap()).unwrap();
1090        let mut limits = CmapIterLimits::default_for_font(&font);
1091        // Ensure we obey the glyph count limit.
1092        // This font has 11 glyphs
1093        for glyph_count in 0..=11 {
1094            limits.glyph_count = glyph_count;
1095            assert_eq!(
1096                cmap12.iter_with_limits(limits).count(),
1097                // We always return one less than glyph count limit because
1098                // notdef is not mapped
1099                (glyph_count as usize).saturating_sub(1)
1100            );
1101        }
1102    }
1103
1104    #[test]
1105    fn cmap12_iter_range_clamping() {
1106        let data = be_buffer! {
1107            12u16,      // format
1108            0u16,       // reserved, set to 0
1109            0u32,       // length, ignored
1110            0u32,       // language, ignored
1111            2u32,       // numGroups
1112            // groups: [startCode, endCode, startGlyphID]
1113            [0u32, 16777215, 0], // group 0
1114            [255u32, 0xFFFFFFFF, 0] // group 1
1115        };
1116        let cmap12 = Cmap12::read(data.data().into()).unwrap();
1117        let ranges = cmap12
1118            .groups()
1119            .iter()
1120            .map(|group| (group.start_char_code(), group.end_char_code()))
1121            .collect::<Vec<_>>();
1122        // These groups overlap and extend to the whole u32 range
1123        assert_eq!(ranges, &[(0, 16777215), (255, u32::MAX)]);
1124        // But we produce at most char::MAX + 1 results
1125        let limits = CmapIterLimits {
1126            glyph_count: u32::MAX,
1127            ..Default::default()
1128        };
1129        assert!(cmap12.iter_with_limits(limits).count() <= char::MAX as usize + 1);
1130    }
1131
1132    #[test]
1133    fn cmap12_iter_explicit_notdef() {
1134        let data = be_buffer! {
1135            12u16,      // format
1136            0u16,       // reserved, set to 0
1137            0u32,       // length, ignored
1138            0u32,       // language, ignored
1139            1u32,       // numGroups
1140            // groups: [startCode, endCode, startGlyphID]
1141            [0_u32, 1_u32, 0] // group 0
1142        };
1143        let cmap12 = Cmap12::read(data.data().into()).unwrap();
1144        for (i, (codepoint, glyph_id)) in cmap12.iter().enumerate() {
1145            assert_eq!(codepoint as usize, i);
1146            assert_eq!(glyph_id.to_u32() as usize, i);
1147        }
1148        assert_eq!(cmap12.iter().next().unwrap().1, GlyphId::NOTDEF);
1149    }
1150
1151    fn cmap13_data() -> Vec<u8> {
1152        let data = be_buffer! {
1153            13u16,      // format
1154            0u16,       // reserved, set to 0
1155            0u32,       // length, ignored
1156            0u32,       // language, ignored
1157            2u32,       // numGroups
1158            // groups: [startCode, endCode, startGlyphID]
1159            [0u32, 8, 20], // group 0
1160            [42u32, 46u32, 30] // group 1
1161        };
1162        data.to_vec()
1163    }
1164
1165    #[test]
1166    fn cmap13_map() {
1167        let data = cmap13_data();
1168        let cmap13 = Cmap13::read(FontData::new(&data)).unwrap();
1169        for ch in 0u32..=8 {
1170            assert_eq!(cmap13.map_codepoint(ch), Some(GlyphId::new(20)));
1171        }
1172        for ch in 9u32..42 {
1173            assert_eq!(cmap13.map_codepoint(ch), None);
1174        }
1175        for ch in 42u32..=46 {
1176            assert_eq!(cmap13.map_codepoint(ch), Some(GlyphId::new(30)));
1177        }
1178        for ch in 47u32..1024 {
1179            assert_eq!(cmap13.map_codepoint(ch), None);
1180        }
1181    }
1182
1183    #[test]
1184    fn cmap13_iter() {
1185        let data = cmap13_data();
1186        let cmap13 = Cmap13::read(FontData::new(&data)).unwrap();
1187        for (ch, gid) in cmap13.iter() {
1188            assert_eq!(cmap13.map_codepoint(ch), Some(gid));
1189        }
1190    }
1191
1192    #[test]
1193    fn cmap14_iter() {
1194        let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
1195        let cmap14 = find_cmap14(&font.cmap().unwrap()).unwrap();
1196        let mut count = 0;
1197        for (codepoint, selector, mapping) in cmap14.iter() {
1198            assert_eq!(cmap14.map_variant(codepoint, selector), Some(mapping));
1199            count += 1;
1200        }
1201        assert_eq!(count, 7);
1202    }
1203
1204    fn find_cmap4<'a>(cmap: &Cmap<'a>) -> Option<Cmap4<'a>> {
1205        cmap.encoding_records()
1206            .iter()
1207            .filter_map(|record| record.subtable(cmap.offset_data()).ok())
1208            .find_map(|subtable| match subtable {
1209                CmapSubtable::Format4(cmap4) => Some(cmap4),
1210                _ => None,
1211            })
1212    }
1213
1214    fn find_cmap12<'a>(cmap: &Cmap<'a>) -> Option<Cmap12<'a>> {
1215        cmap.encoding_records()
1216            .iter()
1217            .filter_map(|record| record.subtable(cmap.offset_data()).ok())
1218            .find_map(|subtable| match subtable {
1219                CmapSubtable::Format12(cmap12) => Some(cmap12),
1220                _ => None,
1221            })
1222    }
1223
1224    fn find_cmap14<'a>(cmap: &Cmap<'a>) -> Option<Cmap14<'a>> {
1225        cmap.encoding_records()
1226            .iter()
1227            .filter_map(|record| record.subtable(cmap.offset_data()).ok())
1228            .find_map(|subtable| match subtable {
1229                CmapSubtable::Format14(cmap14) => Some(cmap14),
1230                _ => None,
1231            })
1232    }
1233
1234    /// <https://github.com/googlefonts/fontations/issues/1100>
1235    ///
1236    /// Note that this doesn't demonstrate the timeout, merely that we've eliminated the underlying
1237    /// enthusiasm for non-ascending ranges that enabled it
1238    #[test]
1239    fn cmap4_bad_data() {
1240        let buf = font_test_data::cmap::repetitive_cmap4();
1241        let cmap4 = Cmap4::read(FontData::new(buf.as_slice())).unwrap();
1242
1243        // we should have unique, ascending codepoints, not duplicates and overlaps
1244        assert_eq!(
1245            (6..=64).collect::<Vec<_>>(),
1246            cmap4.iter().map(|(cp, _)| cp).collect::<Vec<_>>()
1247        );
1248    }
1249
1250    fn cmap0_data() -> BeBuffer {
1251        be_buffer! {
1252            // version
1253            0u16,
1254            // numTables
1255            1u16,
1256            // platformID
1257            1u16,
1258            // encodingID
1259            0u16,
1260            // subtableOffset
1261            12u32,
1262            // format
1263            0u16,
1264            // length
1265            274u16,
1266            // language
1267            0u16,
1268            // glyphIDArray
1269            [0u8, 249, 32, 2, 198, 23, 1, 4, 26, 36,
1270            171, 168, 69, 151, 208, 238, 226, 153, 161, 138,
1271            160, 130, 169, 223, 162, 207, 146, 227, 111, 248,
1272            163, 79, 178, 27, 50, 234, 213, 57, 45, 63,
1273            103, 186, 30, 105, 131, 118, 35, 140, 51, 211,
1274            75, 172, 56, 71, 137, 99, 22, 76, 61, 125,
1275            39, 8, 177, 117, 108, 97, 202, 92, 49, 134,
1276            93, 43, 80, 66, 84, 54, 180, 113, 11, 176,
1277            229, 48, 47, 17, 124, 40, 119, 21, 13, 133,
1278            181, 224, 33, 128, 44, 46, 38, 24, 65, 152,
1279            197, 225, 102, 251, 157, 126, 182, 242, 28, 184,
1280            90, 170, 201, 144, 193, 189, 250, 142, 77, 221,
1281            81, 164, 154, 60, 37, 200, 12, 53, 219, 89,
1282            31, 209, 188, 179, 253, 220, 127, 18, 19, 64,
1283            20, 141, 98, 173, 55, 194, 70, 107, 228, 104,
1284            10, 9, 15, 217, 255, 222, 196, 236, 67, 165,
1285            5, 143, 149, 100, 91, 95, 135, 235, 145, 204,
1286            72, 114, 246, 82, 245, 233, 106, 158, 185, 212,
1287            86, 243, 16, 195, 123, 190, 120, 187, 132, 139,
1288            192, 239, 110, 183, 240, 214, 166, 41, 59, 231,
1289            42, 94, 244, 83, 121, 25, 215, 96, 73, 87,
1290            174, 136, 62, 206, 156, 175, 230, 150, 116, 147,
1291            68, 122, 78, 112, 6, 167, 232, 254, 52, 34,
1292            191, 85, 241, 14, 216, 155, 29, 101, 115, 210,
1293            252, 218, 129, 247, 203, 159, 109, 74, 7, 58,
1294            237, 199, 88, 205, 148, 3]
1295        }
1296    }
1297
1298    #[test]
1299    fn best_subtable_full() {
1300        let font = FontRef::new(font_test_data::VORG).unwrap();
1301        let cmap = font.cmap().unwrap();
1302        let (index, record, _) = cmap.best_subtable().unwrap();
1303        assert_eq!(
1304            (index, record.platform_id(), record.encoding_id()),
1305            (3, PlatformId::Windows, WINDOWS_UNICODE_FULL_ENCODING)
1306        );
1307    }
1308
1309    #[test]
1310    fn best_subtable_bmp() {
1311        let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
1312        let cmap = font.cmap().unwrap();
1313        let (index, record, _) = cmap.best_subtable().unwrap();
1314        assert_eq!(
1315            (index, record.platform_id(), record.encoding_id()),
1316            (0, PlatformId::Windows, WINDOWS_UNICODE_BMP_ENCODING)
1317        );
1318    }
1319
1320    #[test]
1321    fn best_subtable_symbol() {
1322        let font = FontRef::new(font_test_data::CMAP4_SYMBOL_PUA).unwrap();
1323        let cmap = font.cmap().unwrap();
1324        let (index, record, _) = cmap.best_subtable().unwrap();
1325        assert!(record.is_symbol());
1326        assert_eq!(
1327            (index, record.platform_id(), record.encoding_id()),
1328            (0, PlatformId::Windows, WINDOWS_SYMBOL_ENCODING)
1329        );
1330    }
1331
1332    #[test]
1333    fn uvs_subtable() {
1334        let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
1335        let cmap = font.cmap().unwrap();
1336        let (index, _) = cmap.uvs_subtable().unwrap();
1337        assert_eq!(index, 0);
1338    }
1339}