structure.rs: Completely move NodeUse, NodeSymbol to Rust

rsvg-structure.c is no more.  Yay!
parent 573a3ee7
......@@ -41,7 +41,6 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_SOURCES = \
rsvg-mask.c \
rsvg-mask.h \
rsvg-shapes.h \
rsvg-structure.c \
rsvg-structure.h \
rsvg-styles.c \
rsvg-styles.h \
......@@ -76,21 +75,22 @@ RUST_SOURCES = \
rust/src/cnode.rs \
rust/src/drawing_ctx.rs \
rust/src/error.rs \
rust/src/handle.rs \
rust/src/gradient.rs \
rust/src/handle.rs \
rust/src/length.rs \
rust/src/lib.rs \
rust/src/marker.rs \
rust/src/node.rs \
rust/src/paint_server.rs \
rust/src/pt.rs \
rust/src/parsers.rs \
rust/src/parse_transform.lalrpop \
rust/src/path_builder.rs \
rust/src/path_parser.rs \
rust/src/pattern.rs \
rust/src/property_bag.rs \
rust/src/state.rs \
rust/src/pt.rs \
rust/src/shapes.rs \
rust/src/state.rs \
rust/src/structure.rs \
rust/src/transform.rs \
rust/src/util.rs \
......
......@@ -327,13 +327,13 @@ static const NodeCreator node_creators[] = {
{ "subImageRef", FALSE, rsvg_new_image },
{ "svg", TRUE, rsvg_node_svg_new },
{ "switch", TRUE, rsvg_node_switch_new },
{ "symbol", TRUE, rsvg_new_symbol },
{ "symbol", TRUE, rsvg_node_symbol_new },
{ "text", TRUE, rsvg_new_text },
/* "textPath", TRUE, */
/* "title", TRUE, */
{ "tref", TRUE, rsvg_new_tref },
{ "tspan", TRUE, rsvg_new_tspan },
{ "use", TRUE, rsvg_new_use },
{ "use", TRUE, rsvg_node_use_new },
/* "view", FALSE, */
/* "vkern", FALSE, */
};
......
......@@ -32,7 +32,7 @@
#include <string.h>
#include "rsvg.h"
#include "rsvg-private.h"
#include "rsvg-defs.h"
#include "rsvg-cairo.h"
#include "rsvg-cairo-draw.h"
#include "rsvg-cairo-render.h"
......
......@@ -32,6 +32,7 @@
#include <glib.h>
#include "rsvg.h"
#include "rsvg-private.h"
G_BEGIN_DECLS
......
......@@ -35,6 +35,7 @@
#include <errno.h>
#include "rsvg-css.h"
#include "rsvg-io.h"
#include "rsvg-styles.h"
cairo_surface_t *
rsvg_cairo_surface_new_from_href (RsvgHandle *handle,
......
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 sts=4 ts=4 expandtab: */
/*
rsvg-structure.c: Rsvg's structual elements
Copyright (C) 2000 Eazel, Inc.
Copyright (C) 2002 - 2005 Dom Lachowicz <cinamod@hotmail.com>
Copyright (C) 2003 - 2005 Caleb Moore <c.moore@student.unsw.edu.au>
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.
Authors: Raph Levien <raph@artofcode.com>,
Dom Lachowicz <cinamod@hotmail.com>,
Caleb Moore <c.moore@student.unsw.edu.au>
*/
#include "rsvg-structure.h"
#include "rsvg-image.h"
#include "rsvg-css.h"
#include "string.h"
#include <stdio.h>
typedef struct _RsvgNodeUse RsvgNodeUse;
typedef struct _RsvgNodeSymbol RsvgNodeSymbol;
struct _RsvgNodeSymbol {
guint32 preserve_aspect_ratio;
RsvgViewBox vbox;
};
struct _RsvgNodeUse {
char *link;
RsvgLength x, y, w, h;
};
static gboolean
rsvg_node_is_ancestor (RsvgNode *potential_ancestor, RsvgNode *descendant)
{
descendant = rsvg_node_ref (descendant);
while (descendant != NULL) {
RsvgNode *parent;
if (rsvg_node_is_same (potential_ancestor, descendant)) {
descendant = rsvg_node_unref (descendant);
return TRUE;
}
parent = rsvg_node_get_parent (descendant);
descendant = rsvg_node_unref (descendant);
descendant = parent;
}
return FALSE;
}
static void
rsvg_node_use_draw (RsvgNode *node, gpointer impl, RsvgDrawingCtx *ctx, int dominate)
{
RsvgNodeUse *use = impl;
RsvgNode *child;
RsvgState *state;
cairo_matrix_t affine;
double x, y, w, h;
x = rsvg_length_normalize (&use->x, ctx);
y = rsvg_length_normalize (&use->y, ctx);
w = rsvg_length_normalize (&use->w, ctx);
h = rsvg_length_normalize (&use->h, ctx);
rsvg_state_reinherit_top (ctx, rsvg_node_get_state (node), dominate);
if (use->link == NULL)
return;
child = rsvg_drawing_ctx_acquire_node (ctx, use->link);
if (!child)
return;
else if (rsvg_node_is_ancestor (child, node)) { /* or, if we're <use>'ing ourself */
rsvg_drawing_ctx_release_node (ctx, child);
return;
}
state = rsvg_current_state (ctx);
if (rsvg_node_get_type (child) != RSVG_NODE_TYPE_SYMBOL) {
cairo_matrix_init_translate (&affine, x, y);
cairo_matrix_multiply (&state->affine, &affine, &state->affine);
rsvg_push_discrete_layer (ctx);
rsvg_drawing_ctx_draw_node_from_stack (ctx, child, 1);
rsvg_pop_discrete_layer (ctx);
} else {
RsvgNodeSymbol *symbol = rsvg_rust_cnode_get_impl (child);
if (symbol->vbox.active) {
rsvg_aspect_ratio_compute (symbol->preserve_aspect_ratio,
symbol->vbox.rect.width,
symbol->vbox.rect.height,
&x, &y, &w, &h);
cairo_matrix_init_translate (&affine, x, y);
cairo_matrix_multiply (&state->affine, &affine, &state->affine);
cairo_matrix_init_scale (&affine, w / symbol->vbox.rect.width, h / symbol->vbox.rect.height);
cairo_matrix_multiply (&state->affine, &affine, &state->affine);
cairo_matrix_init_translate (&affine, -symbol->vbox.rect.x, -symbol->vbox.rect.y);
cairo_matrix_multiply (&state->affine, &affine, &state->affine);
rsvg_drawing_ctx_push_view_box (ctx, symbol->vbox.rect.width, symbol->vbox.rect.height);
rsvg_push_discrete_layer (ctx);
if (!state->overflow || (!state->has_overflow && rsvg_node_get_state (child)->overflow))
rsvg_drawing_ctx_add_clipping_rect (ctx, symbol->vbox.rect.x, symbol->vbox.rect.y,
symbol->vbox.rect.width, symbol->vbox.rect.height);
} else {
cairo_matrix_init_translate (&affine, x, y);
cairo_matrix_multiply (&state->affine, &affine, &state->affine);
rsvg_push_discrete_layer (ctx);
}
rsvg_state_push (ctx);
rsvg_node_draw_children (child, ctx, 1);
rsvg_state_pop (ctx);
rsvg_pop_discrete_layer (ctx);
if (symbol->vbox.active)
rsvg_drawing_ctx_pop_view_box (ctx);
}
rsvg_drawing_ctx_release_node (ctx, child);
}
static void
rsvg_node_use_free (gpointer impl)
{
RsvgNodeUse *use = impl;
g_free (use->link);
g_free (use);
}
static void
rsvg_node_use_set_atts (RsvgNode *node, gpointer impl, RsvgHandle *handle, RsvgPropertyBag *atts)
{
RsvgNodeUse *use = impl;
const char *value;
if ((value = rsvg_property_bag_lookup (atts, "x")))
use->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
if ((value = rsvg_property_bag_lookup (atts, "y")))
use->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
if ((value = rsvg_property_bag_lookup (atts, "width")))
use->w = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
if ((value = rsvg_property_bag_lookup (atts, "height")))
use->h = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
if ((value = rsvg_property_bag_lookup (atts, "xlink:href"))) {
g_free (use->link);
use->link = g_strdup (value);
}
}
RsvgNode *
rsvg_new_use (const char *element_name, RsvgNode *parent)
{
RsvgNodeUse *use;
use = g_new0 (RsvgNodeUse, 1);
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 rsvg_rust_cnode_new (RSVG_NODE_TYPE_USE,
parent,
rsvg_state_new (),
use,
rsvg_node_use_set_atts,
rsvg_node_use_draw,
rsvg_node_use_free);
}
static void
rsvg_node_symbol_set_atts (RsvgNode *node, gpointer impl, RsvgHandle *handle, RsvgPropertyBag *atts)
{
RsvgNodeSymbol *symbol = impl;
const char *value;
if ((value = rsvg_property_bag_lookup (atts, "viewBox")))
symbol->vbox = rsvg_css_parse_vbox (value);
if ((value = rsvg_property_bag_lookup (atts, "preserveAspectRatio")))
symbol->preserve_aspect_ratio = rsvg_aspect_ratio_parse (value);
}
static void
rsvg_node_symbol_draw (RsvgNode *node, gpointer impl, RsvgDrawingCtx *ctx, int dominate)
{
/* nothing; this gets drawn specially in rsvg_node_use_draw() */
}
static void
rsvg_node_symbol_free (gpointer impl)
{
RsvgNodeSymbol *symbol = impl;
g_free (symbol);
}
RsvgNode *
rsvg_new_symbol (const char *element_name, RsvgNode *parent)
{
RsvgNodeSymbol *symbol;
symbol = g_new0 (RsvgNodeSymbol, 1);
symbol->vbox.active = FALSE;
symbol->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
return rsvg_rust_cnode_new (RSVG_NODE_TYPE_SYMBOL,
parent,
rsvg_state_new (),
symbol,
rsvg_node_symbol_set_atts,
rsvg_node_symbol_draw,
rsvg_node_symbol_free);
}
......@@ -31,16 +31,9 @@
#define RSVG_STRUCTURE_H
#include "rsvg-private.h"
#include "rsvg-defs.h"
#include "rsvg-styles.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
RsvgNode *rsvg_new_use (const char *element_name, RsvgNode *parent);
G_GNUC_INTERNAL
RsvgNode *rsvg_new_symbol (const char *element_name, RsvgNode *parent);
/* Implemented in rust/src/structure.rs */
G_GNUC_INTERNAL
RsvgNode *rsvg_node_group_new (const char *element_name, RsvgNode *parent);
......@@ -57,6 +50,14 @@ RsvgNode *rsvg_node_switch_new (const char *element_name, RsvgNode *parent);
G_GNUC_INTERNAL
RsvgNode *rsvg_node_svg_new (const char *element_name, RsvgNode *parent);
/* Implemented in rust/src/structure.rs */
G_GNUC_INTERNAL
RsvgNode *rsvg_node_use_new (const char *element_name, RsvgNode *parent);
/* Implemented in rust/src/structure.rs */
G_GNUC_INTERNAL
RsvgNode *rsvg_node_symbol_new (const char *element_name, RsvgNode *parent);
/* Implemented in rust/src/structure.rs */
G_GNUC_INTERNAL
void rsvg_node_svg_get_size (RsvgNode *node, RsvgLength *out_width, RsvgLength *out_height);
......
......@@ -84,10 +84,12 @@ pub use structure::{
rsvg_node_group_new,
rsvg_node_defs_new,
rsvg_node_switch_new,
rsvg_node_symbol_new,
rsvg_node_svg_new,
rsvg_node_svg_get_size,
rsvg_node_svg_get_view_box,
rsvg_node_svg_apply_atts,
rsvg_node_use_new,
};
pub use transform::{
......
enum State {
Whitespace,
IntegralPart,
FractionalPart,
ExponentSign,
Exponent
}
pub fn strtod (string: &str) -> (f64, &str) {
let mut state = State::Whitespace;
let mut value: f64 = 0.0;
let mut sign: f64 = 1.0;
let mut fraction: f64 = 1.0;
let mut exponent_sign: f64 = 1.0;
let mut exponent: f64 = 0.0;
let mut last_pos: usize = 0;
for (pos, c) in string.chars ().enumerate () {
last_pos = pos;
match state {
State::Whitespace => {
if c.is_whitespace () {
continue;
} else if c == '+' || c == '-' {
if c == '-' {
sign = -1.0;
}
state = State::IntegralPart;
} else if c.is_digit (10) {
state = State::IntegralPart;
value = (c as i32 - '0' as i32) as f64;
} else if c == '.' {
state = State::FractionalPart;
} else {
break;
}
},
State::IntegralPart => {
if c.is_digit (10) {
value = value * 10.0 + (c as i32 - '0' as i32) as f64;
} else if c == '.' {
state = State::FractionalPart;
} else if c == 'e' || c == 'E' {
state = State::ExponentSign;
} else {
break;
}
},
State::FractionalPart => {
if c.is_digit (10) {
fraction *= 0.1;
value += fraction * (c as i32 - '0' as i32) as f64;
} else if c == 'e' || c == 'E' {
state = State::ExponentSign;
} else {
break;
}
},
State::ExponentSign => {
if c == '+' || c == '-' {
if c == '-' {
exponent_sign = -1.0;
}
state = State::Exponent;
} else if c.is_digit (10) {
exponent = (c as i32 - '0' as i32) as f64;
state = State::Exponent;
} else {
// un-consume the 'e' or 'E', so strtod("42em") produces the expected value.
last_pos -= 1;
break;
}
},
State::Exponent => {
if c.is_digit (10) {
exponent = exponent * 10.0 + (c as i32 - '0' as i32) as f64;
} else {
break;
}
}
}
last_pos += 1;
}
// return tuple with the value and the non-matching slice
(sign * value * 10.0f64.powf (exponent * exponent_sign),
&string[last_pos .. ])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn handles_empty_string () {
let str = "";
assert_eq! (strtod (str), (0.0f64, &str[0..]));
}
#[test]
fn handles_integer () {
let str = "-123foo";
assert_eq! (strtod (str), (-123.0f64, &str[4..]));
let str = "12345";
assert_eq! (strtod (str), (12345.0f64, &str[5..]));
}
#[test]
fn handles_float () {
let str = "-123.25";
assert_eq! (strtod (str), (-123.25f64, &str[7..]));
let str = "123.25bar";
assert_eq! (strtod (str), (123.25f64, &str[6..]));
}
#[test]
fn handles_dot_numbers () {
let str = "-.25foo";
assert_eq! (strtod (str), (-0.25f64, &str[4..]));
let str = ".25bar";
assert_eq! (strtod (str), (0.25f64, &str[3..]));
let str = "22.5em";
assert_eq! (strtod (str), (22.5, "em"));
let str = "22.5e1Ex";
assert_eq! (strtod (str), (225.0, "Ex"));
let str = "22.5Ex";
assert_eq! (strtod (str), (22.5, "Ex"));
}
#[test]
fn handles_dot () {
let str = "-.";
assert_eq! (strtod (str), (-0.0, &str[2..]));
let str = ".bar";
assert_eq! (strtod (str), (0.0, &str[1..]));
}
#[test]
fn handles_exponent () {
let str = "-123.45e2foo";
assert_eq! (strtod (str), (-12345.0, &str[9..]));
let str = "123.45E2";
assert_eq! (strtod (str), (12345.0, &str[8..]));
let str = "123.45E10";
assert_eq! (strtod (str), (1234500000000.0, &str[9..]));
}
#[test]
fn handles_negative_exponent () {
let str = "-123.25e-2";
assert_eq! (strtod (str), (-1.2325, &str[10..]));
let str = "123.25E-2bar";
assert_eq! (strtod (str), (1.2325, &str[9..]));
}
}
......@@ -4,6 +4,7 @@ extern crate libc;
use self::glib::translate::*;
use std::cell::RefCell;
use std::cell::Cell;
use std::ptr;
......@@ -256,6 +257,212 @@ impl Drop for NodeSvg {
}
}
/***** NodeUse *****/
struct NodeUse {
link: RefCell<Option<String>>,
x: Cell<RsvgLength>,
y: Cell<RsvgLength>,
w: Cell<Option<RsvgLength>>,
h: Cell<Option<RsvgLength>>,
}
impl NodeUse {
fn new () -> NodeUse {
NodeUse {
link: RefCell::new (None),
x: Cell::new (RsvgLength::default ()),
y: Cell::new (RsvgLength::default ()),
w: Cell::new (None),
h: Cell::new (None)
}
}
}
impl NodeTrait for NodeUse {
fn set_atts (&self, _: &RsvgNode, _: *const RsvgHandle, pbag: *const RsvgPropertyBag) -> NodeResult {
*self.link.borrow_mut () = property_bag::lookup (pbag, "xlink:href");
self.x.set (property_bag::length_or_default (pbag, "x", LengthDir::Horizontal)?);
self.y.set (property_bag::length_or_default (pbag, "y", LengthDir::Vertical)?);
let opt_w = property_bag::length_or_none (pbag, "width", LengthDir::Horizontal)?;
match opt_w {
Some (w) => {
if length_is_negative (&w) {
return Err (NodeError::value_error ("width", "Must not be negative"));
} else {
self.w.set (Some (w));
}
},
None => {
self.w.set (None);
}
}
let opt_h = property_bag::length_or_none (pbag, "height", LengthDir::Vertical)?;
match opt_h {
Some (h) => {
if length_is_negative (&h) {
return Err (NodeError::value_error ("height", "Must not be negative"));
} else {
self.h.set (Some (h));
}
},
None => {
self.h.set (None);
}
}
Ok (())
}
fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
let link = self.link.borrow ();
if link.is_none () {
return;
}
let raw_child = drawing_ctx::acquire_node (draw_ctx, link.as_ref ().unwrap ());
if raw_child.is_null () {
return;
}
let child: &RsvgNode = unsafe { &*raw_child };
if Node::is_ancestor (node.clone (), child.clone ()) {
// or, if we're <use>'ing ourselves
drawing_ctx::release_node (draw_ctx, raw_child);
return;
}
let nx = self.x.get ().normalize (draw_ctx);
let ny = self.y.get ().normalize (draw_ctx);
let nw = self.w.get ().map (|l| l.normalize (draw_ctx));
let nh = self.h.get ().map (|l| l.normalize (draw_ctx));
// width or height set to 0 disables rendering of the element
// https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
if let Some (w) = nw {
if double_equals (w, 0.0) {
drawing_ctx::release_node (draw_ctx, raw_child);
return;
}
}
if let Some (h) = nh {
if double_equals (h, 0.0) {
drawing_ctx::release_node (draw_ctx, raw_child);
return;
}
}
drawing_ctx::state_reinherit_top (draw_ctx, node.get_state (), dominate);
let state = drawing_ctx::get_current_state (draw_ctx);
if child.get_type () != NodeType::Symbol {
let mut affine = drawing_ctx::get_current_state_affine (draw_ctx);
affine.translate (nx, ny);
drawing_ctx::set_current_state_affine (draw_ctx, affine);
let boxed_child = box_node (child.clone ());
drawing_ctx::push_discrete_layer (draw_ctx);
drawing_ctx::draw_node_from_stack (draw_ctx, boxed_child, 1);
drawing_ctx::pop_discrete_layer (draw_ctx);
} else {
child.with_impl (|symbol: &NodeSymbol| {
let vbox = symbol.vbox.get ();
if vbox.is_active () {
// If attributes ‘width’ and/or ‘height’ are not specified, [...] use values of '100%' for these attributes.
// From https://www.w3.org/TR/SVG/struct.html#UseElement in "If the ‘use’ element references a ‘symbol’ element"
let nw = nw.unwrap_or (RsvgLength::parse ("100%", LengthDir::Horizontal).unwrap ().normalize (draw_ctx));
let nh = nh.unwrap_or (RsvgLength::parse ("100%", LengthDir::Vertical).unwrap ().normalize (draw_ctx));
let (x, y, w, h) = symbol.preserve_aspect_ratio.get ().compute (vbox.rect.width, vbox.rect.height,
nx, ny, nw, nh);
let mut affine = drawing_ctx::get_current_state_affine (draw_ctx);
affine.translate (x, y);
affine.scale (w / vbox.rect.width, h / vbox.rect.height);
affine.translate (-vbox.rect.x, -vbox.rect.y);
drawing_ctx::set_current_state_affine (draw_ctx, affine);
drawing_ctx::push_view_box (draw_ctx, vbox.rect.width, vbox.rect.height);
drawing_ctx::push_discrete_layer (draw_ctx);
if !drawing_ctx::state_is_overflow (state) || (!drawing_ctx::state_has_overflow (state)
&& drawing_ctx::state_is_overflow (child.get_state ())) {
drawing_ctx::add_clipping_rect (draw_ctx, vbox.rect.x, vbox.rect.y, vbox.rect.width, vbox.rect.height);
}
} else {
let mut affine = drawing_ctx::get_current_state_affine (draw_ctx);
affine.translate (nx, ny);
drawing_ctx::set_current_state_affine (draw_ctx, affine);
drawing_ctx::push_discrete_layer (draw_ctx);
}
drawing_ctx::state_push (draw_ctx);
child.draw_children (draw_ctx, 1);
drawing_ctx::state_pop (draw_ctx);
drawing_ctx::pop_discrete_layer (draw_ctx);
if vbox.is_active () {
drawing_ctx::pop_view_box (draw_ctx);
}
});
}
drawing_ctx::release_node (draw_ctx, raw_child);
}
fn get_c_impl (&self) -> *const RsvgCNodeImpl {
unreachable! ();
}
}
/***** NodeSymbol *****/
struct NodeSymbol {
preserve_aspect_ratio: Cell<AspectRatio>,
vbox: Cell<RsvgViewBox>
}
impl NodeSymbol {
fn new () -> NodeSymbol {
NodeSymbol {
preserve_aspect_ratio: Cell::new (AspectRatio::default ()),
vbox: Cell::new (RsvgViewBox::default ())
}
}
}