Skip to main content

kas_text/fonts/
resolver.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! KAS Rich-Text library — font resolver
7//!
8//! Many items are copied from font-kit to avoid any public dependency.
9
10use super::{FontStyle, FontWeight, FontWidth};
11use fontique::{
12    Attributes, Collection, FamilyId, GenericFamily, QueryFamily, QueryFont, QueryStatus, Script,
13    SourceCache,
14};
15use log::debug;
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19use std::collections::hash_map::Entry;
20use std::hash::{BuildHasher, Hash};
21
22/// A tool to resolve a single font face given a family and style
23pub struct Resolver {
24    collection: Collection,
25    cache: SourceCache,
26    /// Cached family selectors:
27    families: HashMap<FamilySelector, FamilySet>,
28}
29
30impl Resolver {
31    pub(crate) fn new() -> Self {
32        Resolver {
33            collection: Collection::new(Default::default()),
34            cache: SourceCache::new(Default::default()),
35            families: HashMap::new(),
36        }
37    }
38
39    /// Get a font family name from an id
40    pub fn font_family(&mut self, id: FamilyId) -> Option<&str> {
41        self.collection.family_name(id)
42    }
43
44    /// Get a font family name for some generic font family
45    pub fn font_family_from_generic(&mut self, generic: GenericFamily) -> Option<&str> {
46        let id = self.collection.generic_families(generic).next()?;
47        self.collection.family_name(id)
48    }
49
50    /// Construct a [`FamilySelector`] for the given `families`
51    pub fn select_families<I, F>(&mut self, families: I) -> FamilySelector
52    where
53        I: IntoIterator<Item = F>,
54        F: Into<FamilyName>,
55    {
56        let set = FamilySet(families.into_iter().map(|f| f.into()).collect());
57        let hash = self.families.hasher().hash_one(&set);
58        let sel = FamilySelector(hash | (1 << 63));
59
60        match self.families.entry(sel) {
61            Entry::Vacant(entry) => {
62                entry.insert(set);
63            }
64            Entry::Occupied(entry) => {
65                // Unlikely but possible case:
66                log::warn!(
67                    "Resolver::select_families: hash collision for family selector {set:?} and {:?}",
68                    entry.get()
69                );
70                // TODO: inject a random value into the FamilySet and rehash?
71            }
72        }
73
74        sel
75    }
76
77    /// Resolve families from a [`FamilySelector`]
78    ///
79    /// Returns an empty [`Vec`] on error.
80    pub fn resolve_families(&self, selector: &FamilySelector) -> Vec<FamilyName> {
81        if let Some(gf) = selector.as_generic() {
82            vec![FamilyName::Generic(gf)]
83        } else if let Some(set) = self.families.get(selector) {
84            set.0.clone()
85        } else {
86            vec![]
87        }
88    }
89}
90
91/// A family name
92#[derive(Clone, Debug, Eq, PartialEq, Hash)]
93#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
94pub enum FamilyName {
95    /// A family named with a `String`
96    Named(String),
97    /// A generic family
98    #[cfg_attr(feature = "serde", serde(with = "remote::GenericFamily"))]
99    Generic(GenericFamily),
100}
101
102impl From<GenericFamily> for FamilyName {
103    fn from(gf: GenericFamily) -> Self {
104        FamilyName::Generic(gf)
105    }
106}
107
108impl<'a> From<&'a FamilyName> for QueryFamily<'a> {
109    fn from(family: &'a FamilyName) -> Self {
110        match family {
111            FamilyName::Named(name) => QueryFamily::Named(name),
112            FamilyName::Generic(gf) => QueryFamily::Generic(*gf),
113        }
114    }
115}
116
117#[derive(Clone, Debug, PartialEq, Eq, Hash)]
118struct FamilySet(Vec<FamilyName>);
119
120/// A (cached) family selector
121///
122/// This may be constructed directly for some generic families; for other
123/// families use [`Resolver::select_families`].
124///
125/// This is a small, `Copy` type (a newtype over `u64`).
126#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
127pub struct FamilySelector(u64);
128
129impl FamilySelector {
130    /// Glyphs have finishing strokes, flared or tapering ends, or have actual serifed endings.
131    pub const SERIF: FamilySelector = FamilySelector(0);
132
133    /// Glyphs have stroke endings that are plain.
134    pub const SANS_SERIF: FamilySelector = FamilySelector(1);
135
136    /// All glyphs have the same fixed width.
137    pub const MONOSPACE: FamilySelector = FamilySelector(2);
138
139    /// Glyphs in cursive fonts generally have either joining strokes or other cursive characteristics beyond those of italic typefaces. The glyphs are partially or completely connected, and the result looks more like handwritten pen or brush writing than printed letter work.
140    pub const CURSIVE: FamilySelector = FamilySelector(3);
141
142    /// Glyphs are taken from the default user interface font on a given platform.
143    pub const SYSTEM_UI: FamilySelector = FamilySelector(5);
144
145    /// Fonts that are specifically designed to render emoji.
146    pub const EMOJI: FamilySelector = FamilySelector(10);
147
148    /// This is for the particular stylistic concerns of representing mathematics: superscript and subscript, brackets that cross several lines, nesting expressions, and double struck glyphs with distinct meanings.
149    pub const MATH: FamilySelector = FamilySelector(11);
150
151    /// A particular style of Chinese characters that are between serif-style Song and cursive-style Kai forms. This style is often used for government documents.
152    pub const FANG_SONG: FamilySelector = FamilySelector(12);
153
154    /// Convert to a [`GenericFamily`] where possible
155    pub fn as_generic(self) -> Option<GenericFamily> {
156        match self.0 {
157            0 => Some(GenericFamily::Serif),
158            1 => Some(GenericFamily::SansSerif),
159            2 => Some(GenericFamily::Monospace),
160            3 => Some(GenericFamily::Cursive),
161            5 => Some(GenericFamily::SystemUi),
162            10 => Some(GenericFamily::Emoji),
163            11 => Some(GenericFamily::Math),
164            12 => Some(GenericFamily::FangSong),
165            _ => None,
166        }
167    }
168
169    /// Get a CSS-style generic family name where possible
170    pub fn generic_name(self) -> Option<&'static str> {
171        Some(match self.as_generic()? {
172            GenericFamily::Serif => "serif",
173            GenericFamily::SansSerif => "sans-serif",
174            GenericFamily::Monospace => "monospace",
175            GenericFamily::Cursive => "cursive",
176            GenericFamily::SystemUi => "system-ui",
177            GenericFamily::Emoji => "emoji",
178            GenericFamily::Math => "math",
179            GenericFamily::FangSong => "fangsong",
180            _ => return None,
181        })
182    }
183
184    /// Parse a CSS-style family descriptor
185    pub fn parse_generic(name: &str) -> Option<Self> {
186        Some(match name.trim() {
187            "serif" => Self::SERIF,
188            "sans-serif" => Self::SANS_SERIF,
189            "monospace" => Self::MONOSPACE,
190            "cursive" => Self::CURSIVE,
191            "system-ui" => Self::SYSTEM_UI,
192            "emoji" => Self::EMOJI,
193            "math" => Self::MATH,
194            "fangsong" => Self::FANG_SONG,
195            _ => return None,
196        })
197    }
198}
199
200/// Default-constructs to [`FamilySelector::SYSTEM_UI`].
201impl Default for FamilySelector {
202    fn default() -> Self {
203        FamilySelector::SYSTEM_UI
204    }
205}
206
207/// A font face selection tool
208///
209/// This tool selects a font according to the given criteria from available
210/// system fonts. Selection criteria are based on CSS.
211///
212/// This can be converted [from](From) a [`FamilySelector`], selecting the
213/// default styles.
214#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
215pub struct FontSelector {
216    /// Family selector
217    pub family: FamilySelector,
218    /// Weight
219    pub weight: FontWeight,
220    /// Width
221    pub width: FontWidth,
222    /// Italic / oblique style
223    pub style: FontStyle,
224}
225
226impl FontSelector {
227    /// Synonym for default
228    ///
229    /// Without further parametrization, this will select a generic sans-serif
230    /// font which should be suitable for most uses.
231    #[inline]
232    pub fn new() -> Self {
233        FontSelector::default()
234    }
235
236    /// Resolve font faces for each matching font
237    ///
238    /// All font faces matching steps 1-4 will be returned through the `add_face` closure.
239    pub(crate) fn select<F>(&self, resolver: &mut Resolver, script: Script, add_face: F)
240    where
241        F: FnMut(&QueryFont) -> QueryStatus,
242    {
243        let mut query = resolver.collection.query(&mut resolver.cache);
244        if let Some(gf) = self.family.as_generic() {
245            debug!(
246                "select: Script::{:?}, GenericFamily::{:?}, {:?}, {:?}, {:?}",
247                &script, gf, &self.weight, &self.width, &self.style
248            );
249
250            query.set_families([gf]);
251        } else if let Some(set) = resolver.families.get(&self.family) {
252            debug!(
253                "select: Script::{:?}, {:?}, {:?}, {:?}, {:?}",
254                &script, set, &self.weight, &self.width, &self.style
255            );
256
257            query.set_families(set.0.iter());
258        }
259
260        query.set_attributes(Attributes {
261            width: self.width.into(),
262            style: self.style.into(),
263            weight: self.weight.into(),
264        });
265
266        query.set_fallbacks(script);
267
268        query.matches_with(add_face);
269    }
270
271    /// Format CSS-style
272    ///
273    /// This is similar to the CSS `font` property, though it does not support
274    /// size or variant or using relative or global values. Examples:
275    ///
276    /// - `system-ui`
277    /// - `italic bold expanded sans-serif`
278    /// - `oblique 10deg 500 175% monospace`
279    /// - `300 cursive`
280    ///
281    /// Weight, width and style will be omitted if normal. Family is required
282    /// and must be a single generic name.
283    ///
284    /// Will return `None` if [`Self::family`] is not one of the generic
285    /// families supported by [`FamilySelector`].
286    pub fn format_css(&self) -> Option<String> {
287        let family = self.family.generic_name()?;
288        let mut s = String::new();
289        if self.style != FontStyle::Normal {
290            s.push_str(&format!("{} ", self.style));
291        }
292        if self.weight != FontWeight::NORMAL {
293            s.push_str(&format!("{} ", self.weight));
294        }
295        if self.width != FontWidth::NORMAL {
296            s.push_str(&format!("{} ", self.width));
297        }
298        s.push_str(family);
299        Some(s)
300    }
301
302    /// Parse a CSS-style selector
303    ///
304    /// Format support is similar to [`Self::format_css`].
305    ///
306    /// Does not (yet) support non-generic font families.
307    /// TODO: write a nicer parser with real error detection!
308    pub fn parse_css(s: &str) -> Option<Self> {
309        let mut weight = FontWeight::NORMAL;
310        let mut width = FontWidth::NORMAL;
311        let mut style = FontStyle::Normal;
312        let mut last_is_oblique = false;
313        for part in s.split_ascii_whitespace() {
314            if last_is_oblique {
315                // Special case: oblique may be followed by a numeric specifier in degrees
316                if part.ends_with("deg") {
317                    style = FontStyle::parse(&format!("oblique {part}"))
318                        .expect("failed to parse oblique angle");
319                }
320                last_is_oblique = false;
321            } else if let Some(v) = FontStyle::parse(part) {
322                style = v;
323                if style == FontStyle::Oblique(None) {
324                    last_is_oblique = true;
325                }
326            } else if let Some(v) = FontWeight::parse(part) {
327                weight = v;
328            } else if let Some(v) = FontWidth::parse(part) {
329                width = v;
330            } else {
331                let family = FamilySelector::parse_generic(part)?;
332                return Some(FontSelector {
333                    family,
334                    weight,
335                    width,
336                    style,
337                });
338            }
339        }
340        None
341    }
342}
343
344impl From<FamilySelector> for FontSelector {
345    #[inline]
346    fn from(family: FamilySelector) -> Self {
347        FontSelector {
348            family,
349            ..Default::default()
350        }
351    }
352}
353
354// See: https://serde.rs/remote-derive.html
355#[cfg(feature = "serde")]
356mod remote {
357    use serde::{Deserialize, Serialize};
358
359    #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
360    #[repr(u8)]
361    #[serde(remote = "fontique::GenericFamily")]
362    pub enum GenericFamily {
363        Serif = 0,
364        SansSerif = 1,
365        Monospace = 2,
366        Cursive = 3,
367        Fantasy = 4,
368        SystemUi = 5,
369        UiSerif = 6,
370        UiSansSerif = 7,
371        UiMonospace = 8,
372        UiRounded = 9,
373        Emoji = 10,
374        Math = 11,
375        FangSong = 12,
376    }
377}
378
379#[cfg(feature = "serde")]
380mod serde_impls {
381    use super::*;
382    use serde::{de, ser};
383    use std::fmt;
384
385    impl ser::Serialize for FamilySelector {
386        fn serialize<S: ser::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
387            if let Some(name) = self.generic_name() {
388                ser.serialize_str(name)
389            } else {
390                Err(ser::Error::custom(
391                    "unable to serialize non-generic family selectors",
392                ))
393            }
394        }
395    }
396
397    impl<'de> de::Deserialize<'de> for FamilySelector {
398        fn deserialize<D: de::Deserializer<'de>>(de: D) -> Result<FamilySelector, D::Error> {
399            struct Visitor;
400            impl<'de> de::Visitor<'de> for Visitor {
401                type Value = FamilySelector;
402
403                fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
404                    write!(fmt, "a generic family name")
405                }
406
407                fn visit_str<E: de::Error>(self, s: &str) -> Result<FamilySelector, E> {
408                    // TODO: support non-generic font families
409                    FamilySelector::parse_generic(s)
410                        .ok_or_else(|| de::Error::invalid_value(de::Unexpected::Str(s), &self))
411                }
412            }
413
414            de.deserialize_str(Visitor)
415        }
416    }
417
418    impl ser::Serialize for FontSelector {
419        fn serialize<S: ser::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
420            if let Some(s) = self.format_css() {
421                ser.serialize_str(&s)
422            } else {
423                Err(ser::Error::custom(
424                    "unable to serialize non-generic family selectors",
425                ))
426            }
427        }
428    }
429
430    impl<'de> de::Deserialize<'de> for FontSelector {
431        fn deserialize<D: de::Deserializer<'de>>(de: D) -> Result<FontSelector, D::Error> {
432            struct Visitor;
433            impl<'de> de::Visitor<'de> for Visitor {
434                type Value = FontSelector;
435
436                fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
437                    write!(fmt, "a CSS-style font selector")
438                }
439
440                fn visit_str<E: de::Error>(self, s: &str) -> Result<FontSelector, E> {
441                    FontSelector::parse_css(s)
442                        .ok_or_else(|| de::Error::invalid_value(de::Unexpected::Str(s), &self))
443                }
444            }
445
446            de.deserialize_str(Visitor)
447        }
448    }
449}