[librsvg/rustification] length.rs: New file with RsvgLength and a public rsvg_length_parse()



commit 03d7716b4dd2e4e7cb04f58b14f00d2bff42c0d4
Author: Federico Mena Quintero <federico gnome org>
Date:   Thu Nov 10 19:12:21 2016 -0600

    length.rs: New file with RsvgLength and a public rsvg_length_parse()
    
    This is the parser for SVG length values, converted to Rust.
    
    The new rsvg_length_parse() replaces the old _rsvg_css_parse_length()
    throughout the code.

 rsvg-css.c          |  127 ----------------------
 rsvg-filter.c       |   44 ++++----
 rsvg-image.c        |   10 +-
 rsvg-marker.c       |   12 +-
 rsvg-mask.c         |   16 ++--
 rsvg-paint-server.c |   38 ++++----
 rsvg-private.h      |    8 ++-
 rsvg-shapes.c       |   42 ++++----
 rsvg-structure.c    |   32 +++---
 rsvg-styles.c       |   16 ++--
 rsvg-text.c         |   12 +-
 rust/src/length.rs  |  300 +++++++++++++++++++++++++++++++++++++++++++++++++++
 rust/src/lib.rs     |   10 ++
 13 files changed, 428 insertions(+), 239 deletions(-)
---
diff --git a/rsvg-css.c b/rsvg-css.c
index 2280776..e132d7a 100644
--- a/rsvg-css.c
+++ b/rsvg-css.c
@@ -91,133 +91,6 @@ rsvg_css_parse_vbox (const char *vbox)
     }
 }
 
-typedef enum _RelativeSize {
-    RELATIVE_SIZE_NORMAL,
-    RELATIVE_SIZE_SMALLER,
-    RELATIVE_SIZE_LARGER
-} RelativeSize;
-
-static double
-rsvg_css_parse_raw_length (const char *str, gboolean * in,
-                           gboolean * percent, gboolean * em, gboolean * ex, RelativeSize * relative_size)
-{
-    double length = 0.0;
-    char *p = NULL;
-
-    /* 
-     *  The supported CSS length unit specifiers are: 
-     *  em, ex, px, pt, pc, cm, mm, in, and %
-     */
-    *percent = FALSE;
-    *em = FALSE;
-    *ex = FALSE;
-    *relative_size = RELATIVE_SIZE_NORMAL;
-
-    length = g_ascii_strtod (str, &p);
-
-    if ((length == -HUGE_VAL || length == HUGE_VAL) && (ERANGE == errno)) {
-        /* todo: error condition - figure out how to best represent it */
-        return 0.0;
-    }
-
-    /* test for either pixels or no unit, which is assumed to be pixels */
-    if (p && *p && (strcmp (p, "px") != 0)) {
-        if (!strcmp (p, "pt")) {
-            length /= POINTS_PER_INCH;
-            *in = TRUE;
-        } else if (!strcmp (p, "in"))
-            *in = TRUE;
-        else if (!strcmp (p, "cm")) {
-            length /= CM_PER_INCH;
-            *in = TRUE;
-        } else if (!strcmp (p, "mm")) {
-            length /= MM_PER_INCH;
-            *in = TRUE;
-        } else if (!strcmp (p, "pc")) {
-            length /= PICA_PER_INCH;
-            *in = TRUE;
-        } else if (!strcmp (p, "em"))
-            *em = TRUE;
-        else if (!strcmp (p, "ex"))
-            *ex = TRUE;
-        else if (!strcmp (p, "%")) {
-            *percent = TRUE;
-            length *= 0.01;
-        } else {
-            double pow_factor = 0.0;
-
-            if (!g_ascii_strcasecmp (p, "larger")) {
-                *relative_size = RELATIVE_SIZE_LARGER;
-                return 0.0;
-            } else if (!g_ascii_strcasecmp (p, "smaller")) {
-                *relative_size = RELATIVE_SIZE_SMALLER;
-                return 0.0;
-            } else if (!g_ascii_strcasecmp (p, "xx-small")) {
-                pow_factor = -3.0;
-            } else if (!g_ascii_strcasecmp (p, "x-small")) {
-                pow_factor = -2.0;
-            } else if (!g_ascii_strcasecmp (p, "small")) {
-                pow_factor = -1.0;
-            } else if (!g_ascii_strcasecmp (p, "medium")) {
-                pow_factor = 0.0;
-            } else if (!g_ascii_strcasecmp (p, "large")) {
-                pow_factor = 1.0;
-            } else if (!g_ascii_strcasecmp (p, "x-large")) {
-                pow_factor = 2.0;
-            } else if (!g_ascii_strcasecmp (p, "xx-large")) {
-                pow_factor = 3.0;
-            } else {
-                return 0.0;
-            }
-
-            length = 12.0 * pow (1.2, pow_factor) / POINTS_PER_INCH;
-            *in = TRUE;
-        }
-    }
-
-    return length;
-}
-
-/* https://www.w3.org/TR/SVG/types.html#DataTypeLength
- * https://www.w3.org/TR/2008/REC-CSS2-20080411/syndata.html#length-units
- *
- * Lengths have units.  When they need to be need resolved to
- * units in the user's coordinate system, some unit types
- * need to know if they are horizontal/vertical/both.  For example,
- * a some_object.width="50%" is 50% with respect to the current
- * viewport's width.  In this case, the @dir argument is used
- * when _rsvg_css_normalize_length() needs to know to what the
- * length refers.
- */
-RsvgLength
-_rsvg_css_parse_length (const char *str, LengthDir dir)
-{
-    RsvgLength out;
-    gboolean percent, em, ex, in;
-    RelativeSize relative_size = RELATIVE_SIZE_NORMAL;
-    percent = em = ex = in = FALSE;
-
-    out.length = rsvg_css_parse_raw_length (str, &in, &percent, &em, &ex, &relative_size);
-    if (percent)
-        out.unit = LENGTH_UNIT_PERCENT;
-    else if (em)
-        out.unit = LENGTH_UNIT_FONT_EM;
-    else if (ex)
-        out.unit = LENGTH_UNIT_FONT_EX;
-    else if (in)
-        out.unit = LENGTH_UNIT_INCH;
-    else if (relative_size == RELATIVE_SIZE_LARGER)
-        out.unit = LENGTH_UNIT_RELATIVE_LARGER;
-    else if (relative_size == RELATIVE_SIZE_SMALLER)
-        out.unit = LENGTH_UNIT_RELATIVE_SMALLER;
-    else
-        out.unit = LENGTH_UNIT_DEFAULT;
-
-    out.dir = dir;
-
-    return out;
-}
-
 /* Recursive evaluation of all parent elements regarding absolute font size */
 double
 _rsvg_css_normalize_font_size (RsvgState * state, RsvgDrawingCtx * ctx)
