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.
This commit is contained in:
dotCooCoo
2026-11-10 21:39:36 -08:00
parent acc6acfdb2
commit 5452b516f6
2 changed files with 51 additions and 1 deletions
+7 -1
View File
@@ -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<u8> {
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<Node>, w: u16, h: u16, view: Affine, opts: &RenderOptions) -> Option<Rendered> {
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<Rendered> {
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)?;
+44
View File
@@ -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]