tinymist_world/font/
info.rs

1/// Trim style naming from a family name and fix bad names.
2#[allow(dead_code)]
3pub fn typst_typographic_family(mut family: &str) -> &str {
4    // Separators between names, modifiers and styles.
5    const SEPARATORS: [char; 3] = [' ', '-', '_'];
6
7    // Modifiers that can appear in combination with suffixes.
8    const MODIFIERS: &[&str] = &[
9        "extra", "ext", "ex", "x", "semi", "sem", "sm", "demi", "dem", "ultra",
10    ];
11
12    // Style suffixes.
13    #[rustfmt::skip]
14    const SUFFIXES: &[&str] = &[
15        "normal", "italic", "oblique", "slanted",
16        "thin", "th", "hairline", "light", "lt", "regular", "medium", "med",
17        "md", "bold", "bd", "demi", "extb", "black", "blk", "bk", "heavy",
18        "narrow", "condensed", "cond", "cn", "cd", "compressed", "expanded", "exp"
19    ];
20
21    let mut extra = [].as_slice();
22    let newcm = family.starts_with("NewCM") || family.starts_with("NewComputerModern");
23    if newcm {
24        extra = &["book"];
25    }
26
27    // Trim spacing and weird leading dots in Apple fonts.
28    family = family.trim().trim_start_matches('.');
29
30    // Lowercase the string so that the suffixes match case-insensitively.
31    let lower = family.to_ascii_lowercase();
32    let mut len = usize::MAX;
33    let mut trimmed = lower.as_str();
34
35    // Trim style suffixes repeatedly.
36    while trimmed.len() < len {
37        len = trimmed.len();
38
39        // Find style suffix.
40        let mut t = trimmed;
41        let mut shortened = false;
42        while let Some(s) = SUFFIXES.iter().chain(extra).find_map(|s| t.strip_suffix(s)) {
43            shortened = true;
44            t = s;
45        }
46
47        if !shortened {
48            break;
49        }
50
51        // Strip optional separator.
52        if let Some(s) = t.strip_suffix(SEPARATORS) {
53            trimmed = s;
54            t = s;
55        }
56
57        // Also allow an extra modifier, but apply it only if it is separated it
58        // from the text before it (to prevent false positives).
59        if let Some(t) = MODIFIERS.iter().find_map(|s| t.strip_suffix(s))
60            && let Some(stripped) = t.strip_suffix(SEPARATORS)
61        {
62            trimmed = stripped;
63        }
64    }
65
66    // Apply style suffix trimming.
67    family = &family[..len];
68
69    if newcm {
70        family = family.trim_end_matches("10");
71    }
72
73    // Fix bad names.
74    match family {
75        "Noto Sans Symbols2" => "Noto Sans Symbols 2",
76        "NewComputerModern" => "New Computer Modern",
77        "NewComputerModernMono" => "New Computer Modern Mono",
78        "NewComputerModernSans" => "New Computer Modern Sans",
79        "NewComputerModernMath" => "New Computer Modern Math",
80        "NewCMUncial" | "NewComputerModernUncial" => "New Computer Modern Uncial",
81        other => other,
82    }
83}