[librsvg/librsvg-2.52: 13/21] Path::from_cairo() - New function to convert a librsvg path to a Cairo path




commit 73ffca9567899bf6f7e0e4deace0b5d3b31cb1fd
Author: Federico Mena Quintero <federico gnome org>
Date:   Fri Feb 4 20:02:47 2022 -0600

    Path::from_cairo() - New function to convert a librsvg path to a Cairo path
    
    This handles the quirk quere Cairo sometimes appends a MoveTo to
    paths, and in particular, for empty paths that should have no commands
    at all.
    
    The text rendering code really needs to know whether a path produced
    inked pixels or not, so that for "empty text" (an empty string, or
    just whitespace) it can avoid producing a bounding box equal to a
    zero-sized rectangle - it wants to really produce "no bounds" in that
    case.
    
    So, let's strip Cairo's MoveTo(0, 0) from paths that have no other commands.
    
    Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/673>

 src/drawing_ctx.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)
---
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index 1b05b22af..b9550057b 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -2184,6 +2184,36 @@ impl Path {
 
         cr.status().map_err(|e| e.into())
     }
+
+    /// Converts a `cairo::Path` to a librsvg `Path`.
+    fn from_cairo(cairo_path: cairo::Path) -> Path {
+        let mut builder = PathBuilder::default();
+
+        // First, see if the path is a single MoveTo(0, 0).  Cairo does this when the code that
+        // generated the path didn't include any commands, due to the way it appends a MoveTo to
+        // some paths.
+        //
+        // Only do the conversion if the path is not empty; otherwise
+        // really return a librsvg path with no commands.
+
+        if !cairo_path
+            .iter()
+            .eq([cairo::PathSegment::MoveTo((0.0, 0.0))])
+        {
+            for segment in cairo_path.iter() {
+                match segment {
+                    cairo::PathSegment::MoveTo((x, y)) => builder.move_to(x, y),
+                    cairo::PathSegment::LineTo((x, y)) => builder.line_to(x, y),
+                    cairo::PathSegment::CurveTo((x2, y2), (x3, y3), (x4, y4)) => {
+                        builder.curve_to(x2, y2, x3, y3, x4, y4)
+                    }
+                    cairo::PathSegment::ClosePath => builder.close_path(),
+                }
+            }
+        }
+
+        builder.into_path()
+    }
 }
 
 impl PathCommand {
@@ -2232,3 +2262,37 @@ impl CubicBezierCurve {
         cr.curve_to(pt1.0, pt1.1, pt2.0, pt2.1, to.0, to.1);
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn rsvg_path_from_cairo_path() {
+        let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, 10, 10).unwrap();
+        let cr = cairo::Context::new(&surface).unwrap();
+
+        cr.move_to(1.0, 2.0);
+        cr.line_to(3.0, 4.0);
+        cr.curve_to(5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
+        cr.close_path();
+
+        let cairo_path = cr.copy_path().unwrap();
+        let path = Path::from_cairo(cairo_path);
+
+        assert_eq!(
+            path.iter().collect::<Vec<PathCommand>>(),
+            vec![
+                PathCommand::MoveTo(1.0, 2.0),
+                PathCommand::LineTo(3.0, 4.0),
+                PathCommand::CurveTo(CubicBezierCurve {
+                    pt1: (5.0, 6.0),
+                    pt2: (7.0, 8.0),
+                    to: (9.0, 10.0),
+                }),
+                PathCommand::ClosePath,
+                PathCommand::MoveTo(1.0, 2.0), // cairo inserts a MoveTo after ClosePath
+            ],
+        );
+    }
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]