Skip to main content

blockworx/export/
level.rs

1//! One level of the document, rendered to SVG — the entry both the SVG/PNG
2//! export and the PDF's per-scope pages draw through, so a page and a saved
3//! picture cannot disagree about what the level looks like.
4
5use blockworx_editor::{
6    gesture::Gesture,
7    path::BlockPath,
8    presentation::Presentation,
9    widget::{display::render, drawing::Drawing},
10};
11use blockworx_geom::Rect;
12use blockworx_paint::FontChoice;
13
14use crate::{
15    export::svg::SvgRenderer,
16    theme::{Style, Theme},
17};
18
19/// One level as the SVG exporter drew it: the source, the world-space frame
20/// its `viewBox` covers, and where each child block landed inside that frame.
21/// The rects come from the very [`Drawing`] that was drawn, so a consumer
22/// placing them back onto the picture (the PDF export's scope links) cannot
23/// disagree with it.
24pub struct RenderedLevel {
25    pub svg: String,
26    pub frame: Rect,
27    pub blocks: Vec<(blockworx_doc::id::BlockId, Rect)>,
28}
29
30/// Render the level framed by `path` of `indexed` to an SVG string (also the
31/// source PNG export rasterizes). The sink is local because a render authors nothing:
32/// whatever it were handed would come back empty.
33pub fn render_svg(
34    theme: &Theme,
35    font: FontChoice,
36    indexed: blockworx_doc::document::IndexedDocument<'_>,
37    path: &BlockPath,
38    presentation: &mut Presentation,
39) -> String {
40    render_level(theme, font, indexed, path, presentation).svg
41}
42
43/// [`render_svg`], keeping what the render learned about the picture's extent
44/// and the blocks in it.
45pub fn render_level(
46    theme: &Theme,
47    font: FontChoice,
48    indexed: blockworx_doc::document::IndexedDocument<'_>,
49    path: &BlockPath,
50    presentation: &mut Presentation,
51) -> RenderedLevel {
52    // A read-only pass: the gesture it opens is never sealed.
53    let mut gesture = Gesture::idle();
54    let mut drawing = Drawing::new(indexed, path, presentation, &mut gesture);
55    let mut svg = SvgRenderer::new(theme.palette().clone(), font);
56    {
57        let mut style = Style::new(theme, &mut svg);
58        // An export is a frame too: measure before drawing, or a box the editor
59        // never typed into exports at its estimated size.
60        drawing.refresh_text_extents(&style);
61        render(&drawing, &mut style);
62    }
63    let blocks = drawing
64        .blocks_layer()
65        .filter_map(|(id, shape)| Some((id.block()?, shape.gui_rect())))
66        .collect();
67    let (svg, frame) = svg.finish();
68    RenderedLevel { svg, frame, blocks }
69}