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.
This commit is contained in:
dotCooCoo
2026-11-12 16:23:10 -08:00
parent 4b344e91ce
commit b1a35f8428
4 changed files with 65 additions and 9 deletions
+2
View File
@@ -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"] }
+21 -3
View File
@@ -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<Rect> = 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:?}");
+21 -3
View File
@@ -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<Rect> = 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:?}");
+21 -3
View File
@@ -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<Rect> = 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:?}");