diff --git a/rsvg-filter.c b/rsvg-filter.c
index 5fcc079..b099daf 100644
--- a/rsvg-filter.c
+++ b/rsvg-filter.c
@@ -126,19 +126,19 @@ filter_primitive_set_x_y_width_height_atts (RsvgFilterPrimitive *prim, RsvgPrope
     const char *value;
 
     if ((value = rsvg_property_bag_lookup (atts, "x"))) {
-        prim->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+        prim->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         prim->x_specified = TRUE;
     }
     if ((value = rsvg_property_bag_lookup (atts, "y"))) {
-        prim->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+        prim->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         prim->y_specified = TRUE;
     }
     if ((value = rsvg_property_bag_lookup (atts, "width"))) {
-        prim->width = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+        prim->width = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         prim->width_specified = TRUE;
     }
     if ((value = rsvg_property_bag_lookup (atts, "height"))) {
-        prim->height = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+        prim->height = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         prim->height_specified = TRUE;
     }
 }
@@ -791,13 +791,13 @@ rsvg_filter_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
                 filter->primitiveunits = userSpaceOnUse;
         }
         if ((value = rsvg_property_bag_lookup (atts, "x")))
-            filter->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            filter->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "y")))
-            filter->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            filter->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "width")))
-            filter->width = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            filter->width = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "height")))
-            filter->height = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            filter->height = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
     }
 }
 
@@ -815,10 +815,10 @@ rsvg_new_filter (void)
     _rsvg_node_init (&filter->super, RSVG_NODE_TYPE_FILTER);
     filter->filterunits = objectBoundingBox;
     filter->primitiveunits = userSpaceOnUse;
-    filter->x = _rsvg_css_parse_length ("-10%", LENGTH_DIR_HORIZONTAL);
-    filter->y = _rsvg_css_parse_length ("-10%", LENGTH_DIR_VERTICAL);
-    filter->width = _rsvg_css_parse_length ("120%", LENGTH_DIR_HORIZONTAL);
-    filter->height = _rsvg_css_parse_length ("120%", LENGTH_DIR_VERTICAL);
+    filter->x = rsvg_length_parse ("-10%", LENGTH_DIR_HORIZONTAL);
+    filter->y = rsvg_length_parse ("-10%", LENGTH_DIR_VERTICAL);
+    filter->width = rsvg_length_parse ("120%", LENGTH_DIR_HORIZONTAL);
+    filter->height = rsvg_length_parse ("120%", LENGTH_DIR_VERTICAL);
     filter->super.set_atts = rsvg_filter_set_atts;
     return (RsvgNode *) filter;
 }
@@ -2057,9 +2057,9 @@ rsvg_filter_primitive_offset_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPr
         filter_primitive_set_x_y_width_height_atts ((RsvgFilterPrimitive *) filter, atts);
 
         if ((value = rsvg_property_bag_lookup (atts, "dx")))
-            filter->dx = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            filter->dx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "dy")))
-            filter->dy = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            filter->dy = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
     }
 }
 
@@ -2071,8 +2071,8 @@ rsvg_new_filter_primitive_offset (void)
     _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_OFFSET);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
-    filter->dx = _rsvg_css_parse_length ("0", LENGTH_DIR_HORIZONTAL);
-    filter->dy = _rsvg_css_parse_length ("0", LENGTH_DIR_VERTICAL);
+    filter->dx = rsvg_length_parse ("0", LENGTH_DIR_HORIZONTAL);
+    filter->dy = rsvg_length_parse ("0", LENGTH_DIR_VERTICAL);
     filter->super.render = rsvg_filter_primitive_offset_render;
     filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_offset_set_atts;
@@ -4334,17 +4334,17 @@ rsvg_node_light_source_set_atts (RsvgNode * self,
         if ((value = rsvg_property_bag_lookup (atts, "limitingConeAngle")))
             data->limitingconeAngle = rsvg_css_parse_angle (value) / 180.0 * M_PI;
         if ((value = rsvg_property_bag_lookup (atts, "x")))
-            data->x = data->pointsAtX = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            data->x = data->pointsAtX = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "y")))
-            data->y = data->pointsAtX = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            data->y = data->pointsAtX = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "z")))
-            data->z = data->pointsAtX = _rsvg_css_parse_length (value, LENGTH_DIR_BOTH);
+            data->z = data->pointsAtX = rsvg_length_parse (value, LENGTH_DIR_BOTH);
         if ((value = rsvg_property_bag_lookup (atts, "pointsAtX")))
-            data->pointsAtX = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            data->pointsAtX = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "pointsAtY")))
-            data->pointsAtY = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            data->pointsAtY = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "pointsAtZ")))
-            data->pointsAtZ = _rsvg_css_parse_length (value, LENGTH_DIR_BOTH);
+            data->pointsAtZ = rsvg_length_parse (value, LENGTH_DIR_BOTH);
         if ((value = rsvg_property_bag_lookup (atts, "specularExponent")))
             data->specularExponent = g_ascii_strtod (value, NULL);
     }
