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