mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #2027 from lcslrx/feat/text-bundled-fonts-feature
Text: a `bundled-fonts` feature (on by default) lets embedders that bring their own fonts leave ours out
This commit is contained in:
+3
-1
@@ -21,7 +21,9 @@ vectorcraft-doc = { path = "crates/doc" }
|
||||
vectorcraft-pathops = { path = "crates/pathops" }
|
||||
vectorcraft-brush = { path = "crates/brush" }
|
||||
vectorcraft-trace = { path = "crates/trace" }
|
||||
vectorcraft-text = { path = "crates/text" }
|
||||
# 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.
|
||||
vectorcraft-text = { path = "crates/text", default-features = false }
|
||||
vectorcraft-effects = { path = "crates/effects" }
|
||||
vectorcraft-three-d = { path = "crates/three-d" }
|
||||
vectorcraft-render = { path = "crates/render" }
|
||||
|
||||
@@ -21,3 +21,5 @@ image = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = { workspace = true }
|
||||
# The tests lay type out in the bundled fonts (the workspace leaves turning them on to the engine).
|
||||
vectorcraft-text = { workspace = true, features = ["bundled-fonts"] }
|
||||
|
||||
@@ -8,6 +8,12 @@ description = "VectorCraft engine: sessions, history, selection, the command reg
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = ["bundled-fonts"]
|
||||
# The fonts vectorcraft-text bundles (see its `bundled-fonts`). Embedders that supply every font at
|
||||
# run time (`vectorcraft_text::FontDb::add_font`) build with `default-features = false`.
|
||||
bundled-fonts = ["vectorcraft-text/bundled-fonts"]
|
||||
|
||||
[dependencies]
|
||||
vectorcraft-geom = { workspace = true }
|
||||
vectorcraft-color = { workspace = true }
|
||||
|
||||
@@ -17,3 +17,7 @@ vectorcraft-brush = { workspace = true }
|
||||
vectorcraft-text = { workspace = true }
|
||||
kurbo = { workspace = true }
|
||||
image = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# The tests lay type out in the bundled fonts (the workspace leaves turning them on to the engine).
|
||||
vectorcraft-text = { workspace = true, features = ["bundled-fonts"] }
|
||||
|
||||
@@ -33,6 +33,12 @@ unicode-segmentation = "1.13"
|
||||
write-fonts = { version = "0.48", optional = true }
|
||||
|
||||
[features]
|
||||
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).
|
||||
bundled-fonts = []
|
||||
# `vectorcraft_text::test_fonts` for other crates' tests (dev-dependencies only).
|
||||
test-fonts = ["dep:write-fonts"]
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ use skrifa::{GlyphId, MetadataProvider, Tag};
|
||||
/// The family used when a requested family is unknown (Illustrator's Myriad Pro analogue).
|
||||
pub const FALLBACK_FAMILY: &str = "Source Sans 3";
|
||||
|
||||
/// The fonts every database starts with (feature `bundled-fonts`, on by default).
|
||||
#[cfg(feature = "bundled-fonts")]
|
||||
static BUNDLED: &[&[u8]] = &[
|
||||
include_bytes!("../../../assets/fonts/SourceSans3-Regular.ttf"),
|
||||
include_bytes!("../../../assets/fonts/SourceSans3-Semibold.ttf"),
|
||||
@@ -27,6 +29,10 @@ static BUNDLED: &[&[u8]] = &[
|
||||
include_bytes!("../../../assets/fonts/Inter-SemiBold.ttf"),
|
||||
include_bytes!("../../../assets/fonts/JetBrainsMono-Regular.ttf"),
|
||||
];
|
||||
/// Without `bundled-fonts` a database starts with none: the embedder adds its fonts
|
||||
/// ([`FontDb::add_font`]); until then text has no glyphs.
|
||||
#[cfg(not(feature = "bundled-fonts"))]
|
||||
static BUNDLED: &[&[u8]] = &[];
|
||||
|
||||
/// An axis setting of a variable font: the axis tag (`wght`) and its value in user units (700).
|
||||
pub type Variation = ([u8; 4], f32);
|
||||
@@ -1919,8 +1925,8 @@ impl FontDb {
|
||||
|
||||
/// Resolve a family + style to a face, falling back to the closest style of the family, then to
|
||||
/// Source Sans 3 Regular. Installed system fonts are found by name whatever ran before. `None`
|
||||
/// only if no font at all is loaded (the bundled fonts failed to parse), in which case text has
|
||||
/// no glyphs.
|
||||
/// only if no font at all is loaded (the bundled fonts failed to parse, or were left out with
|
||||
/// the `bundled-fonts` feature and none was added), in which case text has no glyphs.
|
||||
pub fn face(&self, family: &str, style: &str) -> Option<Arc<FontFace>> {
|
||||
let (family, style) = self.canonical(family, style);
|
||||
let (family, style) = (family.as_str(), style.as_str());
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! VectorCraft text: font database, shaping, layout, glyph outlines.
|
||||
//!
|
||||
//! API contract used by `vectorcraft-render`, `vectorcraft-tools` and the UI:
|
||||
//! - [`FontDb::global`]: process-wide database preloaded with the bundled OFL fonts.
|
||||
//! - [`FontDb::global`]: process-wide database preloaded with the bundled OFL fonts (feature
|
||||
//! `bundled-fonts`, on by default; without it the embedder adds fonts with [`FontDb::add_font`]).
|
||||
//! - [`layout`]: lays out a [`TextObject`] into glyph outlines in *text space* (apply `t.xf` to
|
||||
//! get document coordinates), plus line and caret information.
|
||||
//! - [`caret_position`] / [`hit_byte`]: caret geometry and hit testing for the Type tool.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//! The `bundled-fonts` feature turned off, as by an embedder that supplies its own fonts at run
|
||||
//! time: a database starts without the bundled fonts, text in no font lays out with no glyphs
|
||||
//! instead of failing, and the fonts added later are used. Run with
|
||||
//! `cargo test -p vectorcraft-text --no-default-features --test without_bundled_fonts`.
|
||||
#![cfg(not(feature = "bundled-fonts"))]
|
||||
// Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md › Robustness).
|
||||
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
||||
|
||||
use kurbo::Point;
|
||||
use vectorcraft_doc::CharStyle;
|
||||
use vectorcraft_text::{CRAFT_FONTS, FALLBACK_FAMILY, FontDb, TextObject, layout};
|
||||
|
||||
#[test]
|
||||
fn fonts_come_from_the_embedder() {
|
||||
// No system fonts, as on the web.
|
||||
let db = FontDb::with_font_dirs(vec![]);
|
||||
db.set_system_fallback(false);
|
||||
let families = db.families();
|
||||
for bundled in ["Source Sans 3", "Source Serif 4", "Inter", "JetBrains Mono"] {
|
||||
assert!(!families.iter().any(|f| f == bundled), "{bundled} is bundled: {families:?}");
|
||||
}
|
||||
let t = TextObject::point(Point::ZERO, "Hello", CharStyle { size: 24.0, ..CharStyle::default() });
|
||||
if CRAFT_FONTS.is_empty() {
|
||||
assert!(families.is_empty(), "{families:?}");
|
||||
assert!(db.face(FALLBACK_FAMILY, "Regular").is_none());
|
||||
assert!(layout(&db, &t).glyphs.is_empty(), "no font, no glyphs");
|
||||
}
|
||||
// The embedder adds its fonts: text lays out in them, and a family the database lacks (the
|
||||
// default style's Source Sans 3 too) falls back to them.
|
||||
let font = |file: &str| std::fs::read(format!("{}/../../assets/fonts/{file}", env!("CARGO_MANIFEST_DIR"))).unwrap();
|
||||
assert_eq!(db.add_font(font("Inter-Regular.ttf")), 1);
|
||||
assert!(db.families().iter().any(|f| f == "Inter"));
|
||||
if CRAFT_FONTS.is_empty() {
|
||||
assert_eq!(db.face("No Such Font", "Regular").unwrap().family, "Inter");
|
||||
}
|
||||
let l = layout(&db, &t);
|
||||
assert_eq!(l.glyphs.len(), 5);
|
||||
assert!(l.glyphs.iter().all(|g| g.advance > 0.0));
|
||||
// With Source Sans 3 added, the usual fallback is back.
|
||||
assert_eq!(db.add_font(font("SourceSans3-Regular.ttf")), 1);
|
||||
assert_eq!(db.face("No Such Font", "Regular").unwrap().family, FALLBACK_FAMILY);
|
||||
}
|
||||
@@ -20,4 +20,5 @@ serde_json = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
image = { workspace = true }
|
||||
vectorcraft-text = { workspace = true, features = ["test-fonts"] }
|
||||
# The tests lay type out in the bundled fonts (the workspace leaves turning them on to the engine).
|
||||
vectorcraft-text = { workspace = true, features = ["test-fonts", "bundled-fonts"] }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Development
|
||||
|
||||
> **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.
|
||||
|
||||
## Web build
|
||||
@@ -148,6 +149,22 @@ CRAFT_FONTS_DIR="$PWD/../craft-fonts" cargo xtask ci # also runs the Japanese-
|
||||
CI job and every release job (`release.yml`) check craft-fonts out at a pinned commit; release
|
||||
packages carry each embedded font's `OFL-<family>.txt`.
|
||||
|
||||
### Leaving out the bundled fonts (embedders)
|
||||
|
||||
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
|
||||
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).
|
||||
- `cargo test -p vectorcraft-text --no-default-features --test without_bundled_fonts` checks the
|
||||
configuration without them.
|
||||
|
||||
### Arabic fonts and the web build
|
||||
|
||||
Desktop builds load craft-fonts' Arabic families into the document font database (Noto Sans
|
||||
@@ -346,5 +363,6 @@ do not replace expected hashes automatically in response to a download mismatch.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 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). |
|
||||
|
||||
+10
-1
@@ -28,7 +28,7 @@ commands:
|
||||
pdf-deps verify immutable shared PDF writer and patched Krilla sources
|
||||
layers enforce the crate dependency layering (plan/architecture.md §3)
|
||||
wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates and the web app
|
||||
ci fmt --check, clippy -D warnings, test, pdf-deps, assets, brands, layers, wasm (stops at first failure)
|
||||
ci fmt --check, clippy -D warnings, test, no-bundled-fonts, pdf-deps, assets, brands, layers, wasm (stops at first failure)
|
||||
corpus [--download] [--affinity]
|
||||
show where test corpora live; --download fetches PngSuite into corpus/pngsuite,
|
||||
--affinity the pinned public Affinity documents into corpus/affinity
|
||||
@@ -203,6 +203,15 @@ fn cmd_ci() -> Result<(), String> {
|
||||
run(c, "cargo test --workspace")
|
||||
}),
|
||||
),
|
||||
(
|
||||
"no-bundled-fonts",
|
||||
Box::new(|| {
|
||||
// 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")
|
||||
}),
|
||||
),
|
||||
("pdf-deps", Box::new(|| pdf_deps::run(&root()))),
|
||||
("assets", Box::new(|| assets::run(&root()))),
|
||||
("brands", Box::new(|| brands::run(&root()))),
|
||||
|
||||
Reference in New Issue
Block a user