diff --git a/rsvg-image.c b/rsvg-image.c
index 69127e3..1c2b438 100644
--- a/rsvg-image.c
+++ b/rsvg-image.c
@@ -203,13 +203,13 @@ rsvg_node_image_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
 
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "x")))
-            image->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            image->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "y")))
-            image->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            image->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "width")))
-            image->w = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            image->w = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "height")))
-            image->h = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            image->h = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         /* path is used by some older adobe illustrator versions */
         if ((value = rsvg_property_bag_lookup (atts, "path"))
             || (value = rsvg_property_bag_lookup (atts, "xlink:href"))) {
@@ -244,7 +244,7 @@ rsvg_new_image (void)
     g_assert (image->super.state);
     image->surface = NULL;
     image->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
-    image->x = image->y = image->w = image->h = _rsvg_css_parse_length ("0", LENGTH_DIR_BOTH);
+    image->x = image->y = image->w = image->h = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
     image->super.free = rsvg_node_image_free;
     image->super.draw = rsvg_node_image_draw;
     image->super.set_atts = rsvg_node_image_set_atts;
diff --git a/rsvg-marker.c b/rsvg-marker.c
index 8d333df..83c34bd 100644
--- a/rsvg-marker.c
+++ b/rsvg-marker.c
@@ -67,13 +67,13 @@ rsvg_node_marker_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag *
         if ((value = rsvg_property_bag_lookup (atts, "viewBox")))
             marker->vbox = rsvg_css_parse_vbox (value);
         if ((value = rsvg_property_bag_lookup (atts, "refX")))
-            marker->refX = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            marker->refX = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "refY")))
-            marker->refY = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            marker->refY = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "markerWidth")))
-            marker->width = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            marker->width = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "markerHeight")))
-            marker->height = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            marker->height = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "orient"))) {
             if (!strcmp (value, "auto"))
                 marker->orientAuto = TRUE;
@@ -101,8 +101,8 @@ rsvg_new_marker (void)
     marker->orient = 0;
     marker->orientAuto = FALSE;
     marker->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
-    marker->refX = marker->refY = _rsvg_css_parse_length ("0", LENGTH_DIR_BOTH);
-    marker->width = marker->height = _rsvg_css_parse_length ("3", LENGTH_DIR_BOTH);
+    marker->refX = marker->refY = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
+    marker->width = marker->height = rsvg_length_parse ("3", LENGTH_DIR_BOTH);
     marker->bbox = TRUE;
     marker->vbox.active = FALSE;
     marker->super.set_atts = rsvg_node_marker_set_atts;
diff --git a/rsvg-mask.c b/rsvg-mask.c
index 90ccd76..7976186 100644
--- a/rsvg-mask.c
+++ b/rsvg-mask.c
@@ -50,13 +50,13 @@ rsvg_mask_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
                 mask->contentunits = userSpaceOnUse;
         }
         if ((value = rsvg_property_bag_lookup (atts, "x")))
-            mask->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            mask->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "y")))
-            mask->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            mask->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "width")))
-            mask->width = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            mask->width = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "height")))
-            mask->height = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            mask->height = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "id")))
             id = value;
 
@@ -76,10 +76,10 @@ rsvg_new_mask (void)
     _rsvg_node_init (&mask->super, RSVG_NODE_TYPE_MASK);
     mask->maskunits = objectBoundingBox;
     mask->contentunits = userSpaceOnUse;
-    mask->x = _rsvg_css_parse_length ("0", LENGTH_DIR_HORIZONTAL);
-    mask->y = _rsvg_css_parse_length ("0", LENGTH_DIR_VERTICAL);
-    mask->width = _rsvg_css_parse_length ("1", LENGTH_DIR_HORIZONTAL);
-    mask->height = _rsvg_css_parse_length ("1", LENGTH_DIR_VERTICAL);
+    mask->x = rsvg_length_parse ("0", LENGTH_DIR_HORIZONTAL);
+    mask->y = rsvg_length_parse ("0", LENGTH_DIR_VERTICAL);
+    mask->width = rsvg_length_parse ("1", LENGTH_DIR_HORIZONTAL);
+    mask->height = rsvg_length_parse ("1", LENGTH_DIR_VERTICAL);
     mask->super.set_atts = rsvg_mask_set_atts;
     return &mask->super;
 }
diff --git a/rsvg-paint-server.c b/rsvg-paint-server.c
index ebf24bb..d86aff6 100644
--- a/rsvg-paint-server.c
+++ b/rsvg-paint-server.c
@@ -160,7 +160,7 @@ rsvg_stop_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "offset"))) {
             /* either a number [0,1] or a percentage */
-            RsvgLength length = _rsvg_css_parse_length (value, LENGTH_DIR_BOTH);
+            RsvgLength length = rsvg_length_parse (value, LENGTH_DIR_BOTH);
             offset = _rsvg_css_hand_normalize_length (&length, rsvg_dpi_percentage (ctx), 1., 0.);
 
             if (offset < 0.)
@@ -209,19 +209,19 @@ rsvg_linear_gradient_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBa
 
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "x1"))) {
-            grad->x1 = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            grad->x1 = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
             grad->hasx1 = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "y1"))) {
-            grad->y1 = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            grad->y1 = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
             grad->hasy1 = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "x2"))) {
-            grad->x2 = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            grad->x2 = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
             grad->hasx2 = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "y2"))) {
-            grad->y2 = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            grad->y2 = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
             grad->hasy2 = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "spreadMethod"))) {
@@ -269,9 +269,9 @@ rsvg_new_linear_gradient (void)
     _rsvg_node_init (&grad->super, RSVG_NODE_TYPE_LINEAR_GRADIENT);
     cairo_matrix_init_identity (&grad->affine);
     grad->has_current_color = FALSE;
-    grad->x1 = _rsvg_css_parse_length ("0", LENGTH_DIR_HORIZONTAL);
-    grad->y1 = grad->y2 = _rsvg_css_parse_length ("0", LENGTH_DIR_VERTICAL);
-    grad->x2 = _rsvg_css_parse_length ("1", LENGTH_DIR_HORIZONTAL);
+    grad->x1 = rsvg_length_parse ("0", LENGTH_DIR_HORIZONTAL);
+    grad->y1 = grad->y2 = rsvg_length_parse ("0", LENGTH_DIR_VERTICAL);
+    grad->x2 = rsvg_length_parse ("1", LENGTH_DIR_HORIZONTAL);
     grad->fallback = NULL;
     grad->obj_bbox = TRUE;
     grad->spread = CAIRO_EXTEND_PAD;
@@ -290,27 +290,27 @@ rsvg_radial_gradient_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBa
 
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "cx"))) {
-            grad->cx = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            grad->cx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
             grad->hascx = TRUE;
             if (!grad->hasfx)
                 grad->fx = grad->cx;
         }
         if ((value = rsvg_property_bag_lookup (atts, "cy"))) {
-            grad->cy = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            grad->cy = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
             grad->hascy = TRUE;
             if (!grad->hasfy)
                 grad->fy = grad->cy;
         }
         if ((value = rsvg_property_bag_lookup (atts, "r"))) {
-            grad->r = _rsvg_css_parse_length (value, LENGTH_DIR_BOTH);
+            grad->r = rsvg_length_parse (value, LENGTH_DIR_BOTH);
             grad->hasr = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "fx"))) {
-            grad->fx = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            grad->fx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
             grad->hasfx = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "fy"))) {
-            grad->fy = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            grad->fy = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
             grad->hasfy = TRUE;
         }
         g_free (grad->fallback);
