Commit d06e52ad authored by Federico Mena Quintero's avatar Federico Mena Quintero

Merge branch 'xml-processing'

The dodgy stack of XML handlers we had in C is gone now; it's replaced
with a much simpler scheme in Rust code.

Pending things:

- Actually call libxml2 from Rust

- Handle XML entities in Rust

- Handle XML processing instructions in Rust

- Move the rsvg_handle_acquire_() machinery to Rust; this is basically
  rsvg-io.c and the allow_load() stuff.
parents 9dfa5755 ea63ebfc
Pipeline #41205 failed with stages
in 24 minutes and 44 seconds
This diff is collapsed.
......@@ -110,13 +110,15 @@ RUST_SRC = \
rsvg_internals/src/state.rs \
rsvg_internals/src/stop.rs \
rsvg_internals/src/structure.rs \
rsvg_internals/src/style.rs \
rsvg_internals/src/text.rs \
rsvg_internals/src/transform.rs \
rsvg_internals/src/tree.rs \
rsvg_internals/src/unitinterval.rs \
rsvg_internals/src/util.rs \
rsvg_internals/src/viewbox.rs \
rsvg_internals/src/viewport.rs
rsvg_internals/src/viewport.rs \
rsvg_internals/src/xml.rs
RUST_EXTRA = \
Cargo.lock \
......
This diff is collapsed.
......@@ -31,6 +31,9 @@ RsvgLoad *rsvg_load_new (RsvgHandle *handle, gboolean unlimited_size) G_GNUC_WAR
G_GNUC_INTERNAL
void rsvg_load_free (RsvgLoad *load);
G_GNUC_INTERNAL
gboolean rsvg_load_handle_xml_xinclude (RsvgHandle *handle, const char *url);
G_GNUC_INTERNAL
RsvgTree *rsvg_load_steal_tree (RsvgLoad *load) G_GNUC_WARN_UNUSED_RESULT;
......
......@@ -120,56 +120,17 @@ struct RsvgHandlePrivate {
#endif
};
/* Implemented in rust/src/node.rs */
/* Call this as newref = rsvg_node_ref (node); You don't own the node anymore, just the newref! */
G_GNUC_INTERNAL
RsvgNode *rsvg_node_ref (RsvgNode *node) G_GNUC_WARN_UNUSED_RESULT;
/* Implemented in rust/src/node.rs */
/* Call this as node = rsvg_node_unref (node); Then node will be NULL and you don't own it anymore! */
G_GNUC_INTERNAL
RsvgNode *rsvg_node_unref (RsvgNode *node) G_GNUC_WARN_UNUSED_RESULT;
/* Implemented in rust/src/node.rs
*
* Returns a new strong reference to the parent (or NULL); use rsvg_node_unref()
* when you are done.
*/
G_GNUC_INTERNAL
RsvgNode *rsvg_node_get_parent (RsvgNode *node) G_GNUC_WARN_UNUSED_RESULT;
/* Implemented in rust/src/node.rs */
G_GNUC_INTERNAL
void rsvg_node_add_child (RsvgNode *node, RsvgNode *child);
/* Implemented in rust/src/node.rs */
G_GNUC_INTERNAL
void rsvg_node_set_overridden_properties (RsvgNode *node);
typedef struct RsvgNodeChildrenIter *RsvgNodeChildrenIter;
/* Implemented in rust/src/node.rs */
G_GNUC_INTERNAL
RsvgNodeChildrenIter *rsvg_node_children_iter_begin (RsvgNode *node);
/* Implemented in rust/src/node.rs */
G_GNUC_INTERNAL
gboolean rsvg_node_children_iter_next (RsvgNodeChildrenIter *iter,
RsvgNode **out_child);
/* Implemented in rust/src/node.rs */
G_GNUC_INTERNAL
gboolean rsvg_node_children_iter_next_back (RsvgNodeChildrenIter *iter,
RsvgNode **out_child);
/* Implemented in rust/src/node.rs */
G_GNUC_INTERNAL
void rsvg_node_children_iter_end (RsvgNodeChildrenIter *iter);
/* Implemented in rsvg_internals/src/tree.rs */
G_GNUC_INTERNAL
RsvgTree *rsvg_tree_new (RsvgNode *root);
/* Implemented in rsvg_internals/src/tree.rs */
G_GNUC_INTERNAL
void rsvg_tree_free (RsvgTree *tree);
......
......@@ -22,6 +22,7 @@ phf_codegen = "0.7.21"
[dependencies]
libc = "0.2"
downcast-rs = "^1.0.0"
encoding = "0.2.33"
regex = "1"
itertools = "0.7.4"
pango = "0.4"
......
......@@ -86,7 +86,11 @@ struct DocHandlerData {
selector: *mut CRSelector,
}
fn parse_into_handle(handle: *mut RsvgHandle, buf: &str) {
pub fn parse_into_handle(handle: *mut RsvgHandle, buf: &str) {
if buf.len() == 0 {
return; // libcroco doesn't like empty strings :(
}
unsafe {
let handler_data = DocHandlerData {
handle,
......
......@@ -29,18 +29,27 @@ impl Defs {
self.nodes.entry(id.to_string()).or_insert(node.clone());
}
/// Returns a node from an URI reference, or `None`
///
/// This may return a node within the same RSVG handle, or a node in a secondary RSVG
/// handle that is referenced by the current one. If the element's id is not found,
/// returns `None`.
pub fn lookup(&mut self, name: &str) -> Option<&Rc<Node>> {
match name.rfind('#') {
None => None,
Some(p) if p == 0 => self.nodes.get(&name[1..]),
Some(p) => {
let handle = self.get_extern_handle(&name[..p]);
if handle.is_null() {
None
} else {
handle::get_defs(handle).nodes.get(&name[(p + 1)..])
if let Ok(reference) = Reference::parse(name) {
match reference {
Reference::PlainUri(_) => None,
Reference::FragmentId(fragment) => self.nodes.get(fragment),
Reference::UriWithFragmentId(uri, fragment) => {
let handle = self.get_extern_handle(uri);
if handle.is_null() {
None
} else {
handle::get_defs(handle).nodes.get(fragment)
}
}
}
} else {
None
}
}
......@@ -61,6 +70,39 @@ impl Defs {
}
}
/// Represents a possibly non-canonical URI with an optional fragment identifier
///
/// Sometimes in SVG element references (e.g. the `href` in the `<feImage>` element) we
/// must decide between referencing an external file, or using a plain fragment identifier
/// like `href="#foo"` as a reference to an SVG element in the same file as the one being
/// processes. This enum makes that distinction.
#[derive(Debug, PartialEq)]
pub enum Reference<'a> {
PlainUri(&'a str),
FragmentId(&'a str),
UriWithFragmentId(&'a str, &'a str),
}
impl<'a> Reference<'a> {
pub fn parse(s: &str) -> Result<Reference, ()> {
let (uri, fragment) = match s.rfind('#') {
None => (Some(s), None),
Some(p) if p == 0 => (None, Some(&s[1..])),
Some(p) => (Some(&s[..p]), Some(&s[(p + 1)..])),
};
match (uri, fragment) {
(None, Some(f)) if f.len() == 0 => Err(()),
(None, Some(f)) => Ok(Reference::FragmentId(f)),
(Some(u), _) if u.len() == 0 => Err(()),
(Some(u), None) => Ok(Reference::PlainUri(u)),
(Some(_u), Some(f)) if f.len() == 0 => Err(()),
(Some(u), Some(f)) => Ok(Reference::UriWithFragmentId(u, f)),
(_, _) => Err(()),
}
}
}
#[no_mangle]
pub extern "C" fn rsvg_defs_new(handle: *const RsvgHandle) -> *mut RsvgDefs {
Box::into_raw(Box::new(Defs::new(handle))) as *mut RsvgDefs
......@@ -92,3 +134,28 @@ pub extern "C" fn rsvg_defs_lookup(
None => ptr::null(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reference_kinds() {
assert_eq!(Reference::parse("uri"), Ok(Reference::PlainUri("uri")));
assert_eq!(
Reference::parse("#fragment"),
Ok(Reference::FragmentId("fragment"))
);
assert_eq!(
Reference::parse("uri#fragment"),
Ok(Reference::UriWithFragmentId("uri", "fragment"))
);
}
#[test]
fn reference_errors() {
assert!(Reference::parse("").is_err());
assert!(Reference::parse("#").is_err());
assert!(Reference::parse("uri#").is_err());
}
}
......@@ -5,6 +5,7 @@ use cairo::{self, ImageSurface, MatrixTrait, Pattern};
use aspect_ratio::AspectRatio;
use attributes::Attribute;
use defs::Reference;
use drawing_ctx::DrawingCtx;
use error::RenderingError;
use handle::{self, RsvgHandle};
......@@ -209,12 +210,11 @@ impl Filter for Image {
let bounds_builder = self.base.get_bounds(ctx);
let bounds = bounds_builder.into_irect(draw_ctx);
let output_surface = match self.render_node(ctx, draw_ctx, bounds, href) {
Err(FilterError::InvalidInput) => {
let output_surface = match Reference::parse(href).map_err(|_| FilterError::InvalidInput)? {
Reference::PlainUri(_) => {
self.render_external_image(ctx, draw_ctx, bounds_builder, href)?
}
Err(err) => return Err(err),
Ok(surface) => surface,
_ => self.render_node(ctx, draw_ctx, bounds, href)?,
};
Ok(FilterResult {
......
......@@ -40,12 +40,17 @@ extern "C" {
) -> *mut u8;
fn rsvg_handle_keep_image_data(handle: *const RsvgHandle) -> glib_sys::gboolean;
fn rsvg_load_handle_xml_xinclude(
handle: *mut RsvgHandle,
url: *const libc::c_char,
) -> glib_sys::gboolean;
}
pub fn get_defs<'a>(handle: *const RsvgHandle) -> &'a Defs {
pub fn get_defs<'a>(handle: *const RsvgHandle) -> &'a mut Defs {
unsafe {
let d = rsvg_handle_get_defs(handle);
&*(d as *const Defs)
&mut *(d as *mut Defs)
}
}
......@@ -184,3 +189,7 @@ pub fn image_surface_new_from_href(
Ok(surface)
}
pub fn load_xml_xinclude(handle: *mut RsvgHandle, url: &str) -> bool {
unsafe { from_glib(rsvg_load_handle_xml_xinclude(handle, url.to_glib_none().0)) }
}
......@@ -5,6 +5,8 @@
extern crate cairo;
extern crate cairo_sys;
extern crate cssparser;
extern crate downcast_rs;
extern crate encoding;
extern crate float_cmp;
extern crate gdk_pixbuf;
extern crate glib;
......@@ -26,8 +28,6 @@ extern crate regex;
#[macro_use]
extern crate lazy_static;
extern crate downcast_rs;
pub use color::{rsvg_css_parse_color, ColorKind, ColorSpec};
pub use css::{rsvg_css_parse_into_handle, rsvg_css_styles_free, rsvg_css_styles_new};
......@@ -42,25 +42,13 @@ pub use drawing_ctx::{
rsvg_drawing_ctx_new,
};
pub use load::{rsvg_load_new_node, rsvg_load_set_node_atts, rsvg_load_set_svg_node_atts};
pub use node::{
rsvg_node_add_child,
rsvg_node_children_iter_begin,
rsvg_node_children_iter_end,
rsvg_node_children_iter_next,
rsvg_node_find_last_chars_child,
rsvg_node_get_parent,
rsvg_node_ref,
rsvg_node_unref,
};
pub use node::rsvg_node_unref;
pub use tree::{
rsvg_tree_cascade,
rsvg_tree_free,
rsvg_tree_get_root,
rsvg_tree_is_root,
rsvg_tree_new,
rsvg_tree_root_is_svg,
};
......@@ -74,7 +62,14 @@ pub use property_bag::{
pub use structure::rsvg_node_svg_get_size;
pub use text::{rsvg_node_chars_append, rsvg_node_chars_new};
pub use xml::{
rsvg_xml_state_characters,
rsvg_xml_state_end_element,
rsvg_xml_state_free,
rsvg_xml_state_new,
rsvg_xml_state_start_element,
rsvg_xml_state_steal_tree,
};
#[macro_use]
mod log;
......@@ -124,6 +119,7 @@ pub mod srgb;
mod state;
mod stop;
mod structure;
mod style;
pub mod surface_utils;
mod text;
mod transform;
......@@ -132,3 +128,4 @@ mod unitinterval;
mod util;
mod viewbox;
mod viewport;
mod xml;
use libc;
use std::collections::HashMap;
use attributes::Attribute;
use clip_path::NodeClipPath;
use defs::{Defs, RsvgDefs};
use defs::Defs;
use filters::{
blend::Blend,
color_matrix::ColorMatrix,
......@@ -23,7 +22,6 @@ use filters::{
turbulence::Turbulence,
};
use gradient::NodeGradient;
use handle::RsvgHandle;
use image::NodeImage;
use link::NodeLink;
use marker::NodeMarker;
......@@ -34,8 +32,8 @@ use property_bag::PropertyBag;
use shapes::{NodeCircle, NodeEllipse, NodeLine, NodePath, NodePoly, NodeRect};
use stop::NodeStop;
use structure::{NodeDefs, NodeGroup, NodeSvg, NodeSwitch, NodeSymbol, NodeUse};
use style::NodeStyle;
use text::{NodeTRef, NodeTSpan, NodeText};
use util::utf8_cstr;
macro_rules! node_create_fn {
($name:ident, $node_type:ident, $new_fn:expr) => {
......@@ -43,9 +41,9 @@ macro_rules! node_create_fn {
element_name: &str,
id: Option<&str>,
class: Option<&str>,
parent: *const RsvgNode,
) -> *const RsvgNode {
boxed_node_new(
parent: Option<&RsvgNode>,
) -> RsvgNode {
node_new(
NodeType::$node_type,
parent,
element_name,
......@@ -162,6 +160,7 @@ node_create_fn!(
);
node_create_fn!(create_spot_light, LightSource, LightSource::new_spot_light);
node_create_fn!(create_stop, Stop, NodeStop::new);
node_create_fn!(create_style, Style, NodeStyle::new);
node_create_fn!(create_svg, Svg, NodeSvg::new);
node_create_fn!(create_switch, Switch, NodeSwitch::new);
node_create_fn!(create_symbol, Symbol, NodeSymbol::new);
......@@ -176,7 +175,8 @@ node_create_fn!(
);
node_create_fn!(create_use, Use, NodeUse::new);
type NodeCreateFn = fn(&str, Option<&str>, Option<&str>, *const RsvgNode) -> *const RsvgNode;
type NodeCreateFn =
fn(name: &str, id: Option<&str>, class: Option<&str>, parent: Option<&RsvgNode>) -> RsvgNode;
lazy_static! {
// Lines in comments are elements that we don't support.
......@@ -253,7 +253,7 @@ lazy_static! {
/* h.insert("script", (false, as NodeCreateFn)); */
/* h.insert("set", (false, as NodeCreateFn)); */
h.insert("stop", (true, create_stop as NodeCreateFn));
/* h.insert("style", (false, as NodeCreateFn)); */
h.insert("style", (false, create_style as NodeCreateFn));
h.insert("subImage", (false, create_group as NodeCreateFn));
h.insert("subImageRef", (false, create_image as NodeCreateFn));
h.insert("svg", (true, create_svg as NodeCreateFn));
......@@ -271,21 +271,12 @@ lazy_static! {
};
}
#[no_mangle]
pub extern "C" fn rsvg_load_new_node(
raw_name: *const libc::c_char,
parent: *const RsvgNode,
pbag: *const PropertyBag<'_>,
defs: *mut RsvgDefs,
) -> *const RsvgNode {
assert!(!raw_name.is_null());
assert!(!pbag.is_null());
assert!(!defs.is_null());
let name = unsafe { utf8_cstr(raw_name) };
let pbag = unsafe { &*pbag };
let defs = unsafe { &mut *(defs as *mut Defs) };
pub fn rsvg_load_new_node(
name: &str,
parent: Option<&RsvgNode>,
pbag: &PropertyBag,
defs: &mut Defs,
) -> RsvgNode {
let mut id = None;
let mut class = None;
......@@ -310,53 +301,10 @@ pub extern "C" fn rsvg_load_new_node(
};
let node = create_fn(name, id, class, parent);
assert!(!node.is_null());
if id.is_some() {
let n = unsafe { &*node };
defs.insert(id.unwrap(), n);
defs.insert(id.unwrap(), &node);
}
node
}
#[no_mangle]
pub extern "C" fn rsvg_load_set_node_atts(
handle: *const RsvgHandle,
raw_node: *mut RsvgNode,
pbag: *const PropertyBag<'_>,
) {
assert!(!raw_node.is_null());
assert!(!pbag.is_null());
let node: &RsvgNode = unsafe { &*raw_node };
let pbag = unsafe { &*pbag };
node.set_atts(node, handle, pbag);
// The "svg" node is special; it will load its id/class
// attributes until the end, when sax_end_element_cb() calls
// rsvg_node_svg_apply_atts()
if node.get_type() != NodeType::Svg {
node.set_style(handle, pbag);
}
node.set_overridden_properties();
}
#[no_mangle]
pub extern "C" fn rsvg_load_set_svg_node_atts(
handle: *const RsvgHandle,
raw_node: *const RsvgNode,
) {
assert!(!raw_node.is_null());
let node: &RsvgNode = unsafe { &*raw_node };
if node.get_type() != NodeType::Svg {
return;
}
node.with_impl(|svg: &NodeSvg| {
svg.set_delayed_style(node, handle);
});
}
use cairo::{Matrix, MatrixTrait};
use downcast_rs::*;
use glib;
use glib::translate::*;
use glib_sys;
use std::cell::{Cell, Ref, RefCell};
......@@ -200,6 +201,7 @@ pub enum NodeType {
RadialGradient,
Rect,
Stop,
Style,
Svg,
Switch,
Symbol,
......@@ -655,31 +657,26 @@ impl Node {
}
}
pub fn node_ptr_to_weak(raw_parent: *const RsvgNode) -> Option<Weak<Node>> {
if raw_parent.is_null() {
None
} else {
let p: &RsvgNode = unsafe { &*raw_parent };
Some(Rc::downgrade(&p.clone()))
}
}
pub fn boxed_node_new(
pub fn node_new(
node_type: NodeType,
raw_parent: *const RsvgNode,
parent: Option<&RsvgNode>,
element_name: &str,
id: Option<&str>,
class: Option<&str>,
node_impl: Box<NodeTrait>,
) -> *mut RsvgNode {
box_node(Rc::new(Node::new(
) -> RsvgNode {
Rc::new(Node::new(
node_type,
node_ptr_to_weak(raw_parent),
if let Some(parent) = parent {
Some(Rc::downgrade(parent))
} else {
None
},
element_name,
id,
class,
node_impl,
)))
))
}
impl Children {
......@@ -737,26 +734,6 @@ pub fn box_node(node: RsvgNode) -> *mut RsvgNode {
Box::into_raw(Box::new(node))
}
#[no_mangle]
pub extern "C" fn rsvg_node_get_parent(raw_node: *const RsvgNode) -> *const RsvgNode {
assert!(!raw_node.is_null());
let node: &RsvgNode = unsafe { &*raw_node };
match node.get_parent() {
None => ptr::null(),
Some(node) => box_node(node),
}
}
#[no_mangle]
pub extern "C" fn rsvg_node_ref(raw_node: *mut RsvgNode) -> *mut RsvgNode {
assert!(!raw_node.is_null());
let node: &RsvgNode = unsafe { &*raw_node };
box_node(node.clone())
}
#[no_mangle]
pub extern "C" fn rsvg_node_unref(raw_node: *mut RsvgNode) -> *mut RsvgNode {
if !raw_node.is_null() {
......@@ -767,81 +744,10 @@ pub extern "C" fn rsvg_node_unref(raw_node: *mut RsvgNode) -> *mut RsvgNode {
ptr::null_mut()
}
#[no_mangle]
pub extern "C" fn rsvg_node_add_child(raw_node: *mut RsvgNode, raw_child: *const RsvgNode) {
assert!(!raw_node.is_null());
assert!(!raw_child.is_null());
let node: &mut RsvgNode = unsafe { &mut *raw_node };
let child: &RsvgNode = unsafe { &*raw_child };
node.add_child(child);
}
#[no_mangle]
pub extern "C" fn rsvg_node_find_last_chars_child(
raw_node: *const RsvgNode,
out_accept_chars: *mut glib_sys::gboolean,
) -> *mut RsvgNode {
assert!(!raw_node.is_null());
let node: &RsvgNode = unsafe { &*raw_node };
let accept_chars = node.accept_chars();
assert!(!out_accept_chars.is_null());
unsafe {
*out_accept_chars = accept_chars.to_glib();
}
if accept_chars {
if let Some(chars) = node.find_last_chars_child() {
return box_node(chars);
}
}
ptr::null_mut()
}
#[no_mangle]
pub extern "C" fn rsvg_node_children_iter_begin(raw_node: *const RsvgNode) -> *mut Children {
assert!(!raw_node.is_null());
let node: &RsvgNode = unsafe { &*raw_node };
Box::into_raw(Box::new(node.children()))
}
#[no_mangle]
pub extern "C" fn rsvg_node_children_iter_end(iter: *mut Children) {
assert!(!iter.is_null());
unsafe { Box::from_raw(iter) };
}
#[no_mangle]
pub extern "C" fn rsvg_node_children_iter_next(
iter: *mut Children,
out_child: *mut *mut RsvgNode,
) -> glib_sys::gboolean {
assert!(!iter.is_null());
let iter = unsafe { &mut *iter };
if let Some(child) = iter.next() {
unsafe {
*out_child = box_node(child);
}
true.to_glib()
} else {
unsafe {
*out_child = ptr::null_mut();
}
false.to_glib()
}
}
#[cfg(test)]
mod tests {
use super::*;
use handle::RsvgHandle;
use std::mem;
use std::rc::Rc;
struct TestNodeImpl {}
......@@ -852,6 +758,13 @@ mod tests {
}
}
fn rsvg_node_ref(raw_node: *mut RsvgNode) -> *mut RsvgNode {
assert!(!raw_node.is_null());
let node: &RsvgNode = unsafe { &*raw_node };
box_node(node.clone())
}
#[test]
fn node_refs_and_unrefs() {
let node = Rc::new(Node::new(
......@@ -988,45 +901,4 @@ mod tests {
assert!(children.next().is_none());
assert!(children.next_back().is_none());
}
#[test]
fn node_children_iterator_c() {
let node = Rc::new(Node::new(
NodeType::Path,
None,
"path",
None,
None,
Box::new(TestNodeImpl {}),
));
let child = Rc::new(Node::new(
NodeType::Path,
Some(Rc::downgrade(&node)),
"path",
None,
None,
Box::new(TestNodeImpl {}),
));
let second_child = Rc::new(Node::new(
NodeType::Path,
Some(Rc::downgrade(&node)),
"path",
None,
None,
Box::new(TestNodeImpl {}),
));
node.add_child(&child);
node.add_child(&second_child);
let iter = rsvg_node_children_iter_begin(&node);
let mut c = unsafe { mem::uninitialized() };
let result: bool = from_glib(rsvg_node_children_iter_next(iter, &mut c));
assert_eq!(result, true);
assert!(Rc::ptr_eq(unsafe { &*c }, &child));
rsvg_node_unref(c);
}
}
use attributes::Attribute;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, NodeType, RsvgNode};
use property_bag::PropertyBag;
use text::NodeChars;
use std::cell::RefCell;
/// Represents a <style> node.
///
/// It does not render itself, and just holds CSS stylesheet information for the rest of
/// the code to use.
pub struct NodeStyle {