1use super::icon_registry::IconRegistry;
2use super::pipeline::{ScopedCssPostprocessor, SvgPipeline, SvgPostprocessMetadata};
3use crate::model::{
4 ArchitectureDiagramLayout, BlockDiagramLayout, Bounds, ClassDiagramV2Layout, ErDiagramLayout,
5 ErrorDiagramLayout, EventModelingDiagramLayout, FlowchartV2Layout, InfoDiagramLayout,
6 IshikawaDiagramLayout, LayoutCluster, LayoutNode, MindmapDiagramLayout, PacketDiagramLayout,
7 PieDiagramLayout, QuadrantChartDiagramLayout, RadarDiagramLayout, RequirementDiagramLayout,
8 SankeyDiagramLayout, SequenceDiagramLayout, StateDiagramV2Layout, TimelineDiagramLayout,
9 TreeViewDiagramLayout, VennDiagramLayout, XyChartDiagramLayout,
10};
11use crate::text::{TextMeasurer, TextStyle, WrapMode};
12use crate::{Error, Result};
13use base64::Engine as _;
14use indexmap::IndexMap;
15use std::fmt::Write as _;
16
17mod architecture;
18mod block;
19mod c4;
20mod class;
21mod css;
22mod curve;
23mod emitted_bounds;
24mod er;
25mod error;
26mod eventmodeling;
27mod flowchart;
28mod gantt;
29mod gitgraph;
30mod info;
31mod ishikawa;
32mod journey;
33mod kanban;
34mod layout_debug;
35mod mindmap;
36mod packet;
37mod path_bounds;
38mod pie;
39mod quadrantchart;
40mod radar;
41mod requirement;
42mod root_svg;
43mod roughjs_common;
44mod sankey;
45mod sequence;
46mod state;
47mod style;
48pub(crate) mod theme;
49mod timeline;
50mod timing;
51mod tree_view;
52mod treemap;
53mod util;
54mod venn;
55mod xychart;
56use crate::math::MathRenderer;
57use css::{
58 er_css, gantt_css, info_css_parts_with_config, info_css_parts_with_theme_font_size_only,
59 info_css_with_config, pie_css, push_xychart_css, requirement_css, sankey_css, treemap_css,
60};
61use path_bounds::svg_path_bounds_from_d;
62pub(crate) fn mindmap_cloud_rendered_bbox_size_px(w: f64, h: f64) -> Option<(f64, f64)> {
63 mindmap::mindmap_cloud_rendered_bbox_size_px(w, h)
64}
65pub use emitted_bounds::{
66 SvgEmittedBoundsContributor, SvgEmittedBoundsDebug, debug_svg_emitted_bounds,
67};
68use emitted_bounds::{svg_emitted_bounds_from_svg, svg_emitted_bounds_from_svg_inner};
69use state::{roughjs_ops_to_svg_path_d, roughjs_parse_hex_color_to_srgba, roughjs_paths_for_rect};
70use style::{is_rect_style_key, is_text_style_key, parse_style_decl};
71use theme::PresentationTheme;
72use util::{
73 SvgTheme, apply_root_viewport_override, config_bool, config_diagram_look, config_f64,
74 config_f64_css_px, config_string, css_rgba_fade, decode_mermaid_entities_for_render_text,
75 escape_attr, escape_attr_display, escape_attr_into, escape_xml, escape_xml_display,
76 escape_xml_into, fmt, fmt_debug_3dp, fmt_display, fmt_into, fmt_max_width_px, fmt_path,
77 fmt_path_into, fmt_points, fmt_string, json_stringify_points, json_stringify_points_into,
78 normalize_css_font_family, push_points_attr, scoped_svg_id, scoped_svg_url, theme_color,
79};
80
81const MERMAID_SEQUENCE_BASE_DEFS_11_12_2: &str = include_str!(concat!(
82 env!("CARGO_MANIFEST_DIR"),
83 "/assets/sequence_base_defs_11_12_2.svgfrag"
84));
85
86#[derive(Debug, Clone)]
87pub struct SvgRenderOptions {
88 pub viewbox_padding: f64,
90 pub diagram_id: Option<String>,
92 pub aria_roledescription: Option<String>,
97 pub include_edges: bool,
99 pub include_nodes: bool,
101 pub include_clusters: bool,
103 pub include_cluster_debug_markers: bool,
105 pub include_edge_id_labels: bool,
107 pub now_ms_override: Option<i64>,
110 pub math_renderer: Option<std::sync::Arc<dyn MathRenderer + Send + Sync>>,
112 pub icon_registry: Option<std::sync::Arc<IconRegistry>>,
114 pub apply_root_overrides: bool,
116}
117
118impl Default for SvgRenderOptions {
119 fn default() -> Self {
120 Self {
121 viewbox_padding: 8.0,
122 diagram_id: None,
123 aria_roledescription: None,
124 include_edges: true,
125 include_nodes: true,
126 include_clusters: true,
127 include_cluster_debug_markers: false,
128 include_edge_id_labels: false,
129 now_ms_override: None,
130 math_renderer: None,
131 icon_registry: None,
132 apply_root_overrides: true,
133 }
134 }
135}
136
137pub fn render_layouted_svg(
138 diagram: &crate::model::LayoutedDiagram,
139 measurer: &dyn TextMeasurer,
140 options: &SvgRenderOptions,
141) -> Result<String> {
142 render_layout_svg_parts(
143 &diagram.layout,
144 &diagram.semantic,
145 &diagram.meta.effective_config,
146 diagram.meta.title.as_deref(),
147 measurer,
148 options,
149 )
150}
151
152pub fn render_layout_svg_parts(
153 layout: &crate::model::LayoutDiagram,
154 semantic: &serde_json::Value,
155 effective_config: &serde_json::Value,
156 title: Option<&str>,
157 measurer: &dyn TextMeasurer,
158 options: &SvgRenderOptions,
159) -> Result<String> {
160 let svg =
161 render_layout_svg_parts_raw(layout, semantic, effective_config, title, measurer, options)?;
162 apply_theme_css(svg, effective_config)
163}
164
165fn render_layout_svg_parts_raw(
166 layout: &crate::model::LayoutDiagram,
167 semantic: &serde_json::Value,
168 effective_config: &serde_json::Value,
169 title: Option<&str>,
170 measurer: &dyn TextMeasurer,
171 options: &SvgRenderOptions,
172) -> Result<String> {
173 use crate::model::LayoutDiagram;
174
175 match layout {
176 LayoutDiagram::ErrorDiagram(layout) => {
177 render_error_diagram_svg(layout, semantic, effective_config, options)
178 }
179 LayoutDiagram::BlockDiagram(layout) => {
180 render_block_diagram_svg(layout, semantic, effective_config, options)
181 }
182 LayoutDiagram::RequirementDiagram(layout) => {
183 render_requirement_diagram_svg(layout, semantic, effective_config, title, options)
184 }
185 LayoutDiagram::ArchitectureDiagram(layout) => {
186 render_architecture_diagram_svg(layout, semantic, effective_config, options)
187 }
188 LayoutDiagram::MindmapDiagram(layout) => {
189 render_mindmap_diagram_svg(layout, semantic, effective_config, options)
190 }
191 LayoutDiagram::SankeyDiagram(layout) => {
192 render_sankey_diagram_svg(layout, semantic, effective_config, options)
193 }
194 LayoutDiagram::RadarDiagram(layout) => {
195 render_radar_diagram_svg(layout, semantic, effective_config, options)
196 }
197 LayoutDiagram::TreemapDiagram(layout) => {
198 render_treemap_diagram_svg(layout, semantic, effective_config, options)
199 }
200 LayoutDiagram::VennDiagram(layout) => {
201 render_venn_diagram_svg(layout, semantic, effective_config, title, options)
202 }
203 LayoutDiagram::XyChartDiagram(layout) => {
204 render_xychart_diagram_svg(layout, semantic, effective_config, options)
205 }
206 LayoutDiagram::QuadrantChartDiagram(layout) => {
207 render_quadrantchart_diagram_svg(layout, semantic, effective_config, options)
208 }
209 LayoutDiagram::FlowchartV2(layout) => {
210 render_flowchart_v2_svg(layout, semantic, effective_config, title, measurer, options)
211 }
212 LayoutDiagram::StateDiagramV2(layout) => render_state_diagram_v2_svg(
213 layout,
214 semantic,
215 effective_config,
216 title,
217 measurer,
218 options,
219 ),
220 LayoutDiagram::ClassDiagramV2(layout) => render_class_diagram_v2_svg(
221 layout,
222 semantic,
223 effective_config,
224 title,
225 measurer,
226 options,
227 ),
228 LayoutDiagram::ErDiagram(layout) => {
229 render_er_diagram_svg(layout, semantic, effective_config, title, measurer, options)
230 }
231 LayoutDiagram::SequenceDiagram(layout) => render_sequence_diagram_svg(
232 layout,
233 semantic,
234 effective_config,
235 title,
236 measurer,
237 options,
238 ),
239 LayoutDiagram::InfoDiagram(layout) => {
240 render_info_diagram_svg(layout, semantic, effective_config, options)
241 }
242 LayoutDiagram::PacketDiagram(layout) => {
243 render_packet_diagram_svg(layout, semantic, effective_config, title, options)
244 }
245 LayoutDiagram::TimelineDiagram(layout) => render_timeline_diagram_svg(
246 layout,
247 semantic,
248 effective_config,
249 title,
250 measurer,
251 options,
252 ),
253 LayoutDiagram::PieDiagram(layout) => {
254 render_pie_diagram_svg(layout, semantic, effective_config, options)
255 }
256 LayoutDiagram::JourneyDiagram(layout) => {
257 render_journey_diagram_svg(layout, semantic, effective_config, title, measurer, options)
258 }
259 LayoutDiagram::KanbanDiagram(layout) => {
260 render_kanban_diagram_svg(layout, semantic, effective_config, options)
261 }
262 LayoutDiagram::GitGraphDiagram(layout) => render_gitgraph_diagram_svg(
263 layout,
264 semantic,
265 effective_config,
266 title,
267 measurer,
268 options,
269 ),
270 LayoutDiagram::GanttDiagram(layout) => {
271 render_gantt_diagram_svg(layout, semantic, effective_config, options)
272 }
273 LayoutDiagram::TreeViewDiagram(layout) => {
274 render_tree_view_diagram_svg(layout, semantic, effective_config, options)
275 }
276 LayoutDiagram::IshikawaDiagram(layout) => {
277 render_ishikawa_diagram_svg(layout, semantic, effective_config, options)
278 }
279 LayoutDiagram::EventModelingDiagram(layout) => {
280 render_eventmodeling_diagram_svg(layout, semantic, effective_config, options)
281 }
282 LayoutDiagram::C4Diagram(layout) => {
283 render_c4_diagram_svg(layout, semantic, effective_config, title, measurer, options)
284 }
285 }
286}
287
288pub fn render_layout_svg_parts_with_config(
289 layout: &crate::model::LayoutDiagram,
290 semantic: &serde_json::Value,
291 effective_config: &merman_core::MermaidConfig,
292 title: Option<&str>,
293 measurer: &dyn TextMeasurer,
294 options: &SvgRenderOptions,
295) -> Result<String> {
296 let svg = render_layout_svg_parts_with_config_raw(
297 layout,
298 semantic,
299 effective_config,
300 title,
301 measurer,
302 options,
303 )?;
304 apply_theme_css(svg, effective_config.as_value())
305}
306
307fn render_layout_svg_parts_with_config_raw(
308 layout: &crate::model::LayoutDiagram,
309 semantic: &serde_json::Value,
310 effective_config: &merman_core::MermaidConfig,
311 title: Option<&str>,
312 measurer: &dyn TextMeasurer,
313 options: &SvgRenderOptions,
314) -> Result<String> {
315 use crate::model::LayoutDiagram;
316
317 let effective_config_value = effective_config.as_value();
318
319 match layout {
320 LayoutDiagram::ErrorDiagram(layout) => {
321 render_error_diagram_svg(layout, semantic, effective_config_value, options)
322 }
323 LayoutDiagram::BlockDiagram(layout) => {
324 render_block_diagram_svg(layout, semantic, effective_config_value, options)
325 }
326 LayoutDiagram::RequirementDiagram(layout) => {
327 render_requirement_diagram_svg(layout, semantic, effective_config_value, title, options)
328 }
329 LayoutDiagram::ArchitectureDiagram(layout) => {
330 architecture::render_architecture_diagram_svg_with_config(
331 layout,
332 semantic,
333 effective_config,
334 options,
335 )
336 }
337 LayoutDiagram::MindmapDiagram(layout) => {
338 render_mindmap_diagram_svg_with_config(layout, semantic, effective_config, options)
339 }
340 LayoutDiagram::SankeyDiagram(layout) => {
341 render_sankey_diagram_svg(layout, semantic, effective_config_value, options)
342 }
343 LayoutDiagram::RadarDiagram(layout) => {
344 render_radar_diagram_svg(layout, semantic, effective_config_value, options)
345 }
346 LayoutDiagram::TreemapDiagram(layout) => {
347 render_treemap_diagram_svg(layout, semantic, effective_config_value, options)
348 }
349 LayoutDiagram::VennDiagram(layout) => {
350 render_venn_diagram_svg(layout, semantic, effective_config_value, title, options)
351 }
352 LayoutDiagram::XyChartDiagram(layout) => {
353 render_xychart_diagram_svg(layout, semantic, effective_config_value, options)
354 }
355 LayoutDiagram::QuadrantChartDiagram(layout) => {
356 render_quadrantchart_diagram_svg(layout, semantic, effective_config_value, options)
357 }
358 LayoutDiagram::FlowchartV2(layout) => render_flowchart_v2_svg_with_config(
359 layout,
360 semantic,
361 effective_config,
362 title,
363 measurer,
364 options,
365 ),
366 LayoutDiagram::StateDiagramV2(layout) => render_state_diagram_v2_svg(
367 layout,
368 semantic,
369 effective_config_value,
370 title,
371 measurer,
372 options,
373 ),
374 LayoutDiagram::ClassDiagramV2(layout) => {
375 let model = crate::json::from_value_ref(semantic)?;
376 class::render_class_diagram_v2_svg_model_with_config(
377 layout,
378 &model,
379 effective_config,
380 title,
381 measurer,
382 options,
383 )
384 }
385 LayoutDiagram::ErDiagram(layout) => render_er_diagram_svg(
386 layout,
387 semantic,
388 effective_config_value,
389 title,
390 measurer,
391 options,
392 ),
393 LayoutDiagram::SequenceDiagram(layout) => {
394 sequence::render_sequence_diagram_svg_with_config(
395 layout,
396 semantic,
397 effective_config,
398 title,
399 measurer,
400 options,
401 )
402 }
403 LayoutDiagram::InfoDiagram(layout) => {
404 render_info_diagram_svg(layout, semantic, effective_config_value, options)
405 }
406 LayoutDiagram::PacketDiagram(layout) => {
407 render_packet_diagram_svg(layout, semantic, effective_config_value, title, options)
408 }
409 LayoutDiagram::TimelineDiagram(layout) => render_timeline_diagram_svg(
410 layout,
411 semantic,
412 effective_config_value,
413 title,
414 measurer,
415 options,
416 ),
417 LayoutDiagram::PieDiagram(layout) => {
418 render_pie_diagram_svg(layout, semantic, effective_config_value, options)
419 }
420 LayoutDiagram::JourneyDiagram(layout) => render_journey_diagram_svg(
421 layout,
422 semantic,
423 effective_config_value,
424 title,
425 measurer,
426 options,
427 ),
428 LayoutDiagram::KanbanDiagram(layout) => {
429 render_kanban_diagram_svg(layout, semantic, effective_config_value, options)
430 }
431 LayoutDiagram::GitGraphDiagram(layout) => render_gitgraph_diagram_svg(
432 layout,
433 semantic,
434 effective_config_value,
435 title,
436 measurer,
437 options,
438 ),
439 LayoutDiagram::GanttDiagram(layout) => {
440 render_gantt_diagram_svg(layout, semantic, effective_config_value, options)
441 }
442 LayoutDiagram::TreeViewDiagram(layout) => {
443 render_tree_view_diagram_svg(layout, semantic, effective_config_value, options)
444 }
445 LayoutDiagram::IshikawaDiagram(layout) => {
446 render_ishikawa_diagram_svg(layout, semantic, effective_config_value, options)
447 }
448 LayoutDiagram::EventModelingDiagram(layout) => {
449 render_eventmodeling_diagram_svg(layout, semantic, effective_config_value, options)
450 }
451 LayoutDiagram::C4Diagram(layout) => render_c4_diagram_svg(
452 layout,
453 semantic,
454 effective_config_value,
455 title,
456 measurer,
457 options,
458 ),
459 }
460}
461
462pub fn render_layout_svg_parts_for_render_model_with_config(
463 layout: &crate::model::LayoutDiagram,
464 semantic: &merman_core::RenderSemanticModel,
465 effective_config: &merman_core::MermaidConfig,
466 title: Option<&str>,
467 measurer: &dyn TextMeasurer,
468 options: &SvgRenderOptions,
469) -> Result<String> {
470 let svg = render_layout_svg_parts_for_render_model_with_config_raw(
471 layout,
472 semantic,
473 effective_config,
474 title,
475 measurer,
476 options,
477 )?;
478 apply_theme_css(svg, effective_config.as_value())
479}
480
481fn render_layout_svg_parts_for_render_model_with_config_raw(
482 layout: &crate::model::LayoutDiagram,
483 semantic: &merman_core::RenderSemanticModel,
484 effective_config: &merman_core::MermaidConfig,
485 title: Option<&str>,
486 measurer: &dyn TextMeasurer,
487 options: &SvgRenderOptions,
488) -> Result<String> {
489 use crate::model::LayoutDiagram;
490 use merman_core::RenderSemanticModel;
491
492 match (layout, semantic) {
493 (LayoutDiagram::ArchitectureDiagram(layout), RenderSemanticModel::Architecture(model)) => {
494 architecture::render_architecture_diagram_svg_typed_with_config(
495 layout,
496 model,
497 effective_config,
498 options,
499 )
500 }
501 (LayoutDiagram::FlowchartV2(layout), RenderSemanticModel::Flowchart(model)) => {
502 render_flowchart_v2_svg_model_with_config(
503 layout,
504 model,
505 effective_config,
506 title,
507 measurer,
508 options,
509 )
510 }
511 (LayoutDiagram::MindmapDiagram(layout), RenderSemanticModel::Mindmap(model)) => {
512 mindmap::render_mindmap_diagram_svg_model_with_config(
513 layout,
514 model,
515 effective_config,
516 options,
517 )
518 }
519 (LayoutDiagram::StateDiagramV2(layout), RenderSemanticModel::State(model)) => {
520 state::render_state_diagram_v2_svg_model(
521 layout,
522 model,
523 effective_config.as_value(),
524 title,
525 measurer,
526 options,
527 )
528 }
529 (LayoutDiagram::ClassDiagramV2(layout), RenderSemanticModel::Class(model)) => {
530 class::render_class_diagram_v2_svg_model_with_config(
531 layout,
532 model,
533 effective_config,
534 title,
535 measurer,
536 options,
537 )
538 }
539 (LayoutDiagram::SequenceDiagram(layout), RenderSemanticModel::Sequence(model)) => {
540 sequence::render_sequence_diagram_svg_model_with_config(
541 layout,
542 model,
543 effective_config,
544 title,
545 measurer,
546 options,
547 )
548 }
549 (LayoutDiagram::KanbanDiagram(layout), RenderSemanticModel::Kanban(_)) => {
550 render_kanban_diagram_svg(
551 layout,
552 &serde_json::Value::Null,
553 effective_config.as_value(),
554 options,
555 )
556 }
557 (LayoutDiagram::GanttDiagram(layout), RenderSemanticModel::Gantt(model)) => {
558 gantt::render_gantt_diagram_svg_model(
559 layout,
560 model,
561 effective_config.as_value(),
562 options,
563 )
564 }
565 (LayoutDiagram::PieDiagram(layout), RenderSemanticModel::Pie(model)) => {
566 pie::render_pie_diagram_svg_model(layout, model, effective_config.as_value(), options)
567 }
568 (LayoutDiagram::PacketDiagram(layout), RenderSemanticModel::Packet(model)) => {
569 packet::render_packet_diagram_svg_model(
570 layout,
571 model,
572 effective_config.as_value(),
573 title,
574 options,
575 )
576 }
577 (LayoutDiagram::TimelineDiagram(layout), RenderSemanticModel::Timeline(model)) => {
578 timeline::render_timeline_diagram_svg_model(
579 layout,
580 model,
581 effective_config.as_value(),
582 title,
583 measurer,
584 options,
585 )
586 }
587 (LayoutDiagram::JourneyDiagram(layout), RenderSemanticModel::Journey(model)) => {
588 journey::render_journey_diagram_svg_model(
589 layout,
590 model,
591 effective_config.as_value(),
592 title,
593 measurer,
594 options,
595 )
596 }
597 (LayoutDiagram::RequirementDiagram(layout), RenderSemanticModel::Requirement(model)) => {
598 requirement::render_requirement_diagram_svg_model(
599 layout,
600 model,
601 effective_config.as_value(),
602 title,
603 options,
604 )
605 }
606 (LayoutDiagram::SankeyDiagram(layout), RenderSemanticModel::Sankey(_)) => {
607 render_sankey_diagram_svg(
608 layout,
609 &serde_json::Value::Null,
610 effective_config.as_value(),
611 options,
612 )
613 }
614 (LayoutDiagram::RadarDiagram(layout), RenderSemanticModel::Radar(model)) => {
615 radar::render_radar_diagram_svg_model(
616 layout,
617 model,
618 effective_config.as_value(),
619 options,
620 )
621 }
622 (LayoutDiagram::InfoDiagram(layout), RenderSemanticModel::Info(_)) => {
623 render_info_diagram_svg(
624 layout,
625 &serde_json::Value::Null,
626 effective_config.as_value(),
627 options,
628 )
629 }
630 (LayoutDiagram::TreemapDiagram(layout), RenderSemanticModel::Treemap(_)) => {
631 render_treemap_diagram_svg(
632 layout,
633 &serde_json::Value::Null,
634 effective_config.as_value(),
635 options,
636 )
637 }
638 (LayoutDiagram::VennDiagram(layout), RenderSemanticModel::Venn(model)) => {
639 venn::render_venn_diagram_svg_model(
640 layout,
641 model,
642 effective_config.as_value(),
643 title,
644 options,
645 )
646 }
647 (LayoutDiagram::BlockDiagram(layout), RenderSemanticModel::Block(model)) => {
648 render_block_diagram_svg_model(layout, model, effective_config.as_value(), options)
649 }
650 (LayoutDiagram::ErDiagram(layout), RenderSemanticModel::Er(model)) => {
651 render_er_diagram_svg_model(
652 layout,
653 model,
654 effective_config.as_value(),
655 title,
656 measurer,
657 options,
658 )
659 }
660 (LayoutDiagram::QuadrantChartDiagram(layout), RenderSemanticModel::QuadrantChart(_)) => {
661 render_quadrantchart_diagram_svg(
662 layout,
663 &serde_json::Value::Null,
664 effective_config.as_value(),
665 options,
666 )
667 }
668 (LayoutDiagram::XyChartDiagram(layout), RenderSemanticModel::XyChart(_)) => {
669 render_xychart_diagram_svg(
670 layout,
671 &serde_json::Value::Null,
672 effective_config.as_value(),
673 options,
674 )
675 }
676 (LayoutDiagram::GitGraphDiagram(layout), RenderSemanticModel::GitGraph(model)) => {
677 gitgraph::render_gitgraph_diagram_svg_model(
678 layout,
679 model,
680 effective_config.as_value(),
681 title,
682 measurer,
683 options,
684 )
685 }
686 (LayoutDiagram::TreeViewDiagram(layout), RenderSemanticModel::TreeView(model)) => {
687 tree_view::render_tree_view_diagram_svg_model(
688 layout,
689 model,
690 effective_config.as_value(),
691 options,
692 )
693 }
694 (LayoutDiagram::IshikawaDiagram(layout), RenderSemanticModel::Ishikawa(_)) => {
695 render_ishikawa_diagram_svg(
696 layout,
697 &serde_json::Value::Null,
698 effective_config.as_value(),
699 options,
700 )
701 }
702 (LayoutDiagram::EventModelingDiagram(layout), RenderSemanticModel::EventModeling(_)) => {
703 render_eventmodeling_diagram_svg(
704 layout,
705 &serde_json::Value::Null,
706 effective_config.as_value(),
707 options,
708 )
709 }
710 (LayoutDiagram::C4Diagram(layout), RenderSemanticModel::C4(model)) => {
711 c4::render_c4_diagram_svg_typed(
712 layout,
713 model,
714 effective_config.as_value(),
715 title,
716 measurer,
717 options,
718 )
719 }
720 (_, RenderSemanticModel::Json(semantic)) => render_layout_svg_parts_with_config_raw(
721 layout,
722 semantic,
723 effective_config,
724 title,
725 measurer,
726 options,
727 ),
728 _ => Err(Error::InvalidModel {
729 message: "semantic model does not match layout diagram type".to_string(),
730 }),
731 }
732}
733
734fn apply_theme_css(svg: String, effective_config: &serde_json::Value) -> Result<String> {
735 let Some(theme_css) = effective_config
736 .get("themeCSS")
737 .and_then(serde_json::Value::as_str)
738 .filter(|css| !css.trim().is_empty())
739 else {
740 return Ok(svg);
741 };
742
743 let metadata = SvgPostprocessMetadata::from_svg(&svg);
744 let pipeline = SvgPipeline::parity().with_postprocessor(ScopedCssPostprocessor::new(theme_css));
745 pipeline.process_to_string_with_metadata(&svg, &metadata)
746}
747
748pub fn render_flowchart_v2_debug_svg(
749 layout: &FlowchartV2Layout,
750 options: &SvgRenderOptions,
751) -> String {
752 flowchart::render_flowchart_v2_debug_svg(layout, options)
753}
754
755pub fn render_sequence_diagram_debug_svg(
756 layout: &SequenceDiagramLayout,
757 options: &SvgRenderOptions,
758) -> String {
759 sequence::render_sequence_diagram_debug_svg(layout, options)
760}
761
762pub fn render_sequence_diagram_svg(
763 layout: &SequenceDiagramLayout,
764 semantic: &serde_json::Value,
765 effective_config: &serde_json::Value,
766 diagram_title: Option<&str>,
767 measurer: &dyn TextMeasurer,
768 options: &SvgRenderOptions,
769) -> Result<String> {
770 sequence::render_sequence_diagram_svg(
771 layout,
772 semantic,
773 effective_config,
774 diagram_title,
775 measurer,
776 options,
777 )
778}
779
780pub fn render_error_diagram_svg(
781 layout: &ErrorDiagramLayout,
782 _semantic: &serde_json::Value,
783 _effective_config: &serde_json::Value,
784 options: &SvgRenderOptions,
785) -> Result<String> {
786 error::render_error_diagram_svg(layout, _semantic, _effective_config, options)
787}
788
789pub fn render_info_diagram_svg(
790 layout: &InfoDiagramLayout,
791 _semantic: &serde_json::Value,
792 _effective_config: &serde_json::Value,
793 options: &SvgRenderOptions,
794) -> Result<String> {
795 info::render_info_diagram_svg(layout, _semantic, _effective_config, options)
796}
797
798pub fn render_pie_diagram_svg(
799 layout: &PieDiagramLayout,
800 semantic: &serde_json::Value,
801 _effective_config: &serde_json::Value,
802 options: &SvgRenderOptions,
803) -> Result<String> {
804 pie::render_pie_diagram_svg(layout, semantic, _effective_config, options)
805}
806
807pub fn render_requirement_diagram_svg(
808 layout: &RequirementDiagramLayout,
809 semantic: &serde_json::Value,
810 effective_config: &serde_json::Value,
811 diagram_title: Option<&str>,
812 options: &SvgRenderOptions,
813) -> Result<String> {
814 requirement::render_requirement_diagram_svg(
815 layout,
816 semantic,
817 effective_config,
818 diagram_title,
819 options,
820 )
821}
822
823pub fn render_block_diagram_svg(
824 layout: &BlockDiagramLayout,
825 semantic: &serde_json::Value,
826 effective_config: &serde_json::Value,
827 options: &SvgRenderOptions,
828) -> Result<String> {
829 block::render_block_diagram_svg(layout, semantic, effective_config, options)
830}
831
832pub fn render_block_diagram_svg_model(
833 layout: &BlockDiagramLayout,
834 model: &merman_core::diagrams::block::BlockDiagramRenderModel,
835 effective_config: &serde_json::Value,
836 options: &SvgRenderOptions,
837) -> Result<String> {
838 block::render_block_diagram_svg_model(layout, model, effective_config, options)
839}
840
841pub fn render_er_diagram_svg_model(
842 layout: &ErDiagramLayout,
843 model: &merman_core::diagrams::er::ErDiagramRenderModel,
844 effective_config: &serde_json::Value,
845 diagram_title: Option<&str>,
846 measurer: &dyn TextMeasurer,
847 options: &SvgRenderOptions,
848) -> Result<String> {
849 er::render_er_diagram_svg_model(
850 layout,
851 model,
852 effective_config,
853 diagram_title,
854 measurer,
855 options,
856 )
857}
858
859pub fn render_radar_diagram_svg(
860 layout: &RadarDiagramLayout,
861 semantic: &serde_json::Value,
862 effective_config: &serde_json::Value,
863 options: &SvgRenderOptions,
864) -> Result<String> {
865 radar::render_radar_diagram_svg(layout, semantic, effective_config, options)
866}
867
868pub fn render_quadrantchart_diagram_svg(
869 layout: &QuadrantChartDiagramLayout,
870 _semantic: &serde_json::Value,
871 _effective_config: &serde_json::Value,
872 options: &SvgRenderOptions,
873) -> Result<String> {
874 quadrantchart::render_quadrantchart_diagram_svg(layout, _semantic, _effective_config, options)
875}
876
877pub fn render_xychart_diagram_svg(
878 layout: &XyChartDiagramLayout,
879 _semantic: &serde_json::Value,
880 _effective_config: &serde_json::Value,
881 options: &SvgRenderOptions,
882) -> Result<String> {
883 xychart::render_xychart_diagram_svg(layout, _semantic, _effective_config, options)
884}
885
886pub fn render_treemap_diagram_svg(
887 layout: &crate::model::TreemapDiagramLayout,
888 _semantic: &serde_json::Value,
889 effective_config: &serde_json::Value,
890 options: &SvgRenderOptions,
891) -> Result<String> {
892 treemap::render_treemap_diagram_svg(layout, _semantic, effective_config, options)
893}
894
895pub fn render_venn_diagram_svg(
896 layout: &VennDiagramLayout,
897 semantic: &serde_json::Value,
898 effective_config: &serde_json::Value,
899 diagram_title: Option<&str>,
900 options: &SvgRenderOptions,
901) -> Result<String> {
902 venn::render_venn_diagram_svg(layout, semantic, effective_config, diagram_title, options)
903}
904
905pub fn render_venn_diagram_svg_model(
906 layout: &VennDiagramLayout,
907 model: &merman_core::diagrams::venn::VennDiagramRenderModel,
908 effective_config: &serde_json::Value,
909 diagram_title: Option<&str>,
910 options: &SvgRenderOptions,
911) -> Result<String> {
912 venn::render_venn_diagram_svg_model(layout, model, effective_config, diagram_title, options)
913}
914
915pub fn render_packet_diagram_svg(
916 layout: &PacketDiagramLayout,
917 semantic: &serde_json::Value,
918 _effective_config: &serde_json::Value,
919 diagram_title: Option<&str>,
920 options: &SvgRenderOptions,
921) -> Result<String> {
922 packet::render_packet_diagram_svg(layout, semantic, _effective_config, diagram_title, options)
923}
924
925pub fn render_timeline_diagram_svg(
926 layout: &TimelineDiagramLayout,
927 semantic: &serde_json::Value,
928 effective_config: &serde_json::Value,
929 _diagram_title: Option<&str>,
930 _measurer: &dyn TextMeasurer,
931 options: &SvgRenderOptions,
932) -> Result<String> {
933 timeline::render_timeline_diagram_svg(
934 layout,
935 semantic,
936 effective_config,
937 _diagram_title,
938 _measurer,
939 options,
940 )
941}
942
943pub fn render_journey_diagram_svg(
944 layout: &crate::model::JourneyDiagramLayout,
945 semantic: &serde_json::Value,
946 effective_config: &serde_json::Value,
947 _diagram_title: Option<&str>,
948 _measurer: &dyn TextMeasurer,
949 options: &SvgRenderOptions,
950) -> Result<String> {
951 journey::render_journey_diagram_svg(
952 layout,
953 semantic,
954 effective_config,
955 _diagram_title,
956 _measurer,
957 options,
958 )
959}
960
961pub fn render_kanban_diagram_svg(
962 layout: &crate::model::KanbanDiagramLayout,
963 _semantic: &serde_json::Value,
964 _effective_config: &serde_json::Value,
965 options: &SvgRenderOptions,
966) -> Result<String> {
967 kanban::render_kanban_diagram_svg(layout, _semantic, _effective_config, options)
968}
969
970pub fn render_gitgraph_diagram_svg(
971 layout: &crate::model::GitGraphDiagramLayout,
972 semantic: &serde_json::Value,
973 _effective_config: &serde_json::Value,
974 diagram_title: Option<&str>,
975 measurer: &dyn TextMeasurer,
976 options: &SvgRenderOptions,
977) -> Result<String> {
978 gitgraph::render_gitgraph_diagram_svg(
979 layout,
980 semantic,
981 _effective_config,
982 diagram_title,
983 measurer,
984 options,
985 )
986}
987
988pub fn render_tree_view_diagram_svg(
989 layout: &TreeViewDiagramLayout,
990 semantic: &serde_json::Value,
991 effective_config: &serde_json::Value,
992 options: &SvgRenderOptions,
993) -> Result<String> {
994 tree_view::render_tree_view_diagram_svg(layout, semantic, effective_config, options)
995}
996
997pub fn render_ishikawa_diagram_svg(
998 layout: &IshikawaDiagramLayout,
999 semantic: &serde_json::Value,
1000 effective_config: &serde_json::Value,
1001 options: &SvgRenderOptions,
1002) -> Result<String> {
1003 ishikawa::render_ishikawa_diagram_svg(layout, semantic, effective_config, options)
1004}
1005
1006pub fn render_eventmodeling_diagram_svg(
1007 layout: &EventModelingDiagramLayout,
1008 semantic: &serde_json::Value,
1009 effective_config: &serde_json::Value,
1010 options: &SvgRenderOptions,
1011) -> Result<String> {
1012 eventmodeling::render_eventmodeling_diagram_svg(layout, semantic, effective_config, options)
1013}
1014
1015pub fn render_gantt_diagram_svg(
1016 layout: &crate::model::GanttDiagramLayout,
1017 semantic: &serde_json::Value,
1018 _effective_config: &serde_json::Value,
1019 options: &SvgRenderOptions,
1020) -> Result<String> {
1021 gantt::render_gantt_diagram_svg(layout, semantic, _effective_config, options)
1022}
1023
1024pub fn render_mindmap_diagram_svg(
1025 layout: &MindmapDiagramLayout,
1026 semantic: &serde_json::Value,
1027 _effective_config: &serde_json::Value,
1028 options: &SvgRenderOptions,
1029) -> Result<String> {
1030 mindmap::render_mindmap_diagram_svg(layout, semantic, _effective_config, options)
1031}
1032
1033pub fn render_mindmap_diagram_svg_with_config(
1034 layout: &MindmapDiagramLayout,
1035 semantic: &serde_json::Value,
1036 effective_config: &merman_core::MermaidConfig,
1037 options: &SvgRenderOptions,
1038) -> Result<String> {
1039 mindmap::render_mindmap_diagram_svg_with_config(layout, semantic, effective_config, options)
1040}
1041
1042pub fn render_architecture_diagram_svg(
1043 layout: &ArchitectureDiagramLayout,
1044 semantic: &serde_json::Value,
1045 effective_config: &serde_json::Value,
1046 options: &SvgRenderOptions,
1047) -> Result<String> {
1048 architecture::render_architecture_diagram_svg(layout, semantic, effective_config, options)
1049}
1050
1051pub fn render_c4_diagram_svg(
1052 layout: &crate::model::C4DiagramLayout,
1053 semantic: &serde_json::Value,
1054 effective_config: &serde_json::Value,
1055 diagram_title: Option<&str>,
1056 _measurer: &dyn TextMeasurer,
1057 options: &SvgRenderOptions,
1058) -> Result<String> {
1059 c4::render_c4_diagram_svg(
1060 layout,
1061 semantic,
1062 effective_config,
1063 diagram_title,
1064 _measurer,
1065 options,
1066 )
1067}
1068
1069pub fn render_flowchart_v2_svg(
1070 layout: &FlowchartV2Layout,
1071 semantic: &serde_json::Value,
1072 effective_config: &serde_json::Value,
1073 diagram_title: Option<&str>,
1074 measurer: &dyn TextMeasurer,
1075 options: &SvgRenderOptions,
1076) -> Result<String> {
1077 flowchart::render_flowchart_v2_svg(
1078 layout,
1079 semantic,
1080 effective_config,
1081 diagram_title,
1082 measurer,
1083 options,
1084 )
1085}
1086
1087pub fn render_flowchart_v2_svg_with_config(
1088 layout: &FlowchartV2Layout,
1089 semantic: &serde_json::Value,
1090 effective_config: &merman_core::MermaidConfig,
1091 diagram_title: Option<&str>,
1092 measurer: &dyn TextMeasurer,
1093 options: &SvgRenderOptions,
1094) -> Result<String> {
1095 flowchart::render_flowchart_v2_svg_with_config(
1096 layout,
1097 semantic,
1098 effective_config,
1099 diagram_title,
1100 measurer,
1101 options,
1102 )
1103}
1104
1105pub fn render_flowchart_v2_svg_model_with_config(
1106 layout: &FlowchartV2Layout,
1107 model: &merman_core::diagrams::flowchart::FlowchartV2Model,
1108 effective_config: &merman_core::MermaidConfig,
1109 diagram_title: Option<&str>,
1110 measurer: &dyn TextMeasurer,
1111 options: &SvgRenderOptions,
1112) -> Result<String> {
1113 flowchart::render_flowchart_v2_svg_model_with_config(
1114 layout,
1115 model,
1116 effective_config,
1117 diagram_title,
1118 measurer,
1119 options,
1120 )
1121}
1122
1123pub fn render_state_diagram_v2_svg(
1124 layout: &StateDiagramV2Layout,
1125 semantic: &serde_json::Value,
1126 effective_config: &serde_json::Value,
1127 diagram_title: Option<&str>,
1128 measurer: &dyn TextMeasurer,
1129 options: &SvgRenderOptions,
1130) -> Result<String> {
1131 state::render_state_diagram_v2_svg(
1132 layout,
1133 semantic,
1134 effective_config,
1135 diagram_title,
1136 measurer,
1137 options,
1138 )
1139}
1140
1141pub fn render_state_diagram_v2_debug_svg(
1142 layout: &StateDiagramV2Layout,
1143 options: &SvgRenderOptions,
1144) -> String {
1145 state::render_state_diagram_v2_debug_svg(layout, options)
1146}
1147
1148pub fn render_class_diagram_v2_debug_svg(
1149 layout: &ClassDiagramV2Layout,
1150 options: &SvgRenderOptions,
1151) -> String {
1152 class::render_class_diagram_v2_debug_svg(layout, options)
1153}
1154
1155pub fn render_class_diagram_v2_svg(
1156 layout: &ClassDiagramV2Layout,
1157 semantic: &serde_json::Value,
1158 effective_config: &serde_json::Value,
1159 diagram_title: Option<&str>,
1160 measurer: &dyn TextMeasurer,
1161 options: &SvgRenderOptions,
1162) -> Result<String> {
1163 class::render_class_diagram_v2_svg(
1164 layout,
1165 semantic,
1166 effective_config,
1167 diagram_title,
1168 measurer,
1169 options,
1170 )
1171}
1172
1173pub fn render_er_diagram_debug_svg(layout: &ErDiagramLayout, options: &SvgRenderOptions) -> String {
1174 er::render_er_diagram_debug_svg(layout, options)
1175}
1176
1177pub fn render_er_diagram_svg(
1178 layout: &ErDiagramLayout,
1179 semantic: &serde_json::Value,
1180 effective_config: &serde_json::Value,
1181 diagram_title: Option<&str>,
1182 measurer: &dyn TextMeasurer,
1183 options: &SvgRenderOptions,
1184) -> Result<String> {
1185 er::render_er_diagram_svg(
1186 layout,
1187 semantic,
1188 effective_config,
1189 diagram_title,
1190 measurer,
1191 options,
1192 )
1193}
1194
1195pub fn render_sankey_diagram_svg(
1196 layout: &SankeyDiagramLayout,
1197 _semantic: &serde_json::Value,
1198 effective_config: &serde_json::Value,
1199 options: &SvgRenderOptions,
1200) -> Result<String> {
1201 sankey::render_sankey_diagram_svg(layout, _semantic, effective_config, options)
1202}
1203
1204fn curve_basis_path_d(points: &[crate::model::LayoutPoint]) -> String {
1206 curve::curve_basis_path_d(points)
1207}
1208fn render_node(out: &mut String, n: &LayoutNode) {
1209 layout_debug::render_node(out, n)
1210}
1211
1212fn render_state_node(out: &mut String, n: &LayoutNode) {
1213 layout_debug::render_state_node(out, n)
1214}
1215
1216fn render_cluster(out: &mut String, c: &LayoutCluster, include_markers: bool) {
1217 layout_debug::render_cluster(out, c, include_markers)
1218}
1219
1220fn compute_layout_bounds(
1221 clusters: &[LayoutCluster],
1222 nodes: &[LayoutNode],
1223 edges: &[crate::model::LayoutEdge],
1224) -> Option<Bounds> {
1225 layout_debug::compute_layout_bounds(clusters, nodes, edges)
1226}