From 1cc315708e2d50675efdd787399547c7fee510a9 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Tue, 13 Oct 2026 07:13:36 -0700 Subject: [PATCH] Find Missing Fonts: fonts found by their PostScript or full name are no longer listed File > Dependencies > Find Missing Fonts listed a text layer whenever its font matched no family name, while text layout finds a font by its family, PostScript or full name (effectcraft_text::resolve). For a layer set to the PostScript name Inter-Bold, EffectCraft draws the text in Inter Bold, and text.fontFeatures reports the layer's font as not missing. Find Missing Fonts listed that layer and a layer set to the full name Inter SemiBold. find_missing now lists a layer when effectcraft_text::resolve, which text layout uses for each run, reports the layer's font as missing. fonts_named_by_their_postscript_or_full_name_are_not_missing adds text layers in Noto Serif, Inter-Bold, Inter SemiBold and a font that no face has, and checks that Find Missing Fonts lists only the last one. --- crates/engine/src/commands/file_more.rs | 3 +-- crates/engine/src/tests_menu_cmds.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/commands/file_more.rs b/crates/engine/src/commands/file_more.rs index e5de4de1..346f8392 100644 --- a/crates/engine/src/commands/file_more.rs +++ b/crates/engine/src/commands/file_more.rs @@ -356,13 +356,12 @@ fn find_missing(s: &mut Session, p: &Value) -> Result { Ok(json!({"effects": found})) } "fonts" => { - let families = crate::text_families(); let mut found = vec![]; for (cid, c) in s.project.comps() { for l in &c.layers { if let Some(pr) = l.props.prop("text/sourceText") && let Some(doc) = pr.value.as_text() - && !families.iter().any(|f| f.eq_ignore_ascii_case(&doc.font)) + && effectcraft_text::resolve(&doc.font, &doc.style).missing { found.push(json!({"comp": cid.0, "layer": l.id.0, "font": doc.font})); } diff --git a/crates/engine/src/tests_menu_cmds.rs b/crates/engine/src/tests_menu_cmds.rs index ec63651f..9f3cb2d6 100644 --- a/crates/engine/src/tests_menu_cmds.rs +++ b/crates/engine/src/tests_menu_cmds.rs @@ -706,6 +706,21 @@ fn sequence_layers() { assert_eq!(layer(&s, c).props.prop("transform/opacity").unwrap().keys.len(), 2); } +/// File ▸ Dependencies ▸ Find Missing Fonts lists a text layer only when no face matches its +/// font by family, PostScript or full name. +#[test] +fn fonts_named_by_their_postscript_or_full_name_are_not_missing() { + let mut s = comp(); + // The first three name bundled faces by family, PostScript and full name; no face has the fourth. + let fonts = ["Noto Serif", "Inter-Bold", "Inter SemiBold", "No Such Font Family 123"]; + let ids: Vec = fonts.iter().map(|f| s.execute("layer.newText", json!({"text": "Aa", "font": f})).unwrap()["layer"].as_u64().unwrap()).collect(); + let r = s.execute("file.findMissing", json!({"what": "fonts"})).unwrap(); + let listed: Vec<(u64, &str)> = r["fonts"].as_array().unwrap().iter().map(|f| (f["layer"].as_u64().unwrap(), f["font"].as_str().unwrap())).collect(); + assert_eq!(listed, [(ids[3], fonts[3])], "{r}"); + let f = s.execute("text.fontFeatures", json!({"layer": ids[1]})).unwrap(); + assert_eq!((&f["family"], &f["style"], &f["missing"]), (&json!("Inter"), &json!("Bold"), &json!(false)), "{f}"); +} + #[test] fn file_menu_project_ops() { let mut s = comp();