merman_render/svg/pipeline/builtin/
foreign_object.rs1use crate::Result;
2use crate::svg::foreign_object_label_fallback_svg_text;
3use std::borrow::Cow;
4
5use super::util::find_tag_end;
6use crate::svg::pipeline::{SvgPostprocessContext, SvgPostprocessor};
7
8#[derive(Debug, Clone, Copy, Default)]
9pub struct ForeignObjectFallbackPostprocessor;
10
11impl SvgPostprocessor for ForeignObjectFallbackPostprocessor {
12 fn name(&self) -> &'static str {
13 "foreign-object-fallback"
14 }
15
16 fn process<'a>(
17 &self,
18 svg: Cow<'a, str>,
19 _ctx: &SvgPostprocessContext<'_>,
20 ) -> Result<Cow<'a, str>> {
21 if !svg.contains("<foreignObject") {
22 return Ok(svg);
23 }
24 Ok(Cow::Owned(foreign_object_fallback_svg(&svg)))
25 }
26}
27
28#[derive(Debug, Clone, Copy, Default)]
29pub struct StripForeignObjectPostprocessor;
30
31impl SvgPostprocessor for StripForeignObjectPostprocessor {
32 fn name(&self) -> &'static str {
33 "strip-foreign-object"
34 }
35
36 fn process<'a>(
37 &self,
38 svg: Cow<'a, str>,
39 _ctx: &SvgPostprocessContext<'_>,
40 ) -> Result<Cow<'a, str>> {
41 if !svg.contains("<foreignObject") {
42 return Ok(svg);
43 }
44 Ok(Cow::Owned(strip_foreign_objects(&svg)))
45 }
46}
47
48pub(crate) fn foreign_object_fallback_svg(svg: &str) -> String {
49 foreign_object_label_fallback_svg_text(svg)
50}
51
52pub(crate) fn strip_foreign_objects(svg: &str) -> String {
53 let mut out = String::with_capacity(svg.len());
54 let mut cursor = 0;
55
56 while let Some(rel_start) = svg[cursor..].find("<foreignObject") {
57 let start = cursor + rel_start;
58 out.push_str(&svg[cursor..start]);
59
60 let Some(open_end) = find_tag_end(svg, start) else {
61 out.push_str(&svg[start..]);
62 return out;
63 };
64
65 if svg[start..=open_end].trim_end().ends_with("/>") {
66 cursor = open_end + 1;
67 continue;
68 }
69
70 let close_start = open_end + 1;
71 let Some(rel_close) = svg[close_start..].find("</foreignObject>") else {
72 cursor = open_end + 1;
73 continue;
74 };
75 cursor = close_start + rel_close + "</foreignObject>".len();
76 }
77
78 out.push_str(&svg[cursor..]);
79 out
80}