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