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