From b1a35f842880e7047cc75ed33a67cb7742e9dcec Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 12 Nov 2026 16:23:10 -0800 Subject: [PATCH] Tests: envelope ink tests set type in a test face and check its outlines The three envelope ink tests set "Type" in the default family and unwrapped ink_bounds(). Without bundled fonts, craft-fonts or system fonts (vectorcraft-effects' tests on a bare Linux host), no face draws and the effects test panicked at the unwrap. Each test now adds the Heighttest Sans face built from the bundled Source Sans 3 (test-fonts feature, now also on vectorcraft-effects' dev-dependency), sets the text in it, and computes the expected box from the layout's glyph outlines. It checks that envelope_ink gives that box as well as the before and after registration boxes, so a change to the registered measure fails the test. --- crates/effects/Cargo.toml | 2 ++ crates/effects/tests/envelope_ink.rs | 24 +++++++++++++++++++++--- crates/eps/tests/envelope_ink.rs | 24 +++++++++++++++++++++--- crates/render/tests/envelope_ink.rs | 24 +++++++++++++++++++++--- 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/crates/effects/Cargo.toml b/crates/effects/Cargo.toml index 4078312855..d5821607ec 100644 --- a/crates/effects/Cargo.toml +++ b/crates/effects/Cargo.toml @@ -26,3 +26,5 @@ serde_json = { workspace = true } [dev-dependencies] vectorcraft-color = { workspace = true } +# A face built in code from the bundled Source Sans 3 (no font file is committed). +vectorcraft-text = { workspace = true, features = ["test-fonts"] } diff --git a/crates/effects/tests/envelope_ink.rs b/crates/effects/tests/envelope_ink.rs index a95906cc1b..45ac0fb64d 100644 --- a/crates/effects/tests/envelope_ink.rs +++ b/crates/effects/tests/envelope_ink.rs @@ -6,20 +6,38 @@ use std::sync::Arc; use vectorcraft_doc::{CharStyle, Node, NodeId, NodeKind, TextObject}; -use vectorcraft_geom::{Point, Rect}; +use vectorcraft_geom::{Point, Rect, Shape}; +use vectorcraft_text::test_fonts::{HEIGHT_FAMILY, height_font}; /// The largest distance between the edges of `a` and `b`. fn off(a: Rect, b: Rect) -> f64 { [a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1].iter().fold(0.0f64, |m, v| m.max(v.abs())) } +/// "Type" at 48 pt in a face the test adds, so its glyphs draw on a machine without craft-fonts or +/// system fonts. +fn type_in_test_face() -> TextObject { + assert!(vectorcraft_text::FontDb::global().add_font(height_font(true).unwrap()) > 0, "the test face loads"); + TextObject::point(Point::new(10.0, 60.0), "Type", CharStyle { font_family: HEIGHT_FAMILY.into(), size: 48.0, ..Default::default() }) +} + +/// The box of the outlines `t`'s glyphs draw, in document space, measured glyph by glyph. +fn outline_box(t: &TextObject) -> Rect { + let laid = vectorcraft_text::layout(vectorcraft_text::FontDb::global(), t); + let outlines: Vec = laid.glyphs.iter().filter(|g| !g.outline.elements().is_empty()).map(|g| (t.xf * &g.outline).bounding_box()).collect(); + assert_eq!(outlines.len(), 4, "every glyph of \"Type\" draws an outline"); + outlines.into_iter().reduce(|a, b| a.union(b)).unwrap() +} + #[test] fn live_evaluation_measures_type_in_envelopes_by_its_glyphs() { - let t = TextObject::point(Point::new(10.0, 60.0), "Type", CharStyle { size: 48.0, ..Default::default() }); + let t = type_in_test_face(); let n = Arc::new(Node::new(NodeId(1), NodeKind::Text(Box::new(t.clone())))); let laid = n.geometric_bounds().unwrap(); - let ink = t.xf.transform_rect_bbox(vectorcraft_text::layout(vectorcraft_text::FontDb::global(), &t).ink_bounds().unwrap()); + let ink = outline_box(&t); assert!(laid.height() - ink.height() > 1.0, "the laid-out box {laid:?} reaches past the glyphs {ink:?}"); + let measured = vectorcraft_text::envelope_ink(&t, t.xf, None).unwrap(); + assert!(off(measured, ink) < 1e-6, "the envelope measure gives the glyphs' box: {measured:?} vs {ink:?}"); let before = vectorcraft_doc::live::envelope_content_bounds(std::slice::from_ref(&n)).unwrap(); assert!(off(before, laid) < 1e-6, "with nothing registered, type keeps its laid-out box: {before:?} vs {laid:?}"); diff --git a/crates/eps/tests/envelope_ink.rs b/crates/eps/tests/envelope_ink.rs index eccbf8a8f0..3086043238 100644 --- a/crates/eps/tests/envelope_ink.rs +++ b/crates/eps/tests/envelope_ink.rs @@ -6,20 +6,38 @@ use std::sync::Arc; use vectorcraft_doc::{CharStyle, Node, NodeId, NodeKind, TextObject}; -use vectorcraft_geom::{Point, Rect}; +use vectorcraft_geom::{Point, Rect, Shape}; +use vectorcraft_text::test_fonts::{HEIGHT_FAMILY, height_font}; /// The largest distance between the edges of `a` and `b`. fn off(a: Rect, b: Rect) -> f64 { [a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1].iter().fold(0.0f64, |m, v| m.max(v.abs())) } +/// "Type" at 48 pt in a face the test adds, so its glyphs draw on a machine without craft-fonts or +/// system fonts. +fn type_in_test_face() -> TextObject { + assert!(vectorcraft_text::FontDb::global().add_font(height_font(true).unwrap()) > 0, "the test face loads"); + TextObject::point(Point::new(10.0, 60.0), "Type", CharStyle { font_family: HEIGHT_FAMILY.into(), size: 48.0, ..Default::default() }) +} + +/// The box of the outlines `t`'s glyphs draw, in document space, measured glyph by glyph. +fn outline_box(t: &TextObject) -> Rect { + let laid = vectorcraft_text::layout(vectorcraft_text::FontDb::global(), t); + let outlines: Vec = laid.glyphs.iter().filter(|g| !g.outline.elements().is_empty()).map(|g| (t.xf * &g.outline).bounding_box()).collect(); + assert_eq!(outlines.len(), 4, "every glyph of \"Type\" draws an outline"); + outlines.into_iter().reduce(|a, b| a.union(b)).unwrap() +} + #[test] fn reading_an_ai_file_measures_type_in_envelopes_by_its_glyphs() { - let t = TextObject::point(Point::new(10.0, 60.0), "Type", CharStyle { size: 48.0, ..Default::default() }); + let t = type_in_test_face(); let n = Arc::new(Node::new(NodeId(1), NodeKind::Text(Box::new(t.clone())))); let laid = n.geometric_bounds().unwrap(); - let ink = t.xf.transform_rect_bbox(vectorcraft_text::layout(vectorcraft_text::FontDb::global(), &t).ink_bounds().unwrap()); + let ink = outline_box(&t); assert!(laid.height() - ink.height() > 1.0, "the laid-out box {laid:?} reaches past the glyphs {ink:?}"); + let measured = vectorcraft_text::envelope_ink(&t, t.xf, None).unwrap(); + assert!(off(measured, ink) < 1e-6, "the envelope measure gives the glyphs' box: {measured:?} vs {ink:?}"); let before = vectorcraft_doc::live::envelope_content_bounds(std::slice::from_ref(&n)).unwrap(); assert!(off(before, laid) < 1e-6, "with nothing registered, type keeps its laid-out box: {before:?} vs {laid:?}"); diff --git a/crates/render/tests/envelope_ink.rs b/crates/render/tests/envelope_ink.rs index 7622143f50..512f56398b 100644 --- a/crates/render/tests/envelope_ink.rs +++ b/crates/render/tests/envelope_ink.rs @@ -7,20 +7,38 @@ use std::sync::Arc; use vectorcraft_doc::{CharStyle, Node, NodeId, NodeKind, TextObject}; -use vectorcraft_geom::{Point, Rect}; +use vectorcraft_geom::{Point, Rect, Shape}; +use vectorcraft_text::test_fonts::{HEIGHT_FAMILY, height_font}; /// The largest distance between the edges of `a` and `b`. fn off(a: Rect, b: Rect) -> f64 { [a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1].iter().fold(0.0f64, |m, v| m.max(v.abs())) } +/// "Type" at 48 pt in a face the test adds, so its glyphs draw on a machine without craft-fonts or +/// system fonts. +fn type_in_test_face() -> TextObject { + assert!(vectorcraft_text::FontDb::global().add_font(height_font(true).unwrap()) > 0, "the test face loads"); + TextObject::point(Point::new(10.0, 60.0), "Type", CharStyle { font_family: HEIGHT_FAMILY.into(), size: 48.0, ..Default::default() }) +} + +/// The box of the outlines `t`'s glyphs draw, in document space, measured glyph by glyph. +fn outline_box(t: &TextObject) -> Rect { + let laid = vectorcraft_text::layout(vectorcraft_text::FontDb::global(), t); + let outlines: Vec = laid.glyphs.iter().filter(|g| !g.outline.elements().is_empty()).map(|g| (t.xf * &g.outline).bounding_box()).collect(); + assert_eq!(outlines.len(), 4, "every glyph of \"Type\" draws an outline"); + outlines.into_iter().reduce(|a, b| a.union(b)).unwrap() +} + #[test] fn a_new_renderer_measures_type_in_envelopes_by_its_glyphs() { - let t = TextObject::point(Point::new(10.0, 60.0), "Type", CharStyle { size: 48.0, ..Default::default() }); + let t = type_in_test_face(); let n = Arc::new(Node::new(NodeId(1), NodeKind::Text(Box::new(t.clone())))); let laid = n.geometric_bounds().unwrap(); - let ink = t.xf.transform_rect_bbox(vectorcraft_text::layout(vectorcraft_text::FontDb::global(), &t).ink_bounds().unwrap()); + let ink = outline_box(&t); assert!(laid.height() - ink.height() > 1.0, "the laid-out box {laid:?} reaches past the glyphs {ink:?}"); + let measured = vectorcraft_text::envelope_ink(&t, t.xf, None).unwrap(); + assert!(off(measured, ink) < 1e-6, "the envelope measure gives the glyphs' box: {measured:?} vs {ink:?}"); let before = vectorcraft_doc::live::envelope_content_bounds(std::slice::from_ref(&n)).unwrap(); assert!(off(before, laid) < 1e-6, "with nothing registered, type keeps its laid-out box: {before:?} vs {laid:?}");