@@ -361,7 +361,7 @@ rsvg_new_radial_gradient (void)
     grad->obj_bbox = TRUE;
     grad->spread = CAIRO_EXTEND_PAD;
     grad->fallback = NULL;
-    grad->cx = grad->cy = grad->r = grad->fx = grad->fy = _rsvg_css_parse_length ("0.5", LENGTH_DIR_BOTH);
+    grad->cx = grad->cy = grad->r = grad->fx = grad->fy = rsvg_length_parse ("0.5", LENGTH_DIR_BOTH);
     grad->super.free = rsvg_radial_gradient_free;
     grad->super.set_atts = rsvg_radial_gradient_set_atts;
     grad->hascx = grad->hascy = grad->hasfx = grad->hasfy = grad->hasr = grad->hasbbox =
@@ -381,19 +381,19 @@ rsvg_pattern_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts
             pattern->hasvbox = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "x"))) {
-            pattern->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            pattern->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
             pattern->hasx = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "y"))) {
-            pattern->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            pattern->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
             pattern->hasy = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "width"))) {
-            pattern->width = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            pattern->width = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
             pattern->haswidth = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "height"))) {
-            pattern->height = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            pattern->height = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
             pattern->hasheight = TRUE;
         }
         g_free (pattern->fallback);
@@ -440,7 +440,7 @@ rsvg_new_pattern (void)
     cairo_matrix_init_identity (&pattern->affine);
     pattern->obj_bbox = TRUE;
     pattern->obj_cbbox = FALSE;
-    pattern->x = pattern->y = pattern->width = pattern->height = _rsvg_css_parse_length ("0", 
LENGTH_DIR_BOTH);
+    pattern->x = pattern->y = pattern->width = pattern->height = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
     pattern->fallback = NULL;
     pattern->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
     pattern->vbox.active = FALSE;
diff --git a/rsvg-private.h b/rsvg-private.h
index 1c9f2f5..e6062aa 100644
--- a/rsvg-private.h
+++ b/rsvg-private.h
@@ -242,6 +242,7 @@ _rsvg_render_check_type (RsvgRender *render,
 #define _RSVG_RENDER_CIC(render, render_type, RenderCType) \
   ((RenderCType*) _rsvg_render_check_type ((render), (render_type)))
 
+/* Keep this in sync with rust/src/length.rs:LengthUnit */
 typedef enum {
     LENGTH_UNIT_DEFAULT,
     LENGTH_UNIT_PERCENT,
@@ -252,12 +253,14 @@ typedef enum {
     LENGTH_UNIT_RELATIVE_SMALLER
 } LengthUnit;
 
+/* Keep this in sync with rust/src/length.rs:LengthDir */
 typedef enum {
     LENGTH_DIR_HORIZONTAL,
     LENGTH_DIR_VERTICAL,
     LENGTH_DIR_BOTH
 } LengthDir;
 
+/* Keep this in sync with rust/src/length.rs:RsvgLength */
 typedef struct {
     double length;
     LengthUnit unit;
@@ -425,8 +428,11 @@ double _rsvg_css_hand_normalize_length  (const RsvgLength * in, gdouble pixels_p
 double _rsvg_css_normalize_font_size    (RsvgState * state, RsvgDrawingCtx * ctx);
 G_GNUC_INTERNAL
 double _rsvg_css_accumulate_baseline_shift (RsvgState * state, RsvgDrawingCtx * ctx);
+
+/* Implemented in rust/src/length.rs */
 G_GNUC_INTERNAL
-RsvgLength _rsvg_css_parse_length (const char *str, LengthDir dir);
+RsvgLength rsvg_length_parse (const char *str, LengthDir dir);
+
 G_GNUC_INTERNAL
 void _rsvg_push_view_box    (RsvgDrawingCtx * ctx, double w, double h);
 G_GNUC_INTERNAL
diff --git a/rsvg-shapes.c b/rsvg-shapes.c
index 6c46798..12ba717 100644
--- a/rsvg-shapes.c
+++ b/rsvg-shapes.c
@@ -253,13 +253,13 @@ _rsvg_node_line_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
 
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "x1")))
-            line->x1 = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            line->x1 = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "y1")))
-            line->y1 = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            line->y1 = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "x2")))
-            line->x2 = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            line->x2 = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "y2")))
-            line->y2 = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            line->y2 = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "class")))
             klazz = value;
         if ((value = rsvg_property_bag_lookup (atts, "id")))
