From 96dd39898fc17fe447b9591199ae0d21bbf351c7 Mon Sep 17 00:00:00 2001 From: Laurenz Stampfl <47084093+LaurenzV@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:14:55 +0100 Subject: [PATCH] Add support for markers in-between fills/strokes. Closes #550 --- .../paint-order/fill-markers-stroke.png | Bin 655 -> 15252 bytes .../paint-order/stroke-markers-fill.png | Bin 0 -> 11977 bytes .../paint-order/stroke-markers-fill.svg | 16 +++++ .../painting/paint-order/stroke-markers.png | Bin 463 -> 11977 bytes crates/usvg-parser/src/converter.rs | 66 ++++++++++++++---- 5 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 crates/resvg/tests/tests/painting/paint-order/stroke-markers-fill.png create mode 100644 crates/resvg/tests/tests/painting/paint-order/stroke-markers-fill.svg 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 b345dea5a6bcdd9da3c8d40f3ec56886749258ac..1b229ad341a5c0ea651e644beec23f4ae9faa49b 100644 GIT binary patch literal 15252 zcmeHOe@qis9B)B^U<$HCNF+)oCN7(-bIC9POt)ENYzEvSl7SYJk(#Yz&BAJ1CU=D_ z>g+~0%|O-w3&!Xm+GGo<8+BK=5k>?nF6=T0w8AV1k`}H&i`To|do8u?=u(sYihGyz zpT6(Ae((GJeBZs#>-Y2FzM=v_;>JWCk0&U6N&Fg*$KMV<331#v>e>bckC$6sD1KqT zia%@Pf3mgk^wz@1v>nae1!A#ODm_rbT}p5*_5U5$d*IfkrQlbj(mi{`A9U-R6D@nC zrAmcTS!@A6xwl<2VSeS^p_0-=3!)Kgf~v}9yfWR&~xZXJlqHFE(&!T6-gO9Ied3K_1 zOaNfy?OvIMw}jz>GT8j#0NKLR{Ili^R9No+bm=NMl*Jdi0Uc5kL3!mhYw0gLO z?Ic56?aIw;8O)B+^vfIA2`l3w?fK`GyCcLt-!d zT>4f|gz2Q@K85S8YFY;KW}Pq7c{XA9f3DD{eKsbDkd?GS8P3S5TpgYTORiipvxE3S zjxWoo_FEBgzNKc?!d79EbA}#J7Wj49F1yk_TZ~sp_H(7OI_M>0gV#e0IP#b-71mI7 zaQu9Ri!@!tj`YFOa+R%1_Fnf$W4>L<536kzwJi^|n#u?c%W32Qg8Etr30|fJvxDG-&jwFZI$% zg!istK>KnrSi_f<^O>qoAK*2_!f`ypTBDun60t^4OD~aXu>>ddS-4W2$+)Jy7r`Q` zlm{d040w4kEp&pUG6)pG7SUjjqJkja+7Ds$TY+=S`vG)@SVU6q zG6}Vaq8dYi_eeui^f8{U%4%(kXOm1zGFXJk3!{X<_+6^d?9fQ`Y+m@=3r#^0Lx4f` zf|d)gWkIVYdRd@%CE6;Wof6t8g2T55XIyBXjP}WBpN#g&XrGMs$%rPPGbMDShE5dG zfhsyrhX2tcn&59>Kr9KdB*c;sOF}FOu_VNj5KBTV39%%^k`PNmED8OW6zFnCBoUEB zL=vMXi9lcw-2NWYPK+9kQNuA*DxhBYhmb!6ce?*Yz3|n?s+*dyn-x_1@@yvTTfab# zabgA1qI&aavW^N7NJJxlcr>LaP@W|ZmP|w=h=6jrtYGa%WMxLJYpM1*C}a_R55z9m z38)-_dLcs(u7OD!a?B0>iI@k9%wQ7#v#Kg2O#`aHKS-Va7ph2r%;?0Nx;W-@0DmCn u+~|JG=F_11HKL0-F%7XozpC?X+^ai%>WR9eUviM=uEP8x@#Pl}*Z&S$C=S#B literal 655 zcmeAS@N?(olHy`uVBq!ia0y~yVAKI&4rZW;-{LxVAY~cg6XFV_F~ENYhX4PUGUR(q zU}#`)(AsaWd4NFyi1ssZd|=>s50nLJ;hXhu8<65C3GxeO5U_X1FW7%zQ}&%XKwJKL zx;TbZ+Fk zN8-SWkE(#xrj+Uyr|DV?g3N^IMrKUag($)%hpBp)iERC~_4LfGzk?N!!eGsX$mPyL z%b#zWH1Vn;B(Qh)rCRI;1-5NhL0BwuICf3D3XP_zZIJl68gggB&l8W_N&~OmSy98< Xbau0$-REt2pk(Fg>gTe~DWM4fFhUb6 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 0000000000000000000000000000000000000000..e0e56cb5529cd22d4e4b313cb4746f7ce9f524a5 GIT binary patch literal 11977 zcmeHNZAep57`}6Uv6vr$1xi^ML}VHz8b+;;=~_vKXrm9cA2EqqD{9Q7cBe!XBeM^x zG5R1XP(go$X=&UAg;ds}h(S^Jl4Plf(-}?O?(Lj&H>wLafAmH0Uf7?Vd(M5|=Y7w4 zpL5Q0V$(*QT9vAzC`zqgr!AoNd-AX2=tKKfQa_x7-VL!ChvQZT}H)&1qz1eO- zuZKLNuia)vYX(PJ;{ql$a8om1bqLvG_a^&Dt1_62?I;s&5yJ+FwRW$<=E8O~30p+N zM6(AeCStZ|*k3W5PYEh73b8eL4=|#`@6+AEyyu;Bd5hFErjDlk?5g2o7;U~h(u;W} zL2caNOWeVyeR`XOU1uEOUIDKZ89~J>dT8anwD&lA){$^8Az$i!<$aBP+=0Bj?rZwhI<7ErQp*s0@cUVzEk5g3Ux+$(U&B7;cw-_aNuas zGsC?`TK$sP|9cQ!G)XDs^i?l$?I%X!bPr7i;vUd(2z+@M@obs+GLm>)NI>Z!ALt&E zIlbvg!YqWn42o&}k4X47j3)j$G+&=jyvY8b^OIhTH)alTW_U`J=HaFX?7fu@dLDi2*@%@VTu zl-QyLqdama$99rM;6^Z>e+s~%sXH}r#l387g=6x_ZL$bmnAqc|7-fx#_e-a^ki(m} zwhp$+K_b@Z$xZYvM^i)NkuFy-Bwz_iglN-u>;z^HEE}+1{lfEoZb0BQi#0FLU>MHbvqz~v0l1fW}g zpawt + stroke markers fill (SVG 2) + + + + + + + + + + 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 8e199b06fac463450465e6ef0a0739883c41c811..e0e56cb5529cd22d4e4b313cb4746f7ce9f524a5 100644 GIT binary patch literal 11977 zcmeHNZAep57`}6Uv6vr$1xi^ML}VHz8b+;;=~_vKXrm9cA2EqqD{9Q7cBe!XBeM^x zG5R1XP(go$X=&UAg;ds}h(S^Jl4Plf(-}?O?(Lj&H>wLafAmH0Uf7?Vd(M5|=Y7w4 zpL5Q0V$(*QT9vAzC`zqgr!AoNd-AX2=tKKfQa_x7-VL!ChvQZT}H)&1qz1eO- zuZKLNuia)vYX(PJ;{ql$a8om1bqLvG_a^&Dt1_62?I;s&5yJ+FwRW$<=E8O~30p+N zM6(AeCStZ|*k3W5PYEh73b8eL4=|#`@6+AEyyu;Bd5hFErjDlk?5g2o7;U~h(u;W} zL2caNOWeVyeR`XOU1uEOUIDKZ89~J>dT8anwD&lA){$^8Az$i!<$aBP+=0Bj?rZwhI<7ErQp*s0@cUVzEk5g3Ux+$(U&B7;cw-_aNuas zGsC?`TK$sP|9cQ!G)XDs^i?l$?I%X!bPr7i;vUd(2z+@M@obs+GLm>)NI>Z!ALt&E zIlbvg!YqWn42o&}k4X47j3)j$G+&=jyvY8b^OIhTH)alTW_U`J=HaFX?7fu@dLDi2*@%@VTu zl-QyLqdama$99rM;6^Z>e+s~%sXH}r#l387g=6x_ZL$bmnAqc|7-fx#_e-a^ki(m} zwhp$+K_b@Z$xZYvM^i)NkuFy-Bwz_iglN-u>;z^HEE}+1{lfEoZb0BQi#0FLU>MHbvqz~v0l1fW}g zpawtpK^R3>7+dKHl3C<*clW)QG<$S>G`U{m&;IY5&=JzX3_DsCkS1WC0x zOg#ARt^!NH31soX|0!7;0KFVyLAh&?2$mY1`#K}?T?oRr8 z_q+6r>j%#)UFWK!v*yX6Lx-Yv%e+$3JNP2+VFVb2pP&2AEGcXI?jIJ-Ut@jOaD`>8 zncV_Z2n1Z(I&1uvpWIltn@xvmV9kZY5YvezPyH6R5`%!Spw?rDcKv(xWtV)g`rV`7 zCD+7>7aLx^uPeh10@|U8iHWL0Vq#&neqPOdD;VZ}%@b%k&U5Hkvw#E>Lxj)`jmX>( QO;EIWy85}Sb4q9e0F551Jpcdz 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(), } }