diff options
author | Federico Mena Quintero <federico@gnome.org> | 2020-10-29 18:13:33 -0600 |
---|---|---|
committer | Federico Mena Quintero <federico@gnome.org> | 2020-10-30 17:37:45 -0600 |
commit | 47b8889ca57d94f568d83eaf126cd2e402d962c1 (patch) | |
tree | 4c9a4ceb0f79123946fd26bc9b013aacc217148f /librsvg | |
parent | 8abf768a90556c83b01e25cdedf7b3126e47768c (diff) | |
download | librsvg-47b8889ca57d94f568d83eaf126cd2e402d962c1.tar.gz |
Move the c_api sources to src/c_api
Diffstat (limited to 'librsvg')
-rw-r--r-- | librsvg/Cargo.toml | 41 | ||||
-rw-r--r-- | librsvg/build.rs | 63 | ||||
-rw-r--r-- | librsvg/c_api.rs | 2319 | ||||
-rw-r--r-- | librsvg/color_utils.rs | 179 | ||||
-rw-r--r-- | librsvg/dpi.rs | 78 | ||||
-rw-r--r-- | librsvg/messages.rs | 119 | ||||
-rw-r--r-- | librsvg/pixbuf_utils.rs | 365 | ||||
-rw-r--r-- | librsvg/sizing.rs | 76 |
8 files changed, 0 insertions, 3240 deletions
diff --git a/librsvg/Cargo.toml b/librsvg/Cargo.toml deleted file mode 100644 index 6e8c94f7..00000000 --- a/librsvg/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "librsvg_c_api" -version = "0.0.1" -authors = ["Federico Mena Quintero <federico@gnome.org>"] -workspace = "../" -build = "build.rs" -edition = "2018" - -[lib] -name = "rsvg_c_api" -path = "lib.rs" -crate-type = [ "staticlib", "rlib" ] - -[dependencies] -bitflags = "1.0" -cairo-rs = { version="0.8.0", features=["v1_16"] } -cairo-sys-rs = "0.9.0" -cast = "0.2.3" -float-cmp = "0.8.0" -gdk-pixbuf = "0.8.0" -gdk-pixbuf-sys = "0.9.0" -glib = "0.9.0" -glib-sys = { version="0.9.1", features=["v2_50"] } -gio = { version="0.8.1", features=["v2_50"] } # per configure.ac -gio-sys = "0.9.1" -gobject-sys = "0.9.0" -libc = "0.2" -rgb = { version="0.8", features=["argb"] } -librsvg = { path = "../librsvg_crate" } -rsvg_internals = { path = "../rsvg_internals" } -url = "2" - -[dev-dependencies] -criterion = "0.3" - -[build-dependencies] -regex = "1.3.9" - -[[bench]] -name = "pixbuf_from_surface" -harness = false diff --git a/librsvg/build.rs b/librsvg/build.rs deleted file mode 100644 index 36e595d0..00000000 --- a/librsvg/build.rs +++ /dev/null @@ -1,63 +0,0 @@ -use regex::Regex; -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::io::BufReader; -use std::path::Path; - -fn main() { - let mut major = None; - let mut minor = None; - let mut micro = None; - - { - let file = File::open("../configure.ac") - .expect("builds must take place within the librsvg source tree"); - - let major_regex = Regex::new(r#"^m4_define\(\[rsvg_major_version\],\[(\d+)\]\)"#).unwrap(); - let minor_regex = Regex::new(r#"^m4_define\(\[rsvg_minor_version\],\[(\d+)\]\)"#).unwrap(); - let micro_regex = Regex::new(r#"^m4_define\(\[rsvg_micro_version\],\[(\d+)\]\)"#).unwrap(); - - for line in BufReader::new(file).lines() { - if let Ok(line) = line { - if let Some(nums) = major_regex.captures(&line) { - major = Some(String::from( - nums.get(1).expect("major_regex matched once").as_str(), - )); - } else if let Some(nums) = minor_regex.captures(&line) { - minor = Some(String::from( - nums.get(1).expect("minor_regex matched once").as_str(), - )); - } else if let Some(nums) = micro_regex.captures(&line) { - micro = Some(String::from( - nums.get(1).expect("micro_regex matched once").as_str(), - )); - } - } - } - } - - let output = Path::new(&env::var("OUT_DIR").unwrap()).join("version.rs"); - let mut file = File::create(output).expect("open version.rs for writing"); - file.write_all( - format!( - r#" -use std::os::raw::c_uint; - -#[no_mangle] -pub static rsvg_major_version: c_uint = {}; - -#[no_mangle] -pub static rsvg_minor_version: c_uint = {}; - -#[no_mangle] -pub static rsvg_micro_version: c_uint = {}; -"#, - major.expect("major version is set"), - minor.expect("minor version is set"), - micro.expect("micro version is set") - ) - .as_bytes(), - ) - .expect("write version.rs"); -} diff --git a/librsvg/c_api.rs b/librsvg/c_api.rs deleted file mode 100644 index 563508f4..00000000 --- a/librsvg/c_api.rs +++ /dev/null @@ -1,2319 +0,0 @@ -//! Rust implementation of the C API for librsvg. -//! -//! The C API of librsvg revolves around an `RsvgHandle` GObject class, which is -//! implemented as follows: -//! -//! * [`RsvgHandle`] and [`RsvgHandleClass`] are derivatives of `GObject` and -//! `GObjectClass`. These are coded explicitly, instead of using -//! `glib::subclass::simple::InstanceStruct<T>` and -//! `glib::subclass::simple::ClassStruct<T>`, as the structs need need to be kept -//! ABI-compatible with the traditional C API/ABI. -//! -//! * The actual data for a handle (e.g. the `RsvgHandle`'s private data, in GObject -//! parlance) is in [`CHandle`]. -//! -//! * Public C ABI functions are the `#[no_mangle]` functions with an `rsvg_` prefix. -//! -//! The C API is implemented in terms of the Rust API in `librsvg_crate`. In effect, -//! `RsvgHandle` is a rather convoluted builder or adapter pattern that translates all the -//! historical idiosyncrasies of the C API into the simple Rust API. -//! -//! [`RsvgHandle`]: struct.RsvgHandle.html -//! [`RsvgHandleClass`]: struct.RsvgHandleClass.html -//! [`CHandle`]: struct.CHandle.html - -use std::cell::{Cell, Ref, RefCell, RefMut}; -use std::ffi::{CStr, CString}; -use std::ops; -use std::path::PathBuf; -use std::ptr; -use std::slice; -use std::str; -use std::sync::Once; -use std::{f64, i32}; - -use bitflags::bitflags; -use gdk_pixbuf::Pixbuf; -use gio::prelude::*; -use glib::error::ErrorDomain; -use url::Url; - -use glib::object::ObjectClass; -use glib::subclass; -use glib::subclass::object::ObjectClassSubclassExt; -use glib::subclass::prelude::*; -use glib::translate::*; -use glib::value::{FromValue, FromValueOptional, SetValue}; -use glib::{ - glib_object_impl, glib_object_subclass, Bytes, Cast, ParamFlags, ParamSpec, StaticType, - ToValue, Type, Value, -}; - -use glib::types::instance_of; - -use gobject_sys::{GEnumValue, GFlagsValue}; - -use librsvg::{ - CairoRenderer, DefsLookupErrorKind, IntrinsicDimensions, Loader, LoadingError, RenderingError, - SvgHandle, -}; - -use rsvg_internals::{ - rsvg_log, - surface_utils::shared_surface::{SharedImageSurface, SurfaceType}, - RsvgLength, -}; - -use crate::dpi::Dpi; -use crate::messages::{rsvg_g_critical, rsvg_g_warning}; -use crate::pixbuf_utils::{empty_pixbuf, pixbuf_from_surface}; -use crate::sizing::LegacySize; - -include!(concat!(env!("OUT_DIR"), "/version.rs")); - -mod handle_flags { - // The following is entirely stolen from the auto-generated code - // for GBindingFlags, from gtk-rs/glib/src/gobject/auto/flags.rs - - use super::*; - - // Keep these in sync with rsvg.h:RsvgHandleFlags - #[rustfmt::skip] - bitflags! { - pub struct HandleFlags: u32 { - const NONE = 0; - const UNLIMITED = 1 << 0; - const KEEP_IMAGE_DATA = 1 << 1; - } - } - - pub type RsvgHandleFlags = libc::c_uint; - - impl ToGlib for HandleFlags { - type GlibType = RsvgHandleFlags; - - fn to_glib(&self) -> RsvgHandleFlags { - self.bits() - } - } - - impl FromGlib<RsvgHandleFlags> for HandleFlags { - fn from_glib(value: RsvgHandleFlags) -> HandleFlags { - HandleFlags::from_bits_truncate(value) - } - } - - impl StaticType for HandleFlags { - fn static_type() -> Type { - unsafe { from_glib(rsvg_handle_flags_get_type()) } - } - } - - impl<'a> FromValueOptional<'a> for HandleFlags { - unsafe fn from_value_optional(value: &Value) -> Option<Self> { - Some(FromValue::from_value(value)) - } - } - - impl<'a> FromValue<'a> for HandleFlags { - unsafe fn from_value(value: &Value) -> Self { - from_glib(gobject_sys::g_value_get_flags(value.to_glib_none().0)) - } - } - - impl SetValue for HandleFlags { - unsafe fn set_value(value: &mut Value, this: &Self) { - gobject_sys::g_value_set_flags(value.to_glib_none_mut().0, this.to_glib()) - } - } -} - -#[derive(Default, Copy, Clone)] -struct LoadFlags { - unlimited_size: bool, - keep_image_data: bool, -} - -use self::handle_flags::*; - -impl From<HandleFlags> for LoadFlags { - fn from(hflags: HandleFlags) -> LoadFlags { - LoadFlags { - unlimited_size: hflags.contains(HandleFlags::UNLIMITED), - keep_image_data: hflags.contains(HandleFlags::KEEP_IMAGE_DATA), - } - } -} - -impl From<LoadFlags> for HandleFlags { - fn from(lflags: LoadFlags) -> HandleFlags { - let mut hflags = HandleFlags::empty(); - - if lflags.unlimited_size { - hflags.insert(HandleFlags::UNLIMITED); - } - - if lflags.keep_image_data { - hflags.insert(HandleFlags::KEEP_IMAGE_DATA); - } - - hflags - } -} - -/// GObject class struct for RsvgHandle. -/// -/// This is not done through `glib::subclass::simple::ClassStruct<T>` because we need -/// to include the `_abi_padding` field for ABI compatibility with the C headers, and -/// `simple::ClassStruct` does not allow that. -#[repr(C)] -pub struct RsvgHandleClass { - // Keep this in sync with rsvg.h:RsvgHandleClass - parent: gobject_sys::GObjectClass, - - _abi_padding: [glib_sys::gpointer; 15], -} - -/// GObject instance struct for RsvgHandle. -/// -/// This is not done through `glib::subclass::simple::InstanceStruct<T>` because we need -/// to include the `_abi_padding` field for ABI compatibility with the C headers, and -/// `simple::InstanceStruct` does not allow that. -#[repr(C)] -pub struct RsvgHandle { - // Keep this in sync with rsvg.h:RsvgHandle - parent: gobject_sys::GObject, - - _abi_padding: [glib_sys::gpointer; 16], -} - -#[allow(clippy::large_enum_variant)] -enum LoadState { - // Just created the CHandle - Start, - - // Being loaded using the legacy write()/close() API - Loading { buffer: Vec<u8> }, - - ClosedOk { handle: SvgHandle }, - - ClosedError, -} - -impl LoadState { - fn set_from_loading_result( - &mut self, - result: Result<SvgHandle, LoadingError>, - ) -> Result<(), LoadingError> { - match result { - Ok(handle) => { - *self = LoadState::ClosedOk { handle }; - Ok(()) - } - - Err(e) => { - *self = LoadState::ClosedError; - Err(e) - } - } - } -} - -/// Holds the base URL for loading a handle, and the C-accessible version of it -/// -/// There is a public API to query the base URL, and we need to -/// produce a CString with it. However, that API returns a -/// *const char, so we need to maintain a long-lived CString along with the -/// internal Url. -#[derive(Default)] -struct BaseUrl { - inner: Option<BaseUrlInner>, -} - -struct BaseUrlInner { - url: Url, - cstring: CString, -} - -impl BaseUrl { - fn set(&mut self, url: Url) { - let cstring = CString::new(url.as_str()).unwrap(); - - self.inner = Some(BaseUrlInner { url, cstring }); - } - - fn get(&self) -> Option<&Url> { - self.inner.as_ref().map(|b| &b.url) - } - - fn get_gfile(&self) -> Option<gio::File> { - self.get().map(|url| gio::File::new_for_uri(url.as_str())) - } - - fn get_ptr(&self) -> *const libc::c_char { - self.inner - .as_ref() - .map(|b| b.cstring.as_ptr()) - .unwrap_or_else(ptr::null) - } -} - -#[derive(Default, Clone, Copy, Debug, PartialEq)] -#[repr(C)] -pub struct RsvgRectangle { - pub x: f64, - pub y: f64, - pub width: f64, - pub height: f64, -} - -impl From<cairo::Rectangle> for RsvgRectangle { - fn from(r: cairo::Rectangle) -> RsvgRectangle { - RsvgRectangle { - x: r.x, - y: r.y, - width: r.width, - height: r.height, - } - } -} - -impl From<RsvgRectangle> for cairo::Rectangle { - fn from(r: RsvgRectangle) -> cairo::Rectangle { - cairo::Rectangle { - x: r.x, - y: r.y, - width: r.width, - height: r.height, - } - } -} - -/// Contains all the interior mutability for a RsvgHandle to be called -/// from the C API. -pub struct CHandle { - inner: RefCell<CHandleInner>, - load_state: RefCell<LoadState>, -} - -struct CHandleInner { - dpi: Dpi, - load_flags: LoadFlags, - base_url: BaseUrl, - size_callback: SizeCallback, - is_testing: bool, -} - -unsafe impl ClassStruct for RsvgHandleClass { - type Type = CHandle; -} - -unsafe impl InstanceStruct for RsvgHandle { - type Type = CHandle; -} - -impl ops::Deref for RsvgHandleClass { - type Target = ObjectClass; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const _ as *const Self::Target) } - } -} - -impl ops::DerefMut for RsvgHandleClass { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *(self as *mut _ as *mut Self::Target) } - } -} - -static PROPERTIES: [subclass::Property; 11] = [ - subclass::Property("flags", |name| { - ParamSpec::flags( - name, - "Flags", - "Loading flags", - HandleFlags::static_type(), - 0, - ParamFlags::READWRITE | ParamFlags::CONSTRUCT_ONLY, - ) - }), - subclass::Property("dpi-x", |name| { - ParamSpec::double( - name, - "Horizontal DPI", - "Horizontal resolution in dots per inch", - 0.0, - f64::MAX, - 0.0, - ParamFlags::READWRITE | ParamFlags::CONSTRUCT, - ) - }), - subclass::Property("dpi-y", |name| { - ParamSpec::double( - name, - "Vertical DPI", - "Vertical resolution in dots per inch", - 0.0, - f64::MAX, - 0.0, - ParamFlags::READWRITE | ParamFlags::CONSTRUCT, - ) - }), - subclass::Property("base-uri", |name| { - ParamSpec::string( - name, - "Base URI", - "Base URI for resolving relative references", - None, - ParamFlags::READWRITE | ParamFlags::CONSTRUCT, - ) - }), - subclass::Property("width", |name| { - ParamSpec::int( - name, - "Image width", - "Image width", - 0, - i32::MAX, - 0, - ParamFlags::READABLE, - ) - }), - subclass::Property("height", |name| { - ParamSpec::int( - name, - "Image height", - "Image height", - 0, - i32::MAX, - 0, - ParamFlags::READABLE, - ) - }), - subclass::Property("em", |name| { - ParamSpec::double(name, "em", "em", 0.0, f64::MAX, 0.0, ParamFlags::READABLE) - }), - subclass::Property("ex", |name| { - ParamSpec::double(name, "ex", "ex", 0.0, f64::MAX, 0.0, ParamFlags::READABLE) - }), - subclass::Property("title", |name| { - ParamSpec::string(name, "deprecated", "deprecated", None, ParamFlags::READABLE) - }), - subclass::Property("desc", |name| { - ParamSpec::string(name, "deprecated", "deprecated", None, ParamFlags::READABLE) - }), - subclass::Property("metadata", |name| { - ParamSpec::string(name, "deprecated", "deprecated", None, ParamFlags::READABLE) - }), -]; - -impl ObjectSubclass for CHandle { - const NAME: &'static str = "RsvgHandle"; - - type ParentType = glib::Object; - - // We don't use subclass:simple::InstanceStruct and ClassStruct - // because we need to maintain the respective _abi_padding of each - // of RsvgHandleClass and RsvgHandle. - - type Instance = RsvgHandle; - type Class = RsvgHandleClass; - - glib_object_subclass!(); - - fn class_init(klass: &mut RsvgHandleClass) { - klass.install_properties(&PROPERTIES); - } - - fn new() -> Self { - CHandle { - inner: RefCell::new(CHandleInner { - dpi: Dpi::default(), - load_flags: LoadFlags::default(), - base_url: BaseUrl::default(), - size_callback: SizeCallback::default(), - is_testing: false, - }), - load_state: RefCell::new(LoadState::Start), - } - } -} - -impl StaticType for CHandle { - fn static_type() -> Type { - CHandle::get_type() - } -} - -impl ObjectImpl for CHandle { - glib_object_impl!(); - - fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("flags", ..) => { - let v: HandleFlags = value.get_some().expect("flags value has incorrect type"); - self.set_flags(v); - } - - subclass::Property("dpi-x", ..) => { - let dpi_x: f64 = value.get_some().expect("dpi-x value has incorrect type"); - self.set_dpi_x(dpi_x); - } - - subclass::Property("dpi-y", ..) => { - let dpi_y: f64 = value.get_some().expect("dpi-y value has incorrect type"); - self.set_dpi_y(dpi_y); - } - - subclass::Property("base-uri", ..) => { - let v: Option<String> = value.get().expect("base-uri value has incorrect type"); - - // rsvg_handle_set_base_uri() expects non-NULL URI strings, - // but the "base-uri" property can be set to NULL due to a missing - // construct-time property. - - if let Some(s) = v { - self.set_base_url(&s); - } - } - - _ => unreachable!("invalid property id {}", id), - } - } - - #[rustfmt::skip] - fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("flags", ..) => Ok(self.get_flags().to_value()), - - subclass::Property("dpi-x", ..) => Ok(self.get_dpi_x().to_value()), - subclass::Property("dpi-y", ..) => Ok(self.get_dpi_y().to_value()), - - subclass::Property("base-uri", ..) => Ok(self.get_base_url().to_value()), - - subclass::Property("width", ..) => - Ok(self.get_dimensions_or_empty().width.to_value()), - - subclass::Property("height", ..) => - Ok(self.get_dimensions_or_empty().height.to_value()), - - subclass::Property("em", ..) => - Ok(self.get_dimensions_or_empty().em.to_value()), - - subclass::Property("ex", ..) => - Ok(self.get_dimensions_or_empty().ex.to_value()), - - // the following three are deprecated - subclass::Property("title", ..) => Ok((None as Option<String>).to_value()), - subclass::Property("desc", ..) => Ok((None as Option<String>).to_value()), - subclass::Property("metadata", ..) => Ok((None as Option<String>).to_value()), - - _ => unreachable!("invalid property id={} for RsvgHandle", id), - } - } -} - -// Keep in sync with tests/src/reference.rs -pub(crate) fn checked_i32(x: f64) -> Result<i32, cairo::Status> { - cast::i32(x).map_err(|_| cairo::Status::InvalidSize) -} - -// Keep in sync with rsvg.h:RsvgPositionData -#[repr(C)] -pub struct RsvgPositionData { - pub x: libc::c_int, - pub y: libc::c_int, -} - -// Keep in sync with rsvg.h:RsvgDimensionData -#[repr(C)] -pub struct RsvgDimensionData { - pub width: libc::c_int, - pub height: libc::c_int, - pub em: f64, - pub ex: f64, -} - -impl RsvgDimensionData { - // This is not #[derive(Default)] to make it clear that it - // shouldn't be the default value for anything; it is actually a - // special case we use to indicate an error to the public API. - pub fn empty() -> RsvgDimensionData { - RsvgDimensionData { - width: 0, - height: 0, - em: 0.0, - ex: 0.0, - } - } -} - -// Keep in sync with rsvg.h:RsvgSizeFunc -pub type RsvgSizeFunc = Option< - unsafe extern "C" fn( - inout_width: *mut libc::c_int, - inout_height: *mut libc::c_int, - user_data: glib_sys::gpointer, - ), ->; - -struct SizeCallback { - size_func: RsvgSizeFunc, - user_data: glib_sys::gpointer, - destroy_notify: glib_sys::GDestroyNotify, - in_loop: Cell<bool>, -} - -impl SizeCallback { - fn new( - size_func: RsvgSizeFunc, - user_data: glib_sys::gpointer, - destroy_notify: glib_sys::GDestroyNotify, - ) -> Self { - SizeCallback { - size_func, - user_data, - destroy_notify, - in_loop: Cell::new(false), - } - } - - fn call(&self, width: libc::c_int, height: libc::c_int) -> (libc::c_int, libc::c_int) { - unsafe { - let mut w = width; - let mut h = height; - - if let Some(ref f) = self.size_func { - f(&mut w, &mut h, self.user_data); - }; - - (w, h) - } - } - - fn start_loop(&self) { - assert!(!self.in_loop.get()); - self.in_loop.set(true); - } - - fn end_loop(&self) { - assert!(self.in_loop.get()); - self.in_loop.set(false); - } - - fn get_in_loop(&self) -> bool { - self.in_loop.get() - } -} - -impl Default for SizeCallback { - fn default() -> SizeCallback { - SizeCallback { - size_func: None, - user_data: ptr::null_mut(), - destroy_notify: None, - in_loop: Cell::new(false), - } - } -} - -impl Drop for SizeCallback { - fn drop(&mut self) { - unsafe { - if let Some(ref f) = self.destroy_notify { - f(self.user_data); - }; - } - } -} - -trait CairoRectangleExt { - fn from_size(width: f64, height: f64) -> Self; -} - -impl CairoRectangleExt for cairo::Rectangle { - fn from_size(width: f64, height: f64) -> Self { - Self { - x: 0.0, - y: 0.0, - width, - height, - } - } -} - -impl CHandle { - fn set_base_url(&self, url: &str) { - let state = self.load_state.borrow(); - - match *state { - LoadState::Start => (), - _ => { - rsvg_g_critical( - "Please set the base file or URI before loading any data into RsvgHandle", - ); - return; - } - } - - match Url::parse(&url) { - Ok(u) => { - rsvg_log!("setting base_uri to \"{}\"", u.as_str()); - let mut inner = self.inner.borrow_mut(); - inner.base_url.set(u); - } - - Err(e) => { - rsvg_log!( - "not setting base_uri to \"{}\" since it is invalid: {}", - url, - e - ); - } - } - } - - fn set_base_gfile(&self, file: &gio::File) { - self.set_base_url(&file.get_uri()); - } - - fn get_base_url(&self) -> Option<String> { - let inner = self.inner.borrow(); - inner.base_url.get().map(|url| url.as_str().to_string()) - } - - fn get_base_url_as_ptr(&self) -> *const libc::c_char { - let inner = self.inner.borrow(); - inner.base_url.get_ptr() - } - - fn set_dpi_x(&self, dpi_x: f64) { - let mut inner = self.inner.borrow_mut(); - let dpi = inner.dpi; - inner.dpi = Dpi::new(dpi_x, dpi.y()); - } - - fn set_dpi_y(&self, dpi_y: f64) { - let mut inner = self.inner.borrow_mut(); - let dpi = inner.dpi; - inner.dpi = Dpi::new(dpi.x(), dpi_y); - } - - fn get_dpi_x(&self) -> f64 { - let inner = self.inner.borrow(); - inner.dpi.x() - } - - fn get_dpi_y(&self) -> f64 { - let inner = self.inner.borrow(); - inner.dpi.y() - } - - fn set_flags(&self, flags: HandleFlags) { - let mut inner = self.inner.borrow_mut(); - inner.load_flags = LoadFlags::from(flags); - } - - fn get_flags(&self) -> HandleFlags { - let inner = self.inner.borrow(); - HandleFlags::from(inner.load_flags) - } - - fn set_size_callback( - &self, - size_func: RsvgSizeFunc, - user_data: glib_sys::gpointer, - destroy_notify: glib_sys::GDestroyNotify, - ) { - let mut inner = self.inner.borrow_mut(); - inner.size_callback = SizeCallback::new(size_func, user_data, destroy_notify); - } - - fn write(&self, buf: &[u8]) { - let mut state = self.load_state.borrow_mut(); - - match *state { - LoadState::Start => { - *state = LoadState::Loading { - buffer: Vec::from(buf), - } - } - - LoadState::Loading { ref mut buffer } => { - buffer.extend_from_slice(buf); - } - - _ => { - rsvg_g_critical("Handle must not be closed in order to write to it"); - } - } - } - - fn close(&self) -> Result<(), LoadingError> { - let inner = self.inner.borrow(); - let mut state = self.load_state.borrow_mut(); - - match *state { - LoadState::Start => { - *state = LoadState::ClosedError; - Err(LoadingError::NoDataPassedToParser) - } - - LoadState::Loading { ref buffer } => { - let bytes = Bytes::from(&*buffer); - let stream = gio::MemoryInputStream::new_from_bytes(&bytes); - - let base_file = inner.base_url.get_gfile(); - self.read_stream(state, &stream.upcast(), base_file.as_ref(), None) - } - - // Closing is idempotent - LoadState::ClosedOk { .. } => Ok(()), - LoadState::ClosedError => Ok(()), - } - } - - fn read_stream_sync( - &self, - stream: &gio::InputStream, - cancellable: Option<&gio::Cancellable>, - ) -> Result<(), LoadingError> { - let state = self.load_state.borrow_mut(); - let inner = self.inner.borrow(); - - match *state { - LoadState::Start => { - let base_file = inner.base_url.get_gfile(); - self.read_stream(state, stream, base_file.as_ref(), cancellable) - } - - LoadState::Loading { .. } | LoadState::ClosedOk { .. } | LoadState::ClosedError => { - rsvg_g_critical( - "handle must not be already loaded in order to call \ - rsvg_handle_read_stream_sync()", - ); - Err(LoadingError::Unknown) - } - } - } - - fn read_stream( - &self, - mut load_state: RefMut<LoadState>, - stream: &gio::InputStream, - base_file: Option<&gio::File>, - cancellable: Option<&gio::Cancellable>, - ) -> Result<(), LoadingError> { - let loader = self.make_loader(); - - load_state.set_from_loading_result(loader.read_stream(stream, base_file, cancellable)) - } - - fn get_handle_ref(&self) -> Result<Ref<SvgHandle>, RenderingError> { - let state = self.load_state.borrow(); - - match *state { - LoadState::Start => { - rsvg_g_critical("Handle has not been loaded"); - Err(RenderingError::HandleIsNotLoaded) - } - - LoadState::Loading { .. } => { - rsvg_g_critical("Handle is still loading; call rsvg_handle_close() first"); - Err(RenderingError::HandleIsNotLoaded) - } - - LoadState::ClosedError => { - rsvg_g_critical( - "Handle could not read or parse the SVG; did you check for errors during the \ - loading stage?", - ); - Err(RenderingError::HandleIsNotLoaded) - } - - LoadState::ClosedOk { .. } => Ok(Ref::map(state, |s| match *s { - LoadState::ClosedOk { ref handle } => handle, - _ => unreachable!(), - })), - } - } - - fn make_loader(&self) -> Loader { - let inner = self.inner.borrow(); - - let mut loader = Loader::new(); - - if inner.load_flags.unlimited_size { - loader = loader.with_unlimited_size(); - } - - if inner.load_flags.keep_image_data { - loader = loader.keep_image_data(); - } - - loader - } - - fn has_sub(&self, id: &str) -> Result<bool, RenderingError> { - let handle = self.get_handle_ref()?; - handle.has_element_with_id(id).map_err(warn_on_invalid_id) - } - - fn get_dimensions_or_empty(&self) -> RsvgDimensionData { - self.get_dimensions_sub(None) - .unwrap_or_else(|_| RsvgDimensionData::empty()) - } - - fn get_dimensions_sub(&self, id: Option<&str>) -> Result<RsvgDimensionData, RenderingError> { - let inner = self.inner.borrow(); - - // This function is probably called from the cairo_render functions, - // or is being erroneously called within the size_func. - // To prevent an infinite loop we are saving the state, and - // returning a meaningless size. - if inner.size_callback.get_in_loop() { - return Ok(RsvgDimensionData { - width: 1, - height: 1, - em: 1.0, - ex: 1.0, - }); - } - - inner.size_callback.start_loop(); - - let res = self - .get_geometry_sub(id) - .and_then(|(ink_r, _)| { - // Keep these in sync with tests/src/reference.rs - let width = checked_i32(ink_r.width.round())?; - let height = checked_i32(ink_r.height.round())?; - - Ok((ink_r, width, height)) - }) - .map(|(ink_r, width, height)| { - let (w, h) = inner.size_callback.call(width, height); - - RsvgDimensionData { - width: w, - height: h, - em: ink_r.width, - ex: ink_r.height, - } - }); - - inner.size_callback.end_loop(); - - res.map_err(warn_on_invalid_id) - } - - fn get_position_sub(&self, id: Option<&str>) -> Result<RsvgPositionData, RenderingError> { - let inner = self.inner.borrow(); - - if id.is_none() { - return Ok(RsvgPositionData { x: 0, y: 0 }); - } - - self.get_geometry_sub(id) - .and_then(|(ink_r, _)| { - let width = checked_i32(ink_r.width.round())?; - let height = checked_i32(ink_r.height.round())?; - - Ok((ink_r, width, height)) - }) - .and_then(|(ink_r, width, height)| { - inner.size_callback.call(width, height); - - Ok(RsvgPositionData { - x: checked_i32(ink_r.x)?, - y: checked_i32(ink_r.y)?, - }) - }) - .map_err(warn_on_invalid_id) - } - - fn make_renderer<'a>(&self, handle_ref: &'a Ref<SvgHandle>) -> CairoRenderer<'a> { - let inner = self.inner.borrow(); - - let mut renderer = CairoRenderer::new(&*handle_ref).with_dpi(inner.dpi.x(), inner.dpi.y()); - - if inner.is_testing { - renderer = renderer.test_mode(); - } - - renderer - } - - fn get_geometry_sub( - &self, - id: Option<&str>, - ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError> { - let handle = self.get_handle_ref()?; - let renderer = self.make_renderer(&handle); - - match id { - Some(id) => renderer.geometry_for_layer(Some(id), &unit_rectangle()), - - None => renderer.legacy_document_size_in_pixels().map(|(w, h)| { - let rect = cairo::Rectangle::from_size(w, h); - (rect, rect) - }), - } - } - - fn set_stylesheet(&self, css: &str) -> Result<(), LoadingError> { - match *self.load_state.borrow_mut() { - LoadState::ClosedOk { ref mut handle } => handle.set_stylesheet(css), - - _ => { - rsvg_g_critical( - "handle must already be loaded in order to call \ - rsvg_handle_set_stylesheet()", - ); - Err(LoadingError::Unknown) - } - } - } - - fn render_cairo_sub( - &self, - cr: &cairo::Context, - id: Option<&str>, - ) -> Result<(), RenderingError> { - check_cairo_context(cr)?; - - let dimensions = self.get_dimensions_sub(None)?; - if dimensions.width == 0 || dimensions.height == 0 { - // nothing to render - return Ok(()); - } - - let viewport = cairo::Rectangle { - x: 0.0, - y: 0.0, - width: f64::from(dimensions.width), - height: f64::from(dimensions.height), - }; - - self.render_layer(cr, id, &viewport) - } - - fn get_pixbuf_sub(&self, id: Option<&str>) -> Result<Pixbuf, RenderingError> { - let dimensions = self.get_dimensions_sub(None)?; - - if dimensions.width == 0 || dimensions.height == 0 { - return empty_pixbuf(); - } - - let surface = cairo::ImageSurface::create( - cairo::Format::ARgb32, - dimensions.width, - dimensions.height, - )?; - - { - let cr = cairo::Context::new(&surface); - self.render_cairo_sub(&cr, id)?; - } - - let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?; - - pixbuf_from_surface(&surface) - } - - fn render_document( - &self, - cr: &cairo::Context, - viewport: &cairo::Rectangle, - ) -> Result<(), RenderingError> { - check_cairo_context(cr)?; - - let handle = self.get_handle_ref()?; - - let renderer = self.make_renderer(&handle); - renderer.render_document(cr, viewport) - } - - fn get_geometry_for_layer( - &self, - id: Option<&str>, - viewport: &cairo::Rectangle, - ) -> Result<(RsvgRectangle, RsvgRectangle), RenderingError> { - let handle = self.get_handle_ref()?; - let renderer = self.make_renderer(&handle); - - renderer - .geometry_for_layer(id, viewport) - .map(|(i, l)| (RsvgRectangle::from(i), RsvgRectangle::from(l))) - .map_err(warn_on_invalid_id) - } - - fn render_layer( - &self, - cr: &cairo::Context, - id: Option<&str>, - viewport: &cairo::Rectangle, - ) -> Result<(), RenderingError> { - check_cairo_context(cr)?; - - let handle = self.get_handle_ref()?; - - let renderer = self.make_renderer(&handle); - - renderer - .render_layer(cr, id, viewport) - .map_err(warn_on_invalid_id) - } - - fn get_geometry_for_element( - &self, - id: Option<&str>, - ) -> Result<(RsvgRectangle, RsvgRectangle), RenderingError> { - let handle = self.get_handle_ref()?; - - let renderer = self.make_renderer(&handle); - - renderer - .geometry_for_element(id) - .map(|(i, l)| (RsvgRectangle::from(i), RsvgRectangle::from(l))) - .map_err(warn_on_invalid_id) - } - - fn render_element( - &self, - cr: &cairo::Context, - id: Option<&str>, - element_viewport: &cairo::Rectangle, - ) -> Result<(), RenderingError> { - check_cairo_context(cr)?; - - let handle = self.get_handle_ref()?; - - let renderer = self.make_renderer(&handle); - - renderer - .render_element(cr, id, element_viewport) - .map_err(warn_on_invalid_id) - } - - fn get_intrinsic_dimensions(&self) -> Result<IntrinsicDimensions, RenderingError> { - let handle = self.get_handle_ref()?; - let renderer = self.make_renderer(&handle); - Ok(renderer.intrinsic_dimensions()) - } - - fn get_intrinsic_size_in_pixels(&self) -> Result<Option<(f64, f64)>, RenderingError> { - let handle = self.get_handle_ref()?; - let renderer = self.make_renderer(&handle); - Ok(renderer.intrinsic_size_in_pixels()) - } - - fn set_testing(&self, is_testing: bool) { - let mut inner = self.inner.borrow_mut(); - inner.is_testing = is_testing; - } -} - -pub(crate) fn unit_rectangle() -> cairo::Rectangle { - cairo::Rectangle::from_size(1.0, 1.0) -} - -fn is_rsvg_handle(obj: *const RsvgHandle) -> bool { - unsafe { instance_of::<CHandle>(obj as *const _) } -} - -fn is_input_stream(obj: *mut gio_sys::GInputStream) -> bool { - unsafe { instance_of::<gio::InputStream>(obj as *const _) } -} - -fn is_gfile(obj: *const gio_sys::GFile) -> bool { - unsafe { instance_of::<gio::File>(obj as *const _) } -} - -fn is_cancellable(obj: *mut gio_sys::GCancellable) -> bool { - unsafe { instance_of::<gio::Cancellable>(obj as *const _) } -} - -fn get_rust_handle<'a>(handle: *const RsvgHandle) -> &'a CHandle { - let handle = unsafe { &*handle }; - handle.get_impl() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_type() -> glib_sys::GType { - CHandle::get_type().to_glib() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_error_get_type() -> glib_sys::GType { - static ONCE: Once = Once::new(); - static mut ETYPE: glib_sys::GType = gobject_sys::G_TYPE_INVALID; - - // We have to store the GEnumValue in a static variable but - // that requires it to be Sync. It is not Sync by default - // because it contains pointers, so we have define a custom - // wrapper type here on which we can implement Sync. - #[repr(transparent)] - struct GEnumValueWrapper(GEnumValue); - unsafe impl Sync for GEnumValueWrapper {} - - static VALUES: [GEnumValueWrapper; 2] = [ - GEnumValueWrapper(GEnumValue { - value: RSVG_ERROR_FAILED, - value_name: b"RSVG_ERROR_FAILED\0" as *const u8 as *const _, - value_nick: b"failed\0" as *const u8 as *const _, - }), - GEnumValueWrapper(GEnumValue { - value: 0, - value_name: 0 as *const _, - value_nick: 0 as *const _, - }), - ]; - - ONCE.call_once(|| { - ETYPE = gobject_sys::g_enum_register_static( - b"RsvgError\0" as *const u8 as *const _, - &VALUES as *const GEnumValueWrapper as *const GEnumValue, - ); - }); - - ETYPE -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_flags_get_type() -> glib_sys::GType { - static ONCE: Once = Once::new(); - static mut FTYPE: glib_sys::GType = gobject_sys::G_TYPE_INVALID; - - // We have to store the GFlagsValue in a static variable but - // that requires it to be Sync. It is not Sync by default - // because it contains pointers, so we have define a custom - // wrapper type here on which we can implement Sync. - #[repr(transparent)] - struct GFlagsValueWrapper(GFlagsValue); - unsafe impl Sync for GFlagsValueWrapper {} - - static VALUES: [GFlagsValueWrapper; 4] = [ - GFlagsValueWrapper(GFlagsValue { - value: 0, // handle_flags::HandleFlags::NONE.bits(), - value_name: b"RSVG_HANDLE_FLAGS_NONE\0" as *const u8 as *const _, - value_nick: b"flags-none\0" as *const u8 as *const _, - }), - GFlagsValueWrapper(GFlagsValue { - value: 1 << 0, // HandleFlags::UNLIMITED.to_glib(), - value_name: b"RSVG_HANDLE_FLAG_UNLIMITED\0" as *const u8 as *const _, - value_nick: b"flag-unlimited\0" as *const u8 as *const _, - }), - GFlagsValueWrapper(GFlagsValue { - value: 1 << 1, // HandleFlags::KEEP_IMAGE_DATA.to_glib(), - value_name: b"RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA\0" as *const u8 as *const _, - value_nick: b"flag-keep-image-data\0" as *const u8 as *const _, - }), - GFlagsValueWrapper(GFlagsValue { - value: 0, - value_name: 0 as *const _, - value_nick: 0 as *const _, - }), - ]; - - ONCE.call_once(|| { - FTYPE = gobject_sys::g_flags_register_static( - b"RsvgHandleFlags\0" as *const u8 as *const _, - &VALUES as *const GFlagsValueWrapper as *const GFlagsValue, - ); - }); - - FTYPE -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_set_base_uri( - handle: *const RsvgHandle, - uri: *const libc::c_char, -) { - rsvg_return_if_fail! { - rsvg_handle_set_base_uri; - - is_rsvg_handle(handle), - !uri.is_null(), - } - - let rhandle = get_rust_handle(handle); - - assert!(!uri.is_null()); - let uri: String = from_glib_none(uri); - - rhandle.set_base_url(&uri); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_set_base_gfile( - handle: *const RsvgHandle, - raw_gfile: *mut gio_sys::GFile, -) { - rsvg_return_if_fail! { - rsvg_handle_set_base_gfile; - - is_rsvg_handle(handle), - is_gfile(raw_gfile), - } - - let rhandle = get_rust_handle(handle); - - assert!(!raw_gfile.is_null()); - - let file: gio::File = from_glib_none(raw_gfile); - - rhandle.set_base_gfile(&file); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_base_uri( - handle: *const RsvgHandle, -) -> *const libc::c_char { - rsvg_return_val_if_fail! { - rsvg_handle_get_base_uri => ptr::null(); - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - - rhandle.get_base_url_as_ptr() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_set_dpi(handle: *const RsvgHandle, dpi: libc::c_double) { - rsvg_return_if_fail! { - rsvg_handle_set_dpi; - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - rhandle.set_dpi_x(dpi); - rhandle.set_dpi_y(dpi); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_set_dpi_x_y( - handle: *const RsvgHandle, - dpi_x: libc::c_double, - dpi_y: libc::c_double, -) { - rsvg_return_if_fail! { - rsvg_handle_set_dpi_x_y; - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - rhandle.set_dpi_x(dpi_x); - rhandle.set_dpi_y(dpi_y); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_set_size_callback( - handle: *const RsvgHandle, - size_func: RsvgSizeFunc, - user_data: glib_sys::gpointer, - destroy_notify: glib_sys::GDestroyNotify, -) { - rsvg_return_if_fail! { - rsvg_handle_set_size_callback; - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - - rhandle.set_size_callback(size_func, user_data, destroy_notify); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_internal_set_testing( - handle: *const RsvgHandle, - testing: glib_sys::gboolean, -) { - rsvg_return_if_fail! { - rsvg_handle_internal_set_testing; - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - - rhandle.set_testing(from_glib(testing)); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_read_stream_sync( - handle: *const RsvgHandle, - stream: *mut gio_sys::GInputStream, - cancellable: *mut gio_sys::GCancellable, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_read_stream_sync => false.to_glib(); - - is_rsvg_handle(handle), - is_input_stream(stream), - cancellable.is_null() || is_cancellable(cancellable), - error.is_null() || (*error).is_null(), - } - - let rhandle = get_rust_handle(handle); - - let stream = gio::InputStream::from_glib_none(stream); - let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable); - - match rhandle.read_stream_sync(&stream, cancellable.as_ref()) { - Ok(()) => true.to_glib(), - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_write( - handle: *const RsvgHandle, - buf: *const u8, - count: usize, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_write => false.to_glib(); - - is_rsvg_handle(handle), - error.is_null() || (*error).is_null(), - (!buf.is_null() && count != 0) || (count == 0), - } - - let rhandle = get_rust_handle(handle); - let buffer = slice::from_raw_parts(buf, count); - rhandle.write(buffer); - - true.to_glib() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_close( - handle: *const RsvgHandle, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_close => false.to_glib(); - - is_rsvg_handle(handle), - error.is_null() || (*error).is_null(), - } - - let rhandle = get_rust_handle(handle); - - match rhandle.close() { - Ok(()) => true.to_glib(), - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - false.to_glib() - } - } -} -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_has_sub( - handle: *const RsvgHandle, - id: *const libc::c_char, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_has_sub => false.to_glib(); - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - - if id.is_null() { - return false.to_glib(); - } - - let id: String = from_glib_none(id); - rhandle.has_sub(&id).unwrap_or(false).to_glib() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_render_cairo( - handle: *const RsvgHandle, - cr: *mut cairo_sys::cairo_t, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_render_cairo => false.to_glib(); - - is_rsvg_handle(handle), - !cr.is_null(), - } - - let rhandle = get_rust_handle(handle); - let cr = from_glib_none(cr); - - match rhandle.render_cairo_sub(&cr, None) { - Ok(()) => true.to_glib(), - - Err(e) => { - rsvg_log!("could not render: {}", e); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_render_cairo_sub( - handle: *const RsvgHandle, - cr: *mut cairo_sys::cairo_t, - id: *const libc::c_char, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_render_cairo_sub => false.to_glib(); - - is_rsvg_handle(handle), - !cr.is_null(), - } - - let rhandle = get_rust_handle(handle); - let cr = from_glib_none(cr); - let id: Option<String> = from_glib_none(id); - - match rhandle.render_cairo_sub(&cr, id.as_deref()) { - Ok(()) => true.to_glib(), - - Err(e) => { - rsvg_log!("could not render: {}", e); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_pixbuf( - handle: *const RsvgHandle, -) -> *mut gdk_pixbuf_sys::GdkPixbuf { - rsvg_return_val_if_fail! { - rsvg_handle_get_pixbuf => ptr::null_mut(); - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - - match rhandle.get_pixbuf_sub(None) { - Ok(pixbuf) => pixbuf.to_glib_full(), - Err(e) => { - rsvg_log!("could not render: {}", e); - ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_pixbuf_sub( - handle: *const RsvgHandle, - id: *const libc::c_char, -) -> *mut gdk_pixbuf_sys::GdkPixbuf { - rsvg_return_val_if_fail! { - rsvg_handle_get_pixbuf_sub => ptr::null_mut(); - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - let id: Option<String> = from_glib_none(id); - - match rhandle.get_pixbuf_sub(id.as_deref()) { - Ok(pixbuf) => pixbuf.to_glib_full(), - Err(e) => { - rsvg_log!("could not render: {}", e); - ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_dimensions( - handle: *const RsvgHandle, - dimension_data: *mut RsvgDimensionData, -) { - rsvg_handle_get_dimensions_sub(handle, dimension_data, ptr::null()); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_dimensions_sub( - handle: *const RsvgHandle, - dimension_data: *mut RsvgDimensionData, - id: *const libc::c_char, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_get_dimensions_sub => false.to_glib(); - - is_rsvg_handle(handle), - !dimension_data.is_null(), - } - - let rhandle = get_rust_handle(handle); - - let id: Option<String> = from_glib_none(id); - - match rhandle.get_dimensions_sub(id.as_deref()) { - Ok(dimensions) => { - *dimension_data = dimensions; - true.to_glib() - } - - Err(e) => { - rsvg_log!("could not get dimensions: {}", e); - *dimension_data = RsvgDimensionData::empty(); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_position_sub( - handle: *const RsvgHandle, - position_data: *mut RsvgPositionData, - id: *const libc::c_char, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_get_position_sub => false.to_glib(); - - is_rsvg_handle(handle), - !position_data.is_null(), - } - - let rhandle = get_rust_handle(handle); - - let id: Option<String> = from_glib_none(id); - - match rhandle.get_position_sub(id.as_deref()) { - Ok(position) => { - *position_data = position; - true.to_glib() - } - - Err(e) => { - let p = &mut *position_data; - - p.x = 0; - p.y = 0; - - rsvg_log!("could not get position: {}", e); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_new() -> *const RsvgHandle { - let obj: *mut gobject_sys::GObject = glib::Object::new(CHandle::get_type(), &[]) - .unwrap() - .to_glib_full(); - - obj as *mut _ -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_new_with_flags(flags: RsvgHandleFlags) -> *const RsvgHandle { - let obj: *mut gobject_sys::GObject = - glib::Object::new(CHandle::get_type(), &[("flags", &flags)]) - .unwrap() - .to_glib_full(); - - obj as *mut _ -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_new_from_file( - filename: *const libc::c_char, - error: *mut *mut glib_sys::GError, -) -> *const RsvgHandle { - rsvg_return_val_if_fail! { - rsvg_handle_new_from_file => ptr::null(); - - !filename.is_null(), - error.is_null() || (*error).is_null(), - } - - let file = match PathOrUrl::new(filename) { - Ok(PathOrUrl::Path(path)) => gio::File::new_for_path(path), - - Ok(PathOrUrl::Url(url)) => gio::File::new_for_uri(url.as_str()), - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - return ptr::null_mut(); - } - }; - - rsvg_handle_new_from_gfile_sync(file.to_glib_none().0, 0, ptr::null_mut(), error) -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_new_from_gfile_sync( - file: *mut gio_sys::GFile, - flags: RsvgHandleFlags, - cancellable: *mut gio_sys::GCancellable, - error: *mut *mut glib_sys::GError, -) -> *const RsvgHandle { - rsvg_return_val_if_fail! { - rsvg_handle_new_from_gfile_sync => ptr::null(); - - is_gfile(file), - cancellable.is_null() || is_cancellable(cancellable), - error.is_null() || (*error).is_null(), - } - - let raw_handle = rsvg_handle_new_with_flags(flags); - - let rhandle = get_rust_handle(raw_handle); - - let file = gio::File::from_glib_none(file); - rhandle.set_base_gfile(&file); - - let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable); - - let res = file - .read(cancellable.as_ref()) - .map_err(LoadingError::from) - .and_then(|stream| rhandle.read_stream_sync(&stream.upcast(), cancellable.as_ref())); - - match res { - Ok(()) => raw_handle, - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - gobject_sys::g_object_unref(raw_handle as *mut _); - ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_new_from_stream_sync( - input_stream: *mut gio_sys::GInputStream, - base_file: *mut gio_sys::GFile, - flags: RsvgHandleFlags, - cancellable: *mut gio_sys::GCancellable, - error: *mut *mut glib_sys::GError, -) -> *const RsvgHandle { - rsvg_return_val_if_fail! { - rsvg_handle_new_from_stream_sync => ptr::null(); - - is_input_stream(input_stream), - base_file.is_null() || is_gfile(base_file), - cancellable.is_null() || is_cancellable(cancellable), - error.is_null() || (*error).is_null(), - } - - let raw_handle = rsvg_handle_new_with_flags(flags); - - let rhandle = get_rust_handle(raw_handle); - - let base_file: Option<gio::File> = from_glib_none(base_file); - if let Some(base_file) = base_file { - rhandle.set_base_gfile(&base_file); - } - - let stream: gio::InputStream = from_glib_none(input_stream); - let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable); - - match rhandle.read_stream_sync(&stream, cancellable.as_ref()) { - Ok(()) => raw_handle, - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - gobject_sys::g_object_unref(raw_handle as *mut _); - ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_new_from_data( - data: *const u8, - data_len: usize, - error: *mut *mut glib_sys::GError, -) -> *const RsvgHandle { - rsvg_return_val_if_fail! { - rsvg_handle_new_from_data => ptr::null(); - - (!data.is_null() && data_len != 0) || (data_len == 0), - data_len <= std::isize::MAX as usize, - error.is_null() || (*error).is_null(), - } - - // We create the MemoryInputStream without the gtk-rs binding because of this: - // - // - The binding doesn't provide _new_from_data(). All of the binding's ways to - // put data into a MemoryInputStream involve copying the data buffer. - // - // - We can't use glib::Bytes from the binding either, for the same reason. - // - // - For now, we are using the other C-visible constructor, so we need a raw pointer to the - // stream, anyway. - - assert!(data_len <= std::isize::MAX as usize); - let data_len = data_len as isize; - - let raw_stream = gio_sys::g_memory_input_stream_new_from_data(data as *mut u8, data_len, None); - - let ret = rsvg_handle_new_from_stream_sync( - raw_stream as *mut _, - ptr::null_mut(), // base_file - 0, // flags - ptr::null_mut(), // cancellable - error, - ); - - gobject_sys::g_object_unref(raw_stream as *mut _); - ret -} - -unsafe fn set_out_param<T: Copy>( - out_has_param: *mut glib_sys::gboolean, - out_param: *mut T, - value: &Option<T>, -) { - let has_value = if let Some(ref v) = *value { - if !out_param.is_null() { - *out_param = *v; - } - - true - } else { - false - }; - - if !out_has_param.is_null() { - *out_has_param = has_value.to_glib(); - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_free(handle: *mut RsvgHandle) { - gobject_sys::g_object_unref(handle as *mut _); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_set_stylesheet( - handle: *const RsvgHandle, - css: *const u8, - css_len: usize, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_set_stylesheet => false.to_glib(); - - is_rsvg_handle(handle), - !css.is_null() || (css.is_null() && css_len == 0), - error.is_null() || (*error).is_null(), - } - - let rhandle = get_rust_handle(handle); - - let css = match (css, css_len) { - (p, 0) if p.is_null() => "", - (_, _) => { - let s = slice::from_raw_parts(css, css_len); - match str::from_utf8(s) { - Ok(s) => s, - Err(e) => { - set_gerror(error, 0, &format!("CSS is not valid UTF-8: {}", e)); - return false.to_glib(); - } - } - } - }; - - match rhandle.set_stylesheet(css) { - Ok(()) => true.to_glib(), - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_intrinsic_dimensions( - handle: *const RsvgHandle, - out_has_width: *mut glib_sys::gboolean, - out_width: *mut RsvgLength, - out_has_height: *mut glib_sys::gboolean, - out_height: *mut RsvgLength, - out_has_viewbox: *mut glib_sys::gboolean, - out_viewbox: *mut RsvgRectangle, -) { - rsvg_return_if_fail! { - rsvg_handle_get_intrinsic_dimensions; - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - - let d = rhandle - .get_intrinsic_dimensions() - .unwrap_or_else(|_| panic!("API called out of order")); - - let w = d.width; - let h = d.height; - let r = d.vbox.map(RsvgRectangle::from); - - set_out_param(out_has_width, out_width, &w.map(Into::into)); - set_out_param(out_has_height, out_height, &h.map(Into::into)); - set_out_param(out_has_viewbox, out_viewbox, &r); -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_intrinsic_size_in_pixels( - handle: *const RsvgHandle, - out_width: *mut f64, - out_height: *mut f64, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_get_intrinsic_size_in_pixels => false.to_glib(); - - is_rsvg_handle(handle), - } - - let rhandle = get_rust_handle(handle); - - let dim = rhandle - .get_intrinsic_size_in_pixels() - .unwrap_or_else(|_| panic!("API called out of order")); - - let (w, h) = dim.unwrap_or((0.0, 0.0)); - - if !out_width.is_null() { - *out_width = w; - } - - if !out_height.is_null() { - *out_height = h; - } - - dim.is_some().to_glib() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_render_document( - handle: *const RsvgHandle, - cr: *mut cairo_sys::cairo_t, - viewport: *const RsvgRectangle, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_render_document => false.to_glib(); - - is_rsvg_handle(handle), - !cr.is_null(), - !viewport.is_null(), - error.is_null() || (*error).is_null(), - } - - let rhandle = get_rust_handle(handle); - let cr = from_glib_none(cr); - - match rhandle.render_document(&cr, &(*viewport).into()) { - Ok(()) => true.to_glib(), - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_geometry_for_layer( - handle: *mut RsvgHandle, - id: *const libc::c_char, - viewport: *const RsvgRectangle, - out_ink_rect: *mut RsvgRectangle, - out_logical_rect: *mut RsvgRectangle, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_get_geometry_for_layer => false.to_glib(); - - is_rsvg_handle(handle), - !viewport.is_null(), - error.is_null() || (*error).is_null(), - } - - let rhandle = get_rust_handle(handle); - - let id: Option<String> = from_glib_none(id); - - match rhandle.get_geometry_for_layer(id.as_deref(), &(*viewport).into()) { - Ok((ink_rect, logical_rect)) => { - if !out_ink_rect.is_null() { - *out_ink_rect = ink_rect; - } - - if !out_logical_rect.is_null() { - *out_logical_rect = logical_rect; - } - - true.to_glib() - } - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_render_layer( - handle: *const RsvgHandle, - cr: *mut cairo_sys::cairo_t, - id: *const libc::c_char, - viewport: *const RsvgRectangle, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_render_layer => false.to_glib(); - - is_rsvg_handle(handle), - !cr.is_null(), - !viewport.is_null(), - error.is_null() || (*error).is_null(), - } - - let rhandle = get_rust_handle(handle); - let cr = from_glib_none(cr); - let id: Option<String> = from_glib_none(id); - - match rhandle.render_layer(&cr, id.as_deref(), &(*viewport).into()) { - Ok(()) => true.to_glib(), - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_geometry_for_element( - handle: *const RsvgHandle, - id: *const libc::c_char, - out_ink_rect: *mut RsvgRectangle, - out_logical_rect: *mut RsvgRectangle, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_get_geometry_for_element => false.to_glib(); - - is_rsvg_handle(handle), - error.is_null() || (*error).is_null(), - } - - let rhandle = get_rust_handle(handle); - - let id: Option<String> = from_glib_none(id); - - match rhandle.get_geometry_for_element(id.as_deref()) { - Ok((ink_rect, logical_rect)) => { - if !out_ink_rect.is_null() { - *out_ink_rect = ink_rect; - } - - if !out_logical_rect.is_null() { - *out_logical_rect = logical_rect; - } - - true.to_glib() - } - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_render_element( - handle: *const RsvgHandle, - cr: *mut cairo_sys::cairo_t, - id: *const libc::c_char, - element_viewport: *const RsvgRectangle, - error: *mut *mut glib_sys::GError, -) -> glib_sys::gboolean { - rsvg_return_val_if_fail! { - rsvg_handle_render_element => false.to_glib(); - - is_rsvg_handle(handle), - !cr.is_null(), - !element_viewport.is_null(), - error.is_null() || (*error).is_null(), - } - - let rhandle = get_rust_handle(handle); - let cr = from_glib_none(cr); - let id: Option<String> = from_glib_none(id); - - match rhandle.render_element(&cr, id.as_deref(), &(*element_viewport).into()) { - Ok(()) => true.to_glib(), - - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - false.to_glib() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_desc(handle: *const RsvgHandle) -> *mut libc::c_char { - rsvg_return_val_if_fail! { - rsvg_handle_get_desc => ptr::null_mut(); - - is_rsvg_handle(handle), - } - - ptr::null_mut() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_metadata(handle: *const RsvgHandle) -> *mut libc::c_char { - rsvg_return_val_if_fail! { - rsvg_handle_get_metadata => ptr::null_mut(); - - is_rsvg_handle(handle), - } - - ptr::null_mut() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_handle_get_title(handle: *const RsvgHandle) -> *mut libc::c_char { - rsvg_return_val_if_fail! { - rsvg_handle_get_title => ptr::null_mut(); - - is_rsvg_handle(handle), - } - - ptr::null_mut() -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_init() {} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_term() {} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_cleanup() {} - -/// Detects whether a `*const libc::c_char` is a path or a URI -/// -/// `rsvg_handle_new_from_file()` takes a `filename` argument, and advertises -/// that it will detect either a file system path, or a proper URI. It will then use -/// `gio::File::new_for_path()` or `gio::File::new_for_uri()` as appropriate. -/// -/// This enum does the magic heuristics to figure this out. -enum PathOrUrl { - Path(PathBuf), - Url(Url), -} - -impl PathOrUrl { - unsafe fn new(s: *const libc::c_char) -> Result<PathOrUrl, LoadingError> { - let cstr = CStr::from_ptr(s); - - Ok(cstr - .to_str() - .map_err(|_| ()) - .and_then(|utf8| Url::parse(utf8).map_err(|_| ())) - .and_then(|url| { - if url.origin().is_tuple() || url.scheme() == "file" { - Ok(PathOrUrl::Url(url)) - } else { - Ok(PathOrUrl::Path(url.to_file_path()?)) - } - }) - .unwrap_or_else(|_| PathOrUrl::Path(PathBuf::from_glib_none(s)))) - } -} - -fn check_cairo_context(cr: &cairo::Context) -> Result<(), RenderingError> { - let status = cr.status(); - if status == cairo::Status::Success { - Ok(()) - } else { - let msg = format!( - "cannot render on a cairo_t with a failure status (status={:?})", - status, - ); - - rsvg_g_warning(&msg); - Err(RenderingError::Cairo(status)) - } -} - -fn warn_on_invalid_id(e: RenderingError) -> RenderingError { - if e == RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences) { - rsvg_g_warning("the public API is not allowed to look up external references"); - } - - e -} - -pub(crate) fn set_gerror(err: *mut *mut glib_sys::GError, code: u32, msg: &str) { - unsafe { - // this is RSVG_ERROR_FAILED, the only error code available in RsvgError - assert!(code == 0); - - // Log this, in case the calling program passes a NULL GError, so we can at least - // diagnose things by asking for RSVG_LOG. - // - // See https://gitlab.gnome.org/GNOME/gtk/issues/2294 for an example of code that - // passed a NULL GError and so we had no easy way to see what was wrong. - rsvg_log!("{}", msg); - - glib_sys::g_set_error_literal( - err, - rsvg_error_quark(), - code as libc::c_int, - msg.to_glib_none().0, - ); - } -} - -/// Used as a generic error to translate to glib::Error -/// -/// This type implements `glib::error::ErrorDomain`, so it can be used -/// to obtain the error code while calling `glib::Error::new()`. Unfortunately -/// the public librsvg API does not have detailed error codes yet, so we use -/// this single value as the only possible error code to return. -#[derive(Copy, Clone)] -struct RsvgError; - -// Keep in sync with rsvg.h:RsvgError -const RSVG_ERROR_FAILED: i32 = 0; - -impl ErrorDomain for RsvgError { - fn domain() -> glib::Quark { - glib::Quark::from_string("rsvg-error-quark") - } - - fn code(self) -> i32 { - RSVG_ERROR_FAILED - } - - fn from(_code: i32) -> Option<Self> { - // We don't have enough information from glib error codes - Some(RsvgError) - } -} - -#[no_mangle] -pub extern "C" fn rsvg_error_quark() -> glib_sys::GQuark { - RsvgError::domain().to_glib() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn path_or_url_unix() { - unsafe { - match PathOrUrl::new(b"/foo/bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Path(_) => (), - _ => panic!("unix filename should be a PathOrUrl::Path"), - } - - match PathOrUrl::new(b"foo/bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Path(_) => (), - _ => panic!("unix filename should be a PathOrUrl::Path"), - } - } - } - - #[test] - fn path_or_url_windows() { - unsafe { - match PathOrUrl::new(b"c:/foo/bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Path(_) => (), - _ => panic!("windows filename should be a PathOrUrl::Path"), - } - - match PathOrUrl::new(b"C:/foo/bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Path(_) => (), - _ => panic!("windows filename should be a PathOrUrl::Path"), - } - - match PathOrUrl::new(b"c:\\foo\\bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Path(_) => (), - _ => panic!("windows filename should be a PathOrUrl::Path"), - } - - match PathOrUrl::new(b"C:\\foo\\bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Path(_) => (), - _ => panic!("windows filename should be a PathOrUrl::Path"), - } - } - } - - #[test] - fn path_or_url_unix_url() { - unsafe { - match PathOrUrl::new(b"file:///foo/bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Url(_) => (), - _ => panic!("file:// unix filename should be a PathOrUrl::Url"), - } - } - } - - #[test] - fn path_or_url_windows_url() { - unsafe { - match PathOrUrl::new(b"file://c:/foo/bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Url(_) => (), - _ => panic!("file:// windows filename should be a PathOrUrl::Url"), - } - - match PathOrUrl::new(b"file://C:/foo/bar\0" as *const u8 as *const _).unwrap() { - PathOrUrl::Url(_) => (), - _ => panic!("file:// windows filename should be a PathOrUrl::Url"), - } - } - } - - #[test] - fn base_url_works() { - let mut u = BaseUrl::default(); - - assert!(u.get().is_none()); - assert_eq!(u.get_ptr(), ptr::null()); - - u.set(Url::parse("file:///example.txt").unwrap()); - - assert_eq!(u.get().unwrap().as_str(), "file:///example.txt"); - - unsafe { - let p = u.get_ptr(); - let cstr = CStr::from_ptr(p); - assert_eq!(cstr.to_str().unwrap(), "file:///example.txt"); - } - } -} diff --git a/librsvg/color_utils.rs b/librsvg/color_utils.rs deleted file mode 100644 index 7f2c4279..00000000 --- a/librsvg/color_utils.rs +++ /dev/null @@ -1,179 +0,0 @@ -use std::ffi::CStr; - -use rsvg_internals::{Color, Parse}; - -// There are two quirks here: -// -// First, we need to expose the Color algebraic type *and* a parse -// error to C, but we can't repr(C) them plainly. So, we define a -// ColorKind enum and a ColorSpec struct that can both be represented -// in C. -// -// Second, the C code in librsvg expects ARGB colors passed around as -// guint32. However, in Rust we'd prefer to use cssparser's RGBA -// structure, which has explicit fields for red/green/blue/alpha. -// We'll do those conversions here, for the benefit of the C code, and -// then just wait until the C code gradually disappears. - -// Keep this in sync with rsvg-css.h:RsvgCssColorKind -#[repr(C)] -#[derive(Clone, Copy, PartialEq, Debug)] -enum ColorKind { - ARGB, - ParseError, -} - -// Keep this in sync with rsvg-css.h:RsvgCssColorSpec -#[repr(C)] -#[derive(Clone, Copy, PartialEq, Debug)] -pub struct ColorSpec { - kind: ColorKind, - argb: u32, -} - -fn rgba_to_argb(r: u8, g: u8, b: u8, a: u8) -> u32 { - u32::from(a) << 24 | u32::from(r) << 16 | u32::from(g) << 8 | u32::from(b) -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_css_parse_color(string: *const libc::c_char) -> ColorSpec { - let s = CStr::from_ptr(string).to_string_lossy(); - let r = <Color as Parse>::parse_str(&s); - - match r { - Ok(Color::RGBA(rgba)) => ColorSpec { - kind: ColorKind::ARGB, - argb: rgba_to_argb(rgba.red, rgba.green, rgba.blue, rgba.alpha), - }, - - _ => ColorSpec { - kind: ColorKind::ParseError, - argb: 0, - }, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use glib::translate::*; - - fn parse(s: &str) -> ColorSpec { - unsafe { rsvg_css_parse_color(s.to_glib_none().0) } - } - - #[test] - fn parses_hash_hex_colors() { - assert_eq!( - parse("#AB10fa20"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0x20ab10fa, - } - ); - assert_eq!( - parse("#10fa20"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xff10fa20, - } - ); - assert_eq!( - parse("#abcd"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xddaabbcc, - } - ); - assert_eq!( - parse("#123"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xff112233, - } - ); - } - - #[test] - fn parses_color_keywords() { - assert_eq!( - parse("red"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xffff0000, - } - ); - assert_eq!( - parse("lime"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xff00ff00, - } - ); - assert_eq!( - parse("blue"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xff0000ff, - } - ); - } - - #[test] - fn parses_color_functions() { - assert_eq!( - parse("rgb(255, 0, 0)"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xffff0000, - } - ); - assert_eq!( - parse("rgb(0, 255, 0)"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xff00ff00, - } - ); - assert_eq!( - parse("rgb(0, 0, 255)"), - ColorSpec { - kind: ColorKind::ARGB, - argb: 0xff0000ff, - } - ); - } - - #[test] - fn current_color_is_error() { - assert_eq!( - parse("currentColor"), - ColorSpec { - kind: ColorKind::ParseError, - argb: 0, - } - ); - } - - fn make_error() -> ColorSpec { - ColorSpec { - kind: ColorKind::ParseError, - argb: 0, - } - } - - #[test] - fn invalid_hash_hex_colors_yield_error() { - assert_eq!(parse("#"), make_error()); - assert_eq!(parse("#xyz"), make_error()); - assert_eq!(parse("#112233gg"), make_error()); - } - - #[test] - fn invalid_colors_yield_error() { - assert_eq!(parse(""), make_error()); - assert_eq!(parse("foo"), make_error()); - assert_eq!(parse("rgb(chilaquil)"), make_error()); - assert_eq!(parse("rgb(1, 2, 3, 4, 5)"), make_error()); - } -} diff --git a/librsvg/dpi.rs b/librsvg/dpi.rs deleted file mode 100644 index c27667d8..00000000 --- a/librsvg/dpi.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Legacy C API for setting a default DPI (dots per inch = DPI). -//! -//! There are two deprecated functions, `rsvg_set_default_dpi` and -//! `rsvg_set_default_dpi_x_y`, which set global values for the default DPI to be used -//! with `RsvgHandle`. In turn, `RsvgHandle` assumes that when its own DPI value is set -//! to `0.0` (which is in fact its default), it will fall back to the global DPI. -//! -//! This is clearly not thread-safe, but it is the legacy behavior. -//! -//! This module encapsulates that behavior so that the `rsvg_internals` crate -//! can always have immutable DPI values as intended. - -// This is configurable at runtime -const DEFAULT_DPI_X: f64 = 90.0; -const DEFAULT_DPI_Y: f64 = 90.0; - -static mut DPI_X: f64 = DEFAULT_DPI_X; -static mut DPI_Y: f64 = DEFAULT_DPI_Y; - -#[derive(Debug, Copy, Clone, Default)] -pub(crate) struct Dpi { - x: f64, - y: f64, -} - -impl Dpi { - pub(crate) fn new(x: f64, y: f64) -> Dpi { - Dpi { x, y } - } - - pub(crate) fn x(&self) -> f64 { - if self.x <= 0.0 { - unsafe { DPI_X } - } else { - self.x - } - } - - pub(crate) fn y(&self) -> f64 { - if self.y <= 0.0 { - unsafe { DPI_Y } - } else { - self.y - } - } -} - -impl From<Dpi> for rsvg_internals::Dpi { - fn from(dpi: Dpi) -> rsvg_internals::Dpi { - rsvg_internals::Dpi::new(dpi.x(), dpi.y()) - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_set_default_dpi_x_y(dpi_x: libc::c_double, dpi_y: libc::c_double) { - if dpi_x <= 0.0 { - DPI_X = DEFAULT_DPI_X; - } else { - DPI_X = dpi_x; - } - - if dpi_y <= 0.0 { - DPI_Y = DEFAULT_DPI_Y; - } else { - DPI_Y = dpi_y; - } -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_set_default_dpi(dpi: libc::c_double) { - if dpi <= 0.0 { - DPI_X = DEFAULT_DPI_X; - DPI_Y = DEFAULT_DPI_Y; - } else { - DPI_X = dpi; - DPI_Y = dpi; - } -} diff --git a/librsvg/messages.rs b/librsvg/messages.rs deleted file mode 100644 index 9157184d..00000000 --- a/librsvg/messages.rs +++ /dev/null @@ -1,119 +0,0 @@ -use glib::translate::*; -use glib_sys::{g_log_structured_array, GLogField, G_LOG_LEVEL_CRITICAL, G_LOG_LEVEL_WARNING}; - -/* - G_LOG_LEVEL_CRITICAL = 1 << 3, - G_LOG_LEVEL_WARNING = 1 << 4, - -#define g_critical(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \ - __FILE__, G_STRINGIFY (__LINE__), \ - G_STRFUNC, __VA_ARGS__) -#define g_warning(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \ - __FILE__, G_STRINGIFY (__LINE__), \ - G_STRFUNC, __VA_ARGS__) - GLogField fields[] = - { - { "PRIORITY", log_level_to_priority (log_level), -1 }, - { "CODE_FILE", file, -1 }, - { "CODE_LINE", line, -1 }, - { "CODE_FUNC", func, -1 }, - /* Filled in later: */ - { "MESSAGE", NULL, -1 }, - /* If @log_domain is %NULL, we will not pass this field: */ - { "GLIB_DOMAIN", log_domain, -1 }, - }; - - g_log_structured_array (log_level, fields, n_fields); - */ - -/// Helper for `rsvg_g_warning` and `rsvg_g_critical` -/// -/// This simulates what in C would be a call to the g_warning() or g_critical() -/// macros, but with the underlying function g_log_structured_array(). -/// -/// If the implementation of g_warning() or g_critical() changes, we'll have -/// to change this function. -fn rsvg_g_log(level: glib_sys::GLogLevelFlags, msg: &str) { - // stolen from gmessages.c:log_level_to_priority() - let priority = match level { - G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL => b"4\0", - _ => unreachable!("please add another log level priority to rsvg_g_log()"), - }; - - let c_msg = msg.to_glib_none(); - let c_char_msg: *const libc::c_char = c_msg.0; - - // Glib's g_log_structured_standard() adds a few more fields for the source - // file, line number, etc., but those are not terribly useful without a stack - // trace. So, we'll omit them. - let fields = [ - GLogField { - key: b"PRIORITY\0" as *const u8 as *const _, - value: priority as *const u8 as *const _, - length: -1, - }, - GLogField { - key: b"MESSAGE\0" as *const u8 as *const _, - value: c_char_msg as *const _, - length: msg.len() as _, - }, - // This is the G_LOG_DOMAIN set from the Makefile - GLogField { - key: b"GLIB_DOMAIN\0" as *const u8 as *const _, - value: b"librsvg\0" as *const u8 as *const _, - length: -1, - }, - ]; - - unsafe { - g_log_structured_array(level, fields.as_ptr(), fields.len()); - } -} - -pub(crate) fn rsvg_g_warning(msg: &str) { - rsvg_g_log(glib_sys::G_LOG_LEVEL_WARNING, msg); -} - -pub(crate) fn rsvg_g_critical(msg: &str) { - rsvg_g_log(glib_sys::G_LOG_LEVEL_CRITICAL, msg); -} - -// Once Rust has a function! macro that gives us the current function name, we -// can remove the $func_name argument. -#[macro_export] -macro_rules! rsvg_return_if_fail { - { - $func_name:ident; - $($condition:expr,)+ - } => { - $( - if !$condition { - glib_sys::g_return_if_fail_warning( - b"librsvg\0" as *const u8 as *const _, - concat!(stringify!($func_name), "\0").as_ptr() as *const _, - concat!(stringify!($condition), "\0").as_ptr() as *const _, - ); - return; - } - )+ - } -} - -#[macro_export] -macro_rules! rsvg_return_val_if_fail { - { - $func_name:ident => $retval:expr; - $($condition:expr,)+ - } => { - $( - if !$condition { - glib_sys::g_return_if_fail_warning( - b"librsvg\0" as *const u8 as *const _, - concat!(stringify!($func_name), "\0").as_ptr() as *const _, - concat!(stringify!($condition), "\0").as_ptr() as *const _, - ); - return $retval; - } - )+ - } -} diff --git a/librsvg/pixbuf_utils.rs b/librsvg/pixbuf_utils.rs deleted file mode 100644 index 3766cea3..00000000 --- a/librsvg/pixbuf_utils.rs +++ /dev/null @@ -1,365 +0,0 @@ -//! Legacy C API for functions that render directly to a `GdkPixbuf`. -//! -//! This is the implementation of the `rsvg_pixbuf_*` family of functions. - -use std::path::PathBuf; -use std::ptr; - -use gdk_pixbuf::{Colorspace, Pixbuf}; -use glib::translate::*; -use librsvg::{CairoRenderer, Loader}; -use rgb::FromSlice; - -use crate::c_api::checked_i32; -use crate::dpi::Dpi; -use crate::sizing::LegacySize; - -use rsvg_internals::{ - surface_utils::shared_surface::{SharedImageSurface, SurfaceType}, - surface_utils::{Pixel, PixelOps}, - RenderingError, -}; - -use crate::c_api::set_gerror; - -fn pixbuf_new(width: i32, height: i32) -> Result<Pixbuf, RenderingError> { - assert!(width > 0 && height > 0); - - Pixbuf::new(Colorspace::Rgb, true, 8, width, height).ok_or(RenderingError::OutOfMemory) -} - -pub fn empty_pixbuf() -> Result<Pixbuf, RenderingError> { - // GdkPixbuf does not allow zero-sized pixbufs, but Cairo allows zero-sized - // surfaces. In this case, return a 1-pixel transparent pixbuf. - - let pixbuf = pixbuf_new(1, 1)?; - pixbuf.put_pixel(0, 0, 0, 0, 0, 0); - - Ok(pixbuf) -} - -pub fn pixbuf_from_surface(surface: &SharedImageSurface) -> Result<Pixbuf, RenderingError> { - let width = surface.width(); - let height = surface.height(); - - let pixbuf = pixbuf_new(width, height)?; - assert!(pixbuf.get_colorspace() == Colorspace::Rgb); - assert!(pixbuf.get_bits_per_sample() == 8); - assert!(pixbuf.get_n_channels() == 4); - - let pixels = unsafe { pixbuf.get_pixels() }; - let stride = pixbuf.get_rowstride() as usize; - - // We use chunks_mut(), not chunks_exact_mut(), because gdk-pixbuf tends - // to make the last row *not* have the full stride (i.e. it is - // only as wide as the pixels in that row). - let pixbuf_rows = pixels.chunks_mut(stride).take(height as usize); - - for (src_row, dest_row) in surface.rows().zip(pixbuf_rows) { - let row: &mut [Pixel] = dest_row.as_rgba_mut(); - - for (src, dest) in src_row.iter().zip(row.iter_mut()) { - *dest = Pixel::from(*src).unpremultiply(); - } - } - - Ok(pixbuf) -} - -enum SizeKind { - Zoom, - WidthHeight, - WidthHeightMax, - ZoomMax, -} - -struct SizeMode { - kind: SizeKind, - x_zoom: f64, - y_zoom: f64, - width: i32, - height: i32, -} - -fn get_final_size(in_width: f64, in_height: f64, size_mode: &SizeMode) -> (f64, f64) { - if in_width == 0.0 || in_height == 0.0 { - return (0.0, 0.0); - } - - let mut out_width; - let mut out_height; - - match size_mode.kind { - SizeKind::Zoom => { - out_width = size_mode.x_zoom * in_width; - out_height = size_mode.y_zoom * in_height; - } - - SizeKind::ZoomMax => { - out_width = size_mode.x_zoom * in_width; - out_height = size_mode.y_zoom * in_height; - - if out_width > f64::from(size_mode.width) || out_height > f64::from(size_mode.height) { - let zoom_x = f64::from(size_mode.width) / out_width; - let zoom_y = f64::from(size_mode.height) / out_height; - let zoom = zoom_x.min(zoom_y); - - out_width *= zoom; - out_height *= zoom; - } - } - - SizeKind::WidthHeightMax => { - let zoom_x = f64::from(size_mode.width) / in_width; - let zoom_y = f64::from(size_mode.height) / in_height; - - let zoom = zoom_x.min(zoom_y); - - out_width = zoom * in_width; - out_height = zoom * in_height; - } - - SizeKind::WidthHeight => { - if size_mode.width != -1 { - out_width = f64::from(size_mode.width); - } else { - out_width = in_width; - } - - if size_mode.height != -1 { - out_height = f64::from(size_mode.height); - } else { - out_height = in_height; - } - } - } - - (out_width, out_height) -} - -fn render_to_pixbuf_at_size( - renderer: &CairoRenderer, - document_width: f64, - document_height: f64, - desired_width: f64, - desired_height: f64, -) -> Result<Pixbuf, RenderingError> { - if desired_width == 0.0 - || desired_height == 0.0 - || document_width == 0.0 - || document_height == 0.0 - { - return empty_pixbuf(); - } - - let surface = cairo::ImageSurface::create( - cairo::Format::ARgb32, - checked_i32(desired_width.round())?, - checked_i32(desired_height.round())?, - )?; - - { - let cr = cairo::Context::new(&surface); - cr.scale( - desired_width / document_width, - desired_height / document_height, - ); - - let viewport = cairo::Rectangle { - x: 0.0, - y: 0.0, - width: document_width, - height: document_height, - }; - - // We do it with a cr transform so we can scale non-proportionally. - renderer.render_document(&cr, &viewport)?; - } - - let shared_surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?; - - pixbuf_from_surface(&shared_surface) -} - -unsafe fn pixbuf_from_file_with_size_mode( - filename: *const libc::c_char, - size_mode: &SizeMode, - error: *mut *mut glib_sys::GError, -) -> *mut gdk_pixbuf_sys::GdkPixbuf { - let path = PathBuf::from_glib_none(filename); - - let handle = match Loader::new().read_path(path) { - Ok(handle) => handle, - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - return ptr::null_mut(); - } - }; - - let dpi = Dpi::default(); - let renderer = CairoRenderer::new(&handle).with_dpi(dpi.x(), dpi.y()); - - let (document_width, document_height) = match renderer.legacy_document_size_in_pixels() { - Ok(dim) => dim, - Err(e) => { - set_gerror(error, 0, &format!("{}", e)); - return ptr::null_mut(); - } - }; - - let (desired_width, desired_height) = - get_final_size(document_width, document_height, size_mode); - - render_to_pixbuf_at_size( - &renderer, - document_width, - document_height, - desired_width, - desired_height, - ) - .map(|pixbuf| pixbuf.to_glib_full()) - .unwrap_or_else(|e| { - set_gerror(error, 0, &format!("{}", e)); - ptr::null_mut() - }) -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_pixbuf_from_file( - filename: *const libc::c_char, - error: *mut *mut glib_sys::GError, -) -> *mut gdk_pixbuf_sys::GdkPixbuf { - rsvg_return_val_if_fail! { - rsvg_pixbuf_from_file => ptr::null_mut(); - - !filename.is_null(), - error.is_null() || (*error).is_null(), - } - - pixbuf_from_file_with_size_mode( - filename, - &SizeMode { - kind: SizeKind::WidthHeight, - x_zoom: 0.0, - y_zoom: 0.0, - width: -1, - height: -1, - }, - error, - ) -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_pixbuf_from_file_at_size( - filename: *const libc::c_char, - width: libc::c_int, - height: libc::c_int, - error: *mut *mut glib_sys::GError, -) -> *mut gdk_pixbuf_sys::GdkPixbuf { - rsvg_return_val_if_fail! { - rsvg_pixbuf_from_file_at_size => ptr::null_mut(); - - !filename.is_null(), - (width >= 1 && height >= 1) || (width == -1 && height == -1), - error.is_null() || (*error).is_null(), - } - - pixbuf_from_file_with_size_mode( - filename, - &SizeMode { - kind: SizeKind::WidthHeight, - x_zoom: 0.0, - y_zoom: 0.0, - width, - height, - }, - error, - ) -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_pixbuf_from_file_at_zoom( - filename: *const libc::c_char, - x_zoom: libc::c_double, - y_zoom: libc::c_double, - error: *mut *mut glib_sys::GError, -) -> *mut gdk_pixbuf_sys::GdkPixbuf { - rsvg_return_val_if_fail! { - rsvg_pixbuf_from_file_at_zoom => ptr::null_mut(); - - !filename.is_null(), - x_zoom > 0.0 && y_zoom > 0.0, - error.is_null() || (*error).is_null(), - } - - pixbuf_from_file_with_size_mode( - filename, - &SizeMode { - kind: SizeKind::Zoom, - x_zoom, - y_zoom, - width: 0, - height: 0, - }, - error, - ) -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_pixbuf_from_file_at_zoom_with_max( - filename: *const libc::c_char, - x_zoom: libc::c_double, - y_zoom: libc::c_double, - max_width: libc::c_int, - max_height: libc::c_int, - error: *mut *mut glib_sys::GError, -) -> *mut gdk_pixbuf_sys::GdkPixbuf { - rsvg_return_val_if_fail! { - rsvg_pixbuf_from_file_at_zoom_with_max => ptr::null_mut(); - - !filename.is_null(), - x_zoom > 0.0 && y_zoom > 0.0, - max_width >= 1 && max_height >= 1, - error.is_null() || (*error).is_null(), - } - - pixbuf_from_file_with_size_mode( - filename, - &SizeMode { - kind: SizeKind::ZoomMax, - x_zoom, - y_zoom, - width: max_width, - height: max_height, - }, - error, - ) -} - -#[no_mangle] -pub unsafe extern "C" fn rsvg_pixbuf_from_file_at_max_size( - filename: *const libc::c_char, - max_width: libc::c_int, - max_height: libc::c_int, - error: *mut *mut glib_sys::GError, -) -> *mut gdk_pixbuf_sys::GdkPixbuf { - rsvg_return_val_if_fail! { - rsvg_pixbuf_from_file_at_max_size => ptr::null_mut(); - - !filename.is_null(), - max_width >= 1 && max_height >= 1, - error.is_null() || (*error).is_null(), - } - - pixbuf_from_file_with_size_mode( - filename, - &SizeMode { - kind: SizeKind::WidthHeightMax, - x_zoom: 0.0, - y_zoom: 0.0, - width: max_width, - height: max_height, - }, - error, - ) -} diff --git a/librsvg/sizing.rs b/librsvg/sizing.rs deleted file mode 100644 index 1e0b996b..00000000 --- a/librsvg/sizing.rs +++ /dev/null @@ -1,76 +0,0 @@ -use float_cmp::approx_eq; -use librsvg::{CairoRenderer, IntrinsicDimensions, Length, RenderingError}; - -use crate::c_api::unit_rectangle; - -pub trait LegacySize { - /// Returns the SVG's size suitable for the legacy C API. - /// - /// The legacy C API can compute an SVG document's size from the - /// `width`, `height`, and `viewBox` attributes of the toplevel `<svg>` - /// element. If these are not available, then the size must be computed - /// by actually measuring the geometries of elements in the document. - /// - /// See https://www.w3.org/TR/css-images-3/#sizing-terms for terminology and logic. - fn legacy_document_size_in_pixels(&self) -> Result<(f64, f64), RenderingError>; -} - -impl<'a> LegacySize for CairoRenderer<'a> { - fn legacy_document_size_in_pixels(&self) -> Result<(f64, f64), RenderingError> { - let size_from_intrinsic_dimensions = self.intrinsic_size_in_pixels().or_else(|| { - size_in_pixels_from_percentage_width_and_height(&self.intrinsic_dimensions()) - }); - - if let Some(dim) = size_from_intrinsic_dimensions { - // We have a size directly computed from the <svg> attributes - Ok(dim) - } else { - // Compute the extents of all objects in the SVG - let (ink_r, _) = self.geometry_for_layer(None, &unit_rectangle())?; - Ok((ink_r.width, ink_r.height)) - } - } -} - -/// If the width and height are in percentage units, computes a size equal to the -/// `viewBox`'s aspect ratio if it exists, or else returns None. -/// -/// For example, a `viewBox="0 0 100 200"` will yield `Some(100.0, 200.0)`. -/// -/// Note that this only checks that the width and height are in percentage units, but -/// it actually ignores their values. This is because at the point this function is -/// called, there is no viewport to embed the SVG document in, so those percentage -/// units cannot be resolved against anything in particular. The idea is to return -/// some dimensions with the correct aspect ratio. -fn size_in_pixels_from_percentage_width_and_height( - dim: &IntrinsicDimensions, -) -> Option<(f64, f64)> { - let IntrinsicDimensions { - width, - height, - vbox, - } = *dim; - - use librsvg::LengthUnit::*; - - // If both width and height are 100%, just use the vbox size as a pixel size. - // This gives a size with the correct aspect ratio. - - match (width, height, vbox) { - (None, None, Some(vbox)) => Some((vbox.width, vbox.height)), - - ( - Some(Length { - length: w, - unit: Percent, - }), - Some(Length { - length: h, - unit: Percent, - }), - Some(vbox), - ) if approx_eq!(f64, w, 1.0) && approx_eq!(f64, h, 1.0) => Some((vbox.width, vbox.height)), - - _ => None, - } -} |