Skip to main content

merman_render/svg/
parity.rs

1use crate::model::{
2    ArchitectureDiagramLayout, BlockDiagramLayout, Bounds, ClassDiagramV2Layout, ErDiagramLayout,
3    ErrorDiagramLayout, FlowchartV2Layout, InfoDiagramLayout, LayoutCluster, LayoutNode,
4    MindmapDiagramLayout, PacketDiagramLayout, PieDiagramLayout, QuadrantChartDiagramLayout,
5    RadarDiagramLayout, RequirementDiagramLayout, SankeyDiagramLayout, SequenceDiagramLayout,
6    StateDiagramV2Layout, TimelineDiagramLayout, XyChartDiagramLayout,
7};
8use crate::text::{TextMeasurer, TextStyle, WrapMode};
9use crate::{Error, Result};
10use base64::Engine as _;
11use indexmap::IndexMap;
12use std::fmt::Write as _;
13
14mod architecture;
15mod block;
16mod c4;
17mod class;
18mod css;
19mod curve;
20mod er;
21mod error;
22mod fallback;
23mod flowchart;
24mod gantt;
25mod gitgraph;
26mod info;
27mod journey;
28mod kanban;
29mod layout_debug;
30mod mindmap;
31mod packet;
32mod path_bounds;
33mod pie;
34mod quadrantchart;
35mod radar;
36mod requirement;
37mod root_svg;
38mod roughjs46;
39mod roughjs_common;
40mod sankey;
41mod sequence;
42mod state;
43mod style;
44mod timeline;
45mod timing;
46mod treemap;
47mod util;
48mod xychart;
49use crate::math::MathRenderer;
50use css::{
51    er_css, gantt_css, info_css_parts_with_config, info_css_parts_with_theme_font_size_only,
52    info_css_with_config, pie_css, push_xychart_css, requirement_css, sankey_css, treemap_css,
53};
54pub use fallback::foreign_object_label_fallback_svg_text;
55use path_bounds::svg_path_bounds_from_d;
56pub(crate) fn mindmap_cloud_rendered_bbox_size_px(w: f64, h: f64) -> Option<(f64, f64)> {
57    mindmap::mindmap_cloud_rendered_bbox_size_px(w, h)
58}
59pub use state::{SvgEmittedBoundsContributor, SvgEmittedBoundsDebug, debug_svg_emitted_bounds};
60use state::{
61    roughjs_ops_to_svg_path_d, roughjs_parse_hex_color_to_srgba, roughjs_paths_for_rect,
62    svg_emitted_bounds_from_svg, svg_emitted_bounds_from_svg_inner,
63};
64use style::{is_rect_style_key, is_text_style_key, parse_style_decl};
65use util::{
66    apply_root_viewport_override, config_bool, config_f64, config_f64_css_px, config_string,
67    decode_mermaid_entities_for_render_text, escape_attr, escape_attr_display, escape_xml,
68    escape_xml_display, escape_xml_into, fmt, fmt_debug_3dp, fmt_display, fmt_into,
69    fmt_max_width_px, fmt_path, fmt_path_into, fmt_string, json_f64, json_stringify_points,
70    json_stringify_points_into, normalize_css_font_family, theme_color,
71};
72
73const MERMAID_SEQUENCE_BASE_DEFS_11_12_2: &str = include_str!(concat!(
74    env!("CARGO_MANIFEST_DIR"),
75    "/assets/sequence_base_defs_11_12_2.svgfrag"
76));
77
78#[derive(Debug, Clone)]
79pub struct SvgRenderOptions {
80    /// Adds extra space around the computed viewBox.
81    pub viewbox_padding: f64,
82    /// Optional diagram id used for Mermaid-like marker ids.
83    pub diagram_id: Option<String>,
84    /// Optional override for the root SVG `aria-roledescription` attribute.
85    ///
86    /// This is primarily used to reproduce Mermaid's per-header accessibility metadata quirks
87    /// (e.g. `classDiagram-v2` differs from `classDiagram` at Mermaid 11.12.2).
88    pub aria_roledescription: Option<String>,
89    /// When true, include edge polylines.
90    pub include_edges: bool,
91    /// When true, include node bounding boxes and ids.
92    pub include_nodes: bool,
93    /// When true, include cluster bounding boxes and titles.
94    pub include_clusters: bool,
95    /// When true, draw markers that visualize Mermaid cluster positioning metadata.
96    pub include_cluster_debug_markers: bool,
97    /// When true, label edge routes with edge ids.
98    pub include_edge_id_labels: bool,
99    /// Optional override for "current time" used by diagrams that render time-dependent markers
100    /// (e.g. Gantt `today` line). This exists to make parity/golden comparisons reproducible.
101    pub now_ms_override: Option<i64>,
102    /// Optional math renderer for `$$...$$` style labels.
103    pub math_renderer: Option<std::sync::Arc<dyn MathRenderer + Send + Sync>>,
104    /// When false, renderers that support root viewport override lookup emit computed bounds.
105    pub apply_root_overrides: bool,
106}
107
108impl Default for SvgRenderOptions {
109    fn default() -> Self {
110        Self {
111            viewbox_padding: 8.0,
112            diagram_id: None,
113            aria_roledescription: None,
114            include_edges: true,
115            include_nodes: true,
116            include_clusters: true,
117            include_cluster_debug_markers: false,
118            include_edge_id_labels: false,
119            now_ms_override: None,
120            math_renderer: None,
121            apply_root_overrides: true,
122        }
123    }
124}
125
126pub fn render_layouted_svg(
127    diagram: &crate::model::LayoutedDiagram,
128    measurer: &dyn TextMeasurer,
129    options: &SvgRenderOptions,
130) -> Result<String> {
131    render_layout_svg_parts(
132        &diagram.layout,
133        &diagram.semantic,
134        &diagram.meta.effective_config,
135        diagram.meta.title.as_deref(),
136        measurer,
137        options,
138    )
139}
140
141pub fn render_layout_svg_parts(
142    layout: &crate::model::LayoutDiagram,
143    semantic: &serde_json::Value,
144    effective_config: &serde_json::Value,
145    title: Option<&str>,
146    measurer: &dyn TextMeasurer,
147    options: &SvgRenderOptions,
148) -> Result<String> {
149    use crate::model::LayoutDiagram;
150
151    match layout {
152        LayoutDiagram::ErrorDiagram(layout) => {
153            render_error_diagram_svg(layout, semantic, effective_config, options)
154        }
155        LayoutDiagram::BlockDiagram(layout) => {
156            render_block_diagram_svg(layout, semantic, effective_config, options)
157        }
158        LayoutDiagram::RequirementDiagram(layout) => {
159            render_requirement_diagram_svg(layout, semantic, effective_config, title, options)
160        }
161        LayoutDiagram::ArchitectureDiagram(layout) => {
162            render_architecture_diagram_svg(layout, semantic, effective_config, options)
163        }
164        LayoutDiagram::MindmapDiagram(layout) => {
165            render_mindmap_diagram_svg(layout, semantic, effective_config, options)
166        }
167        LayoutDiagram::SankeyDiagram(layout) => {
168            render_sankey_diagram_svg(layout, semantic, effective_config, options)
169        }
170        LayoutDiagram::RadarDiagram(layout) => {
171            render_radar_diagram_svg(layout, semantic, effective_config, options)
172        }
173        LayoutDiagram::TreemapDiagram(layout) => {
174            render_treemap_diagram_svg(layout, semantic, effective_config, options)
175        }
176        LayoutDiagram::XyChartDiagram(layout) => {
177            render_xychart_diagram_svg(layout, semantic, effective_config, options)
178        }
179        LayoutDiagram::QuadrantChartDiagram(layout) => {
180            render_quadrantchart_diagram_svg(layout, semantic, effective_config, options)
181        }
182        LayoutDiagram::FlowchartV2(layout) => {
183            render_flowchart_v2_svg(layout, semantic, effective_config, title, measurer, options)
184        }
185        LayoutDiagram::StateDiagramV2(layout) => render_state_diagram_v2_svg(
186            layout,
187            semantic,
188            effective_config,
189            title,
190            measurer,
191            options,
192        ),
193        LayoutDiagram::ClassDiagramV2(layout) => render_class_diagram_v2_svg(
194            layout,
195            semantic,
196            effective_config,
197            title,
198            measurer,
199            options,
200        ),
201        LayoutDiagram::ErDiagram(layout) => {
202            render_er_diagram_svg(layout, semantic, effective_config, title, measurer, options)
203        }
204        LayoutDiagram::SequenceDiagram(layout) => render_sequence_diagram_svg(
205            layout,
206            semantic,
207            effective_config,
208            title,
209            measurer,
210            options,
211        ),
212        LayoutDiagram::InfoDiagram(layout) => {
213            render_info_diagram_svg(layout, semantic, effective_config, options)
214        }
215        LayoutDiagram::PacketDiagram(layout) => {
216            render_packet_diagram_svg(layout, semantic, effective_config, title, options)
217        }
218        LayoutDiagram::TimelineDiagram(layout) => render_timeline_diagram_svg(
219            layout,
220            semantic,
221            effective_config,
222            title,
223            measurer,
224            options,
225        ),
226        LayoutDiagram::PieDiagram(layout) => {
227            render_pie_diagram_svg(layout, semantic, effective_config, options)
228        }
229        LayoutDiagram::JourneyDiagram(layout) => {
230            render_journey_diagram_svg(layout, semantic, effective_config, title, measurer, options)
231        }
232        LayoutDiagram::KanbanDiagram(layout) => {
233            render_kanban_diagram_svg(layout, semantic, effective_config, options)
234        }
235        LayoutDiagram::GitGraphDiagram(layout) => render_gitgraph_diagram_svg(
236            layout,
237            semantic,
238            effective_config,
239            title,
240            measurer,
241            options,
242        ),
243        LayoutDiagram::GanttDiagram(layout) => {
244            render_gantt_diagram_svg(layout, semantic, effective_config, options)
245        }
246        LayoutDiagram::C4Diagram(layout) => {
247            render_c4_diagram_svg(layout, semantic, effective_config, title, measurer, options)
248        }
249    }
250}
251
252pub fn render_layout_svg_parts_with_config(
253    layout: &crate::model::LayoutDiagram,
254    semantic: &serde_json::Value,
255    effective_config: &merman_core::MermaidConfig,
256    title: Option<&str>,
257    measurer: &dyn TextMeasurer,
258    options: &SvgRenderOptions,
259) -> Result<String> {
260    use crate::model::LayoutDiagram;
261
262    let effective_config_value = effective_config.as_value();
263
264    match layout {
265        LayoutDiagram::ErrorDiagram(layout) => {
266            render_error_diagram_svg(layout, semantic, effective_config_value, options)
267        }
268        LayoutDiagram::BlockDiagram(layout) => {
269            render_block_diagram_svg(layout, semantic, effective_config_value, options)
270        }
271        LayoutDiagram::RequirementDiagram(layout) => {
272            render_requirement_diagram_svg(layout, semantic, effective_config_value, title, options)
273        }
274        LayoutDiagram::ArchitectureDiagram(layout) => {
275            architecture::render_architecture_diagram_svg_with_config(
276                layout,
277                semantic,
278                effective_config,
279                options,
280            )
281        }
282        LayoutDiagram::MindmapDiagram(layout) => {
283            render_mindmap_diagram_svg_with_config(layout, semantic, effective_config, options)
284        }
285        LayoutDiagram::SankeyDiagram(layout) => {
286            render_sankey_diagram_svg(layout, semantic, effective_config_value, options)
287        }
288        LayoutDiagram::RadarDiagram(layout) => {
289            render_radar_diagram_svg(layout, semantic, effective_config_value, options)
290        }
291        LayoutDiagram::TreemapDiagram(layout) => {
292            render_treemap_diagram_svg(layout, semantic, effective_config_value, options)
293        }
294        LayoutDiagram::XyChartDiagram(layout) => {
295            render_xychart_diagram_svg(layout, semantic, effective_config_value, options)
296        }
297        LayoutDiagram::QuadrantChartDiagram(layout) => {
298            render_quadrantchart_diagram_svg(layout, semantic, effective_config_value, options)
299        }
300        LayoutDiagram::FlowchartV2(layout) => render_flowchart_v2_svg_with_config(
301            layout,
302            semantic,
303            effective_config,
304            title,
305            measurer,
306            options,
307        ),
308        LayoutDiagram::StateDiagramV2(layout) => render_state_diagram_v2_svg(
309            layout,
310            semantic,
311            effective_config_value,
312            title,
313            measurer,
314            options,
315        ),
316        LayoutDiagram::ClassDiagramV2(layout) => {
317            let model = crate::json::from_value_ref(semantic)?;
318            class::render_class_diagram_v2_svg_model_with_config(
319                layout,
320                &model,
321                effective_config,
322                title,
323                measurer,
324                options,
325            )
326        }
327        LayoutDiagram::ErDiagram(layout) => render_er_diagram_svg(
328            layout,
329            semantic,
330            effective_config_value,
331            title,
332            measurer,
333            options,
334        ),
335        LayoutDiagram::SequenceDiagram(layout) => {
336            sequence::render_sequence_diagram_svg_with_config(
337                layout,
338                semantic,
339                effective_config,
340                title,
341                measurer,
342                options,
343            )
344        }
345        LayoutDiagram::InfoDiagram(layout) => {
346            render_info_diagram_svg(layout, semantic, effective_config_value, options)
347        }
348        LayoutDiagram::PacketDiagram(layout) => {
349            render_packet_diagram_svg(layout, semantic, effective_config_value, title, options)
350        }
351        LayoutDiagram::TimelineDiagram(layout) => render_timeline_diagram_svg(
352            layout,
353            semantic,
354            effective_config_value,
355            title,
356            measurer,
357            options,
358        ),
359        LayoutDiagram::PieDiagram(layout) => {
360            render_pie_diagram_svg(layout, semantic, effective_config_value, options)
361        }
362        LayoutDiagram::JourneyDiagram(layout) => render_journey_diagram_svg(
363            layout,
364            semantic,
365            effective_config_value,
366            title,
367            measurer,
368            options,
369        ),
370        LayoutDiagram::KanbanDiagram(layout) => {
371            render_kanban_diagram_svg(layout, semantic, effective_config_value, options)
372        }
373        LayoutDiagram::GitGraphDiagram(layout) => render_gitgraph_diagram_svg(
374            layout,
375            semantic,
376            effective_config_value,
377            title,
378            measurer,
379            options,
380        ),
381        LayoutDiagram::GanttDiagram(layout) => {
382            render_gantt_diagram_svg(layout, semantic, effective_config_value, options)
383        }
384        LayoutDiagram::C4Diagram(layout) => render_c4_diagram_svg(
385            layout,
386            semantic,
387            effective_config_value,
388            title,
389            measurer,
390            options,
391        ),
392    }
393}
394
395pub fn render_layout_svg_parts_for_render_model_with_config(
396    layout: &crate::model::LayoutDiagram,
397    semantic: &merman_core::RenderSemanticModel,
398    effective_config: &merman_core::MermaidConfig,
399    title: Option<&str>,
400    measurer: &dyn TextMeasurer,
401    options: &SvgRenderOptions,
402) -> Result<String> {
403    use crate::model::LayoutDiagram;
404    use merman_core::RenderSemanticModel;
405
406    match (layout, semantic) {
407        (LayoutDiagram::ArchitectureDiagram(layout), RenderSemanticModel::Architecture(model)) => {
408            architecture::render_architecture_diagram_svg_typed_with_config(
409                layout,
410                model,
411                effective_config,
412                options,
413            )
414        }
415        (LayoutDiagram::FlowchartV2(layout), RenderSemanticModel::Flowchart(model)) => {
416            render_flowchart_v2_svg_model_with_config(
417                layout,
418                model,
419                effective_config,
420                title,
421                measurer,
422                options,
423            )
424        }
425        (LayoutDiagram::MindmapDiagram(layout), RenderSemanticModel::Mindmap(model)) => {
426            mindmap::render_mindmap_diagram_svg_model_with_config(
427                layout,
428                model,
429                effective_config,
430                options,
431            )
432        }
433        (LayoutDiagram::StateDiagramV2(layout), RenderSemanticModel::State(model)) => {
434            state::render_state_diagram_v2_svg_model(
435                layout,
436                model,
437                effective_config.as_value(),
438                title,
439                measurer,
440                options,
441            )
442        }
443        (LayoutDiagram::ClassDiagramV2(layout), RenderSemanticModel::Class(model)) => {
444            class::render_class_diagram_v2_svg_model_with_config(
445                layout,
446                model,
447                effective_config,
448                title,
449                measurer,
450                options,
451            )
452        }
453        (LayoutDiagram::SequenceDiagram(layout), RenderSemanticModel::Sequence(model)) => {
454            sequence::render_sequence_diagram_svg_model_with_config(
455                layout,
456                model,
457                effective_config,
458                title,
459                measurer,
460                options,
461            )
462        }
463        (LayoutDiagram::KanbanDiagram(layout), RenderSemanticModel::Kanban(_)) => {
464            render_kanban_diagram_svg(
465                layout,
466                &serde_json::Value::Null,
467                effective_config.as_value(),
468                options,
469            )
470        }
471        (LayoutDiagram::GanttDiagram(layout), RenderSemanticModel::Gantt(model)) => {
472            gantt::render_gantt_diagram_svg_model(
473                layout,
474                model,
475                effective_config.as_value(),
476                options,
477            )
478        }
479        (LayoutDiagram::PieDiagram(layout), RenderSemanticModel::Pie(model)) => {
480            pie::render_pie_diagram_svg_model(layout, model, effective_config.as_value(), options)
481        }
482        (LayoutDiagram::PacketDiagram(layout), RenderSemanticModel::Packet(model)) => {
483            packet::render_packet_diagram_svg_model(
484                layout,
485                model,
486                effective_config.as_value(),
487                title,
488                options,
489            )
490        }
491        (LayoutDiagram::TimelineDiagram(layout), RenderSemanticModel::Timeline(model)) => {
492            timeline::render_timeline_diagram_svg_model(
493                layout,
494                model,
495                effective_config.as_value(),
496                title,
497                measurer,
498                options,
499            )
500        }
501        (LayoutDiagram::JourneyDiagram(layout), RenderSemanticModel::Journey(model)) => {
502            journey::render_journey_diagram_svg_model(
503                layout,
504                model,
505                effective_config.as_value(),
506                title,
507                measurer,
508                options,
509            )
510        }
511        (LayoutDiagram::RequirementDiagram(layout), RenderSemanticModel::Requirement(model)) => {
512            requirement::render_requirement_diagram_svg_model(
513                layout,
514                model,
515                effective_config.as_value(),
516                title,
517                options,
518            )
519        }
520        (LayoutDiagram::SankeyDiagram(layout), RenderSemanticModel::Sankey(_)) => {
521            render_sankey_diagram_svg(
522                layout,
523                &serde_json::Value::Null,
524                effective_config.as_value(),
525                options,
526            )
527        }
528        (LayoutDiagram::RadarDiagram(layout), RenderSemanticModel::Radar(model)) => {
529            radar::render_radar_diagram_svg_model(
530                layout,
531                model,
532                effective_config.as_value(),
533                options,
534            )
535        }
536        (LayoutDiagram::InfoDiagram(layout), RenderSemanticModel::Info(_)) => {
537            render_info_diagram_svg(
538                layout,
539                &serde_json::Value::Null,
540                effective_config.as_value(),
541                options,
542            )
543        }
544        (LayoutDiagram::TreemapDiagram(layout), RenderSemanticModel::Treemap(_)) => {
545            render_treemap_diagram_svg(
546                layout,
547                &serde_json::Value::Null,
548                effective_config.as_value(),
549                options,
550            )
551        }
552        (LayoutDiagram::BlockDiagram(layout), RenderSemanticModel::Block(model)) => {
553            render_block_diagram_svg_model(layout, model, effective_config.as_value(), options)
554        }
555        (LayoutDiagram::ErDiagram(layout), RenderSemanticModel::Er(model)) => {
556            render_er_diagram_svg_model(
557                layout,
558                model,
559                effective_config.as_value(),
560                title,
561                measurer,
562                options,
563            )
564        }
565        (LayoutDiagram::QuadrantChartDiagram(layout), RenderSemanticModel::QuadrantChart(_)) => {
566            render_quadrantchart_diagram_svg(
567                layout,
568                &serde_json::Value::Null,
569                effective_config.as_value(),
570                options,
571            )
572        }
573        (LayoutDiagram::XyChartDiagram(layout), RenderSemanticModel::XyChart(_)) => {
574            render_xychart_diagram_svg(
575                layout,
576                &serde_json::Value::Null,
577                effective_config.as_value(),
578                options,
579            )
580        }
581        (LayoutDiagram::GitGraphDiagram(layout), RenderSemanticModel::GitGraph(model)) => {
582            gitgraph::render_gitgraph_diagram_svg_model(
583                layout,
584                model,
585                effective_config.as_value(),
586                title,
587                measurer,
588                options,
589            )
590        }
591        (LayoutDiagram::C4Diagram(layout), RenderSemanticModel::C4(model)) => {
592            c4::render_c4_diagram_svg_typed(
593                layout,
594                model,
595                effective_config.as_value(),
596                title,
597                measurer,
598                options,
599            )
600        }
601        (_, RenderSemanticModel::Json(semantic)) => render_layout_svg_parts_with_config(
602            layout,
603            semantic,
604            effective_config,
605            title,
606            measurer,
607            options,
608        ),
609        _ => Err(Error::InvalidModel {
610            message: "semantic model does not match layout diagram type".to_string(),
611        }),
612    }
613}
614
615pub fn render_flowchart_v2_debug_svg(
616    layout: &FlowchartV2Layout,
617    options: &SvgRenderOptions,
618) -> String {
619    flowchart::render_flowchart_v2_debug_svg(layout, options)
620}
621
622pub fn render_sequence_diagram_debug_svg(
623    layout: &SequenceDiagramLayout,
624    options: &SvgRenderOptions,
625) -> String {
626    sequence::render_sequence_diagram_debug_svg(layout, options)
627}
628
629pub fn render_sequence_diagram_svg(
630    layout: &SequenceDiagramLayout,
631    semantic: &serde_json::Value,
632    effective_config: &serde_json::Value,
633    diagram_title: Option<&str>,
634    measurer: &dyn TextMeasurer,
635    options: &SvgRenderOptions,
636) -> Result<String> {
637    sequence::render_sequence_diagram_svg(
638        layout,
639        semantic,
640        effective_config,
641        diagram_title,
642        measurer,
643        options,
644    )
645}
646
647pub fn render_error_diagram_svg(
648    layout: &ErrorDiagramLayout,
649    _semantic: &serde_json::Value,
650    _effective_config: &serde_json::Value,
651    options: &SvgRenderOptions,
652) -> Result<String> {
653    error::render_error_diagram_svg(layout, _semantic, _effective_config, options)
654}
655
656pub fn render_info_diagram_svg(
657    layout: &InfoDiagramLayout,
658    _semantic: &serde_json::Value,
659    _effective_config: &serde_json::Value,
660    options: &SvgRenderOptions,
661) -> Result<String> {
662    info::render_info_diagram_svg(layout, _semantic, _effective_config, options)
663}
664
665pub fn render_pie_diagram_svg(
666    layout: &PieDiagramLayout,
667    semantic: &serde_json::Value,
668    _effective_config: &serde_json::Value,
669    options: &SvgRenderOptions,
670) -> Result<String> {
671    pie::render_pie_diagram_svg(layout, semantic, _effective_config, options)
672}
673
674pub fn render_requirement_diagram_svg(
675    layout: &RequirementDiagramLayout,
676    semantic: &serde_json::Value,
677    effective_config: &serde_json::Value,
678    diagram_title: Option<&str>,
679    options: &SvgRenderOptions,
680) -> Result<String> {
681    requirement::render_requirement_diagram_svg(
682        layout,
683        semantic,
684        effective_config,
685        diagram_title,
686        options,
687    )
688}
689
690pub fn render_block_diagram_svg(
691    layout: &BlockDiagramLayout,
692    semantic: &serde_json::Value,
693    effective_config: &serde_json::Value,
694    options: &SvgRenderOptions,
695) -> Result<String> {
696    block::render_block_diagram_svg(layout, semantic, effective_config, options)
697}
698
699pub fn render_block_diagram_svg_model(
700    layout: &BlockDiagramLayout,
701    model: &merman_core::diagrams::block::BlockDiagramRenderModel,
702    effective_config: &serde_json::Value,
703    options: &SvgRenderOptions,
704) -> Result<String> {
705    block::render_block_diagram_svg_model(layout, model, effective_config, options)
706}
707
708pub fn render_er_diagram_svg_model(
709    layout: &ErDiagramLayout,
710    model: &merman_core::diagrams::er::ErDiagramRenderModel,
711    effective_config: &serde_json::Value,
712    diagram_title: Option<&str>,
713    measurer: &dyn TextMeasurer,
714    options: &SvgRenderOptions,
715) -> Result<String> {
716    er::render_er_diagram_svg_model(
717        layout,
718        model,
719        effective_config,
720        diagram_title,
721        measurer,
722        options,
723    )
724}
725
726pub fn render_radar_diagram_svg(
727    layout: &RadarDiagramLayout,
728    semantic: &serde_json::Value,
729    effective_config: &serde_json::Value,
730    options: &SvgRenderOptions,
731) -> Result<String> {
732    radar::render_radar_diagram_svg(layout, semantic, effective_config, options)
733}
734
735pub fn render_quadrantchart_diagram_svg(
736    layout: &QuadrantChartDiagramLayout,
737    _semantic: &serde_json::Value,
738    _effective_config: &serde_json::Value,
739    options: &SvgRenderOptions,
740) -> Result<String> {
741    quadrantchart::render_quadrantchart_diagram_svg(layout, _semantic, _effective_config, options)
742}
743
744pub fn render_xychart_diagram_svg(
745    layout: &XyChartDiagramLayout,
746    _semantic: &serde_json::Value,
747    _effective_config: &serde_json::Value,
748    options: &SvgRenderOptions,
749) -> Result<String> {
750    xychart::render_xychart_diagram_svg(layout, _semantic, _effective_config, options)
751}
752
753pub fn render_treemap_diagram_svg(
754    layout: &crate::model::TreemapDiagramLayout,
755    _semantic: &serde_json::Value,
756    effective_config: &serde_json::Value,
757    options: &SvgRenderOptions,
758) -> Result<String> {
759    treemap::render_treemap_diagram_svg(layout, _semantic, effective_config, options)
760}
761
762pub fn render_packet_diagram_svg(
763    layout: &PacketDiagramLayout,
764    semantic: &serde_json::Value,
765    _effective_config: &serde_json::Value,
766    diagram_title: Option<&str>,
767    options: &SvgRenderOptions,
768) -> Result<String> {
769    packet::render_packet_diagram_svg(layout, semantic, _effective_config, diagram_title, options)
770}
771
772pub fn render_timeline_diagram_svg(
773    layout: &TimelineDiagramLayout,
774    semantic: &serde_json::Value,
775    effective_config: &serde_json::Value,
776    _diagram_title: Option<&str>,
777    _measurer: &dyn TextMeasurer,
778    options: &SvgRenderOptions,
779) -> Result<String> {
780    timeline::render_timeline_diagram_svg(
781        layout,
782        semantic,
783        effective_config,
784        _diagram_title,
785        _measurer,
786        options,
787    )
788}
789
790pub fn render_journey_diagram_svg(
791    layout: &crate::model::JourneyDiagramLayout,
792    semantic: &serde_json::Value,
793    effective_config: &serde_json::Value,
794    _diagram_title: Option<&str>,
795    _measurer: &dyn TextMeasurer,
796    options: &SvgRenderOptions,
797) -> Result<String> {
798    journey::render_journey_diagram_svg(
799        layout,
800        semantic,
801        effective_config,
802        _diagram_title,
803        _measurer,
804        options,
805    )
806}
807
808pub fn render_kanban_diagram_svg(
809    layout: &crate::model::KanbanDiagramLayout,
810    _semantic: &serde_json::Value,
811    _effective_config: &serde_json::Value,
812    options: &SvgRenderOptions,
813) -> Result<String> {
814    kanban::render_kanban_diagram_svg(layout, _semantic, _effective_config, options)
815}
816
817pub fn render_gitgraph_diagram_svg(
818    layout: &crate::model::GitGraphDiagramLayout,
819    semantic: &serde_json::Value,
820    _effective_config: &serde_json::Value,
821    diagram_title: Option<&str>,
822    measurer: &dyn TextMeasurer,
823    options: &SvgRenderOptions,
824) -> Result<String> {
825    gitgraph::render_gitgraph_diagram_svg(
826        layout,
827        semantic,
828        _effective_config,
829        diagram_title,
830        measurer,
831        options,
832    )
833}
834
835pub fn render_gantt_diagram_svg(
836    layout: &crate::model::GanttDiagramLayout,
837    semantic: &serde_json::Value,
838    _effective_config: &serde_json::Value,
839    options: &SvgRenderOptions,
840) -> Result<String> {
841    gantt::render_gantt_diagram_svg(layout, semantic, _effective_config, options)
842}
843
844pub fn render_mindmap_diagram_svg(
845    layout: &MindmapDiagramLayout,
846    semantic: &serde_json::Value,
847    _effective_config: &serde_json::Value,
848    options: &SvgRenderOptions,
849) -> Result<String> {
850    mindmap::render_mindmap_diagram_svg(layout, semantic, _effective_config, options)
851}
852
853pub fn render_mindmap_diagram_svg_with_config(
854    layout: &MindmapDiagramLayout,
855    semantic: &serde_json::Value,
856    effective_config: &merman_core::MermaidConfig,
857    options: &SvgRenderOptions,
858) -> Result<String> {
859    mindmap::render_mindmap_diagram_svg_with_config(layout, semantic, effective_config, options)
860}
861
862pub fn render_architecture_diagram_svg(
863    layout: &ArchitectureDiagramLayout,
864    semantic: &serde_json::Value,
865    effective_config: &serde_json::Value,
866    options: &SvgRenderOptions,
867) -> Result<String> {
868    architecture::render_architecture_diagram_svg(layout, semantic, effective_config, options)
869}
870
871pub fn render_c4_diagram_svg(
872    layout: &crate::model::C4DiagramLayout,
873    semantic: &serde_json::Value,
874    effective_config: &serde_json::Value,
875    diagram_title: Option<&str>,
876    _measurer: &dyn TextMeasurer,
877    options: &SvgRenderOptions,
878) -> Result<String> {
879    c4::render_c4_diagram_svg(
880        layout,
881        semantic,
882        effective_config,
883        diagram_title,
884        _measurer,
885        options,
886    )
887}
888
889pub fn render_flowchart_v2_svg(
890    layout: &FlowchartV2Layout,
891    semantic: &serde_json::Value,
892    effective_config: &serde_json::Value,
893    diagram_title: Option<&str>,
894    measurer: &dyn TextMeasurer,
895    options: &SvgRenderOptions,
896) -> Result<String> {
897    flowchart::render_flowchart_v2_svg(
898        layout,
899        semantic,
900        effective_config,
901        diagram_title,
902        measurer,
903        options,
904    )
905}
906
907pub fn render_flowchart_v2_svg_with_config(
908    layout: &FlowchartV2Layout,
909    semantic: &serde_json::Value,
910    effective_config: &merman_core::MermaidConfig,
911    diagram_title: Option<&str>,
912    measurer: &dyn TextMeasurer,
913    options: &SvgRenderOptions,
914) -> Result<String> {
915    flowchart::render_flowchart_v2_svg_with_config(
916        layout,
917        semantic,
918        effective_config,
919        diagram_title,
920        measurer,
921        options,
922    )
923}
924
925pub fn render_flowchart_v2_svg_model_with_config(
926    layout: &FlowchartV2Layout,
927    model: &merman_core::diagrams::flowchart::FlowchartV2Model,
928    effective_config: &merman_core::MermaidConfig,
929    diagram_title: Option<&str>,
930    measurer: &dyn TextMeasurer,
931    options: &SvgRenderOptions,
932) -> Result<String> {
933    flowchart::render_flowchart_v2_svg_model_with_config(
934        layout,
935        model,
936        effective_config,
937        diagram_title,
938        measurer,
939        options,
940    )
941}
942
943pub fn render_state_diagram_v2_svg(
944    layout: &StateDiagramV2Layout,
945    semantic: &serde_json::Value,
946    effective_config: &serde_json::Value,
947    diagram_title: Option<&str>,
948    measurer: &dyn TextMeasurer,
949    options: &SvgRenderOptions,
950) -> Result<String> {
951    state::render_state_diagram_v2_svg(
952        layout,
953        semantic,
954        effective_config,
955        diagram_title,
956        measurer,
957        options,
958    )
959}
960
961pub fn render_state_diagram_v2_debug_svg(
962    layout: &StateDiagramV2Layout,
963    options: &SvgRenderOptions,
964) -> String {
965    state::render_state_diagram_v2_debug_svg(layout, options)
966}
967
968pub fn render_class_diagram_v2_debug_svg(
969    layout: &ClassDiagramV2Layout,
970    options: &SvgRenderOptions,
971) -> String {
972    class::render_class_diagram_v2_debug_svg(layout, options)
973}
974
975pub fn render_class_diagram_v2_svg(
976    layout: &ClassDiagramV2Layout,
977    semantic: &serde_json::Value,
978    effective_config: &serde_json::Value,
979    diagram_title: Option<&str>,
980    measurer: &dyn TextMeasurer,
981    options: &SvgRenderOptions,
982) -> Result<String> {
983    class::render_class_diagram_v2_svg(
984        layout,
985        semantic,
986        effective_config,
987        diagram_title,
988        measurer,
989        options,
990    )
991}
992
993pub fn render_er_diagram_debug_svg(layout: &ErDiagramLayout, options: &SvgRenderOptions) -> String {
994    er::render_er_diagram_debug_svg(layout, options)
995}
996
997pub fn render_er_diagram_svg(
998    layout: &ErDiagramLayout,
999    semantic: &serde_json::Value,
1000    effective_config: &serde_json::Value,
1001    diagram_title: Option<&str>,
1002    measurer: &dyn TextMeasurer,
1003    options: &SvgRenderOptions,
1004) -> Result<String> {
1005    er::render_er_diagram_svg(
1006        layout,
1007        semantic,
1008        effective_config,
1009        diagram_title,
1010        measurer,
1011        options,
1012    )
1013}
1014
1015pub fn render_sankey_diagram_svg(
1016    layout: &SankeyDiagramLayout,
1017    _semantic: &serde_json::Value,
1018    effective_config: &serde_json::Value,
1019    options: &SvgRenderOptions,
1020) -> Result<String> {
1021    sankey::render_sankey_diagram_svg(layout, _semantic, effective_config, options)
1022}
1023
1024// Ported from D3 `curveBasis` (d3-shape v3.x), used by Mermaid ER renderer `@11.12.2`.
1025fn curve_basis_path_d(points: &[crate::model::LayoutPoint]) -> String {
1026    curve::curve_basis_path_d(points)
1027}
1028fn render_node(out: &mut String, n: &LayoutNode) {
1029    layout_debug::render_node(out, n)
1030}
1031
1032fn render_state_node(out: &mut String, n: &LayoutNode) {
1033    layout_debug::render_state_node(out, n)
1034}
1035
1036fn render_cluster(out: &mut String, c: &LayoutCluster, include_markers: bool) {
1037    layout_debug::render_cluster(out, c, include_markers)
1038}
1039
1040fn compute_layout_bounds(
1041    clusters: &[LayoutCluster],
1042    nodes: &[LayoutNode],
1043    edges: &[crate::model::LayoutEdge],
1044) -> Option<Bounds> {
1045    layout_debug::compute_layout_bounds(clusters, nodes, edges)
1046}