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



commit 62bd4d44c85a9e3a7c8236057ddffe61595ff963
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Jan 27 20:23:51 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

 rsvg_internals/src/drawing_ctx.rs | 26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)
---
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index 38b23299..f7da9675 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -1348,14 +1348,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 target == PaintTarget::Stroke {
                             dc.stroke(&cr, an, values, &bbox, current_color)?;
@@ -1819,7 +1822,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);
@@ -1845,8 +1852,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]