[cairo] Zero length segment and SVG

Jeff Muizelaar jeff at infidigm.net
Tue Jun 20 10:01:27 PDT 2006


I too have a patch that fixes this behaviour as it is also needed for
poppler's cairo backend. I tried to come up with a cleaner way of
doing it than keeping an extra piece of state in the stroker.
Unfortunatly, cairo's stroking code is not very sub-path oriented so I
didn't see anyway to avoid the extra state.

On Tue, Jun 20, 2006 at 08:30:45AM -0600, Keith Wells wrote:
> (Excerpt from Mozilla bug
> https://bugzilla.mozilla.org/show_bug.cgi?id=322976 )
> The SVG 1.1 specification says
> (http://www.w3.org/TR/SVG11/painting.html#StrokeProperties):
> 
> "A subpath consisting of a single moveto  is not stroked. A subpath
> consisting
> of a moveto and lineto to the same exact location or a subpath consisting
> of a
> moveto and a closepath will be stroked only if the 'stroke-linecap'
> property is
> set to "round", producing a circle centered at the given point."
> 
> 
> According to the SVG specification, a blue circle should be rendered in
> firefox for both cases.  This behavior can be fixed in cairo, particularly
> in cairo-path-stroke.c.  There was a patch submitted by Jeff Smith back in
> October 2005
> (http://lists.freedesktop.org/archives/cairo/2005-October/005562.html) that
> would have mostly fixed this problem to cause a round circle or a square to
> be rendered.

Interestingly enough, the svg implementation requirements state that
both square and round dots should be drawn. 
(http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes)
Presumeably this why Jeff Smith's patch had this behaviour. However,
both PDF and PS, like the SVG spec, say that only round caps should have
this behaviour. I think that cairo should do the same and only draw
round caps.

> I have enhanced Jeff's patch with a recent version of cairo-path-stroke.c
> to provide the behavior specified in the SVG 1.1 specification.  Is this
> the correct thing to do in cairo?  Shouldn't this behavior be reflected in
> a future release of firefox?  The included attachment is the result of
> running a diff command on Windows XP.

It is much easier to review patches if you use the '-u' flag when
running diff.

I wasn't quite ready to submit this but here is my patch anyways. The
only non-superficial differences between mine and Keith's patches are I
remove the degenerate sub-path comments and add a little note about the
behaviour to the cairo_stroke documentation.

diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 2f89314..7709216 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -50,6 +50,8 @@ typedef struct cairo_stroker {
     cairo_point_t current_point;
     cairo_point_t first_point;
 
+    cairo_bool_t has_subpath;
+    
     cairo_bool_t has_current_face;
     cairo_stroke_face_t current_face;
 
@@ -164,6 +166,7 @@ _cairo_stroker_init (cairo_stroker_t		*s
 
     stroker->has_current_face = FALSE;
     stroker->has_first_face = FALSE;
+    stroker->has_subpath = FALSE;
 
     if (stroker->style->dash)
 	_cairo_stroker_start_dash (stroker);
@@ -459,26 +462,6 @@ _cairo_stroker_add_trailing_cap (cairo_s
     return _cairo_stroker_add_cap (stroker, face);
 }
 
-static cairo_status_t
-_cairo_stroker_add_caps (cairo_stroker_t *stroker)
-{
-    cairo_status_t status;
-
-    if (stroker->has_first_face) {
-	status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face);
-	if (status)
-	    return status;
-    }
-
-    if (stroker->has_current_face) {
-	status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face);
-	if (status)
-	    return status;
-    }
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
 static void
 _compute_face (cairo_point_t *point, cairo_slope_t *slope, cairo_stroker_t *stroker, cairo_stroke_face_t *face)
 {
@@ -549,6 +532,34 @@ _compute_face (cairo_point_t *point, cai
 }
 
 static cairo_status_t
+_cairo_stroker_add_caps (cairo_stroker_t *stroker)
+{
+    cairo_status_t status;
+    /* check for a degenerative subpath */
+    if (stroker->has_subpath && !stroker->has_first_face && !stroker->has_current_face
+	    && stroker->style->line_cap == CAIRO_LINE_JOIN_ROUND) {
+	/* pick an arbitrary slope to use */
+	cairo_slope_t slope = {1, 0};
+	_compute_face (&stroker->first_point, &slope, stroker, &stroker->first_face);
+	stroker->has_first_face = stroker->has_current_face = TRUE;
+	stroker->current_face = stroker->first_face;
+    }
+    if (stroker->has_first_face) {
+	status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face);
+	if (status)
+	    return status;
+    }
+
+    if (stroker->has_current_face) {
+	status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face);
+	if (status)
+	    return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
 _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_point_t *p2,
 			     cairo_slope_t *slope, cairo_stroke_face_t *start,
 			     cairo_stroke_face_t *end)
@@ -564,9 +575,6 @@ _cairo_stroker_add_sub_edge (cairo_strok
     _compute_face (p2, slope, stroker, end);
 
     if (p1->x == p2->x && p1->y == p2->y) {
-	/* XXX: Need to rethink how this case should be handled, (both
-           here and in _compute_face). The key behavior is that
-           degenerate paths should draw as much as possible. */
 	return CAIRO_STATUS_SUCCESS;
     }
 
@@ -611,6 +619,7 @@ _cairo_stroker_move_to (void *closure, c
 
     stroker->has_first_face = 0;
     stroker->has_current_face = 0;
+    stroker->has_subpath = 0;
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -635,11 +644,8 @@ _cairo_stroker_line_to (void *closure, c
     cairo_point_t *p2 = point;
     cairo_slope_t slope;
 
+    stroker->has_subpath = TRUE;
     if (p1->x == p2->x && p1->y == p2->y) {
-	/* XXX: Need to rethink how this case should be handled, (both
-           here and in cairo_stroker_add_sub_edge and in _compute_face). The
-           key behavior is that degenerate paths should draw as much
-           as possible. */
 	return CAIRO_STATUS_SUCCESS;
     }
 
@@ -686,10 +692,6 @@ _cairo_stroker_line_to_dashed (void *clo
     cairo_slope_t slope;
 
     if (p1->x == p2->x && p1->y == p2->y) {
-	/* XXX: Need to rethink how this case should be handled, (both
-           here and in cairo_stroker_add_sub_edge and in _compute_face). The
-           key behavior is that degenerate paths should draw as much
-           as possible. */
 	return CAIRO_STATUS_SUCCESS;
     }
 
diff --git a/src/cairo.c b/src/cairo.c
index e4fa741..1cd08a1 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -1775,7 +1775,8 @@ cairo_mask_surface (cairo_t         *cr,
  * cairo_stroke, the current path will be cleared from the cairo
  * context. See cairo_set_line_width(), cairo_set_line_join(),
  * cairo_set_line_cap(), cairo_set_dash(), and
- * cairo_stroke_preserve().
+ * cairo_stroke_preserve(). Degenerative paths are only capped if the
+ * line cap is set to CAIRO_LINE_CAP_ROUND.
  **/
 void
 cairo_stroke (cairo_t *cr)


More information about the cairo mailing list