summaryrefslogtreecommitdiff
path: root/capi
diff options
context:
space:
mode:
authorReizner Evgeniy <razrfalcon@gmail.com>2018-03-10 19:34:18 +0200
committerReizner Evgeniy <razrfalcon@gmail.com>2018-03-10 19:34:18 +0200
commit6578eabb83c198a242b9af64f3e19710bffb6ddd (patch)
tree8c1c1aea19b2aff066b8554e152c9812ecd01bb7 /capi
parent647163277742df2403b689d1faf12a1564286aa7 (diff)
C-API rewrite.
Diffstat (limited to 'capi')
-rw-r--r--capi/include/resvg.h102
-rw-r--r--capi/src/lib.rs465
2 files changed, 509 insertions, 58 deletions
diff --git a/capi/include/resvg.h b/capi/include/resvg.h
index 8bfd4ac..e01c30b 100644
--- a/capi/include/resvg.h
+++ b/capi/include/resvg.h
@@ -5,14 +5,43 @@
#ifndef RESVG_H
#define RESVG_H
+#include <stdbool.h>
+
#ifdef RESVG_CAIRO_BACKEND
#include <cairo.h>
#endif
-struct resvg_render_tree;
+typedef struct resvg_handle resvg_handle;
typedef struct resvg_render_tree resvg_render_tree;
+typedef struct resvg_color {
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+} resvg_color;
+
+typedef enum resvg_fit_to_type {
+ RESVG_FIT_TO_ORIGINAL,
+ RESVG_FIT_TO_WIDTH,
+ RESVG_FIT_TO_HEIGHT,
+ RESVG_FIT_TO_ZOOM,
+} resvg_fit_to_type;
+
+typedef struct resvg_fit_to {
+ resvg_fit_to_type type;
+ float value;
+} resvg_fit_to;
+
+typedef struct resvg_options {
+ const char *path;
+ double dpi;
+ resvg_fit_to fit_to;
+ bool draw_background;
+ resvg_color background;
+ bool keep_named_groups;
+} resvg_options;
+
typedef struct resvg_rect {
double x;
double y;
@@ -20,9 +49,31 @@ typedef struct resvg_rect {
double height;
} resvg_rect;
+typedef struct resvg_transform {
+ double a;
+ double b;
+ double c;
+ double d;
+ double e;
+ double f;
+} resvg_transform;
+
+
+resvg_handle* resvg_init();
+void resvg_destroy(resvg_handle *handle);
void resvg_init_log();
+void resvg_init_options(resvg_options *opt)
+{
+ opt->path = NULL;
+ opt->dpi = 96;
+ opt->fit_to.type = RESVG_FIT_TO_ORIGINAL;
+ opt->fit_to.value = 0;
+ opt->draw_background = false;
+ opt->keep_named_groups = false;
+}
+
/**
* @brief Creates <b>resvg_render_tree</b> from file.
*
@@ -34,7 +85,7 @@ void resvg_init_log();
* @return Parsed render tree. NULL on error. Should be destroyed via resvg_rtree_destroy.
*/
resvg_render_tree *resvg_parse_rtree_from_file(const char *file_path,
- double dpi,
+ const resvg_options *opt,
char **error);
/**
@@ -46,28 +97,67 @@ resvg_render_tree *resvg_parse_rtree_from_file(const char *file_path,
* @return Parsed render tree. NULL on error. Should be destroyed via resvg_rtree_destroy.
*/
resvg_render_tree *resvg_parse_rtree_from_data(const char *text,
- double dpi,
+ const resvg_options *opt,
char **error);
-void resvg_get_image_size(resvg_render_tree *rtree,
+void resvg_get_image_size(const resvg_render_tree *rtree,
double *width,
double *height);
+bool resvg_node_exists(const resvg_render_tree *rtree,
+ const char *id);
+
+bool resvg_get_node_transform(const resvg_render_tree *rtree,
+ const char *id,
+ resvg_transform *ts);
+
void resvg_rtree_destroy(resvg_render_tree *rtree);
void resvg_error_msg_destroy(char *msg);
#ifdef RESVG_CAIRO_BACKEND
-void resvg_cairo_render_to_canvas(resvg_render_tree *rtree,
+bool resvg_cairo_get_node_bbox(const resvg_render_tree *rtree,
+ const resvg_options *opt,
+ const char *id,
+ resvg_rect *bbox);
+
+bool resvg_cairo_render_to_image(const resvg_render_tree *rtree,
+ const resvg_options *opt,
+ const char *file_path);
+
+void resvg_cairo_render_to_canvas(const resvg_render_tree *rtree,
+ const resvg_options *opt,
resvg_rect view,
cairo_t *cr);
+
+void resvg_cairo_render_to_canvas_by_id(const resvg_render_tree *rtree,
+ const resvg_options *opt,
+ resvg_rect view,
+ const char *id,
+ void *painter);
#endif
#ifdef RESVG_QT_BACKEND
-void resvg_qt_render_to_canvas(resvg_render_tree *rtree,
+bool resvg_qt_get_node_bbox(const resvg_render_tree *rtree,
+ const resvg_options *opt,
+ const char *id,
+ resvg_rect *bbox);
+
+bool resvg_qt_render_to_image(const resvg_render_tree *rtree,
+ const resvg_options *opt,
+ const char *file_path);
+
+void resvg_qt_render_to_canvas(const resvg_render_tree *rtree,
+ const resvg_options *opt,
resvg_rect view,
void *painter);
+
+void resvg_qt_render_to_canvas_by_id(const resvg_render_tree *rtree,
+ const resvg_options *opt,
+ resvg_rect view,
+ const char *id,
+ void *painter);
#endif
#endif // RESVG_H
diff --git a/capi/src/lib.rs b/capi/src/lib.rs
index d062460..ec50e6c 100644
--- a/capi/src/lib.rs
+++ b/capi/src/lib.rs
@@ -2,8 +2,10 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#![allow(non_camel_case_types)]
+
extern crate resvg;
-extern crate log;
+#[macro_use] extern crate log;
extern crate fern;
#[cfg(feature = "cairo-backend")]
@@ -12,6 +14,7 @@ extern crate glib;
extern crate cairo_sys;
use std::fmt;
+use std::path;
use std::ptr;
use std::ffi::{
CStr,
@@ -30,17 +33,62 @@ use resvg::cairo;
use resvg::RectExt;
use resvg::tree::prelude::*;
+
+#[repr(C)]
+pub struct resvg_options {
+ pub path: *const c_char,
+ pub dpi: f64,
+ pub fit_to: resvg_fit_to,
+ pub draw_background: bool,
+ pub background: resvg_color,
+ pub keep_named_groups: bool,
+}
+
#[repr(C)]
-pub struct Rect {
- x: f64,
- y: f64,
- width: f64,
- height: f64,
+pub struct resvg_color {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+}
+
+#[repr(C)]
+pub enum resvg_fit_to_type {
+ RESVG_FIT_TO_ORIGINAL,
+ RESVG_FIT_TO_WIDTH,
+ RESVG_FIT_TO_HEIGHT,
+ RESVG_FIT_TO_ZOOM,
+}
+
+#[repr(C)]
+pub struct resvg_fit_to {
+ kind: resvg_fit_to_type,
+ value: f32,
+}
+
+#[repr(C)]
+pub struct resvg_rect {
+ pub x: f64,
+ pub y: f64,
+ pub width: f64,
+ pub height: f64,
+}
+
+#[repr(C)]
+pub struct resvg_transform {
+ pub a: f64,
+ pub b: f64,
+ pub c: f64,
+ pub d: f64,
+ pub e: f64,
+ pub f: f64,
}
#[repr(C)]
pub struct resvg_render_tree(resvg::tree::RenderTree);
+#[repr(C)]
+pub struct resvg_handle(resvg::InitObject);
+
macro_rules! on_err {
($err:expr, $msg:expr) => ({
let c_str = CString::new($msg).unwrap();
@@ -49,20 +97,18 @@ macro_rules! on_err {
})
}
-macro_rules! from_raw_str {
- ($raw_str:expr, $err:expr, $msg:expr) => ({
- let rstr = unsafe {
- assert!(!$raw_str.is_null());
- CStr::from_ptr($raw_str)
- };
-
- let rstr = match rstr.to_str() {
- Ok(rstr) => rstr,
- Err(_) => on_err!($err, $msg),
- };
+#[no_mangle]
+pub extern fn resvg_init() -> *mut resvg_handle {
+ let handle = Box::new(resvg_handle(resvg::init()));
+ Box::into_raw(handle)
+}
- rstr
- })
+#[no_mangle]
+pub extern fn resvg_destroy(handle: *mut resvg_handle) {
+ unsafe {
+ assert!(!handle.is_null());
+ Box::from_raw(handle)
+ };
}
#[no_mangle]
@@ -92,24 +138,67 @@ fn log_format(out: fern::FormatCallback, message: &fmt::Arguments, record: &log:
))
}
+fn to_native_opt(opt: &resvg_options) -> resvg::Options {
+ let mut path: Option<path::PathBuf> = None;
+
+ if !opt.path.is_null() {
+ if let Some(p) = cstr_to_str(opt.path) {
+ if !p.is_empty() {
+ path = Some(p.into());
+ }
+ }
+ };
+
+ let fit_to = match opt.fit_to.kind {
+ resvg_fit_to_type::RESVG_FIT_TO_ORIGINAL => {
+ resvg::FitTo::Original
+ }
+ resvg_fit_to_type::RESVG_FIT_TO_WIDTH => {
+ assert!(opt.fit_to.value > 0.0);
+ resvg::FitTo::Width(opt.fit_to.value as u32)
+ }
+ resvg_fit_to_type::RESVG_FIT_TO_HEIGHT => {
+ assert!(opt.fit_to.value > 0.0);
+ resvg::FitTo::Height(opt.fit_to.value as u32)
+ }
+ resvg_fit_to_type::RESVG_FIT_TO_ZOOM => {
+ assert!(opt.fit_to.value > 0.0);
+ resvg::FitTo::Zoom(opt.fit_to.value)
+ }
+ };
+
+ let background = if opt.draw_background {
+ Some(resvg::tree::Color::new(
+ opt.background.r,
+ opt.background.g,
+ opt.background.b,
+ ))
+ } else {
+ None
+ };
+
+ resvg::Options {
+ path,
+ dpi: opt.dpi,
+ fit_to,
+ background,
+ keep_named_groups: opt.keep_named_groups,
+ }
+}
+
#[no_mangle]
pub extern fn resvg_parse_rtree_from_file(
file_path: *const c_char,
- dpi: f64,
+ opt: *const resvg_options,
error: *mut *mut c_char,
) -> *mut resvg_render_tree {
- let file_path = from_raw_str!(
- file_path,
- error,
- "Error: the file path is not an UTF-8 string."
- );
-
- let opt = resvg::Options {
- path: Some(file_path.into()),
- dpi: dpi,
- .. resvg::Options::default()
+ let file_path = match cstr_to_str(file_path) {
+ Some(v) => v,
+ None => on_err!(error, "Error: file path is not an UTF-8 string."),
};
+ let opt = to_native_opt(unsafe { &*opt });
+
let rtree = match resvg::parse_rtree_from_file(file_path, &opt) {
Ok(rtree) => rtree,
Err(e) => on_err!(error, e.to_string()),
@@ -122,20 +211,16 @@ pub extern fn resvg_parse_rtree_from_file(
#[no_mangle]
pub extern fn resvg_parse_rtree_from_data(
text: *const c_char,
- dpi: f64,
+ opt: *const resvg_options,
error: *mut *mut c_char,
) -> *mut resvg_render_tree {
- let text = from_raw_str!(
- text,
- error,
- "Error: the SVG data is not an UTF-8 string."
- );
-
- let opt = resvg::Options {
- dpi: dpi,
- .. resvg::Options::default()
+ let text = match cstr_to_str(text) {
+ Some(v) => v,
+ None => on_err!(error, "Error: SVG data is not an UTF-8 string."),
};
+ let opt = to_native_opt(unsafe { &*opt });
+
let rtree = match resvg::parse_rtree_from_data(text, &opt) {
Ok(rtree) => rtree,
Err(e) => on_err!(error, e.to_string()),
@@ -161,37 +246,128 @@ pub extern fn resvg_rtree_destroy(rtree: *mut resvg_render_tree) {
};
}
+fn cstr_to_str(text: *const c_char) -> Option<&'static str> {
+ let text = unsafe {
+ assert!(!text.is_null());
+ CStr::from_ptr(text)
+ };
+
+ text.to_str().ok()
+}
+
+#[cfg(feature = "qt-backend")]
+#[no_mangle]
+pub extern fn resvg_qt_render_to_image(
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ file_path: *const c_char,
+) -> bool {
+ let backend = Box::new(resvg::render_qt::Backend);
+ render_to_image(rtree, opt, file_path, backend)
+}
+
+#[cfg(feature = "cairo-backend")]
+#[no_mangle]
+pub extern fn resvg_cairo_render_to_image(
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ file_path: *const c_char,
+) -> bool {
+ let backend = Box::new(resvg::render_cairo::Backend);
+ render_to_image(rtree, opt, file_path, backend)
+}
+
+fn render_to_image(
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ file_path: *const c_char,
+ backend: Box<resvg::Render>,
+) -> bool {
+ let rtree = unsafe {
+ assert!(!rtree.is_null());
+ &*rtree
+ };
+
+ let file_path = match cstr_to_str(file_path) {
+ Some(v) => v,
+ None => return false,
+ };
+
+ let opt = to_native_opt(unsafe { &*opt });
+
+ let img = backend.render_to_image(&rtree.0, &opt);
+ let img = match img {
+ Ok(img) => img,
+ Err(e) => {
+ warn!("{}", e);
+ return false;
+ }
+ };
+
+ img.save(path::Path::new(file_path))
+}
+
#[cfg(feature = "qt-backend")]
#[no_mangle]
pub extern fn resvg_qt_render_to_canvas(
- rtree: *mut resvg_render_tree,
- view: Rect,
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ view: resvg_rect,
painter: *mut qt::qtc_qpainter,
) {
let rtree = unsafe {
assert!(!rtree.is_null());
- &mut *rtree
+ &*rtree
};
let painter = unsafe { qt::Painter::from_raw(painter) };
let rect = resvg::Rect::from_xywh(view.x, view.y, view.width, view.height);
- // TODO: to a proper options
- let opt = resvg::Options::default();
+ let opt = to_native_opt(unsafe { &*opt });
resvg::render_qt::render_to_canvas(&rtree.0, &opt, rect, &painter);
}
+#[cfg(feature = "qt-backend")]
+#[no_mangle]
+pub extern fn resvg_qt_render_to_canvas_by_id(
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ view: resvg_rect,
+ id: *const c_char,
+ painter: *mut qt::qtc_qpainter,
+) {
+ let rtree = unsafe {
+ assert!(!rtree.is_null());
+ &*rtree
+ };
+
+ let painter = unsafe { qt::Painter::from_raw(painter) };
+ let rect = resvg::Rect::from_xywh(view.x, view.y, view.width, view.height);
+
+ let opt = to_native_opt(unsafe { &*opt });
+
+ let id = match cstr_to_str(id) {
+ Some(v) => v,
+ None => return,
+ };
+
+ if let Some(node) = node_by_id(&rtree.0, id) {
+ resvg::render_qt::render_node_to_canvas(node, &opt, rect.to_screen_size(), &painter);
+ }
+}
+
#[cfg(feature = "cairo-backend")]
#[no_mangle]
pub extern fn resvg_cairo_render_to_canvas(
- rtree: *mut resvg_render_tree,
- view: Rect,
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ view: resvg_rect,
cr: *mut cairo_sys::cairo_t,
) {
let rtree = unsafe {
assert!(!rtree.is_null());
- &mut *rtree
+ &*rtree
};
use glib::translate::FromGlibPtrNone;
@@ -199,21 +375,51 @@ pub extern fn resvg_cairo_render_to_canvas(
let cr = unsafe { cairo::Context::from_glib_none(cr) };
let rect = resvg::Rect::from_xywh(view.x, view.y, view.width, view.height);
- // TODO: to a proper options
- let opt = resvg::Options::default();
+ let opt = to_native_opt(unsafe { &*opt });
resvg::render_cairo::render_to_canvas(&rtree.0, &opt, rect, &cr);
}
+#[cfg(feature = "cairo-backend")]
+#[no_mangle]
+pub extern fn resvg_cairo_render_to_canvas_by_id(
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ view: resvg_rect,
+ id: *const c_char,
+ cr: *mut cairo_sys::cairo_t,
+) {
+ let rtree = unsafe {
+ assert!(!rtree.is_null());
+ &*rtree
+ };
+
+ let id = match cstr_to_str(id) {
+ Some(v) => v,
+ None => return,
+ };
+
+ use glib::translate::FromGlibPtrNone;
+
+ let cr = unsafe { cairo::Context::from_glib_none(cr) };
+ let rect = resvg::Rect::from_xywh(view.x, view.y, view.width, view.height);
+
+ let opt = to_native_opt(unsafe { &*opt });
+
+ if let Some(node) = node_by_id(&rtree.0, id) {
+ resvg::render_cairo::render_node_to_canvas(node, &opt, rect.to_screen_size(), &cr);
+ }
+}
+
#[no_mangle]
pub extern fn resvg_get_image_size(
- rtree: *mut resvg_render_tree,
+ rtree: *const resvg_render_tree,
width: *mut f64,
height: *mut f64,
) {
let rtree = unsafe {
assert!(!rtree.is_null());
- &mut *rtree
+ &*rtree
};
let size = rtree.0.svg_node().size;
@@ -223,3 +429,158 @@ pub extern fn resvg_get_image_size(
*height = size.height;
}
}
+
+#[cfg(feature = "qt-backend")]
+#[no_mangle]
+pub extern fn resvg_qt_get_node_bbox(
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ id: *const c_char,
+ bbox: *mut resvg_rect,
+) -> bool {
+ let backend = Box::new(resvg::render_qt::Backend);
+ get_node_bbox(rtree, opt, id, bbox, backend)
+}
+
+#[cfg(feature = "cairo-backend")]
+#[no_mangle]
+pub extern fn resvg_cairo_get_node_bbox(
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ id: *const c_char,
+ bbox: *mut resvg_rect,
+) -> bool {
+ let backend = Box::new(resvg::render_cairo::Backend);
+ get_node_bbox(rtree, opt, id, bbox, backend)
+}
+
+fn get_node_bbox(
+ rtree: *const resvg_render_tree,
+ opt: *const resvg_options,
+ id: *const c_char,
+ bbox: *mut resvg_rect,
+ backend: Box<resvg::Render>,
+) -> bool {
+ let id = match cstr_to_str(id) {
+ Some(v) => v,
+ None => {
+ warn!("Provided ID is no an UTF-8 string.");
+ return false;
+ }
+ };
+
+ if id.is_empty() {
+ warn!("Node ID must not be empty.");
+ return false;
+ }
+
+ let rtree = unsafe {
+ assert!(!rtree.is_null());
+ &*rtree
+ };
+
+
+ let opt = to_native_opt(unsafe { &*opt });
+
+ match node_by_id(&rtree.0, id) {
+ Some(node) => {
+ if let Some(r) = backend.calc_node_bbox(node, &opt) {
+ unsafe {
+ (*bbox).x = r.x();
+ (*bbox).y = r.y();
+ (*bbox).width = r.width();
+ (*bbox).height = r.height();
+ }
+
+ true
+ } else {
+ false
+ }
+ }
+ None => {
+ warn!("No node with '{}' ID in the tree.", id);
+ false
+ }
+ }
+}
+
+#[no_mangle]
+pub extern fn resvg_node_exists(
+ rtree: *const resvg_render_tree,
+ id: *const c_char,
+) -> bool {
+ let id = match cstr_to_str(id) {
+ Some(v) => v,
+ None => {
+ warn!("Provided ID is no an UTF-8 string.");
+ return false;
+ }
+ };
+
+ if id.is_empty() {
+ return false;
+ }
+
+ let rtree = unsafe {
+ assert!(!rtree.is_null());
+ &*rtree
+ };
+
+ node_by_id(&rtree.0, id).is_some()
+}
+
+#[no_mangle]
+pub extern fn resvg_get_node_transform(
+ rtree: *const resvg_render_tree,
+ id: *const c_char,
+ ts: *mut resvg_transform,
+) -> bool {
+ let id = match cstr_to_str(id) {
+ Some(v) => v,
+ None => {
+ warn!("Provided ID is no an UTF-8 string.");
+ return false;
+ }
+ };
+
+ if id.is_empty() {
+ return false;
+ }
+
+ let rtree = unsafe {
+ assert!(!rtree.is_null());
+ &*rtree
+ };
+
+ if let Some(node) = node_by_id(&rtree.0, id) {
+ let abs_ts = resvg::utils::abs_transform(node);
+
+ unsafe {
+ (*ts).a = abs_ts.a;
+ (*ts).b = abs_ts.b;
+ (*ts).c = abs_ts.c;
+ (*ts).d = abs_ts.d;
+ (*ts).e = abs_ts.e;
+ (*ts).f = abs_ts.f;
+ }
+
+ return true;
+ }
+
+ false
+}
+
+fn node_by_id<'a>(
+ rtree: &'a resvg::tree::RenderTree,
+ id: &str
+) -> Option<resvg::tree::NodeRef<'a>> {
+ for node in rtree.root().descendants() {
+ if !rtree.is_in_defs(node) {
+ if node.svg_id() == id {
+ return Some(node);
+ }
+ }
+ }
+
+ None
+}