Repository: linebender/resvg (language: rust)

Issue:
Figure out how to implement custom markers paint order
Since `usvg` converts markers into regular nodes, we cannot render them between path's fill and stroke. Which is allowed by the spec. We can render them only below or above the path.

The only way to implement this is by rewriting markers support. Basically removing this simplification step from `usvg`, which is unfortunate.


Discussion / hints:

Fix patch:
diff --git a/crates/usvg-parser/src/converter.rs b/crates/usvg-parser/src/converter.rs
index dd4103178..c121de3ee 100644
--- a/crates/usvg-parser/src/converter.rs
+++ b/crates/usvg-parser/src/converter.rs
@@ -7,7 +7,7 @@ use std::hash::{Hash, Hasher};
 use std::rc::Rc;
 use std::str::FromStr;
 
-use svgtypes::{Length, LengthUnit as Unit};
+use svgtypes::{Length, LengthUnit as Unit, PaintOrderKind};
 use usvg_tree::*;
 
 use crate::svgtree::{self, AId, EId, FromValue, SvgNode};
@@ -665,17 +665,17 @@ fn convert_path(
         node.find_attribute(AId::PaintOrder).unwrap_or_default();
     let paint_order = svg_paint_order_to_usvg(raw_paint_order);
 
-    // If a path doesn't have a fill or a stroke than it's invisible.
+    // If a path doesn't have a fill or a stroke then it's invisible.
     // By setting `visibility` to `hidden` we are disabling rendering of this path.
     if fill.is_none() && stroke.is_none() {
         visibility = Visibility::Hidden;
     }
 
-    let mut markers_group = None;
+    let mut markers_node = None;
     if crate::marker::is_valid(node) && visibility == Visibility::Visible {
-        let mut g = parent.append_kind(NodeKind::Group(Group::default()));
-        crate::marker::convert(node, &path, state, cache, &mut g);
-        markers_group = Some(g);
+        let mut marker = Node::new(NodeKind::Group(Group::default()));
+        crate::marker::convert(node, &path, state, cache, &mut marker);
+        markers_node = Some(marker);
     }
 
     // Nodes generated by markers must not have an ID. Otherwise we would have duplicates.
@@ -685,7 +685,7 @@ fn convert_path(
         String::new()
     };
 
-    parent.append_kind(NodeKind::Path(Path {
+    let path = Path {
         id,
         visibility,
         fill,
@@ -694,14 +694,54 @@ fn convert_path(
         rendering_mode,
         text_bbox: None,
         data: path,
-    }));
+    };
+
+    let append_marker = || {
+        if let Some(markers_node) = markers_node {
+            parent.append(markers_node);
+        }
+    };
+
+    let append_path = || {
+        parent.append(Node::new(NodeKind::Path(path.clone())));
+    };
 
-    if raw_paint_order.order[2] == svgtypes::PaintOrderKind::Markers {
-        // Insert markers group after `path`.
-        if let Some(g) = markers_group {
-            g.detach();
-            parent.append(g);
+    match raw_paint_order.order {
+        [PaintOrderKind::Markers, _, _] => {
+            append_marker();
+            append_path();
+        }
+        [first, PaintOrderKind::Markers, last] => {
+            let append_single_paint_path = |paint_order_kind: PaintOrderKind| match paint_order_kind
+            {
+                PaintOrderKind::Fill => {
+                    if !path.fill.is_none() {
+                        let mut fill_path = path.clone();
+                        fill_path.stroke = None;
+                        fill_path.id = String::new();
+                        parent.append(Node::new(NodeKind::Path(fill_path)));
+                    }
+                }
+                PaintOrderKind::Stroke => {
+                    if !path.stroke.is_none() {
+                        let mut stroke_path = path.clone();
+                        stroke_path.fill = None;
+                        stroke_path.id = String::new();
+                        parent.append(Node::new(NodeKind::Path(stroke_path)));
+                    }
+                }
+                _ => {}
+            };
+
+            append_single_paint_path(first);
+            append_marker();
+            append_single_paint_path(last)
+        }
+        [_, _, PaintOrderKind::Markers] => {
+            append_path();
+            append_marker();
         }
+        _ => append_path(),
     }
 }
 


Test patch:
diff --git a/crates/resvg/tests/tests/painting/paint-order/fill-markers-stroke.png b/crates/resvg/tests/tests/painting/paint-order/fill-markers-stroke.png
index b345dea5a..1b229ad34 100644
Binary files a/crates/resvg/tests/tests/painting/paint-order/fill-markers-stroke.png and b/crates/resvg/tests/tests/painting/paint-order/fill-markers-stroke.png differ
diff --git a/crates/resvg/tests/tests/painting/paint-order/stroke-markers-fill.png b/crates/resvg/tests/tests/painting/paint-order/stroke-markers-fill.png
new file mode 100644
index 000000000..e0e56cb55
Binary files /dev/null and b/crates/resvg/tests/tests/painting/paint-order/stroke-markers-fill.png differ
diff --git a/crates/resvg/tests/tests/painting/paint-order/stroke-markers-fill.svg b/crates/resvg/tests/tests/painting/paint-order/stroke-markers-fill.svg
new file mode 100644
index 000000000..d3b7b9117
--- /dev/null
+++ b/crates/resvg/tests/tests/painting/paint-order/stroke-markers-fill.svg
@@ -0,0 +1,16 @@
+<svg id="svg1" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+    <title>stroke markers fill (SVG 2)</title>
+
+    <marker id="marker1" refX="10" refY="10" markerWidth="40" markerHeight="40"
+            markerUnits="userSpaceOnUse">
+        <rect id="rect" width="20" height="20" fill="orange"/>
+    </marker>
+
+    <path id="path1" d="M 20 20 L 180 20 L 180 180 L 20 180 Z"
+          fill="green" stroke="blue" stroke-width="5"
+          marker-start="url(#marker1)" marker-mid="url(#marker1)" marker-end="url(#marker1)"
+          paint-order="stroke markers fill"/>
+
+    <!-- image frame -->
+    <rect id="frame" x="1" y="1" width="198" height="198" fill="none" stroke="black"/>
+</svg>
diff --git a/crates/resvg/tests/tests/painting/paint-order/stroke-markers.png b/crates/resvg/tests/tests/painting/paint-order/stroke-markers.png
index 8e199b06f..e0e56cb55 100644
Binary files a/crates/resvg/tests/tests/painting/paint-order/stroke-markers.png and b/crates/resvg/tests/tests/painting/paint-order/stroke-markers.png differ