[librsvg: 1/2] support <a> links for PDF output



commit 0b6297030db215a2cfebee55540e151e8d863afc
Author: Dmitry Kontsevoy <dmitry kontsevoy gmail com>
Date:   Wed Feb 21 10:17:42 2018 +0300

    support <a> links for PDF output

 Makefile.am                |   1 +
 librsvg/rsvg-base.c        |   2 +-
 librsvg/rsvg-private.h     |   1 +
 librsvg/rsvg-structure.h   |   4 ++
 rsvg_internals/src/lib.rs  |   5 ++
 rsvg_internals/src/link.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++
 rsvg_internals/src/node.rs |   1 +
 7 files changed, 142 insertions(+), 1 deletion(-)
---
diff --git a/Makefile.am b/Makefile.am
index 72241dbd..a5a16220 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -82,6 +82,7 @@ RUST_SRC =                                    \
        rsvg_internals/src/image.rs             \
        rsvg_internals/src/length.rs            \
        rsvg_internals/src/lib.rs               \
+       rsvg_internals/src/link.rs              \
        rsvg_internals/src/marker.rs            \
        rsvg_internals/src/mask.rs              \
        rsvg_internals/src/node.rs              \
diff --git a/librsvg/rsvg-base.c b/librsvg/rsvg-base.c
index cb74f54f..94990400 100644
--- a/librsvg/rsvg-base.c
+++ b/librsvg/rsvg-base.c
@@ -263,7 +263,7 @@ typedef struct {
  * Lines in comments are elements that we don't support.
  */
 static const NodeCreator node_creators[] = {
-    { "a",                   TRUE,  rsvg_node_group_new },    /* treat anchors as groups for now */
+    { "a",                   TRUE,  rsvg_node_link_new },
     /* "altGlyph",           TRUE,  */
     /* "altGlyphDef",        FALSE, */
     /* "altGlyphItem",       FALSE, */
diff --git a/librsvg/rsvg-private.h b/librsvg/rsvg-private.h
index 3e557b37..a3c981b9 100644
--- a/librsvg/rsvg-private.h
+++ b/librsvg/rsvg-private.h
@@ -303,6 +303,7 @@ typedef enum {
     RSVG_NODE_TYPE_LIGHT_SOURCE,
     RSVG_NODE_TYPE_LINE,
     RSVG_NODE_TYPE_LINEAR_GRADIENT,
+    RSVG_NODE_TYPE_LINK,
     RSVG_NODE_TYPE_MARKER,
     RSVG_NODE_TYPE_MASK,
     RSVG_NODE_TYPE_PATH,
diff --git a/librsvg/rsvg-structure.h b/librsvg/rsvg-structure.h
index 356f448d..61a1c105 100644
--- a/librsvg/rsvg-structure.h
+++ b/librsvg/rsvg-structure.h
@@ -38,6 +38,10 @@ G_BEGIN_DECLS
 G_GNUC_INTERNAL
 RsvgNode *rsvg_node_group_new (const char *element_name, RsvgNode *parent);
 
+/* Implemented in rust/src/link.rs */
+G_GNUC_INTERNAL
+RsvgNode *rsvg_node_link_new (const char *element_name, RsvgNode *parent);
+
 /* Implemented in rust/src/structure.rs */
 G_GNUC_INTERNAL
 RsvgNode *rsvg_node_defs_new (const char *element_name, RsvgNode *parent);
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index 30e4ed52..aedebebf 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -73,6 +73,10 @@ pub use image::{
     rsvg_node_image_new,
 };
 
+pub use link::{
+    rsvg_node_link_new,
+};
+
 pub use marker::{
     rsvg_node_marker_new,
 };
@@ -195,6 +199,7 @@ mod gradient;
 mod handle;
 mod image;
 mod length;
+mod link;
 mod marker;
 mod mask;
 mod node;
diff --git a/rsvg_internals/src/link.rs b/rsvg_internals/src/link.rs
new file mode 100644
index 00000000..cfcb546c
--- /dev/null
+++ b/rsvg_internals/src/link.rs
@@ -0,0 +1,129 @@
+use cairo;
+use cairo_sys;
+use glib::translate::*;
+use libc;
+
+use std::borrow::Cow;
+use std::cell::RefCell;
+use regex::{Regex, Captures};
+
+use attributes::Attribute;
+use drawing_ctx::{self, RsvgDrawingCtx};
+use handle::RsvgHandle;
+use node::*;
+use property_bag::PropertyBag;
+
+struct NodeLink {
+    link: RefCell<Option<String>>,
+}
+
+impl NodeLink {
+    fn new() -> NodeLink {
+        NodeLink { link: RefCell::new(None) }
+    }
+}
+
+impl NodeTrait for NodeLink {
+    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
+        for (_key, attr, value) in pbag.iter() {
+            match attr {
+                Attribute::XlinkHref => *self.link.borrow_mut() = Some(value.to_owned()),
+
+                _ => (),
+            }
+        }
+
+        Ok(())
+    }
+
+    fn draw(&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
+        let link = self.link.borrow();
+
+        if link.is_some() && link.as_ref().unwrap() != "" {
+            const CAIRO_TAG_LINK: &str = "Link";
+
+            let attributes = link.as_ref().map(|i| format!("uri='{}'", escape_value(i)));
+
+            drawing_ctx::get_cairo_context(draw_ctx).with_tag(
+                CAIRO_TAG_LINK,
+                attributes.as_ref().map(|i| i.as_str()),
+                || node.draw_children(draw_ctx, dominate),
+            )
+        } else {
+            node.draw_children(draw_ctx, dominate)
+        }
+    }
+
+    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
+        unreachable!();
+    }
+}
+
+/// escape quotes and backslashes with backslash
+fn escape_value(value: &str) -> Cow<str> {
+    lazy_static! {
+        static ref REGEX: Regex = Regex::new(r"['\\]").unwrap();
+    }
+
+    REGEX.replace_all(
+        value,
+        |caps: &Captures| match caps.get(0).unwrap().as_str() {
+            "'" => "\\'".to_owned(),
+            "\\" => "\\\\".to_owned(),
+            _ => unreachable!(),
+        },
+    )
+}
+
+extern "C" {
+    fn cairo_tag_begin(
+        cr: *mut cairo_sys::cairo_t,
+        tag_name: *const libc::c_char,
+        attibutes: *const libc::c_char,
+    );
+    fn cairo_tag_end(cr: *mut cairo_sys::cairo_t, tag_name: *const libc::c_char);
+}
+
+/// Bindings that aren't supported by `cairo-rs` for now
+trait CairoTagging {
+    fn tag_begin(&self, tag_name: &str, attributes: Option<&str>);
+    fn tag_end(&self, tag_name: &str);
+    fn with_tag<U, T>(&self, tag_name: &str, attributes: Option<&str>, f: U) -> T
+    where
+        U: Fn() -> T,
+    {
+        self.tag_begin(tag_name, attributes);
+        let result = f();
+        self.tag_end(tag_name);
+
+        result
+    }
+}
+
+impl CairoTagging for cairo::Context {
+    fn tag_begin(&self, tag_name: &str, attributes: Option<&str>) {
+        unsafe {
+            cairo_tag_begin(
+                self.to_glib_none().0,
+                tag_name.to_glib_none().0,
+                attributes.to_glib_none().0,
+            );
+        }
+    }
+
+    fn tag_end(&self, tag_name: &str) {
+        unsafe {
+            cairo_tag_end(self.to_glib_none().0, tag_name.to_glib_none().0);
+        }
+    }
+}
+
+/***** C Prototypes *****/
+
+#[no_mangle]
+pub extern "C" fn rsvg_node_link_new(
+    _: *const libc::c_char,
+    raw_parent: *const RsvgNode,
+) -> *const RsvgNode {
+    boxed_node_new(NodeType::Link, raw_parent, Box::new(NodeLink::new()))
+}
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index d3058c12..16718157 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -83,6 +83,7 @@ pub enum NodeType {
     LightSource,
     Line,
     LinearGradient,
+    Link,
     Marker,
     Mask,
     Path,


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