[librsvg] Allow pattern specifications to have a fallback color, per the spec



commit 16ae767a8c43bb35d180923c5a3caad73859e04d
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Feb 8 12:38:42 2017 -0600

    Allow pattern specifications to have a fallback color, per the spec
    
    See here: https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint
    
    A paint specification can be a color, or "<funciri> [somecolor]".  If
    the optional color is specified, it will be used in case the paint
    server specified by the IRI is not valid (not found, doesn't have enough
    parameters, or somehow doesn't manage to paint anything).

 rsvg-cairo-draw.c   |  105 +++++++++++++++++++++++++++++++--------------------
 rsvg-paint-server.c |   63 ++++++++++++++++++++++++-------
 rsvg-paint-server.h |   17 ++++++--
 rust/src/pattern.rs |   17 ++++++--
 4 files changed, 137 insertions(+), 65 deletions(-)
---
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index 3cc2f8c..7eb953c 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -167,46 +167,65 @@ _set_source_rsvg_solid_color (RsvgDrawingCtx * ctx,
     cairo_set_source_rgba (cr, r, g, b, a);
 }
 
-static void
+static gboolean
 _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
                           RsvgPattern * rsvg_pattern, RsvgBbox bbox)
 {
     Pattern *pattern;
+    gboolean result;
 
     pattern = rsvg_pattern_node_to_rust_pattern ((RsvgNode *) rsvg_pattern);
 
-    pattern_resolve_fallbacks_and_set_pattern (pattern, ctx, bbox);
+    result = pattern_resolve_fallbacks_and_set_pattern (pattern, ctx, bbox);
 
     pattern_destroy (pattern);
+
+    return result;
 }
 
 /* note: _set_source_rsvg_paint_server does not change cairo's CTM */
-static void
+static gboolean
 _set_source_rsvg_paint_server (RsvgDrawingCtx * ctx,
                                guint32 current_color_rgb,
                                RsvgPaintServer * ps,
                                guint8 opacity, RsvgBbox bbox, guint32 current_color)
 {
     RsvgNode *node;
+    gboolean had_paint_server;
+
+    had_paint_server = FALSE;
 
     switch (ps->type) {
     case RSVG_PAINT_SERVER_IRI:
-        node = rsvg_drawing_ctx_acquire_node (ctx, ps->core.iri);
+        node = rsvg_drawing_ctx_acquire_node (ctx, ps->core.iri->iri_str);
         if (node == NULL)
             break;
-        else if (rsvg_node_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT)
+        else if (rsvg_node_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
             _set_source_rsvg_linear_gradient (ctx, (RsvgLinearGradient *) node, opacity, bbox);
-        else if (rsvg_node_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT)
+            had_paint_server = TRUE;
+        } else if (rsvg_node_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
             _set_source_rsvg_radial_gradient (ctx, (RsvgRadialGradient *) node, opacity, bbox);
-        else if (rsvg_node_type (node) == RSVG_NODE_TYPE_PATTERN)
-            _set_source_rsvg_pattern (ctx, (RsvgPattern *) node, bbox);
+            had_paint_server = TRUE;
+        } else if (rsvg_node_type (node) == RSVG_NODE_TYPE_PATTERN) {
+            if (_set_source_rsvg_pattern (ctx, (RsvgPattern *) node, bbox)) {
+                had_paint_server = TRUE;
+            } else {
+                if (ps->core.iri->has_alternate) {
+                    _set_source_rsvg_solid_color (ctx, &ps->core.iri->alternate, opacity, current_color);
+                    had_paint_server = TRUE;
+                }
+            }
+        }
 
         rsvg_drawing_ctx_release_node (ctx, node);
         break;
     case RSVG_PAINT_SERVER_SOLID:
         _set_source_rsvg_solid_color (ctx, ps->core.color, opacity, current_color);
+        had_paint_server = TRUE;
         break;
     }
+
+    return had_paint_server;
 }
 
 static void
@@ -348,14 +367,17 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
         cairo_save (render->cr);
         cairo_move_to (render->cr, x, y);
         rsvg_bbox_insert (&render->bbox, &bbox);
