From e321c7179b574c0df52277972a97071805ca6402 Mon Sep 17 00:00:00 2001 From: Drew Dibble Date: Wed, 21 Oct 2026 08:22:31 -0600 Subject: [PATCH] Text: an embedder of the egui panels may leave the bundled fonts out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #2027 gave vectorcraft-text and vectorcraft-engine a `bundled-fonts` feature for an embedder that supplies every document font at run time, but vectorcraft-ui-egui (and vectorcraft-mcp) named the engine with its default features, so an embedder that hosts the panels got the fonts whatever it asked for. Each now has a `bundled-fonts` feature of its own, on by default, that turns the engine's on, so a downstream user keeps the fonts unless it asks otherwise. For a member to leave the engine's default features out, the workspace has to declare the engine without them, as it already declares vectorcraft-text; the crates that used the engine's default now ask for the fonts: the three apps and vectorcraft-testkit. Off, vectorcraft-text's copy of the fonts (3.4 MB) is left out; theme.rs's own copies of five of them, the panels' interface faces, stay either way. Nothing the workspace builds changes: for every workspace member and for the whole workspace, with and without dev-dependencies, for all targets, wasm32-unknown-unknown, aarch64-apple-darwin and x86_64-pc-windows-msvc, `cargo tree -e normal[,dev] -f '{p} [{f}]'` resolves the same features for every package as on main, apart from the new feature names (the engine's `default` no longer listed; ui-egui's and mcp's `bundled-fonts` and `default`). A test of the panels can't see their configuration without the fonts (their dev-dependencies turn them on), so `cargo xtask ci`'s `no-bundled-fonts` step now also reads that tree for vectorcraft-ui-egui and vectorcraft-mcp: with their default features it must show the fonts, and without them, for any target (a wasm-only dependency too), it must not. Documented in docs/development.md (Fonts › Leaving out the bundled fonts). Co-Authored-By: Claude Opus 5.5 (1M context) --- Cargo.toml | 9 +++++--- apps/vectorcraft-cli/Cargo.toml | 2 +- apps/vectorcraft-web/Cargo.toml | 2 +- apps/vectorcraft/Cargo.toml | 2 +- crates/mcp/Cargo.toml | 6 +++++ crates/testkit/Cargo.toml | 2 +- crates/text/Cargo.toml | 3 ++- crates/ui-egui/Cargo.toml | 7 ++++++ docs/development.md | 23 +++++++++++++++---- xtask/src/main.rs | 39 ++++++++++++++++++++++++++++++++- 10 files changed, 82 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63f7a91dc..afd89a8d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,9 @@ vectorcraft-doc = { path = "crates/doc" } vectorcraft-pathops = { path = "crates/pathops" } vectorcraft-brush = { path = "crates/brush" } vectorcraft-trace = { path = "crates/trace" } -# Without its default `bundled-fonts`: the engine turns them on (its own default feature), so a -# library crate never forces them on an embedder that supplies its fonts at run time. +# Without its default `bundled-fonts`: a crate that wants the fonts asks for them (the apps and +# vectorcraft-testkit through the engine's `bundled-fonts`), so a library crate never forces them on +# an embedder that supplies its fonts at run time. vectorcraft-text = { path = "crates/text", default-features = false } vectorcraft-effects = { path = "crates/effects" } vectorcraft-three-d = { path = "crates/three-d" } @@ -35,7 +36,9 @@ vectorcraft-svg = { path = "crates/svg" } vectorcraft-pdf = { path = "crates/pdf" } vectorcraft-format = { path = "crates/format" } vectorcraft-tools = { path = "crates/tools" } -vectorcraft-engine = { path = "crates/engine" } +# Without its default `bundled-fonts`, as vectorcraft-text: each crate that wants the fonts asks for +# them, so an embedder can build without them (docs/development.md › Leaving out the bundled fonts). +vectorcraft-engine = { path = "crates/engine", default-features = false } vectorcraft-ui-egui = { path = "crates/ui-egui" } vectorcraft-mcp = { path = "crates/mcp" } vectorcraft-testkit = { path = "crates/testkit" } diff --git a/apps/vectorcraft-cli/Cargo.toml b/apps/vectorcraft-cli/Cargo.toml index c31998d00..3827ab1eb 100644 --- a/apps/vectorcraft-cli/Cargo.toml +++ b/apps/vectorcraft-cli/Cargo.toml @@ -16,7 +16,7 @@ workspace = true [dependencies] vectorcraft-color = { workspace = true } vectorcraft-doc = { workspace = true } -vectorcraft-engine = { workspace = true } +vectorcraft-engine = { workspace = true, features = ["bundled-fonts"] } vectorcraft-format = { workspace = true } vectorcraft-geom = { workspace = true } vectorcraft-svg = { workspace = true } diff --git a/apps/vectorcraft-web/Cargo.toml b/apps/vectorcraft-web/Cargo.toml index f67a377ef..211bfdb18 100644 --- a/apps/vectorcraft-web/Cargo.toml +++ b/apps/vectorcraft-web/Cargo.toml @@ -14,7 +14,7 @@ name = "vectorcraft-web" path = "src/main.rs" [dependencies] -vectorcraft-engine = { workspace = true } +vectorcraft-engine = { workspace = true, features = ["bundled-fonts"] } vectorcraft-ui-egui = { workspace = true } vectorcraft-format = { workspace = true } vectorcraft-text = { workspace = true } diff --git a/apps/vectorcraft/Cargo.toml b/apps/vectorcraft/Cargo.toml index 9cac2c235..e2ba95d64 100644 --- a/apps/vectorcraft/Cargo.toml +++ b/apps/vectorcraft/Cargo.toml @@ -17,7 +17,7 @@ accessibility = ["eframe/accesskit"] windows7 = ["eframe/glow"] [dependencies] -vectorcraft-engine = { workspace = true } +vectorcraft-engine = { workspace = true, features = ["bundled-fonts"] } vectorcraft-ui-egui = { workspace = true } vectorcraft-text = { workspace = true } # Shared across the Craft suite: decides who draws the window frame per desktop (compositor vs app) diff --git a/crates/mcp/Cargo.toml b/crates/mcp/Cargo.toml index 1ebe4ca9d..30cb6aef0 100644 --- a/crates/mcp/Cargo.toml +++ b/crates/mcp/Cargo.toml @@ -8,6 +8,12 @@ description = "VectorCraft MCP server: JSON-RPC 2.0 over stdio, driving a runnin [lints] workspace = true +[features] +default = ["bundled-fonts"] +# The fonts vectorcraft-text bundles for documents, through the engine's `bundled-fonts`, as +# vectorcraft-ui-egui's. Off, for an embedder that supplies every document font at run time. +bundled-fonts = ["vectorcraft-engine/bundled-fonts"] + [dependencies] vectorcraft-engine = { workspace = true } vectorcraft-render = { workspace = true } diff --git a/crates/testkit/Cargo.toml b/crates/testkit/Cargo.toml index 06db033b4..cc627bb7e 100644 --- a/crates/testkit/Cargo.toml +++ b/crates/testkit/Cargo.toml @@ -13,7 +13,7 @@ workspace = true vectorcraft-geom = { workspace = true } vectorcraft-color = { workspace = true } vectorcraft-doc = { workspace = true } -vectorcraft-engine = { workspace = true } +vectorcraft-engine = { workspace = true, features = ["bundled-fonts"] } vectorcraft-render = { workspace = true } vectorcraft-format = { workspace = true } vectorcraft-svg = { workspace = true } diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index 7f3b52b1d..06facb0e2 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -37,7 +37,8 @@ default = ["bundled-fonts"] # The OFL fonts of `assets/fonts` (Source Sans 3, Source Serif 4, Inter, JetBrains Mono) compiled # in: 3.4 MB of the binary. Off, the font database starts without them (craft-fonts and system # fonts are unaffected), for an embedder that adds its own with `FontDb::add_font` and would ship -# these twice. vectorcraft-engine turns it on by default (its `bundled-fonts` feature). +# these twice. vectorcraft-engine turns it on by default (its `bundled-fonts` feature), and +# vectorcraft-ui-egui and vectorcraft-mcp by default through the engine. bundled-fonts = [] # `vectorcraft_text::test_fonts` for other crates' tests (dev-dependencies only). test-fonts = ["dep:write-fonts"] diff --git a/crates/ui-egui/Cargo.toml b/crates/ui-egui/Cargo.toml index 73578ba02..6c8f6a4f9 100644 --- a/crates/ui-egui/Cargo.toml +++ b/crates/ui-egui/Cargo.toml @@ -8,6 +8,13 @@ description = "VectorCraft egui frontend (menus, panels, tools and canvas); swap [lints] workspace = true +[features] +default = ["bundled-fonts"] +# The fonts vectorcraft-text bundles for documents, through the engine's `bundled-fonts`. Off, for +# an embedder that supplies every document font at run time (`vectorcraft_text::FontDb::add_font`); +# the interface's own faces are separate copies in theme.rs and stay either way. +bundled-fonts = ["vectorcraft-engine/bundled-fonts"] + [dependencies] craft-ui = { workspace = true } vectorcraft-geom = { workspace = true } diff --git a/docs/development.md b/docs/development.md index 55ccbead1..1307079f2 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,5 +1,6 @@ # Development +> **Last updated:** 2026-10-21 · **Change:** leaving out the bundled fonts with the egui panels (vectorcraft-ui-egui's and vectorcraft-mcp's `bundled-fonts` features). > **Last updated:** 2026-10-14 · **Change:** leaving out the bundled fonts (`bundled-fonts` feature). > **Last updated:** 2026-10-14 · **Change:** document-font registration for all explicitly supplied faces. @@ -160,15 +161,28 @@ CRAFT_FONTS_DIR="$PWD/../craft-fonts" cargo xtask ci # also runs the Japanese- The Latin fonts in `assets/fonts/` are compiled into `vectorcraft-text` by its `bundled-fonts` feature (3.4 MB of the binary). It is on by default, and `vectorcraft-engine`'s default -`bundled-fonts` feature turns it on, so the apps and anything built on the engine have them. An +`bundled-fonts` feature turns it on, so the apps and anything built on the engine with its default +features have them. An embedder that brings every font itself at run time (`FontDb::add_font`, say a web app serving its own font catalogue) depends on the engine with `default-features = false` and doesn't ship them twice. Its font database starts without them: on the web and without craft-fonts it starts empty, and text has no glyphs until a font is added. -- The workspace declares `vectorcraft-text` without default features, so no library crate turns the - fonts back on; a crate whose tests lay type out asks for `bundled-fonts` in its - dev-dependencies (or gets it through `vectorcraft-testkit`, which uses the engine). +An embedder that also hosts the egui panels depends on `vectorcraft-ui-egui` with +`default-features = false` as well: its own `bundled-fonts` feature, on by default, turns the +engine's on; `vectorcraft-mcp` has the same feature. The panels' interface faces (Source Sans 3 +Regular and Semibold, Inter Regular and SemiBold, JetBrains Mono) are separate copies in +`theme.rs` and stay either way. + +- The workspace declares `vectorcraft-text` and `vectorcraft-engine` without default features, so + each crate that wants the fonts asks for them (the apps, and `vectorcraft-testkit` for tests); + a crate whose tests lay type out asks for `bundled-fonts` in its dev-dependencies (or gets it + through `vectorcraft-testkit`). +- `cargo xtask ci`'s `no-bundled-fonts` step also checks that `vectorcraft-ui-egui` and + `vectorcraft-mcp` without their default features turn them on nowhere, for any target + (`cargo tree -p vectorcraft-ui-egui --no-default-features --target all -e features,normal -i + vectorcraft-text`, and the same for `vectorcraft-mcp`), and with them do; a test of either can't + see that configuration, since their dev-dependencies turn the fonts on. - `cargo test -p vectorcraft-text --no-default-features --test without_bundled_fonts` checks the configuration without them. @@ -375,6 +389,7 @@ do not replace expected hashes automatically in response to a download mismatch. | Date | Change | Summary | |---|---|---| +| 2026-10-21 | minor | Fonts › Leaving out the bundled fonts: vectorcraft-ui-egui's and vectorcraft-mcp's `bundled-fonts` features, and the engine declared without default features. | | 2026-10-14 | minor | Document the `bundled-fonts` feature of vectorcraft-text and vectorcraft-engine (Fonts › Leaving out the bundled fonts). | | 2026-10-14 | minor | Document registration of every explicitly supplied craft-fonts face after the existing script-specific prefix; empty input and web selection remain unchanged. | | 2026-10-13 | minor | Document pinned PngSuite files, integrity checks and download budgets (#1729). | diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b42e5af1c..609f41f71 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -105,6 +105,40 @@ fn run(mut cmd: Command, what: &str) -> Result<(), String> { if status.success() { Ok(()) } else { Err(format!("{what}: exited with {status}")) } } +/// `vectorcraft-ui-egui` and `vectorcraft-mcp` without their default features leave +/// vectorcraft-text's bundled fonts out: no crate they depend on turns them on (docs/development.md +/// › Leaving out the bundled fonts). The same tree with the default features must show them, so a +/// change in cargo tree's output can't make the check pass by finding nothing. +fn libraries_without_bundled_fonts() -> Result<(), String> { + ["vectorcraft-ui-egui", "vectorcraft-mcp"].into_iter().try_for_each(without_bundled_fonts) +} + +fn without_bundled_fonts(package: &str) -> Result<(), String> { + const FEATURE: &str = "vectorcraft-text feature \"bundled-fonts\""; + let tree = |defaults: bool| -> Result { + let mut args = vec!["tree", "-p", package, "--target", "all", "-e", "features,normal", "-i", "vectorcraft-text"]; + if !defaults { + args.insert(3, "--no-default-features"); + } + let what = format!("cargo {}", args.join(" ")); + eprintln!("$ {what}"); + let out = cargo().args(&args).output().map_err(|e| format!("{what}: failed to spawn: {e}"))?; + if !out.status.success() { + return Err(format!("{what}: exited with {}:\n{}", out.status, String::from_utf8_lossy(&out.stderr))); + } + Ok(String::from_utf8_lossy(&out.stdout).into_owned()) + }; + let with = tree(true)?; + if !with.contains(FEATURE) { + return Err(format!("{package} with its default features shows no {FEATURE} (has cargo tree's output changed?):\n{with}")); + } + let without = tree(false)?; + if without.contains(FEATURE) { + return Err(format!("{package} without default features still bundles the fonts:\n{without}")); + } + Ok(()) +} + pub fn metadata() -> Result { let out = cargo().args(["metadata", "--format-version", "1", "--no-deps"]).output().map_err(|e| format!("cargo metadata: {e}"))?; if !out.status.success() { @@ -212,7 +246,10 @@ fn cmd_ci() -> Result<(), String> { // vectorcraft-text as embedders that bring their own fonts build it. let mut c = cargo(); c.args(["test", "-p", "vectorcraft-text", "--no-default-features", "--test", "without_bundled_fonts"]); - run(c, "cargo test -p vectorcraft-text --no-default-features --test without_bundled_fonts") + run(c, "cargo test -p vectorcraft-text --no-default-features --test without_bundled_fonts")?; + // The egui panels and the MCP server as such an embedder builds them: nothing turns + // the fonts back on. + libraries_without_bundled_fonts() }), ), ("pdf-deps", Box::new(|| pdf_deps::run(&root()))),