mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
Merge pull request #3873 from lehni/chore/colour-glyph-tests
Type: tests pin colour glyphs in variable fonts, on paths and on the canvas
This commit is contained in:
@@ -151,3 +151,20 @@ fn expand_appearance_keeps_the_colour_glyphs_of_type_with_effects() {
|
||||
assert!(gradients > 0, "the colour glyph's gradient layers");
|
||||
vectorcraft_testkit::invariants::check_document(&s.doc().unwrap().doc).unwrap();
|
||||
}
|
||||
|
||||
/// The canvas draws an SVG glyph as its art: its own colours, and the type's fill where it uses
|
||||
/// `currentColor`.
|
||||
#[test]
|
||||
fn the_canvas_draws_svg_glyphs_as_their_art() {
|
||||
let g = source_sans_gid(SVG_CHAR).unwrap();
|
||||
// The left half of a 500 by 700 box red, the right half in the text's colour.
|
||||
let doc = format!(
|
||||
r##"<svg xmlns="http://www.w3.org/2000/svg"><g id="glyph{g}"><path fill="#f00" d="M0 0h250v-700h-250z"/><path fill="currentColor" d="M250 0h250v-700h-250z"/></g></svg>"##
|
||||
);
|
||||
let s = with_svg_type("Svgtest Canvas", &doc);
|
||||
let opts = vectorcraft_render::RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
|
||||
let img = vectorcraft_render::Renderer::new().render(&s.doc().unwrap().doc, 100, 100, vectorcraft_geom::Affine::IDENTITY, &opts);
|
||||
// At 32 pt from x 10 on the baseline at y 50: 16 pt wide, 22.4 pt tall.
|
||||
assert_eq!(img.pixel(14, 40), [255, 0, 0, 255], "its own red");
|
||||
assert_eq!(img.pixel(22, 40), [0, 0, 0, 255], "the type's black fill");
|
||||
}
|
||||
|
||||
@@ -658,10 +658,37 @@ pub fn colr_font(v1: bool) -> Option<Vec<u8>> {
|
||||
|
||||
/// The bundled Source Sans 3 Regular as `family` with `colr` and [`colr_font`]'s palette.
|
||||
pub fn colr_font_with(family: &str, colr: &write_fonts::tables::colr::Colr) -> Option<Vec<u8>> {
|
||||
use write_fonts::tables::cpal::{ColorRecord, Cpal};
|
||||
let font = skrifa::FontRef::new(SOURCE_SANS).ok()?;
|
||||
with_colr(SOURCE_SANS, family_names(family, &[])?, colr)
|
||||
}
|
||||
|
||||
/// The family of [`colr_variable_font`].
|
||||
pub const COLR_VARIABLE_FAMILY: &str = "Colrtest Variable";
|
||||
|
||||
/// [`variable_font`] as family [`COLR_VARIABLE_FAMILY`], with a `COLR` (v0) table in which
|
||||
/// [`COLOUR_CHAR`] is the outline of `l` (one of [`VARIABLE_CHARS`]) in red, so its layer widens
|
||||
/// with the weight. `None` when it can't be built.
|
||||
pub fn colr_variable_font() -> Option<Vec<u8>> {
|
||||
use write_fonts::tables::colr::{BaseGlyph, Colr, Layer};
|
||||
use write_fonts::types::GlyphId16;
|
||||
let [base, l] = source_sans_gids([COLOUR_CHAR, 'l'])?;
|
||||
let colr = Colr::new(1, Some(vec![BaseGlyph::new(GlyphId16::new(base), 0, 1)]), Some(vec![Layer::new(GlyphId16::new(l), 0)]), 1);
|
||||
let axis_names = [(256, "Weight"), (257, "Normal"), (258, "SemiBold"), (259, "Bold")];
|
||||
with_colr(&variable_font()?, family_names(COLR_VARIABLE_FAMILY, &axis_names)?, &colr)
|
||||
}
|
||||
|
||||
/// A `name` table for Regular of `family`, with `more` records.
|
||||
fn family_names(family: &str, more: &[(u16, &str)]) -> Option<Vec<u8>> {
|
||||
let ps = family.replace(' ', "");
|
||||
let name = name_table(&[(1, family), (2, "Regular"), (4, &format!("{family} Regular")), (6, &format!("{ps}-Regular"))])?;
|
||||
let (full, postscript) = (format!("{family} Regular"), format!("{ps}-Regular"));
|
||||
let mut records = vec![(1, family), (2, "Regular"), (4, full.as_str()), (6, postscript.as_str())];
|
||||
records.extend_from_slice(more);
|
||||
name_table(&records)
|
||||
}
|
||||
|
||||
/// Font `base` with the `name` table `name`, `colr` and [`colr_font`]'s palette.
|
||||
fn with_colr(base: &[u8], name: Vec<u8>, colr: &write_fonts::tables::colr::Colr) -> Option<Vec<u8>> {
|
||||
use write_fonts::tables::cpal::{ColorRecord, Cpal};
|
||||
let font = skrifa::FontRef::new(base).ok()?;
|
||||
let records = [COLR_RED, COLR_BLUE, COLR_GREEN].map(|[r, g, b, a]| ColorRecord::new(b, g, r, a)).to_vec();
|
||||
let cpal = Cpal::new(3, 1, 3, Some(records), vec![0]);
|
||||
let mut builder = FontBuilder::new();
|
||||
|
||||
@@ -376,3 +376,38 @@ fn a_glyph_without_the_foreground_colour_is_read_once_for_every_fill() {
|
||||
assert!(tree(true, Color::rgb8(0, 0, 200)), "v1's glyph uses only its palette");
|
||||
assert!(!tree(false, Color::rgb8(0, 0, 200)), "v0's foreground layer follows the fill");
|
||||
}
|
||||
|
||||
/// A variable font's COLR glyph draws its layers at the face's instance: at Bold, the layer's `l`
|
||||
/// is as much wider as the font widens it.
|
||||
#[test]
|
||||
fn a_variable_fonts_colr_layers_follow_its_instance() {
|
||||
use crate::test_fonts::{COLR_VARIABLE_FAMILY, VARIABLE_WIDEN, colr_variable_font};
|
||||
let db = FontDb::with_font_dirs(vec![]);
|
||||
assert_eq!(db.add_font(colr_variable_font().expect("the test font builds")), 3, "Regular, SemiBold and Bold");
|
||||
let width = |style: &str| {
|
||||
let st = CharStyle { font_family: COLR_VARIABLE_FAMILY.into(), font_style: style.into(), size: 100.0, ..CharStyle::default() };
|
||||
let l = layout(&db, &TextObject::point(KPoint::ZERO, &COLOUR_CHAR.to_string(), st));
|
||||
l.glyphs[0].placed_colour(kurbo::Affine::IDENTITY).unwrap().geometric_bounds().unwrap().width()
|
||||
};
|
||||
let (regular, bold) = (width("Regular"), width("Bold"));
|
||||
assert!((bold / regular - 1.0 - f64::from(VARIABLE_WIDEN)).abs() < 0.02, "{regular} → {bold}");
|
||||
}
|
||||
|
||||
/// On a path, a colour glyph's layers turn with it: along a path going down, the tall `l` layer
|
||||
/// lies on its side, across the path.
|
||||
#[test]
|
||||
fn a_colour_glyph_on_a_path_turns_with_it() {
|
||||
use crate::test_fonts::{COLR_VARIABLE_FAMILY, colr_variable_font};
|
||||
use vectorcraft_doc::text::TextKind;
|
||||
let db = FontDb::with_font_dirs(vec![]);
|
||||
db.add_font(colr_variable_font().expect("the test font builds"));
|
||||
let st = CharStyle { font_family: COLR_VARIABLE_FAMILY.into(), size: 100.0, ..CharStyle::default() };
|
||||
let mut t = TextObject::point(KPoint::ZERO, &COLOUR_CHAR.to_string(), st);
|
||||
let upright = layout(&db, &t).glyphs[0].placed_colour(kurbo::Affine::IDENTITY).unwrap().geometric_bounds().unwrap();
|
||||
let down = kurbo::Shape::to_path(&kurbo::Line::new((0.0, 0.0), (0.0, 400.0)), 0.1);
|
||||
t.kind = TextKind::OnPath { path: vectorcraft_geom::PathData::from_bezpath(&down), start: 0.0, end: None };
|
||||
let b = layout(&db, &t).glyphs[0].placed_colour(kurbo::Affine::IDENTITY).unwrap().geometric_bounds().unwrap();
|
||||
assert!(upright.height() > 2.0 * upright.width(), "upright, the `l` is tall: {upright:?}");
|
||||
assert!((b.width() - upright.height()).abs() < 1.0 && (b.height() - upright.width()).abs() < 1.0, "on its side: {b:?}");
|
||||
assert!(b.y0 >= -1.0 && b.y1 <= 400.0, "along the path: {b:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user