-        _set_source_rsvg_paint_server (ctx,
-                                       state->current_color,
-                                       state->fill,
-                                       state->fill_opacity,
-                                       bbox, rsvg_current_state (ctx)->current_color);
-        if (rotation != 0.)
-            cairo_rotate (render->cr, -rotation);
-        pango_cairo_show_layout (render->cr, layout);
+
+        if (_set_source_rsvg_paint_server (ctx,
+                                           state->current_color,
+                                           state->fill,
+                                           state->fill_opacity,
+                                           bbox, rsvg_current_state (ctx)->current_color)) {
+            if (rotation != 0.)
+                cairo_rotate (render->cr, -rotation);
+            pango_cairo_show_layout (render->cr, layout);
+        }
+
         cairo_restore (render->cr);
     }
 
@@ -364,19 +386,20 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
         cairo_move_to (render->cr, x, y);
         rsvg_bbox_insert (&render->bbox, &bbox);
 
-        _set_source_rsvg_paint_server (ctx,
-                                       state->current_color,
-                                       state->stroke,
-                                       state->stroke_opacity,
-                                       bbox, rsvg_current_state (ctx)->current_color);
+        if (_set_source_rsvg_paint_server (ctx,
+                                           state->current_color,
+                                           state->stroke,
+                                           state->stroke_opacity,
+                                           bbox, rsvg_current_state (ctx)->current_color)) {
+            if (rotation != 0.)
+                cairo_rotate (render->cr, -rotation);
+            pango_cairo_layout_path (render->cr, layout);
 
-        if (rotation != 0.)
-            cairo_rotate (render->cr, -rotation);
-        pango_cairo_layout_path (render->cr, layout);
+            setup_cr_for_stroke (render->cr, ctx, state);
 
-        setup_cr_for_stroke (render->cr, ctx, state);
+            cairo_stroke (render->cr);
+        }
 
-        cairo_stroke (render->cr);
         cairo_restore (render->cr);
     }
 }
@@ -452,27 +475,27 @@ rsvg_cairo_render_path_builder (RsvgDrawingCtx * ctx, RsvgPathBuilder *builder)
 
         opacity = state->fill_opacity;
 
-        _set_source_rsvg_paint_server (ctx,
-                                       state->current_color,
-                                       state->fill,
-                                       opacity, bbox, rsvg_current_state (ctx)->current_color);
-
-        if (state->stroke != NULL)
-            cairo_fill_preserve (cr);
-        else
-            cairo_fill (cr);
+        if (_set_source_rsvg_paint_server (ctx,
+                                           state->current_color,
+                                           state->fill,
+                                           opacity, bbox, rsvg_current_state (ctx)->current_color)) {
+            if (state->stroke != NULL)
+                cairo_fill_preserve (cr);
+            else
+                cairo_fill (cr);
+        }
     }
 
     if (state->stroke != NULL) {
         int opacity;
         opacity = state->stroke_opacity;
 
-        _set_source_rsvg_paint_server (ctx,
-                                       state->current_color,
-                                       state->stroke,
-                                       opacity, bbox, rsvg_current_state (ctx)->current_color);
-
-        cairo_stroke (cr);
+        if (_set_source_rsvg_paint_server (ctx,
+                                           state->current_color,
+                                           state->stroke,
+                                           opacity, bbox, rsvg_current_state (ctx)->current_color)) {
+            cairo_stroke (cr);
+        }
     }
 
     cairo_new_path (cr); /* clear the path in case stroke == fill == NULL; otherwise we leave it around from 
computing the bounding box */
diff --git a/rsvg-paint-server.c b/rsvg-paint-server.c
index d675624..a399cd4 100644
--- a/rsvg-paint-server.c
+++ b/rsvg-paint-server.c
@@ -1,25 +1,25 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set sw=4 sts=4 ts=4 expandtab: */
-/* 
+/*
    rsvg-paint-server.c: Implement the SVG paint server abstraction.
- 
+
    Copyright (C) 2000 Eazel, Inc.
-  
+
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
-  
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.
-  
+
    You should have received a copy of the GNU Library General Public
    License along with this program; if not, write to the
    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
-  
+
    Author: Raph Levien <raph artofcode com>
 */
 
