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