//! 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 { 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::>(); 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"); } }