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