From 5452b516f64e65d95b5fd9e996c72c4971a25b53 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Tue, 10 Nov 2026 21:39:36 -0800 Subject: [PATCH] Render: type in a document's own composite font draws in its definition Raster renders, ink renders, thumbnails and node renders now lend the document's composite fonts to the font database while they draw, as the PDF, SVG and EPS writers already do. A document that is not in front, a document read from a file (library thumbnails, save previews) and a placed document's art drew such type in the fallback font or in another definition of that name. --- crates/render/src/renderer.rs | 8 ++++- crates/render/src/tests/fontchange.rs | 44 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/crates/render/src/renderer.rs b/crates/render/src/renderer.rs index a5bfcbf26..205d4d73c 100644 --- a/crates/render/src/renderer.rs +++ b/crates/render/src/renderer.rs @@ -105,8 +105,11 @@ impl Renderer { } } - /// Render `doc` into a `width`×`height` image using `view` (document → pixel transform). + /// Render `doc` into a `width`×`height` image using `view` (document → pixel transform). Type + /// set in one of the document's composite fonts draws in the document's definition, also when + /// no session has registered it (a document that is not in front, or read from a file). pub fn render(&mut self, doc: &Document, width: u32, height: u32, view: Affine, opts: &RenderOptions) -> Rendered { + let _lent = vectorcraft_text::FontDb::global().lend_composite_fonts(&doc.composite_fonts); let _frame = vectorcraft_text::KeptLayoutsFrame::begin(); self.render_as(doc, width, height, view, opts, false, 1) } @@ -340,6 +343,7 @@ impl Renderer { pub fn render_region_inks(&mut self, doc: &Document, region: Rect, scale: f64, opts: &RenderOptions) -> Vec { let (w, h) = region_pixels(region, scale); let view = Affine::scale(scale) * Affine::translate((-region.x0, -region.y0)); + let _lent = vectorcraft_text::FontDb::global().lend_composite_fonts(&doc.composite_fonts); let _frame = vectorcraft_text::KeptLayoutsFrame::begin(); self.render_as(doc, w, h, view, opts, true, 1).pixels } @@ -356,6 +360,7 @@ impl Renderer { /// [`Self::render_node`] with `opts` (e.g. [`RenderOptions::mark_undecoded_images`]). pub fn render_node_with(&mut self, doc: &Document, n: &Arc, w: u16, h: u16, view: Affine, opts: &RenderOptions) -> Option { + let _lent = vectorcraft_text::FontDb::global().lend_composite_fonts(&doc.composite_fonts); self.check_plugins(doc); self.check_symbols(doc); let inv = view.inverse(); @@ -395,6 +400,7 @@ impl Renderer { background: Option<[u8; 4]>, opts: &RenderOptions, ) -> Option { + let _lent = vectorcraft_text::FontDb::global().lend_composite_fonts(&doc.composite_fonts); self.check_plugins(doc); self.check_symbols(doc); let b = brush_fx::cull_bounds(&doc.symbol_boxes(), n)?; diff --git a/crates/render/src/tests/fontchange.rs b/crates/render/src/tests/fontchange.rs index 82c5c77ff..4ead4a5c1 100644 --- a/crates/render/src/tests/fontchange.rs +++ b/crates/render/src/tests/fontchange.rs @@ -138,6 +138,50 @@ fn composite_font_parts_are_not_highlighted_as_substituted_glyphs() { assert!(g.substituted_glyphs.elements().is_empty() && g.substituted_fonts.elements().is_empty()); } +/// Type in one of a document's composite fonts draws in the document's definition when nothing has +/// registered it: in a render, an ink render, a thumbnail and a placed document's art, as type set +/// in the part's font draws. +/// The font database is left without it afterwards. +#[test] +fn a_documents_own_composite_font_draws_without_being_registered() { + use vectorcraft_doc::{CompositeFont, CompositePart, CompositeParts}; + let _adding = adding_fonts(); + const NAME: &str = "CF Unregistered"; + let db = vectorcraft_text::FontDb::global(); + let typed = |family: &str| { + let mut d = Document::new(300.0, 150.0); + let style = CharStyle { size: 60.0, font_family: family.into(), ..Default::default() }; + let n = Node::new(d.alloc_id(), NodeKind::Text(Box::new(TextObject::point(Point::new(10.0, 100.0), "Hamburg", style)))); + let l = d.layers[0].id; + let id = d.insert(Some(l), 0, n).unwrap(); + (d, id) + }; + let (mut composite, id) = typed(NAME); + let mut parts = CompositeParts::all(CompositePart::new("Source Sans 3", "Regular")); + parts.roman = CompositePart::new("JetBrains Mono", "Regular"); + composite.composite_fonts.push(CompositeFont { name: NAME.into(), parts }); + let (plain, plain_id) = typed("JetBrains Mono"); + assert!(db.composite_font(NAME).is_none()); + let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() }; + let view = Affine::IDENTITY; + assert_eq!( + Renderer::new().render(&composite, 300, 150, view, &opts).pixels, + Renderer::new().render(&plain, 300, 150, view, &opts).pixels, + "rendered" + ); + let region = Rect::new(0.0, 0.0, 300.0, 150.0); + assert_eq!( + Renderer::new().render_region_inks(&composite, region, 1.0, &opts), + Renderer::new().render_region_inks(&plain, region, 1.0, &opts), + "rendered as ink amounts" + ); + let thumbnail = |d: &Document, id| Renderer::new().render_thumbnail(d, id, 64).map(|r| r.pixels); + assert_eq!(thumbnail(&composite, id), thumbnail(&plain, plain_id), "thumbnail"); + let placed = |d: &Document| crate::placed_document::render_inside(&mut Renderer::new(), d, &d.layers[0], 300, 150, view).map(|r| r.pixels); + assert_eq!(placed(&composite), placed(&plain), "a placed document's art"); + assert!(db.composite_font(NAME).is_none(), "given back after each render"); +} + /// Type at 80% opacity, in Multiply and with a drop shadow redraws in a font that becomes /// available: the glyph geometry kept for it is laid out again. #[test]