@@ -301,7 +301,7 @@ rsvg_new_line (void)
     _rsvg_node_init (&line->super, RSVG_NODE_TYPE_LINE);
     line->super.draw = _rsvg_node_line_draw;
     line->super.set_atts = _rsvg_node_line_set_atts;
-    line->x1 = line->x2 = line->y1 = line->y2 = _rsvg_css_parse_length ("0", LENGTH_DIR_BOTH);
+    line->x1 = line->x2 = line->y1 = line->y2 = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
     return &line->super;
 }
 
@@ -322,19 +322,19 @@ _rsvg_node_rect_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
     /* FIXME: negative w/h/rx/ry is an error, per http://www.w3.org/TR/SVG11/shapes.html#RectElement */
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "x")))
-            rect->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            rect->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "y")))
-            rect->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            rect->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "width")))
-            rect->w = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            rect->w = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "height")))
-            rect->h = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            rect->h = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "rx"))) {
-            rect->rx = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            rect->rx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
             rect->got_rx = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "ry"))) {
-            rect->ry = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            rect->ry = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
             rect->got_ry = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "class")))
@@ -493,7 +493,7 @@ rsvg_new_rect (void)
     _rsvg_node_init (&rect->super, RSVG_NODE_TYPE_RECT);
     rect->super.draw = _rsvg_node_rect_draw;
     rect->super.set_atts = _rsvg_node_rect_set_atts;
-    rect->x = rect->y = rect->w = rect->h = rect->rx = rect->ry = _rsvg_css_parse_length ("0", 
LENGTH_DIR_BOTH);
+    rect->x = rect->y = rect->w = rect->h = rect->rx = rect->ry = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
     rect->got_rx = rect->got_ry = FALSE;
     return &rect->super;
 }
@@ -513,11 +513,11 @@ _rsvg_node_circle_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag *
 
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "cx")))
-            circle->cx = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            circle->cx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "cy")))
-            circle->cy = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            circle->cy = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "r")))
-            circle->r = _rsvg_css_parse_length (value, LENGTH_DIR_BOTH);
+            circle->r = rsvg_length_parse (value, LENGTH_DIR_BOTH);
         if ((value = rsvg_property_bag_lookup (atts, "class")))
             klazz = value;
         if ((value = rsvg_property_bag_lookup (atts, "id")))
@@ -584,7 +584,7 @@ rsvg_new_circle (void)
     _rsvg_node_init (&circle->super, RSVG_NODE_TYPE_CIRCLE);
     circle->super.draw = _rsvg_node_circle_draw;
     circle->super.set_atts = _rsvg_node_circle_set_atts;
-    circle->cx = circle->cy = circle->r = _rsvg_css_parse_length ("0", LENGTH_DIR_BOTH);
+    circle->cx = circle->cy = circle->r = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
     return &circle->super;
 }
 
@@ -603,13 +603,13 @@ _rsvg_node_ellipse_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag
 
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "cx")))
-            ellipse->cx = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            ellipse->cx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "cy")))
-            ellipse->cy = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            ellipse->cy = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "rx")))
-            ellipse->rx = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            ellipse->rx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "ry")))
-            ellipse->ry = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            ellipse->ry = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "class")))
             klazz = value;
         if ((value = rsvg_property_bag_lookup (atts, "id")))
@@ -677,6 +677,6 @@ rsvg_new_ellipse (void)
     _rsvg_node_init (&ellipse->super, RSVG_NODE_TYPE_ELLIPSE);
     ellipse->super.draw = _rsvg_node_ellipse_draw;
     ellipse->super.set_atts = _rsvg_node_ellipse_set_atts;
-    ellipse->cx = ellipse->cy = ellipse->rx = ellipse->ry = _rsvg_css_parse_length ("0", LENGTH_DIR_BOTH);
+    ellipse->cx = ellipse->cy = ellipse->rx = ellipse->ry = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
     return &ellipse->super;
 }
diff --git a/rsvg-structure.c b/rsvg-structure.c
index cf9e9c5..a74d91a 100644
--- a/rsvg-structure.c
+++ b/rsvg-structure.c
@@ -319,17 +319,17 @@ rsvg_node_svg_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * att
         if ((value = rsvg_property_bag_lookup (atts, "preserveAspectRatio")))
             svg->preserve_aspect_ratio = rsvg_css_parse_aspect_ratio (value);
         if ((value = rsvg_property_bag_lookup (atts, "width")))
-            svg->w = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            svg->w = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "height")))
-            svg->h = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            svg->h = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         /* 
          * x & y attributes have no effect on outermost svg
          * http://www.w3.org/TR/SVG/struct.html#SVGElement 
          */
         if (self->parent && (value = rsvg_property_bag_lookup (atts, "x")))
