1use crate::hb::tables::{SelectedCmapSubtable, TableRanges};
2
3use super::cache::hb_cache_t;
4use read_fonts::{
5 tables::cmap::{Cmap, Cmap14, CmapSubtable, MapVariant},
6 types::GlyphId,
7 FontRef, TableProvider,
8};
9
10pub type cache_t = hb_cache_t<21, 19, 256, 32>;
11
12#[derive(Clone)]
13pub struct Charmap<'a> {
14 subtable: Option<(SelectedCmapSubtable, CmapSubtable<'a>)>,
15 vs_subtable: Option<Cmap14<'a>>,
16}
17
18impl<'a> Charmap<'a> {
19 pub fn new(font: &FontRef<'a>, table_ranges: &TableRanges) -> Self {
20 if let Some(cmap) = table_ranges.cmap.resolve_table::<Cmap>(font) {
21 let data = cmap.offset_data();
22 let records = cmap.encoding_records();
23 let subtable = table_ranges
24 .cmap_subtable
25 .and_then(|s| Some((s, records.get(s.index as usize)?.subtable(data).ok()?)));
26 let vs_subtable = table_ranges
27 .cmap_vs_subtable
28 .and_then(|index| records.get(index as usize))
29 .and_then(|rec| rec.subtable(data).ok())
30 .and_then(|subtable| match subtable {
31 CmapSubtable::Format14(table) => Some(table),
32 _ => None,
33 });
34 Self {
35 subtable,
36 vs_subtable,
37 }
38 } else {
39 Self {
40 subtable: None,
41 vs_subtable: None,
42 }
43 }
44 }
45
46 pub fn from_tables(font: &impl TableProvider<'a>) -> Self {
47 if let Ok(cmap) = font.cmap() {
48 let subtable = if let Some((index, record, subtable)) = cmap.best_subtable() {
49 Some((
50 SelectedCmapSubtable {
51 index,
52 is_mac_roman: record.is_mac_roman(),
53 is_symbol: record.is_symbol(),
54 },
55 subtable,
56 ))
57 } else {
58 None
59 };
60 Self {
61 subtable,
62 vs_subtable: cmap.uvs_subtable().map(|(_, subtable)| subtable),
63 }
64 } else {
65 Self {
66 subtable: None,
67 vs_subtable: None,
68 }
69 }
70 }
71
72 pub fn map(&self, mut c: u32) -> Option<GlyphId> {
73 let subtable = self.subtable.as_ref()?;
74 if subtable.0.is_mac_roman && c > 0x7F {
75 c = unicode_to_macroman(c);
76 }
77 let result = match &subtable.1 {
78 CmapSubtable::Format0(table) => table.map_codepoint(c),
79 CmapSubtable::Format6(table) => table.map_codepoint(c),
80 CmapSubtable::Format10(table) => {
81 if let Some(index) = c.checked_sub(table.start_char_code()) {
82 if index < table.num_chars() {
83 table
84 .glyph_id_array()
85 .get(index as usize)
86 .map(|gid| GlyphId::from(gid.get()))
87 } else {
88 None
89 }
90 } else {
91 None
92 }
93 }
94 CmapSubtable::Format4(table) => table.map_codepoint(c),
95 CmapSubtable::Format12(table) => table.map_codepoint(c),
96 CmapSubtable::Format13(table) => table.map_codepoint(c),
97 _ => None,
98 };
99 if result.is_none() && subtable.0.is_symbol && c <= 0x00FF {
100 return self.map(0xF000 + c);
106 }
107 result
108 }
109
110 pub fn map_variant(&self, c: u32, vs: u32) -> Option<GlyphId> {
111 let subtable = self.vs_subtable.as_ref()?;
112 match subtable.map_variant(c, vs)? {
113 MapVariant::UseDefault => self.map(c),
114 MapVariant::Variant(gid) => Some(gid),
115 }
116 }
117}
118
119#[rustfmt::skip]
120static UNICODE_TO_MACROMAN: &[u16] = &[
121 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
122 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
123 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
124 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
125 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
126 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
127 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
128 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
129 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
130 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
131 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
132 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
133 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
134 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
135 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
136 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7,
137];
138
139fn unicode_to_macroman(c: u32) -> u32 {
140 let u = c as u16;
141 let Some(index) = UNICODE_TO_MACROMAN.iter().position(|m| *m == u) else {
142 return 0;
143 };
144 (0x80 + index) as u32
145}