Use a real NodeStyle for the <style> element instead of special loading logic

Instead of having special cases in the state machine for loading, we
now have a NodeStyle that just stores NodeChars with the stylesheet
data.  The code parses that data if it is text/css when the <style>
element is closed.
parent 550d3961
......@@ -110,6 +110,7 @@ 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 \
......
......@@ -87,6 +87,10 @@ struct DocHandlerData {
}
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,
......
......@@ -121,6 +121,7 @@ pub mod srgb;
mod state;
mod stop;
mod structure;
mod style;
pub mod surface_utils;
mod text;
mod transform;
......
......@@ -32,6 +32,7 @@ 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};
macro_rules! node_create_fn {
......@@ -159,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);
......@@ -251,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));
......
......@@ -199,6 +199,7 @@ pub enum NodeType {
RadialGradient,
Rect,
Stop,
Style,
Svg,
Switch,
Symbol,
......
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 {
type_: RefCell<Option<String>>,
}
impl NodeStyle {
pub fn new() -> NodeStyle {
NodeStyle {
type_: RefCell::new(None),
}
}
pub fn get_css(&self, node: &RsvgNode) -> String {
// FIXME: See these:
//
// https://www.w3.org/TR/SVG11/styling.html#StyleElementTypeAttribute
// https://www.w3.org/TR/SVG11/styling.html#ContentStyleTypeAttribute
//
// If the "type" attribute is not present, we should fallback to the
// "contentStyleType" attribute of the svg element, which in turn
// defaults to "text/css".
let have_css = self
.type_
.borrow()
.as_ref()
.map(|t| t == "text/css")
.unwrap_or(true);
if have_css {
node.children()
.into_iter()
.filter_map(|child| {
if child.get_type() == NodeType::Chars {
Some(child.with_impl(|chars: &NodeChars| chars.get_string()))
} else {
None
}
})
.collect::<String>()
} else {
"".to_string()
}
}
}
impl NodeTrait for NodeStyle {
fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag<'_>) -> NodeResult {
for (_key, attr, value) in pbag.iter() {
if attr == Attribute::Type {
*self.type_.borrow_mut() = Some(value.to_string());
}
}
Ok(())
}
fn accept_chars(&self) -> bool {
true
}
}
......@@ -56,6 +56,10 @@ impl NodeChars {
}
}
pub fn get_string(&self) -> String {
self.string.borrow().clone()
}
pub fn append(&self, s: &str) {
self.string.borrow_mut().push_str(s);
}
......
......@@ -14,6 +14,7 @@ use load::rsvg_load_new_node;
use node::{node_new, Node, NodeType};
use property_bag::PropertyBag;
use structure::NodeSvg;
use style::NodeStyle;
use text::NodeChars;
use tree::{RsvgTree, Tree};
use util::utf8_cstr;
......@@ -25,12 +26,6 @@ enum ContextKind {
// Creating nodes for elements under the current node
ElementCreation,
// Inside a <style> element
Style(StyleContext),
// An element inside a <style> context, to be ignored
UnsupportedStyleChild,
// Inside <xi:include>
XInclude(XIncludeContext),
......@@ -41,12 +36,6 @@ enum ContextKind {
XIncludeFallback(XIncludeContext),
}
/// Handles the `<style>` element by parsing its character contents as CSS
struct StyleContext {
is_text_css: bool,
text: String,
}
#[derive(Clone)]
struct XIncludeContext {
need_fallback: bool,
......@@ -79,9 +68,6 @@ pub enum RsvgXmlState {}
/// that context, all XML events will be forwarded to it, and processed in one of the `XmlHandler`
/// trait objects. Normally the context refers to a `NodeCreationContext` implementation which is
/// what creates normal graphical elements.
///
/// When we get to a `<style>` element, we push a `StyleContext`, which processes its contents
/// specially.
struct XmlState {
tree: Option<Box<Tree>>,
context: Context,
......@@ -89,10 +75,6 @@ struct XmlState {
current_node: Option<Rc<Node>>,
}
fn style_characters(style_ctx: &mut StyleContext, text: &str) {
style_ctx.text.push_str(text);
}
impl XmlState {
fn new() -> XmlState {
XmlState {
......@@ -126,8 +108,6 @@ impl XmlState {
let new_ctx = match ctx.kind {
ContextKind::Start => self.element_creation_start_element(handle, name, pbag),
ContextKind::ElementCreation => self.element_creation_start_element(handle, name, pbag),
ContextKind::Style(_) => self.inside_style_start_element(name),
ContextKind::UnsupportedStyleChild => self.inside_style_start_element(name),
ContextKind::XInclude(ref ctx) => self.inside_xinclude_start_element(ctx, name),
ContextKind::UnsupportedXIncludeChild => self.unsupported_xinclude_start_element(name),
ContextKind::XIncludeFallback(ref ctx) => {
......@@ -150,8 +130,6 @@ impl XmlState {
match context.kind {
ContextKind::Start => panic!("end_element: XML handler stack is empty!?"),
ContextKind::ElementCreation => self.element_creation_end_element(handle),
ContextKind::Style(style_ctx) => self.style_end_element(style_ctx, handle),
ContextKind::UnsupportedStyleChild => (),
ContextKind::XInclude(_) => (),
ContextKind::UnsupportedXIncludeChild => (),
ContextKind::XIncludeFallback(_) => (),
......@@ -159,13 +137,11 @@ impl XmlState {
}
pub fn characters(&mut self, text: &str) {
let mut ctx = mem::replace(&mut self.context, Context::empty());
let ctx = mem::replace(&mut self.context, Context::empty());
match ctx.kind {
ContextKind::Start => panic!("characters: XML handler stack is empty!?"),
ContextKind::ElementCreation => self.element_creation_characters(text),
ContextKind::Style(ref mut style_ctx) => style_characters(style_ctx, text),
ContextKind::UnsupportedStyleChild => (),
ContextKind::XInclude(_) => (),
ContextKind::UnsupportedXIncludeChild => (),
ContextKind::XIncludeFallback(ref ctx) => {
......@@ -184,7 +160,6 @@ impl XmlState {
) -> Context {
match name {
"include" => self.xinclude_start_element(handle, name, pbag),
"style" => self.style_start_element(name, pbag),
_ => {
let node = self.create_node(self.current_node.as_ref(), handle, name, pbag);
if self.current_node.is_none() {
......@@ -211,6 +186,12 @@ impl XmlState {
});
}
if node.get_type() == NodeType::Style {
let css_data = node.with_impl(|style: &NodeStyle| style.get_css(&node));
css::parse_into_handle(handle, &css_data);
}
self.current_node = node.get_parent();
}
......@@ -267,52 +248,6 @@ impl XmlState {
new_node
}
fn style_start_element(&self, name: &str, pbag: &PropertyBag) -> Context {
// FIXME: See these:
//
// https://www.w3.org/TR/SVG11/styling.html#StyleElementTypeAttribute
// https://www.w3.org/TR/SVG11/styling.html#ContentStyleTypeAttribute
//
// If the "type" attribute is not present, we should fallback to the
// "contentStyleType" attribute of the svg element, which in turn
// defaults to "text/css".
//
// See where is_text_css is used to see where we parse the contents
// of the style element.
let mut is_text_css = true;
for (_key, attr, value) in pbag.iter() {
if attr == Attribute::Type {
is_text_css = value == "text/css";
}
}
Context {
element_name: name.to_string(),
kind: ContextKind::Style(StyleContext {
is_text_css,
text: String::new(),
}),
}
}
fn style_end_element(&mut self, style_ctx: StyleContext, handle: *mut RsvgHandle) {
if style_ctx.is_text_css {
css::parse_into_handle(handle, &style_ctx.text);
}
}
fn inside_style_start_element(&self, name: &str) -> Context {
// We are already inside a <style> element, and we don't support
// elements in there. Just push a state that we will ignore.
Context {
element_name: name.to_string(),
kind: ContextKind::UnsupportedStyleChild,
}
}
fn xinclude_start_element(
&mut self,
handle: *mut RsvgHandle,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment