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, title, 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, title, 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    diagram_title: Option<&str>,
590    options: &SvgRenderOptions,
591) -> Result<String> {
592    requirement::render_requirement_diagram_svg(
593        layout,
594        semantic,
595        effective_config,
596        diagram_title,
597        options,
598    )
599}
600
601pub fn render_block_diagram_svg(
602    layout: &BlockDiagramLayout,
603    semantic: &serde_json::Value,
604    effective_config: &serde_json::Value,
605    options: &SvgRenderOptions,
606) -> Result<String> {
607    block::render_block_diagram_svg(layout, semantic, effective_config, options)
608}
609
610pub fn render_radar_diagram_svg(
611    layout: &RadarDiagramLayout,
612    semantic: &serde_json::Value,
613    effective_config: &serde_json::Value,
614    options: &SvgRenderOptions,
615) -> Result<String> {
616    radar::render_radar_diagram_svg(layout, semantic, effective_config, options)
617}
618
619pub fn render_quadrantchart_diagram_svg(
620    layout: &QuadrantChartDiagramLayout,
621    _semantic: &serde_json::Value,
622    _effective_config: &serde_json::Value,
623    options: &SvgRenderOptions,
624) -> Result<String> {
625    quadrantchart::render_quadrantchart_diagram_svg(layout, _semantic, _effective_config, options)
626}
627
628pub fn render_xychart_diagram_svg(
629    layout: &XyChartDiagramLayout,
630    _semantic: &serde_json::Value,
631    _effective_config: &serde_json::Value,
632    options: &SvgRenderOptions,
633) -> Result<String> {
634    xychart::render_xychart_diagram_svg(layout, _semantic, _effective_config, options)
635}
636
637pub fn render_treemap_diagram_svg(
638    layout: &crate::model::TreemapDiagramLayout,
639    _semantic: &serde_json::Value,
640    effective_config: &serde_json::Value,
641    options: &SvgRenderOptions,
642) -> Result<String> {
643    treemap::render_treemap_diagram_svg(layout, _semantic, effective_config, options)
644}
645
646pub fn render_packet_diagram_svg(
647    layout: &PacketDiagramLayout,
648    semantic: &serde_json::Value,
649    _effective_config: &serde_json::Value,
650    diagram_title: Option<&str>,
651    options: &SvgRenderOptions,
652) -> Result<String> {
653    packet::render_packet_diagram_svg(layout, semantic, _effective_config, diagram_title, options)
654}
655
656pub fn render_timeline_diagram_svg(
657    layout: &TimelineDiagramLayout,
658    semantic: &serde_json::Value,
659    effective_config: &serde_json::Value,
660    _diagram_title: Option<&str>,
661    _measurer: &dyn TextMeasurer,
662    options: &SvgRenderOptions,
663) -> Result<String> {
664    timeline::render_timeline_diagram_svg(
665        layout,
666        semantic,
667        effective_config,
668        _diagram_title,
669        _measurer,
670        options,
671    )
672}
673
674pub fn render_journey_diagram_svg(
675    layout: &crate::model::JourneyDiagramLayout,
676    semantic: &serde_json::Value,
677    effective_config: &serde_json::Value,
678    _diagram_title: Option<&str>,
679    _measurer: &dyn TextMeasurer,
680    options: &SvgRenderOptions,
681) -> Result<String> {
682    journey::render_journey_diagram_svg(
683        layout,
684        semantic,
685        effective_config,
686        _diagram_title,
687        _measurer,
688        options,
689    )
690}
691
692pub fn render_kanban_diagram_svg(
693    layout: &crate::model::KanbanDiagramLayout,
694    _semantic: &serde_json::Value,
695    _effective_config: &serde_json::Value,
696    options: &SvgRenderOptions,
697) -> Result<String> {
698    kanban::render_kanban_diagram_svg(layout, _semantic, _effective_config, options)
699}
700
701pub fn render_gitgraph_diagram_svg(
702    layout: &crate::model::GitGraphDiagramLayout,
703    semantic: &serde_json::Value,
704    _effective_config: &serde_json::Value,
705    diagram_title: Option<&str>,
706    measurer: &dyn TextMeasurer,
707    options: &SvgRenderOptions,
708) -> Result<String> {
709    gitgraph::render_gitgraph_diagram_svg(
710        layout,
711        semantic,
712        _effective_config,
713        diagram_title,
714        measurer,
715        options,
716    )
717}
718
719pub fn render_gantt_diagram_svg(
720    layout: &crate::model::GanttDiagramLayout,
721    semantic: &serde_json::Value,
722    _effective_config: &serde_json::Value,
723    options: &SvgRenderOptions,
724) -> Result<String> {
725    gantt::render_gantt_diagram_svg(layout, semantic, _effective_config, options)
726}
727
728#[derive(Debug, Clone, Deserialize)]
729struct C4SvgModelText {
730    #[allow(dead_code)]
731    text: String,
732}
733
734#[derive(Debug, Clone, Deserialize)]
735struct C4SvgModelShape {
736    alias: String,
737    #[serde(default, rename = "bgColor")]
738    bg_color: Option<String>,
739    #[serde(default, rename = "borderColor")]
740    border_color: Option<String>,
741    #[serde(default, rename = "fontColor")]
742    font_color: Option<String>,
743    #[serde(default, rename = "typeC4Shape")]
744    #[allow(dead_code)]
745    type_c4_shape: Option<C4SvgModelText>,
746}
747
748#[derive(Debug, Clone, Deserialize)]
749struct C4SvgModelBoundary {
750    alias: String,
751    #[serde(default, rename = "nodeType")]
752    node_type: Option<String>,
753    #[serde(default, rename = "bgColor")]
754    bg_color: Option<String>,
755    #[serde(default, rename = "borderColor")]
756    border_color: Option<String>,
757    #[serde(default, rename = "fontColor")]
758    #[allow(dead_code)]
759    font_color: Option<String>,
760}
761
762#[derive(Debug, Clone, Deserialize)]
763struct C4SvgModelRel {
764    #[serde(rename = "from")]
765    from_alias: String,
766    #[serde(rename = "to")]
767    to_alias: String,
768    #[serde(default, rename = "lineColor")]
769    line_color: Option<String>,
770    #[serde(default, rename = "textColor")]
771    text_color: Option<String>,
772}
773
774#[derive(Debug, Clone, Deserialize)]
775struct C4SvgModel {
776    #[serde(default, rename = "accTitle")]
777    acc_title: Option<String>,
778    #[serde(default, rename = "accDescr")]
779    acc_descr: Option<String>,
780    #[serde(default)]
781    title: Option<String>,
782    #[serde(default)]
783    shapes: Vec<C4SvgModelShape>,
784    #[serde(default)]
785    boundaries: Vec<C4SvgModelBoundary>,
786    #[serde(default)]
787    rels: Vec<C4SvgModelRel>,
788}
789
790pub fn render_mindmap_diagram_svg(
791    layout: &MindmapDiagramLayout,
792    semantic: &serde_json::Value,
793    _effective_config: &serde_json::Value,
794    options: &SvgRenderOptions,
795) -> Result<String> {
796    mindmap::render_mindmap_diagram_svg(layout, semantic, _effective_config, options)
797}
798
799pub fn render_mindmap_diagram_svg_with_config(
800    layout: &MindmapDiagramLayout,
801    semantic: &serde_json::Value,
802    effective_config: &merman_core::MermaidConfig,
803    options: &SvgRenderOptions,
804) -> Result<String> {
805    mindmap::render_mindmap_diagram_svg_with_config(layout, semantic, effective_config, options)
806}
807
808pub fn render_architecture_diagram_svg(
809    layout: &ArchitectureDiagramLayout,
810    semantic: &serde_json::Value,
811    effective_config: &serde_json::Value,
812    options: &SvgRenderOptions,
813) -> Result<String> {
814    architecture::render_architecture_diagram_svg(layout, semantic, effective_config, options)
815}
816
817pub fn render_c4_diagram_svg(
818    layout: &crate::model::C4DiagramLayout,
819    semantic: &serde_json::Value,
820    effective_config: &serde_json::Value,
821    diagram_title: Option<&str>,
822    _measurer: &dyn TextMeasurer,
823    options: &SvgRenderOptions,
824) -> Result<String> {
825    c4::render_c4_diagram_svg(
826        layout,
827        semantic,
828        effective_config,
829        diagram_title,
830        _measurer,
831        options,
832    )
833}
834
835pub fn render_flowchart_v2_svg(
836    layout: &FlowchartV2Layout,
837    semantic: &serde_json::Value,
838    effective_config: &serde_json::Value,
839    diagram_title: Option<&str>,
840    measurer: &dyn TextMeasurer,
841    options: &SvgRenderOptions,
842) -> Result<String> {
843    flowchart::render_flowchart_v2_svg(
844        layout,
845        semantic,
846        effective_config,
847        diagram_title,
848        measurer,
849        options,
850    )
851}
852
853pub fn render_flowchart_v2_svg_model(
854    layout: &FlowchartV2Layout,
855    model: &merman_core::diagrams::flowchart::FlowchartV2Model,
856    effective_config: &serde_json::Value,
857    diagram_title: Option<&str>,
858    measurer: &dyn TextMeasurer,
859    options: &SvgRenderOptions,
860) -> Result<String> {
861    flowchart::render_flowchart_v2_svg_model(
862        layout,
863        model,
864        effective_config,
865        diagram_title,
866        measurer,
867        options,
868    )
869}
870
871pub fn render_flowchart_v2_svg_with_config(
872    layout: &FlowchartV2Layout,
873    semantic: &serde_json::Value,
874    effective_config: &merman_core::MermaidConfig,
875    diagram_title: Option<&str>,
876    measurer: &dyn TextMeasurer,
877    options: &SvgRenderOptions,
878) -> Result<String> {
879    flowchart::render_flowchart_v2_svg_with_config(
880        layout,
881        semantic,
882        effective_config,
883        diagram_title,
884        measurer,
885        options,
886    )
887}
888
889pub fn render_flowchart_v2_svg_model_with_config(
890    layout: &FlowchartV2Layout,
891    model: &merman_core::diagrams::flowchart::FlowchartV2Model,
892    effective_config: &merman_core::MermaidConfig,
893    diagram_title: Option<&str>,
894    measurer: &dyn TextMeasurer,
895    options: &SvgRenderOptions,
896) -> Result<String> {
897    flowchart::render_flowchart_v2_svg_model_with_config(
898        layout,
899        model,
900        effective_config,
901        diagram_title,
902        measurer,
903        options,
904    )
905}
906
907pub fn render_state_diagram_v2_svg(
908    layout: &StateDiagramV2Layout,
909    semantic: &serde_json::Value,
910    effective_config: &serde_json::Value,
911    diagram_title: Option<&str>,
912    measurer: &dyn TextMeasurer,
913    options: &SvgRenderOptions,
914) -> Result<String> {
915    state::render_state_diagram_v2_svg(
916        layout,
917        semantic,
918        effective_config,
919        diagram_title,
920        measurer,
921        options,
922    )
923}
924
925pub fn render_state_diagram_v2_debug_svg(
926    layout: &StateDiagramV2Layout,
927    options: &SvgRenderOptions,
928) -> String {
929    state::render_state_diagram_v2_debug_svg(layout, options)
930}
931
932pub fn render_class_diagram_v2_debug_svg(
933    layout: &ClassDiagramV2Layout,
934    options: &SvgRenderOptions,
935) -> String {
936    class::render_class_diagram_v2_debug_svg(layout, options)
937}
938
939pub fn render_class_diagram_v2_svg(
940    layout: &ClassDiagramV2Layout,
941    semantic: &serde_json::Value,
942    effective_config: &serde_json::Value,
943    diagram_title: Option<&str>,
944    measurer: &dyn TextMeasurer,
945    options: &SvgRenderOptions,
946) -> Result<String> {
947    class::render_class_diagram_v2_svg(
948        layout,
949        semantic,
950        effective_config,
951        diagram_title,
952        measurer,
953        options,
954    )
955}
956
957pub fn render_er_diagram_debug_svg(layout: &ErDiagramLayout, options: &SvgRenderOptions) -> String {
958    er::render_er_diagram_debug_svg(layout, options)
959}
960
961pub fn render_er_diagram_svg(
962    layout: &ErDiagramLayout,
963    semantic: &serde_json::Value,
964    effective_config: &serde_json::Value,
965    diagram_title: Option<&str>,
966    measurer: &dyn TextMeasurer,
967    options: &SvgRenderOptions,
968) -> Result<String> {
969    er::render_er_diagram_svg(
970        layout,
971        semantic,
972        effective_config,
973        diagram_title,
974        measurer,
975        options,
976    )
977}
978
979pub fn render_sankey_diagram_svg(
980    layout: &SankeyDiagramLayout,
981    _semantic: &serde_json::Value,
982    effective_config: &serde_json::Value,
983    options: &SvgRenderOptions,
984) -> Result<String> {
985    sankey::render_sankey_diagram_svg(layout, _semantic, effective_config, options)
986}
987
988fn curve_monotone_path_d(points: &[crate::model::LayoutPoint], swap_xy: bool) -> String {
989    curve::curve_monotone_path_d(points, swap_xy)
990}
991
992fn curve_monotone_x_path_d(points: &[crate::model::LayoutPoint]) -> String {
993    curve_monotone_path_d(points, false)
994}
995
996fn curve_monotone_y_path_d(points: &[crate::model::LayoutPoint]) -> String {
997    curve_monotone_path_d(points, true)
998}
999
1000// Ported from D3 `curveBasis` (d3-shape v3.x), used by Mermaid ER renderer `@11.12.2`.
1001fn curve_basis_path_d(points: &[crate::model::LayoutPoint]) -> String {
1002    curve::curve_basis_path_d(points)
1003}
1004
1005fn curve_linear_path_d(points: &[crate::model::LayoutPoint]) -> String {
1006    curve::curve_linear_path_d(points)
1007}
1008
1009fn curve_natural_path_d(points: &[crate::model::LayoutPoint]) -> String {
1010    curve::curve_natural_path_d(points)
1011}
1012
1013// Ported from D3 `curveBumpY` (d3-shape v3.x).
1014fn curve_bump_y_path_d(points: &[crate::model::LayoutPoint]) -> String {
1015    curve::curve_bump_y_path_d(points)
1016}
1017
1018// Ported from D3 `curveCatmullRom` (d3-shape v3.x), with the default alpha=0.5.
1019fn curve_catmull_rom_path_d(points: &[crate::model::LayoutPoint]) -> String {
1020    curve::curve_catmull_rom_path_d(points)
1021}
1022
1023// Ported from D3 `curveStepAfter` (d3-shape v3.x).
1024fn curve_step_after_path_d(points: &[crate::model::LayoutPoint]) -> String {
1025    curve::curve_step_after_path_d(points)
1026}
1027
1028// Ported from D3 `curveStepBefore` (d3-shape v3.x).
1029fn curve_step_before_path_d(points: &[crate::model::LayoutPoint]) -> String {
1030    curve::curve_step_before_path_d(points)
1031}
1032
1033// Ported from D3 `curveStep` (d3-shape v3.x).
1034fn curve_step_path_d(points: &[crate::model::LayoutPoint]) -> String {
1035    curve::curve_step_path_d(points)
1036}
1037
1038// Ported from D3 `curveCardinal` (d3-shape v3.x).
1039fn curve_cardinal_path_d(points: &[crate::model::LayoutPoint], tension: f64) -> String {
1040    curve::curve_cardinal_path_d(points, tension)
1041}
1042fn render_node(out: &mut String, n: &LayoutNode) {
1043    layout_debug::render_node(out, n)
1044}
1045
1046fn render_state_node(out: &mut String, n: &LayoutNode) {
1047    layout_debug::render_state_node(out, n)
1048}
1049
1050fn render_cluster(out: &mut String, c: &LayoutCluster, include_markers: bool) {
1051    layout_debug::render_cluster(out, c, include_markers)
1052}
1053
1054fn compute_layout_bounds(
1055    clusters: &[LayoutCluster],
1056    nodes: &[LayoutNode],
1057    edges: &[crate::model::LayoutEdge],
1058) -> Option<Bounds> {
1059    layout_debug::compute_layout_bounds(clusters, nodes, edges)
1060}