Files
vectorcraft/crates/render/src/tests_fontchange.rs
T

201 lines
11 KiB
Rust

//! Type redraws in a font that becomes available while it is on screen (fonts added, Refresh Font
//! List).
use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
use vectorcraft_doc::text::{CharStyle, TextObject};
use vectorcraft_doc::{Node, NodeKind};
use vectorcraft_geom::Point;
use super::*;
/// Taken to add fonts. Renderers lay type out again when the fonts change, so tests of what they
/// keep between frames hold it shared ([`fonts_steady`]) and the tests here hold it alone.
static FONTS: RwLock<()> = RwLock::new(());
/// No fonts are added by these tests until the guard is dropped.
pub(crate) fn fonts_steady() -> RwLockReadGuard<'static, ()> {
// A test that failed holding it leaves nothing to undo.
FONTS.read().unwrap_or_else(PoisonError::into_inner)
}
/// No test holding [`fonts_steady`] runs until the guard is dropped.
fn adding_fonts() -> RwLockWriteGuard<'static, ()> {
FONTS.write().unwrap_or_else(PoisonError::into_inner)
}
/// The bundled Source Serif 4 renamed `family` (as long as the original name).
fn serif_as(family: &str) -> Vec<u8> {
let mut data = include_bytes!("../../../assets/fonts/SourceSerif4-Regular.ttf").to_vec();
let utf16 = |s: &str| s.encode_utf16().flat_map(u16::to_be_bytes).collect::<Vec<u8>>();
for (from, to) in [(utf16("Source Serif 4"), utf16(family)), (b"Source Serif 4".to_vec(), family.as_bytes().to_vec())] {
assert_eq!(from.len(), to.len());
let mut i = 0;
while let Some(at) = data[i..].windows(from.len()).position(|w| w == from) {
data[i + at..i + at + to.len()].copy_from_slice(&to);
i += at + to.len();
}
}
data
}
#[test]
fn type_redraws_when_its_font_becomes_available() {
let _adding = adding_fonts();
const FAMILY: &str = "Arrival Serif4";
let mut d = Document::new(300.0, 150.0);
let style = CharStyle { size: 60.0, font_family: FAMILY.into(), ..Default::default() };
let mut n = Node::new(NodeId(0), NodeKind::Text(Box::new(TextObject::point(Point::new(10.0, 100.0), "Hamburg", style))));
n.id = d.alloc_id();
let l = d.layers[0].id;
d.insert(Some(l), 0, n).unwrap();
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
let mut r = Renderer::new();
let before = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert!(vectorcraft_text::FontDb::global().add_font(serif_as(FAMILY)) > 0);
let after = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert_ne!(before, after, "drawn in the font that arrived");
assert_eq!(after, Renderer::new().render(&d, 300, 150, Affine::IDENTITY, &opts).pixels, "as a new renderer draws it");
}
/// Type with raster effects redraws in a font that becomes available: its drop shadow and its
/// blurred content are made again.
#[test]
fn type_with_raster_effects_redraws_when_its_font_becomes_available() {
let _adding = adding_fonts();
const FAMILY: &str = "Raster Serif 4";
let mut d = Document::new(300.0, 150.0);
let l = d.layers[0].id;
let effects =
[("stylize.dropShadow", serde_json::json!({"x": 4, "y": 4, "blur": 1, "opacity": 100})), ("blur.gaussian", serde_json::json!({"radius": 1}))];
for (i, (id, params)) in effects.into_iter().enumerate() {
let style = CharStyle { size: 50.0, font_family: FAMILY.into(), ..Default::default() };
let mut n = Node::new(d.alloc_id(), NodeKind::Text(Box::new(TextObject::point(Point::new(10.0, 60.0 + 70.0 * i as f64), "Hamburg", style))));
n.appearance.effects.push(vectorcraft_doc::Effect { id: id.into(), params, visible: true });
d.insert(Some(l), i, n).unwrap();
}
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
let mut r = Renderer::new();
let before = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert!(vectorcraft_text::FontDb::global().add_font(serif_as(FAMILY)) > 0);
let after = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert_ne!(before, after, "drawn in the font that arrived");
assert_eq!(after, Renderer::new().render(&d, 300, 150, Affine::IDENTITY, &opts).pixels, "as a new renderer draws it");
}
/// Type with a geometry effect in a symbol's art redraws in a font that becomes available: the art
/// kept for the instance is placed again when the fonts change.
#[test]
fn symbol_type_redraws_when_its_font_becomes_available() {
let _adding = adding_fonts();
const FAMILY: &str = "Placed Serif 4";
let mut d = Document::new(300.0, 150.0);
let style = CharStyle { size: 60.0, font_family: FAMILY.into(), ..Default::default() };
let mut t = Node::new(d.alloc_id(), NodeKind::Text(Box::new(TextObject::point(Point::new(-140.0, 25.0), "Hamburg", style))));
t.appearance.effects.push(vectorcraft_doc::Effect { id: "distort.transform".into(), params: serde_json::json!({"moveH": 0.0}), visible: true });
let art = Node::group(d.alloc_id(), vec![Arc::new(t)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Word".into(), art: Arc::new(art) });
let (l, id) = (d.layers[0].id, d.alloc_id());
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Word".into(), xf: Affine::translate((150.0, 75.0)) })).unwrap();
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
let mut r = Renderer::new();
let before = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert!(vectorcraft_text::FontDb::global().add_font(serif_as(FAMILY)) > 0);
let after = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert_ne!(before, after, "drawn in the font that arrived");
assert_eq!(after, Renderer::new().render(&d, 300, 150, Affine::IDENTITY, &opts).pixels, "as a new renderer draws it");
}
/// Type that an envelope bends redraws in a font that becomes available, in the envelope and in
/// the art of a symbol whose instance an envelope bends: the envelopes are evaluated again when the
/// fonts change.
#[test]
fn enveloped_type_redraws_when_its_font_becomes_available() {
let _adding = adding_fonts();
const FAMILY: &str = "Warped Serif 4";
let mut d = Document::new(300.0, 150.0);
let word = |d: &mut Document, at: Point| {
let style = CharStyle { size: 50.0, font_family: FAMILY.into(), ..Default::default() };
Node::new(d.alloc_id(), NodeKind::Text(Box::new(TextObject::point(at, "Hamburg", style))))
};
let typed = word(&mut d, Point::new(10.0, 60.0));
let art = word(&mut d, Point::new(-140.0, 20.0));
let art = Node::group(d.alloc_id(), vec![Arc::new(art)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Word".into(), art: Arc::new(art) });
let shown = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Word".into(), xf: Affine::translate((150.0, 120.0)) });
let l = d.layers[0].id;
for (i, content) in [typed, shown].into_iter().enumerate() {
let kind = vectorcraft_doc::EnvelopeKind::Warp { style: "arc".into(), bend: 30.0, h: 0.0, v: 0.0, horizontal: true };
let envelope = NodeKind::Envelope {
content: vec![Arc::new(content)],
kind: Box::new(kind),
fidelity: 50.0,
editing: false,
options: Default::default(),
frame: Box::new(Affine::IDENTITY),
};
let id = d.alloc_id();
d.insert(Some(l), i, Node::new(id, envelope)).unwrap();
}
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
let mut r = Renderer::new();
let before = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert!(vectorcraft_text::FontDb::global().add_font(serif_as(FAMILY)) > 0);
let after = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert_ne!(before, after, "drawn in the font that arrived");
assert_eq!(after, Renderer::new().render(&d, 300, 150, Affine::IDENTITY, &opts).pixels, "as a new renderer draws it");
}
/// Type that an envelope bends redraws in a font that becomes available when the art drawn for
/// effects is made from the envelope's evaluation: the envelope with a drop shadow, and the
/// envelope in a group with a geometry effect or with a fill of its own.
#[test]
fn enveloped_type_under_effects_redraws_when_its_font_becomes_available() {
let _adding = adding_fonts();
use vectorcraft_color::{Color, Paint};
use vectorcraft_doc::{AppearanceItem, Effect, FillLayer};
let cases = [
("Shadow Serif 4", "the envelope with a drop shadow"),
("Effect Serif 4", "the group with a geometry effect"),
("Filled Serif 4", "the group with a fill"),
];
for (i, (family, what)) in cases.into_iter().enumerate() {
let mut d = Document::new(300.0, 150.0);
let style = CharStyle { size: 50.0, font_family: family.into(), ..Default::default() };
let typed = Node::new(d.alloc_id(), NodeKind::Text(Box::new(TextObject::point(Point::new(10.0, 80.0), "Hamburg", style))));
let kind = vectorcraft_doc::EnvelopeKind::Warp { style: "arc".into(), bend: 30.0, h: 0.0, v: 0.0, horizontal: true };
let envelope = NodeKind::Envelope {
content: vec![Arc::new(typed)],
kind: Box::new(kind),
fidelity: 50.0,
editing: false,
options: Default::default(),
frame: Box::new(Affine::IDENTITY),
};
let mut object = Node::new(d.alloc_id(), envelope);
match i {
0 => {
let params = serde_json::json!({"x": 4, "y": 4, "blur": 1, "opacity": 100});
object.appearance.effects.push(Effect { id: "stylize.dropShadow".into(), params, visible: true });
}
1 => {
object = Node::group(d.alloc_id(), vec![Arc::new(object)]);
object.appearance.effects.push(Effect { id: "distort.transform".into(), params: serde_json::json!({"moveH": 0.0}), visible: true });
}
_ => {
object = Node::group(d.alloc_id(), vec![Arc::new(object)]);
object.appearance.items.push(AppearanceItem::Fill(FillLayer::new(Paint::solid(Color::rgb(1.0, 0.0, 0.0)))));
}
}
let l = d.layers[0].id;
d.insert(Some(l), 0, object).unwrap();
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
let mut r = Renderer::new();
let before = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert!(vectorcraft_text::FontDb::global().add_font(serif_as(family)) > 0);
let after = r.render(&d, 300, 150, Affine::IDENTITY, &opts).pixels;
assert_ne!(before, after, "{what}: drawn in the font that arrived");
assert_eq!(after, Renderer::new().render(&d, 300, 150, Affine::IDENTITY, &opts).pixels, "{what}: as a new renderer draws it");
}
}