[librsvg: 1/2] (#601) - Compute correct bounds for objects with stroke-width=0




commit a5448c08150c4a30d9e121126c7deb66c11cf218
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Jan 27 20:09:08 2021 -0600

    (#601) - Compute correct bounds for objects with stroke-width=0
    
    Inkscape sometimes generates SVGs with stroke-width=0.  Per the SVG
    spec, these objects should not be rendered.
    
    Cairo indeed does not render them, but cairo_stroke_extents() will
    return extents of (0, 0, 0, 0), which the boounding box code
    interprets as a single point at the origin; it does not interpret it
    as an empty rectangle that should not be included in the bbox.
    
    This caused a bug when running "rsvg-convert --export-id=foo", since
    it actually tries to compute the bounds for a single object, instead
    of the whole document.  Trying to render a single subtree that
    contains an object with stroke_width=0 would create an image whose
    bounds include the origin, even though the subtree may be far away.
    
    In this commit, we directly compare the stroke_width to 0, and just
    not compute the stroke extents in that case.
    
    This could be refactored to be done in the step that resolves a paint
    server or converts it to user space; maybe that code should be made
    aware of the stroke_width and resolve to UserSpacePaintSource::None if
    the stroke_width=0.
    
    The code as it is in the master branch makes this a bit hard to test;
    I'll put the actual test in rustify-rsvg-convert branch.
    
    Fixes https://gitlab.gnome.org/GNOME/librsvg/-/issues/601

 src/drawing_ctx.rs | 26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)
---
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index dc8d62e8..5a2d7a3f 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -1271,14 +1271,17 @@ impl DrawingCtx {
             let mut bounding_box: Option<BoundingBox> = None;
             path_helper.unset();
 
+            let params = dc.get_view_params();
+
             for &target in &values.paint_order().targets {
                 // fill and stroke operations will preserve the path.
                 // markers operation will clear the path.
                 match target {
                     PaintTarget::Fill | PaintTarget::Stroke => {
                         path_helper.set()?;
-                        let bbox = bounding_box
-                            .get_or_insert_with(|| compute_stroke_and_fill_box(&cr, &values));
+                        let bbox = bounding_box.get_or_insert_with(|| {
+                            compute_stroke_and_fill_box(&cr, &values, &params)
+                        });
 
                         if values.is_visible() {
                             if target == PaintTarget::Stroke {
@@ -1749,7 +1752,11 @@ fn get_clip_in_user_and_object_space(
         .unwrap_or((None, None))
 }
 
-fn compute_stroke_and_fill_box(cr: &cairo::Context, values: &ComputedValues) -> BoundingBox {
+fn compute_stroke_and_fill_box(
+    cr: &cairo::Context,
+    values: &ComputedValues,
+    params: &ViewParams,
+) -> BoundingBox {
     let affine = Transform::from(cr.get_matrix());
 
     let mut bbox = BoundingBox::new().with_transform(affine);
@@ -1775,8 +1782,19 @@ fn compute_stroke_and_fill_box(cr: &cairo::Context, values: &ComputedValues) ->
     bbox.insert(&fb);
 
     // Bounding box for stroke
+    //
+    // When presented with a line width of 0, Cairo returns a
+    // stroke_extents rectangle of (0, 0, 0, 0).  This would cause the
+    // bbox to include a lone point at the origin, which is wrong, as a
+    // stroke of zero width should not be painted, per
+    // https://www.w3.org/TR/SVG2/painting.html#StrokeWidth
+    //
+    // So, see if the stroke width is 0 and just not include the stroke in the
+    // bounding box if so.
+
+    let stroke_width = values.stroke_width().0.normalize(values, &params);
 
-    if values.stroke().0 != PaintServer::None {
+    if !stroke_width.approx_eq_cairo(0.0) && values.stroke().0 != PaintServer::None {
         let (x0, y0, x1, y1) = cr.stroke_extents();
         let sb = BoundingBox::new()
             .with_transform(affine)


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