-            svg->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            svg->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if (self->parent && (value = rsvg_property_bag_lookup (atts, "y")))
-            svg->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            svg->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
 
         /*
          * style element is not loaded yet here, so we need to store those attribues
@@ -373,10 +373,10 @@ rsvg_new_svg (void)
     _rsvg_node_init (&svg->super, RSVG_NODE_TYPE_SVG);
     svg->vbox.active = FALSE;
     svg->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
-    svg->x = _rsvg_css_parse_length ("0", LENGTH_DIR_HORIZONTAL);
-    svg->y = _rsvg_css_parse_length ("0", LENGTH_DIR_VERTICAL);
-    svg->w = _rsvg_css_parse_length ("100%", LENGTH_DIR_HORIZONTAL);
-    svg->h = _rsvg_css_parse_length ("100%", LENGTH_DIR_VERTICAL);
+    svg->x = rsvg_length_parse ("0", LENGTH_DIR_HORIZONTAL);
+    svg->y = rsvg_length_parse ("0", LENGTH_DIR_VERTICAL);
+    svg->w = rsvg_length_parse ("100%", LENGTH_DIR_HORIZONTAL);
+    svg->h = rsvg_length_parse ("100%", LENGTH_DIR_VERTICAL);
     svg->super.draw = rsvg_node_svg_draw;
     svg->super.free = _rsvg_svg_free;
     svg->super.set_atts = rsvg_node_svg_set_atts;
@@ -401,13 +401,13 @@ rsvg_node_use_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * att
     use = (RsvgNodeUse *) self;
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "x")))
-            use->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            use->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "y")))
-            use->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            use->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "width")))
-            use->w = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+            use->w = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         if ((value = rsvg_property_bag_lookup (atts, "height")))
-            use->h = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+            use->h = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         if ((value = rsvg_property_bag_lookup (atts, "class")))
             klazz = value;
         if ((value = rsvg_property_bag_lookup (atts, "id")))
@@ -431,10 +431,10 @@ rsvg_new_use (void)
     use->super.draw = rsvg_node_use_draw;
     use->super.free = rsvg_node_use_free;
     use->super.set_atts = rsvg_node_use_set_atts;
-    use->x = _rsvg_css_parse_length ("0", LENGTH_DIR_HORIZONTAL);
-    use->y = _rsvg_css_parse_length ("0", LENGTH_DIR_VERTICAL);
-    use->w = _rsvg_css_parse_length ("0", LENGTH_DIR_HORIZONTAL);
-    use->h = _rsvg_css_parse_length ("0", LENGTH_DIR_VERTICAL);
+    use->x = rsvg_length_parse ("0", LENGTH_DIR_HORIZONTAL);
+    use->y = rsvg_length_parse ("0", LENGTH_DIR_VERTICAL);
+    use->w = rsvg_length_parse ("0", LENGTH_DIR_HORIZONTAL);
+    use->h = rsvg_length_parse ("0", LENGTH_DIR_VERTICAL);
     use->link = NULL;
     return (RsvgNode *) use;
 }
diff --git a/rsvg-styles.c b/rsvg-styles.c
index c5089e0..9ac401b 100644
--- a/rsvg-styles.c
+++ b/rsvg-styles.c
@@ -132,7 +132,7 @@ rsvg_state_init (RsvgState * state)
     state->fill = rsvg_paint_server_parse (NULL, "#000");
     state->fill_opacity = 0xff;
     state->stroke_opacity = 0xff;
-    state->stroke_width = _rsvg_css_parse_length ("1", LENGTH_DIR_BOTH);
+    state->stroke_width = rsvg_length_parse ("1", LENGTH_DIR_BOTH);
     state->miter_limit = 4;
     state->cap = CAIRO_LINE_CAP_BUTT;
     state->join = CAIRO_LINE_JOIN_MITER;
@@ -146,7 +146,7 @@ rsvg_state_init (RsvgState * state)
     state->flood_opacity = 255;
 
     state->font_family = g_strdup (RSVG_DEFAULT_FONT);
-    state->font_size = _rsvg_css_parse_length ("12.0", LENGTH_DIR_BOTH);
+    state->font_size = rsvg_length_parse ("12.0", LENGTH_DIR_BOTH);
     state->font_style = PANGO_STYLE_NORMAL;
     state->font_variant = PANGO_VARIANT_NORMAL;
     state->font_weight = PANGO_WEIGHT_NORMAL;
@@ -155,7 +155,7 @@ rsvg_state_init (RsvgState * state)
     state->text_gravity = PANGO_GRAVITY_SOUTH;
     state->unicode_bidi = UNICODE_BIDI_NORMAL;
     state->text_anchor = TEXT_ANCHOR_START;
-    state->letter_spacing = _rsvg_css_parse_length ("0.0", LENGTH_DIR_HORIZONTAL);
+    state->letter_spacing = rsvg_length_parse ("0.0", LENGTH_DIR_HORIZONTAL);
     state->visible = TRUE;
     state->cond_true = TRUE;
     state->filter = NULL;
@@ -704,7 +704,7 @@ rsvg_parse_style_pair (RsvgHandle * ctx,
 
         rsvg_paint_server_unref (stroke);
     } else if (g_str_equal (name, "stroke-width")) {
-        state->stroke_width = _rsvg_css_parse_length (value, LENGTH_DIR_BOTH);
+        state->stroke_width = rsvg_length_parse (value, LENGTH_DIR_BOTH);
         state->has_stroke_width = TRUE;
     } else if (g_str_equal (name, "stroke-linecap")) {
         state->has_cap = TRUE;
@@ -730,7 +730,7 @@ rsvg_parse_style_pair (RsvgHandle * ctx,
         else
             g_warning (_("unknown line join style %s\n"), value);
     } else if (g_str_equal (name, "font-size")) {
-        state->font_size = _rsvg_css_parse_length (value, LENGTH_DIR_BOTH);
+        state->font_size = rsvg_length_parse (value, LENGTH_DIR_BOTH);
         state->has_font_size = TRUE;
     } else if (g_str_equal (name, "font-family")) {
         char *save = g_strdup (rsvg_css_parse_font_family (value, &state->has_font_family));
@@ -817,7 +817,7 @@ rsvg_parse_style_pair (RsvgHandle * ctx,
         }
     } else if (g_str_equal (name, "letter-spacing")) {
        state->has_letter_spacing = TRUE;
-       state->letter_spacing = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+       state->letter_spacing = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
     } else if (g_str_equal (name, "stop-color")) {
         if (!g_str_equal (value, "inherit")) {
             state->stop_color = rsvg_css_parse_color (value, &state->has_stop_color);
@@ -844,7 +844,7 @@ rsvg_parse_style_pair (RsvgHandle * ctx,
         state->miter_limit = g_ascii_strtod (value, NULL);
     } else if (g_str_equal (name, "stroke-dashoffset")) {
         state->has_dashoffset = TRUE;
-        state->dash.offset = _rsvg_css_parse_length (value, LENGTH_DIR_BOTH);
+        state->dash.offset = rsvg_length_parse (value, LENGTH_DIR_BOTH);
         if (state->dash.offset.length < 0.)
             state->dash.offset.length = 0.;
     } else if (g_str_equal (name, "shape-rendering")) {
@@ -1712,7 +1712,7 @@ rsvg_state_reinherit_top (RsvgDrawingCtx * ctx, RsvgState * state, int dominate)
     RsvgState *current;
 
     if (dominate == 3)
-        return;
+        g_assert_not_reached ();
 
     current = rsvg_current_state (ctx);
     /*This is a special domination mode for patterns, the transform
diff --git a/rsvg-text.c b/rsvg-text.c
index 45ad3f3..a807d90 100644
--- a/rsvg-text.c
+++ b/rsvg-text.c
@@ -131,17 +131,17 @@ set_text_common_atts (RsvgNodeText *text, RsvgPropertyBag * atts)
     const char *value;
 
     if ((value = rsvg_property_bag_lookup (atts, "x"))) {
-        text->x = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+        text->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
         text->x_specified = TRUE;
     }
     if ((value = rsvg_property_bag_lookup (atts, "y"))) {
-        text->y = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+        text->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
         text->y_specified = TRUE;
     }
     if ((value = rsvg_property_bag_lookup (atts, "dx")))
-        text->dx = _rsvg_css_parse_length (value, LENGTH_DIR_HORIZONTAL);
+        text->dx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
     if ((value = rsvg_property_bag_lookup (atts, "dy")))
-        text->dy = _rsvg_css_parse_length (value, LENGTH_DIR_VERTICAL);
+        text->dy = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
 }
 
 
@@ -320,7 +320,7 @@ rsvg_new_text (void)
     _rsvg_node_init (&text->super, RSVG_NODE_TYPE_TEXT);
     text->super.draw = _rsvg_node_text_draw;
     text->super.set_atts = _rsvg_node_text_set_atts;
-    text->x = text->y = text->dx = text->dy = _rsvg_css_parse_length ("0", LENGTH_DIR_BOTH);
+    text->x = text->y = text->dx = text->dy = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
     return &text->super;
 }
 
@@ -412,7 +412,7 @@ rsvg_new_tspan (void)
     text = g_new0 (RsvgNodeText, 1);
     _rsvg_node_init (&text->super, RSVG_NODE_TYPE_TSPAN);
     text->super.set_atts = _rsvg_node_tspan_set_atts;
-    text->dx = text->dy = _rsvg_css_parse_length ("0", LENGTH_DIR_BOTH);
+    text->dx = text->dy = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
     return &text->super;
 }
 
diff --git a/rust/src/length.rs b/rust/src/length.rs
new file mode 100644
index 0000000..9dd222b
--- /dev/null
+++ b/rust/src/length.rs
@@ -0,0 +1,300 @@
+extern crate libc;
+extern crate glib;
+
+use self::glib::translate::*;
+
+use strtod::*;
+
+/* Keep this in sync with ../../rsvg-private.h:LengthUnit */
+#[repr(C)]
+#[derive(Debug, PartialEq)]
+pub enum LengthUnit {
+    Default,
+    Percent,
+    FontEm,
+    FontEx,
+    Inch,
+    RelativeLarger,
+    RelativeSmaller
+}
+
+/* Keep this in sync with ../../rsvg-private.h:LengthDir */
+#[repr(C)]
+#[derive(Debug, PartialEq)]
+pub enum LengthDir {
+    Horizontal,
+    Vertical,
+    Both
+}
+
+/* This is *not* an opaque struct; it is actually visible to the C code.  It is so
+ * that the remaining C code can create RsvgLength values as part of existing
+ * structures or objects, without allocations on the heap.
+ */
+/* Keep this in sync with ../../rsvg-private.h:RsvgLength */
+#[repr(C)]
+#[derive(Debug, PartialEq)]
+pub struct RsvgLength {
+    length: f64,
+    unit: LengthUnit,
+    dir: LengthDir
+}
+
+pub enum RsvgDrawingCtx {}
+
+const POINTS_PER_INCH: f64 = 72.0;
+const CM_PER_INCH:     f64 = 2.54;
+const MM_PER_INCH:     f64 = 25.4;
+const PICA_PER_INCH:   f64 = 6.0;
+
+fn compute_named_size (name: &str) -> f64 {
+    let power: f64;
+
+    match name {
+        "xx-small" => { power = -3.0; },
+        "x-small"  => { power = -2.0; },
+        "small"    => { power = -1.0; },
+        "medium"   => { power = 0.0; },
+        "large"    => { power = 1.0; },
+        "x-large"  => { power = 2.0; },
+        "xx-large" => { power = 3.0; },
+        _          => { return 0.0; }
+    }
+
+    12.0 * 1.2f64.powf (power) / POINTS_PER_INCH
+}
+
+#[no_mangle]
+pub extern fn rsvg_length_parse (string: *const libc::c_char, dir: LengthDir) -> RsvgLength {
+    let my_string = unsafe { &String::from_glib_none (string) };
+
+    parse_length (my_string, dir)
+}
+
+/* https://www.w3.org/TR/SVG/types.html#DataTypeLength
+ * https://www.w3.org/TR/2008/REC-CSS2-20080411/syndata.html#length-units
+ *
+ * Lengths have units.  When they need to be need resolved to
+ * units in the user's coordinate system, some unit types
+ * need to know if they are horizontal/vertical/both.  For example,
+ * a some_object.width="50%" is 50% with respect to the current
+ * viewport's width.  In this case, the @dir argument is used
+ * when _rsvg_css_normalize_length() needs to know to what the
+ * length refers.
+ */
+pub fn parse_length (string: &str, dir: LengthDir) -> RsvgLength {
+    let unit: LengthUnit;
+
+    let (mut value, rest) = strtod (string);
+    println! ("strtod yielded {}, {}", value, rest);
+
+    match rest.as_ref () {
+        "%" => {
+            value *= 0.01; // normalize to [0, 1]
+            unit = LengthUnit::Percent;
+        },
+
+        "em" => {
+            unit = LengthUnit::FontEm;
+        },
+
+        "ex" => {
+            unit = LengthUnit::FontEx;
+        },
+
+        "pt" => {
+            value /= POINTS_PER_INCH;
+            unit = LengthUnit::Inch;
+        },
+
+        "in" => {
+            unit = LengthUnit::Inch;
+        },
+
+        "cm" => {
+            value /= CM_PER_INCH;
+            unit = LengthUnit::Inch;
+        },
+
+        "mm" => {
+            value /= MM_PER_INCH;
+            unit = LengthUnit::Inch;
+        },
+
+        "pc" => {
+            value /= PICA_PER_INCH;
+            unit = LengthUnit::Inch;
+        },
+
+        "larger" => {
+            unit = LengthUnit::RelativeLarger;
+        },
+
+        "smaller" => {
+            unit = LengthUnit::RelativeSmaller;
+        },
+
+        "xx-small" |
+        "x-small" |
+        "small" |
+        "medium" |
+        "large" |
+        "x-large" |
+        "xx-large" => {
+            value = compute_named_size (rest);
+            unit = LengthUnit::Inch;
+        },
+
+        _ => {
+            unit = LengthUnit::Default;
+        }
+    }
+
+    RsvgLength {
+        length: value,
+        unit: unit,
+        dir: dir
+    }
+}
+
+#[no_mangle]
+pub extern fn rsvg_length_normalize (length: *const RsvgLength, draw_ctx: *const RsvgDrawingCtx) -> f64 {
+    unimplemented! ();
+}
+
+#[no_mangle]
+pub extern fn rsvg_length_hand_normalize (length: *const RsvgLength,
+                                          pixels_per_inch: f64,
+                                          width_or_height: f64,
+                                          font_size: f64) -> f64 {
+    unimplemented! ();
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn parses_default () {
+        assert_eq! (parse_length ("42", LengthDir::Horizontal),
+                    RsvgLength {
+                        length: 42.0,
+                        unit:   LengthUnit::Default,
+                        dir:    LengthDir::Horizontal});
+
+        assert_eq! (parse_length ("-42px", LengthDir::Horizontal),
+                    RsvgLength {
+                        length: -42.0,
+                        unit:   LengthUnit::Default,
+                        dir:    LengthDir::Horizontal});
+    }
+
+    #[test]
+    fn parses_percent () {
+        assert_eq! (parse_length ("50.0%", LengthDir::Horizontal),
+                    RsvgLength {
+                        length: 0.5,
+                        unit:   LengthUnit::Percent,
+                        dir:    LengthDir::Horizontal});
+    }
+
+    #[test]
+    fn parses_font_em () {
+        assert_eq! (parse_length ("22.5em", LengthDir::Vertical),
+                    RsvgLength {
+                        length: 22.5,
+                        unit:   LengthUnit::FontEm,
+                        dir:    LengthDir::Vertical });
+    }
+
+    #[test]
+    fn parses_font_ex () {
+        assert_eq! (parse_length ("22.5ex", LengthDir::Vertical),
+                    RsvgLength {
+                        length: 22.5,
+                        unit:   LengthUnit::FontEx,
+                        dir:    LengthDir::Vertical });
+    }
+
+    #[test]
+    fn parses_physical_units () {
+        assert_eq! (parse_length ("72pt", LengthDir::Both),
+                    RsvgLength {
+                        length: 1.0,
+                        unit:   LengthUnit::Inch,
+                        dir:    LengthDir::Both });
+
+        assert_eq! (parse_length ("-22.5in", LengthDir::Both),
+                    RsvgLength {
+                        length: -22.5,
+                        unit:   LengthUnit::Inch,
+                        dir:    LengthDir::Both });
+
+        assert_eq! (parse_length ("-25.4cm", LengthDir::Both),
+                    RsvgLength {
+                        length: -10.0,
+                        unit:   LengthUnit::Inch,
+                        dir:    LengthDir::Both });
+
+        assert_eq! (parse_length ("254mm", LengthDir::Both),
+                    RsvgLength {
+                        length: 10.0,
+                        unit:   LengthUnit::Inch,
+                        dir:    LengthDir::Both });
+
+        assert_eq! (parse_length ("60pc", LengthDir::Both),
+                    RsvgLength {
+                        length: 10.0,
+                        unit:   LengthUnit::Inch,
+                        dir:    LengthDir::Both });
+    }
+
+    #[test]
+    fn parses_relative_larger () {
+        assert_eq! (parse_length ("larger", LengthDir::Vertical),
+                    RsvgLength {
+                        length: 0.0,
+                        unit:   LengthUnit::RelativeLarger,
+                        dir:    LengthDir::Vertical });
+    }
+
+    #[test]
+    fn parses_relative_smaller () {
+        assert_eq! (parse_length ("smaller", LengthDir::Vertical),
+                    RsvgLength {
+                        length: 0.0,
+                        unit:   LengthUnit::RelativeSmaller,
+                        dir:    LengthDir::Vertical });
+    }
+
+    #[test]
+    fn parses_named_sizes () {
+        let names = vec![ "xx-small",
+                           "x-small",
+                           "small",
+                           "medium",
+                           "large",
+                           "x-large",
+                           "xx-large" ];
+
+        let mut previous_value: Option<f64> = None;
+
+        // Just ensure that the values are progressively larger; don't
+        // enforce a particular sequence.
+
+        for name in names {
+            let length = parse_length (name, LengthDir::Both);
+
+            assert_eq! (length.unit, LengthUnit::Inch);
+            assert_eq! (length.dir, LengthDir::Both);
+
+            if let Some (v) = previous_value {
+                assert! (length.length > v);
+            } else {
+                previous_value = Some (length.length);
+            }
+
+        }
+
+    }
+}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 76e2e77..200f5d6 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -17,7 +17,17 @@ pub use path_parser::{
     rsvg_path_parser_from_str_into_builder
 };
 
+pub use length::{
+    LengthUnit,
+    LengthDir,
+    RsvgLength,
+    rsvg_length_parse,
+    rsvg_length_normalize,
+    rsvg_length_hand_normalize,
+};
+
 mod path_builder;
 mod path_parser;
 mod marker;
 mod strtod;
+mod length;


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