Merge pull request #1696

# Conflicts:
#	contributors/people.toml
This commit is contained in:
Brandon Thomas committed 2026-10-18 09:36:05 -04:00
commit 7c99ba6eeb
3 files changed
+61 -5

No files matched your search

+4
View File
@@ -91,3 +91,7 @@ sync_github_name = false
[people.krinry]
display_name = "krinry"
sync_github_name = true
[people.Silvertree2010]
display_name = "Netter"
sync_github_name = false
+9 -4
View File
@@ -2,7 +2,7 @@
//! that keeps files in memory (the web editor's browser storage) answers for its own files.
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, PoisonError};
use effectcraft_engine::{Services, Session};
@@ -13,14 +13,19 @@ struct Mem(Mutex<HashMap<String, Vec<u8>>>);
impl Services for Mem {
fn read_file(&self, path: &str) -> std::io::Result<Vec<u8>> {
self.0.lock().unwrap().get(path).cloned().ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, path.to_string()))
self.0
.lock()
.unwrap_or_else(PoisonError::into_inner)
.get(path)
.cloned()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, path.to_string()))
}
fn write_file(&self, path: &str, data: &[u8]) -> std::io::Result<()> {
self.0.lock().unwrap().insert(path.into(), data.into());
self.0.lock().unwrap_or_else(PoisonError::into_inner).insert(path.into(), data.into());
Ok(())
}
fn exists(&self, path: &str) -> bool {
self.0.lock().unwrap().contains_key(path)
self.0.lock().unwrap_or_else(PoisonError::into_inner).contains_key(path)
}
fn is_dir(&self, path: &str) -> bool {
path == "/browser/footage"
+48 -1
View File
@@ -514,12 +514,37 @@ pub fn resolve(family: &str, style: &str) -> Resolved {
return r;
}
}
if let Some(r) = resolve_alias(family, style) {
return r;
}
// Inter is bundled; face 0 (the first bundled face) is the last resort.
let mut r = resolve_in(DEFAULT_FAMILY, style).unwrap_or(Resolved { face: 0, synth_bold: false, synth_italic: false, missing: true, instance: None });
r.missing = !family.is_empty() && !family.eq_ignore_ascii_case(DEFAULT_FAMILY);
r
}
/// Metric-compatible substitutes for common proprietary families, in order of preference (the
/// same pairs fontconfig's `30-metric-aliases.conf` uses). Text keeps its widths and line breaks.
const METRIC_ALIASES: &[(&str, &[&str])] = &[
("Calibri", &["Carlito"]),
("Cambria", &["Caladea"]),
("Arial", &["Liberation Sans", "Arimo"]),
("Helvetica", &["Liberation Sans", "Arimo"]),
("Arial Narrow", &["Liberation Sans Narrow"]),
("Times New Roman", &["Liberation Serif", "Tinos"]),
("Times", &["Liberation Serif", "Tinos"]),
("Courier New", &["Liberation Mono", "Cousine"]),
("Courier", &["Liberation Mono", "Cousine"]),
];
/// An installed metric-compatible substitute for a missing family. The result stays `missing`,
/// so Find Missing Fonts still lists the requested name.
fn resolve_alias(family: &str, style: &str) -> Option<Resolved> {
let family = family.trim();
let (_, subs) = METRIC_ALIASES.iter().find(|(name, _)| name.eq_ignore_ascii_case(family))?;
subs.iter().find_map(|s| resolve_in(s, style)).map(|r| Resolved { missing: true, ..r })
}
fn resolve_in(family: &str, style: &str) -> Option<Resolved> {
let faces = all_faces();
let fam: Vec<&Arc<Face>> = faces.iter().filter(|f| f.info.family.eq_ignore_ascii_case(family)).collect();
@@ -1150,6 +1175,11 @@ mod tests {
/// A minimal TrueType font built in code (no font file in the repo): U+0020 and U+E000 map to
/// an empty glyph, U+E001 to a triangle.
fn blank_glyph_font() -> Vec<u8> {
test_font("Blank Glyph Test")
}
/// The blank-glyph test font under another family name.
fn test_font(family: &str) -> Vec<u8> {
fn u16s(v: &[u16]) -> Vec<u8> {
v.iter().flat_map(|x| x.to_be_bytes()).collect()
}
@@ -1177,7 +1207,7 @@ mod tests {
hhea.extend(u16s(&[0, 3]));
let hmtx = u16s(&[600, 0, 600, 0, 600, 0]);
let maxp = u16s(&[1, 0, 3, 3, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]);
let strings: Vec<Vec<u8>> = ["Blank Glyph Test", "Regular"].iter().map(|s| s.encode_utf16().flat_map(|u| u.to_be_bytes()).collect()).collect();
let strings: Vec<Vec<u8>> = [family, "Regular"].iter().map(|s| s.encode_utf16().flat_map(|u| u.to_be_bytes()).collect()).collect();
let mut name = u16s(&[0, 2, 30]);
let mut off = 0;
for (id, st) in strings.iter().enumerate() {
@@ -1221,6 +1251,23 @@ mod tests {
assert_eq!(fallback_for('\u{e001}', inter), id, "fallback finds the drawn one");
}
/// A Microsoft family that isn't installed renders in its installed metric-compatible
/// substitute instead of Inter, and still reports itself missing (#1677).
#[test]
fn microsoft_families_resolve_to_metric_compatible_substitutes() {
add_font_data(test_font("Carlito"));
add_font_data(test_font("Caladea"));
let calibri = resolve("Calibri", "Regular");
assert_eq!(face(calibri.face).info.family, "Carlito");
assert!(calibri.missing, "the original name stays missing for Find Missing Fonts");
let cambria = resolve("Cambria", "Bold");
assert_eq!(face(cambria.face).info.family, "Caladea");
assert!(cambria.missing);
let unknown = resolve("No Such Family 1677", "Regular");
assert_eq!(face(unknown.face).info.family, DEFAULT_FAMILY);
assert!(unknown.missing);
}
#[test]
fn the_han_order_follows_the_cjk_locale() {
// Chinese first for any Chinese tag, whether or not it names a script.