@@ -64,17 +64,37 @@ rsvg_paint_server_solid_current_color (void)
 }
 
 static RsvgPaintServer *
-rsvg_paint_server_iri (char *iri)
+rsvg_paint_server_iri (char *iri, gboolean has_alternate, RsvgSolidColor alternate)
 {
     RsvgPaintServer *result = g_new (RsvgPaintServer, 1);
 
     result->refcnt = 1;
     result->type = RSVG_PAINT_SERVER_IRI;
-    result->core.iri = iri;
+    result->core.iri = g_new0 (RsvgPaintServerIri, 1);
+    result->core.iri->iri_str = iri;
+    result->core.iri->has_alternate = has_alternate;
+    result->core.iri->alternate = alternate;
 
     return result;
 }
 
+static gboolean
+parse_current_color_or_argb (const char *str, RsvgSolidColor *dest)
+{
+    if (!strcmp (str, "currentColor")) {
+        dest->currentcolor = TRUE;
+        dest->argb = 0;
+        return TRUE;
+    } else {
+        gboolean parsed;
+
+        dest->currentcolor = FALSE;
+        dest->argb = rsvg_css_parse_color (str, &parsed);
+
+        return parsed;
+    }
+}
+
 /**
  * rsvg_paint_server_parse:
  * @str: The SVG paint specification string to parse.
@@ -86,21 +106,34 @@ rsvg_paint_server_iri (char *iri)
  *   on error.
  **/
 RsvgPaintServer *
