mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-25 10:56:48 -06:00
Text in a family that isn't installed was drawn in a fallback font without a word. Now: - `text.missingFonts` lists the families a document names that no installed or built-in font provides: text layers and their styled ranges, text styles, and string variables bound to a font family (every mode, through aliases), with the number of text layers. - The desktop app shows a Missing fonts window after the user opens a file that has any: once installed fonts are indexed (before that every one of them would count as missing) and after any import report is closed. Files named on the command line count. Programmatic opens and the browser build never open it by themselves. Text > Missing fonts... shows it for the file in front; Escape closes it. - `figcraft-cli info` prints `missing font` lines (`missingFonts` with --json). - file-format-parity: the row claimed REST import warnings that no code produced. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
70 lines
3.6 KiB
Rust
70 lines
3.6 KiB
Rust
//! Headless runs lay text out with the installed fonts, like the desktop app, unless
|
|
//! `--no-system-fonts` or `FIGCRAFT_NO_SYSTEM_FONTS=1` opts out.
|
|
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
|
|
|
use std::process::Command;
|
|
|
|
use serde_json::{Value, json};
|
|
|
|
/// The result of one `figcraft-cli run` step (script file `name`); `opt_out` sets the environment
|
|
/// variable.
|
|
fn run_one(name: &str, step: Value, args: &[&str], opt_out: bool) -> Value {
|
|
let d = std::env::temp_dir().join(format!("figcraft-cli-fonts-{}", std::process::id()));
|
|
std::fs::create_dir_all(&d).unwrap();
|
|
let script = d.join(format!("{name}.json"));
|
|
std::fs::write(&script, json!([step]).to_string()).unwrap();
|
|
let mut cmd = Command::new(env!("CARGO_BIN_EXE_figcraft-cli"));
|
|
cmd.arg("run").arg(&script).args(args);
|
|
if opt_out {
|
|
cmd.env("FIGCRAFT_NO_SYSTEM_FONTS", "1");
|
|
} else {
|
|
cmd.env_remove("FIGCRAFT_NO_SYSTEM_FONTS");
|
|
}
|
|
let out = cmd.output().unwrap();
|
|
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
|
let line: Value = serde_json::from_slice(&out.stdout).unwrap();
|
|
line["result"].clone()
|
|
}
|
|
|
|
fn families(args: &[&str], opt_out: bool) -> Vec<String> {
|
|
serde_json::from_value(run_one("families", json!({"command": "text.fonts"}), args, opt_out)).unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn headless_runs_resolve_installed_families_unless_opted_out() {
|
|
let built_in = families(&["--no-system-fonts"], false);
|
|
assert_eq!(families(&[], true), built_in, "FIGCRAFT_NO_SYSTEM_FONTS=1 opts out like the flag");
|
|
// Any family this machine adds (scanned here, in the test); none (CI): nothing to check.
|
|
figcraft_text::scan_system_fonts();
|
|
let Some(installed) = figcraft_text::family_names().into_iter().find(|f| !built_in.contains(f)) else {
|
|
eprintln!("skipped: no installed fonts beyond the built-in ones");
|
|
return;
|
|
};
|
|
assert!(families(&[], false).contains(&installed), "headless runs list {installed}");
|
|
let face = json!({"command": "text.fonts", "params": {"family": installed, "axes": true}});
|
|
assert_eq!(run_one("face", face.clone(), &[], false)["family"], json!(installed), "text uses the installed family");
|
|
assert_ne!(run_one("face", face, &["--no-system-fonts"], false)["family"], json!(installed), "opted out, it falls back");
|
|
}
|
|
|
|
#[test]
|
|
fn info_lists_the_fonts_a_file_uses_that_are_missing() {
|
|
let d = std::env::temp_dir().join(format!("figcraft-cli-missing-{}", std::process::id()));
|
|
std::fs::create_dir_all(&d).unwrap();
|
|
let (script, file) = (d.join("make.json"), d.join("doc.figcraft"));
|
|
let text =
|
|
|family: &str| json!({"command": "text.create", "params": {"text": "Hamburgefonstiv", "x": 0, "y": 0, "props": {"fontFamily": family}}});
|
|
std::fs::write(&script, json!([text("FigCraft Missing CLI"), text("FigCraft Missing CLI")]).to_string()).unwrap();
|
|
let cli = || Command::new(env!("CARGO_BIN_EXE_figcraft-cli"));
|
|
let made = cli().arg("run").arg(&script).arg("--out").arg(&file).output().unwrap();
|
|
assert!(made.status.success(), "{}", String::from_utf8_lossy(&made.stderr));
|
|
|
|
let out = cli().arg("info").arg(&file).arg("--json").output().unwrap();
|
|
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
|
let v: Value = serde_json::from_slice(&out.stdout).unwrap();
|
|
assert_eq!(v["missingFonts"], json!([{"family": "FigCraft Missing CLI", "layers": 2}]));
|
|
|
|
let out = cli().arg("info").arg(&file).output().unwrap();
|
|
let shown = String::from_utf8_lossy(&out.stdout);
|
|
assert!(shown.contains("missing font \"FigCraft Missing CLI\": 2 text layers"), "{shown}");
|
|
}
|