diff options
Diffstat (limited to 'src/font.rs')
| -rw-r--r-- | src/font.rs | 79 |
1 files changed, 53 insertions, 26 deletions
diff --git a/src/font.rs b/src/font.rs index 3592a1a9..cb711770 100644 --- a/src/font.rs +++ b/src/font.rs @@ -14,7 +14,6 @@ use unicode_segmentation::UnicodeSegmentation; use crate::geom::Em; use crate::loading::{FileHash, Loader}; -use crate::util::decode_mac_roman; /// A unique identifier for a loaded font face. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] @@ -139,8 +138,8 @@ impl FontStore { /// To do that we compute a key for all variants and select the one with the /// minimal key. This key prioritizes: /// - If `like` is some other face: - /// - Are both faces (not) monospaced. - /// - Do both faces (not) have serifs. + /// - Are both faces (not) monospaced? + /// - Do both faces (not) have serifs? /// - How many words do the families share in their prefix? E.g. "Noto /// Sans" and "Noto Sans Arabic" share two words, whereas "IBM Plex /// Arabic" shares none with "Noto Sans", so prefer "Noto Sans Arabic" @@ -165,7 +164,6 @@ impl FontStore { let mut best = None; let mut best_key = None; - // Find the best matching variant of this font. for id in ids { let current = &infos[id.0 as usize]; @@ -237,23 +235,22 @@ fn shared_prefix_words(left: &str, right: &str) -> usize { } impl_track_empty!(FontStore); -impl_track_empty!(&'_ mut FontStore); impl_track_hash!(FaceId); impl_track_hash!(GlyphId); /// A font face. pub struct Face { /// The raw face data, possibly shared with other faces from the same - /// collection. Must stay alive put, because `ttf` points into it using - /// unsafe code. + /// collection. The vector's allocation must not move, because `ttf` points + /// into it using unsafe code. buffer: Arc<Vec<u8>>, /// The face's index in the collection (zero if not a collection). index: u32, /// The underlying ttf-parser/rustybuzz face. ttf: rustybuzz::Face<'static>, - /// The faces metrics. + /// The face's metrics. metrics: FaceMetrics, - /// The parsed ReX math font. + /// The parsed ReX math header. math: OnceCell<Option<MathHeader>>, } @@ -298,7 +295,7 @@ impl Face { &self.ttf } - /// The number of units per em. + /// The number of font units per one em. pub fn units_per_em(&self) -> f64 { self.metrics.units_per_em } @@ -308,16 +305,6 @@ impl Face { &self.metrics } - /// Access the math header, if any. - pub fn math(&self) -> Option<&MathHeader> { - self.math - .get_or_init(|| { - let data = self.ttf().table_data(Tag::from_bytes(b"MATH"))?; - MathHeader::parse(data).ok() - }) - .as_ref() - } - /// Convert from font units to an em length. pub fn to_em(&self, units: impl Into<f64>) -> Em { Em::from_units(units, self.units_per_em()) @@ -329,6 +316,21 @@ impl Face { .glyph_hor_advance(GlyphId(glyph)) .map(|units| self.to_em(units)) } + + /// Access the math header, if any. + pub fn math(&self) -> Option<&MathHeader> { + self.math + .get_or_init(|| { + let data = self.ttf().table_data(Tag::from_bytes(b"MATH"))?; + MathHeader::parse(data).ok() + }) + .as_ref() + } + + /// Lookup a name by id. + pub fn find_name(&self, name_id: u16) -> Option<String> { + find_name_ttf(&self.ttf, name_id) + } } /// Metrics for a font face. @@ -396,7 +398,7 @@ impl FaceMetrics { } } - /// Look up a vertical metric at the given font size. + /// Look up a vertical metric. pub fn vertical(&self, metric: VerticalFontMetric) -> Em { match metric { VerticalFontMetric::Ascender => self.ascender, @@ -491,15 +493,15 @@ impl FaceInfo { // sometimes doesn't for the Display variants and that mixes things // up. let family = { - let mut family = find_name(ttf, name_id::FAMILY)?; + let mut family = find_name_ttf(ttf, name_id::FAMILY)?; if family.starts_with("Noto") { - family = find_name(ttf, name_id::FULL_NAME)?; + family = find_name_ttf(ttf, name_id::FULL_NAME)?; } trim_styles(&family).to_string() }; let variant = { - let mut full = find_name(ttf, name_id::FULL_NAME).unwrap_or_default(); + let mut full = find_name_ttf(ttf, name_id::FULL_NAME).unwrap_or_default(); full.make_ascii_lowercase(); // Some fonts miss the relevant bits for italic or oblique, so @@ -554,7 +556,7 @@ impl FaceInfo { } /// Try to find and decode the name with the given id. -pub fn find_name(ttf: &ttf_parser::Face, name_id: u16) -> Option<String> { +fn find_name_ttf(ttf: &ttf_parser::Face, name_id: u16) -> Option<String> { ttf.names().into_iter().find_map(|entry| { if entry.name_id == name_id { if let Some(string) = entry.to_string() { @@ -570,6 +572,31 @@ pub fn find_name(ttf: &ttf_parser::Face, name_id: u16) -> Option<String> { }) } +/// Decode mac roman encoded bytes into a string. +fn decode_mac_roman(coded: &[u8]) -> String { + #[rustfmt::skip] + const TABLE: [char; 128] = [ + 'Ä', 'Å', 'Ç', 'É', 'Ñ', 'Ö', 'Ü', 'á', 'à', 'â', 'ä', 'ã', 'å', 'ç', 'é', 'è', + 'ê', 'ë', 'í', 'ì', 'î', 'ï', 'ñ', 'ó', 'ò', 'ô', 'ö', 'õ', 'ú', 'ù', 'û', 'ü', + '†', '°', '¢', '£', '§', '•', '¶', 'ß', '®', '©', '™', '´', '¨', '≠', 'Æ', 'Ø', + '∞', '±', '≤', '≥', '¥', 'µ', '∂', '∑', '∏', 'π', '∫', 'ª', 'º', 'Ω', 'æ', 'ø', + '¿', '¡', '¬', '√', 'ƒ', '≈', '∆', '«', '»', '…', '\u{a0}', 'À', 'Ã', 'Õ', 'Œ', 'œ', + '–', '—', '“', '”', '‘', '’', '÷', '◊', 'ÿ', 'Ÿ', '⁄', '€', '‹', '›', 'fi', 'fl', + '‡', '·', '‚', '„', '‰', 'Â', 'Ê', 'Á', 'Ë', 'È', 'Í', 'Î', 'Ï', 'Ì', 'Ó', 'Ô', + '\u{f8ff}', 'Ò', 'Ú', 'Û', 'Ù', 'ı', 'ˆ', '˜', '¯', '˘', '˙', '˚', '¸', '˝', '˛', 'ˇ', + ]; + + fn char_from_mac_roman(code: u8) -> char { + if code < 128 { + code as char + } else { + TABLE[(code - 128) as usize] + } + } + + coded.iter().copied().map(char_from_mac_roman).collect() +} + /// Trim style naming from a family name. fn trim_styles(mut family: &str) -> &str { // Separators between names, modifiers and styles. @@ -944,7 +971,7 @@ mod tests { test(&[0, 1], &[0, 2]); test(&[0, 1, 3], &[0, 2, 1, 1]); test( - // [2, 3, 4, 9, 10, 11, 15, 18, 19] + // {2, 3, 4, 9, 10, 11, 15, 18, 19} &[18, 19, 2, 4, 9, 11, 15, 3, 3, 10], &[2, 3, 4, 3, 3, 1, 2, 2], ) |