-rsvg_paint_server_parse (gboolean * inherit, const char *str)
+rsvg_paint_server_parse (gboolean *inherit, const char *str)
 {
     char *name;
+    const char *rest;
     guint32 argb;
+
     if (inherit != NULL)
-        *inherit = 1;
+        *inherit = TRUE;
+
     if (str == NULL || !strcmp (str, "none"))
         return NULL;
 
-    name = rsvg_get_url_string (str, NULL);
+    name = rsvg_get_url_string (str, &rest);
     if (name) {
-        return rsvg_paint_server_iri (name);
+        RsvgSolidColor alternate;
+        gboolean has_alternate;
+
+        while (*rest && g_ascii_isspace (*rest)) {
+            rest++;
+        }
+
+        has_alternate = parse_current_color_or_argb (rest, &alternate);
+
+        return rsvg_paint_server_iri (name, has_alternate, alternate);
     } else if (!strcmp (str, "inherit")) {
+        /* Do the fallback to black here; don't let the caller do it via inheritance */
         if (inherit != NULL)
-            *inherit = 0;
+            *inherit = FALSE;
         return rsvg_paint_server_solid (0);
     } else if (!strcmp (str, "currentColor")) {
         RsvgPaintServer *ps;
@@ -140,8 +173,10 @@ rsvg_paint_server_unref (RsvgPaintServer * ps)
     if (--ps->refcnt == 0) {
         if (ps->type == RSVG_PAINT_SERVER_SOLID)
             g_free (ps->core.color);
-        else if (ps->type == RSVG_PAINT_SERVER_IRI)
+        else if (ps->type == RSVG_PAINT_SERVER_IRI) {
+            g_free (ps->core.iri->iri_str);
             g_free (ps->core.iri);
+        }
         g_free (ps);
     }
 }
diff --git a/rsvg-paint-server.h b/rsvg-paint-server.h
index e260fba..df63d7a 100644
--- a/rsvg-paint-server.h
+++ b/rsvg-paint-server.h
@@ -173,9 +173,9 @@ void pattern_destroy (Pattern *pattern);
 
 /* Implemented in rust/src/pattern.rs */
 G_GNUC_INTERNAL
-void pattern_resolve_fallbacks_and_set_pattern (Pattern        *pattern,
-                                                RsvgDrawingCtx *draw_ctx,
-                                                RsvgBbox        bbox);
+gboolean pattern_resolve_fallbacks_and_set_pattern (Pattern        *pattern,
+                                                    RsvgDrawingCtx *draw_ctx,
+                                                    RsvgBbox        bbox);
 
 G_GNUC_INTERNAL
 Pattern *rsvg_pattern_node_to_rust_pattern (RsvgNode *node);
@@ -192,10 +192,17 @@ struct _RsvgSolidColor {
 typedef struct _RsvgSolidColor RsvgPaintServerColor;
 typedef enum _RsvgPaintServerType RsvgPaintServerType;
 typedef union _RsvgPaintServerCore RsvgPaintServerCore;
+typedef struct _RsvgPaintServerIri RsvgPaintServerIri;
+
+struct _RsvgPaintServerIri {
+    char *iri_str;
+    gboolean has_alternate;
+    RsvgSolidColor alternate;
+};
 
 union _RsvgPaintServerCore {
     RsvgSolidColor *color;
-    char *iri;
+    RsvgPaintServerIri *iri;
 };
 
 enum _RsvgPaintServerType {
@@ -211,7 +218,7 @@ struct _RsvgPaintServer {
 
 /* Create a new paint server based on a specification string. */
 G_GNUC_INTERNAL
-RsvgPaintServer            *rsvg_paint_server_parse    (gboolean * inherit, const char *str);
+RsvgPaintServer            *rsvg_paint_server_parse    (gboolean *inherit, const char *str);
 G_GNUC_INTERNAL
 void                 rsvg_paint_server_ref      (RsvgPaintServer * ps);
 G_GNUC_INTERNAL
diff --git a/rust/src/pattern.rs b/rust/src/pattern.rs
index e0fc383..ea08818 100644
--- a/rust/src/pattern.rs
+++ b/rust/src/pattern.rs
@@ -202,9 +202,13 @@ impl FallbackSource for NodeFallbackSource {
 
 fn set_pattern_on_draw_context (pattern: &Pattern,
                                 draw_ctx: *mut RsvgDrawingCtx,
-                                bbox:     &RsvgBbox) {
+                                bbox:     &RsvgBbox) -> bool {
     assert! (pattern.is_resolved ());
 
+    if !pattern_node_has_children (pattern.c_node) {
+        return false;
+    }
+
     let obj_bbox              = pattern.obj_bbox.unwrap ();
     let obj_cbbox             = pattern.obj_cbbox.unwrap ();
     let pattern_affine        = pattern.affine.unwrap ();
@@ -248,8 +252,9 @@ fn set_pattern_on_draw_context (pattern: &Pattern,
     let scaled_width = pattern_width * bbwscale;
     let scaled_height = pattern_height * bbhscale;
 
-    if scaled_width.abs () < DBL_EPSILON || scaled_height.abs () < DBL_EPSILON {
-        return
+    if scaled_width.abs () < DBL_EPSILON || scaled_height.abs () < DBL_EPSILON
+        || pw < 1 || ph < 1 {
+        return false;
     }
 
     scwscale = pw as f64 / scaled_width;
@@ -356,6 +361,8 @@ fn set_pattern_on_draw_context (pattern: &Pattern,
     surface_pattern.set_filter (Filter::Best);
 
     cr_save.set_source (&surface_pattern);
+
+    true
 }
 
 #[no_mangle]
@@ -416,7 +423,7 @@ pub unsafe extern fn pattern_destroy (raw_pattern: *mut Pattern) {
 #[no_mangle]
 pub extern fn pattern_resolve_fallbacks_and_set_pattern (raw_pattern: *mut Pattern,
                                                          draw_ctx:    *mut RsvgDrawingCtx,
-                                                         bbox:        RsvgBbox) {
+                                                         bbox:        RsvgBbox) -> bool {
     assert! (!raw_pattern.is_null ());
     let pattern: &mut Pattern = unsafe { &mut (*raw_pattern) };
 
@@ -426,5 +433,5 @@ pub extern fn pattern_resolve_fallbacks_and_set_pattern (raw_pattern: *mut Patte
 
     set_pattern_on_draw_context (&resolved,
                                  draw_ctx,
-                                 &bbox);
+                                 &bbox)
 }


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