use std::cell::{Cell, Ref, RefCell, RefMut};
use std::ffi::{CStr, CString, OsStr};
use std::fmt;
use std::path::PathBuf;
use std::ptr;
use std::slice;
use std::str;
use std::{f64, i32};
#[cfg(feature = "pixbuf")]
use gdk_pixbuf::Pixbuf;
use gio::prelude::*;
use glib::error::ErrorDomain;
use url::Url;
use glib::subclass::prelude::*;
use glib::translate::*;
use glib::types::instance_of;
use glib::Bytes;
use glib::{ffi::gpointer, gobject_ffi};
use rsvg::c_api_only::{rsvg_log, Session};
use rsvg::{CairoRenderer, IntrinsicDimensions, Length, Loader, LoadingError, SvgHandle};
use super::dpi::Dpi;
use super::messages::{rsvg_g_critical, rsvg_g_warning};
#[cfg(feature = "pixbuf")]
use {
super::pixbuf_utils::{empty_pixbuf, pixbuf_from_surface},
rsvg::c_api_only::{SharedImageSurface, SurfaceType},
};
use super::sizing::LegacySize;
include!(concat!(env!("OUT_DIR"), "/version.rs"));
enum RenderingError {
RenderingError(rsvg::RenderingError),
HandleIsNotLoaded,
}
impl<T: Into<rsvg::RenderingError>> From<T> for RenderingError {
fn from(e: T) -> RenderingError {
RenderingError::RenderingError(e.into())
}
}
impl fmt::Display for RenderingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
RenderingError::RenderingError(ref e) => e.fmt(f),
RenderingError::HandleIsNotLoaded => write!(f, "SVG data is not loaded into handle"),
}
}
}
#[glib::flags(name = "RsvgHandleFlags")]
pub enum HandleFlags {
#[flags_value(name = "RSVG_HANDLE_FLAGS_NONE", nick = "flags-none")]
NONE = 0,
#[flags_value(name = "RSVG_HANDLE_FLAG_UNLIMITED", nick = "flag-unlimited")]
UNLIMITED = 1 << 0,
#[flags_value(
name = "RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA",
nick = "flag-keep-image-data"
)]
KEEP_IMAGE_DATA = 1 << 1,
}
impl Default for HandleFlags {
fn default() -> Self {
Self::NONE
}
}
pub type RsvgHandleFlags = u32;
#[repr(C)]
pub struct RsvgHandleClass {
parent: gobject_ffi::GObjectClass,
_abi_padding: [gpointer; 15],
}
unsafe impl ClassStruct for RsvgHandleClass {
type Type = imp::CHandle;
}
#[repr(C)]
pub struct RsvgHandle {
parent: gobject_ffi::GObject,
_abi_padding: [gpointer; 16],
}
unsafe impl InstanceStruct for RsvgHandle {
type Type = imp::CHandle;
}
#[allow(clippy::large_enum_variant)]
enum LoadState {
Start,
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)
}
}
}
}
impl Default for LoadState {
fn default() -> Self {
Self::Start
}
}
#[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::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(Clone, Copy)]
#[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::new(r.x, r.y, r.width, r.height)
}
}
mod imp {
use std::marker::PhantomData;
use super::*;
#[derive(Default, glib::Properties)]
#[properties(wrapper_type = super::CHandle)]
pub struct CHandle {
#[property(name = "dpi-x", construct, type = f64, set = Self::set_dpi_x, get = Self::get_dpi_x)]
#[property(name = "dpi-y", construct, type = f64, set = Self::set_dpi_y, get = Self::get_dpi_y)]
#[property(name = "flags", construct_only, get, set, type = HandleFlags, member = handle_flags)]
pub(super) inner: RefCell<CHandleInner>,
#[property(name = "base-uri", construct, set = Self::set_base_url, get = Self::get_base_url)]
base_uri: PhantomData<Option<String>>,
#[property(name = "width", get = |imp: &Self| imp.obj().get_dimensions_or_empty().width)]
width: PhantomData<i32>,
#[property(name = "height", get = |imp: &Self| imp.obj().get_dimensions_or_empty().height)]
height: PhantomData<i32>,
#[property(name = "em", get = |imp: &Self| imp.obj().get_dimensions_or_empty().em)]
em: PhantomData<f64>,
#[property(name = "ex", get = |imp: &Self| imp.obj().get_dimensions_or_empty().ex)]
ex: PhantomData<f64>,
#[property(name = "title", deprecated, get = |_| None)]
title: PhantomData<Option<String>>,
#[property(name = "desc", deprecated, get = |_| None)]
desc: PhantomData<Option<String>>,
#[property(name = "metadata", deprecated, get = |_| None)]
metadata: PhantomData<Option<String>>,
pub(super) load_state: RefCell<LoadState>,
pub(super) session: Session,
}
#[derive(Default)]
pub(super) struct CHandleInner {
pub(super) dpi: Dpi,
pub(super) handle_flags: HandleFlags,
pub(super) base_url: BaseUrl,
pub(super) size_callback: SizeCallback,
pub(super) is_testing: bool,
}
#[glib::object_subclass]
impl ObjectSubclass for CHandle {
const NAME: &'static str = "RsvgHandle";
type Type = super::CHandle;
type Instance = RsvgHandle;
type Class = RsvgHandleClass;
}
#[glib::derived_properties]
impl ObjectImpl for CHandle {}
impl CHandle {
fn get_base_url(&self) -> Option<String> {
let inner = self.inner.borrow();
inner.base_url.get().map(|url| url.as_str().to_string())
}
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_base_url(&self, url: Option<&str>) {
let Some(url) = url else {
return;
};
let session = &self.session;
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!(session, "setting base_uri to \"{}\"", u.as_str());
let mut inner = self.inner.borrow_mut();
inner.base_url.set(u);
}
Err(e) => {
rsvg_log!(
session,
"not setting base_uri to \"{}\" since it is invalid: {}",
url,
e
);
}
}
}
}
}
glib::wrapper! {
pub struct CHandle(ObjectSubclass<imp::CHandle>);
}
pub(crate) fn checked_i32(x: f64) -> Result<i32, cairo::Error> {
cast::i32(x).map_err(|_| cairo::Error::InvalidSize)
}
#[repr(C)]
pub struct RsvgPositionData {
pub x: libc::c_int,
pub y: libc::c_int,
}
#[repr(C)]
pub struct RsvgDimensionData {
pub width: libc::c_int,
pub height: libc::c_int,
pub em: f64,
pub ex: f64,
}
impl RsvgDimensionData {
pub fn empty() -> RsvgDimensionData {
RsvgDimensionData {
width: 0,
height: 0,
em: 0.0,
ex: 0.0,
}
}
}
pub type RsvgSizeFunc = Option<
unsafe extern "C" fn(
inout_width: *mut libc::c_int,
inout_height: *mut libc::c_int,
user_data: gpointer,
),
>;
struct SizeCallback {
size_func: RsvgSizeFunc,
user_data: gpointer,
destroy_notify: glib::ffi::GDestroyNotify,
in_loop: Cell<bool>,
}
impl SizeCallback {
fn new(
size_func: RsvgSizeFunc,
user_data: gpointer,
destroy_notify: glib::ffi::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);
};
}
}
}
pub trait CairoRectangleExt {
fn from_size(width: f64, height: f64) -> Self;
}
impl CairoRectangleExt for cairo::Rectangle {
fn from_size(width: f64, height: f64) -> Self {
Self::new(0.0, 0.0, width, height)
}
}
impl CHandle {
fn set_base_gfile(&self, file: &gio::File) {
self.set_base_uri(&*file.uri());
}
fn get_base_url_as_ptr(&self) -> *const libc::c_char {
let inner = self.imp().inner.borrow();
inner.base_url.get_ptr()
}
fn set_size_callback(
&self,
size_func: RsvgSizeFunc,
user_data: gpointer,
destroy_notify: glib::ffi::GDestroyNotify,
) {
let mut inner = self.imp().inner.borrow_mut();
inner.size_callback = SizeCallback::new(size_func, user_data, destroy_notify);
}
fn write(&self, buf: &[u8]) {
let mut state = self.imp().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 imp = self.imp();
let inner = imp.inner.borrow();
let mut state = imp.load_state.borrow_mut();
match *state {
LoadState::Start => {
*state = LoadState::ClosedError;
Err(LoadingError::XmlParseError(String::from(
"caller did not write any data",
)))
}
LoadState::Loading { ref buffer } => {
let bytes = Bytes::from(buffer);
let stream = gio::MemoryInputStream::from_bytes(&bytes);
let base_file = inner.base_url.get_gfile();
self.read_stream(state, &stream.upcast(), base_file.as_ref(), None)
}
LoadState::ClosedOk { .. } => Ok(()),
LoadState::ClosedError => Ok(()),
}
}
fn read_stream_sync(
&self,
stream: &gio::InputStream,
cancellable: Option<&gio::Cancellable>,
) -> Result<(), LoadingError> {
let imp = self.imp();
let state = imp.load_state.borrow_mut();
let inner = imp.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::Other(String::from("API ordering")))
}
}
}
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.imp().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 imp = self.imp();
let inner = imp.inner.borrow();
let session = imp.session.clone();
Loader::new_with_session(session)
.with_unlimited_size(inner.handle_flags.contains(HandleFlags::UNLIMITED))
.keep_image_data(inner.handle_flags.contains(HandleFlags::KEEP_IMAGE_DATA))
}
fn has_sub(&self, id: &str) -> Result<bool, RenderingError> {
let handle = self.get_handle_ref()?;
Ok(handle.has_element_with_id(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.imp().inner.borrow();
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, _)| {
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
}
fn get_position_sub(&self, id: Option<&str>) -> Result<RsvgPositionData, RenderingError> {
let inner = self.imp().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())?,
})
})
}
fn make_renderer<'a>(&self, handle_ref: &'a Ref<'_, SvgHandle>) -> CairoRenderer<'a> {
let inner = self.imp().inner.borrow();
CairoRenderer::new(handle_ref)
.with_dpi(inner.dpi.x(), inner.dpi.y())
.test_mode(inner.is_testing)
}
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);
Ok(renderer.legacy_layer_geometry(id)?)
}
fn set_stylesheet(&self, css: &str) -> Result<(), LoadingError> {
match *self.imp().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::Other(String::from("API ordering")))
}
}
}
fn render_cairo_sub(
&self,
cr: *mut cairo::ffi::cairo_t,
id: Option<&str>,
) -> Result<(), RenderingError> {
let dimensions = self.get_dimensions_sub(None)?;
if dimensions.width == 0 || dimensions.height == 0 {
return Ok(());
}
let viewport = cairo::Rectangle::new(
0.0,
0.0,
f64::from(dimensions.width),
f64::from(dimensions.height),
);
self.render_layer(cr, id, &viewport)
}
#[cfg(feature = "pixbuf")]
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 Ok(empty_pixbuf()?);
}
let surface = cairo::ImageSurface::create(
cairo::Format::ARgb32,
dimensions.width,
dimensions.height,
)?;
{
let cr = cairo::Context::new(&surface)?;
let cr_raw = cr.to_raw_none();
self.render_cairo_sub(cr_raw, id)?;
}
let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?;
Ok(pixbuf_from_surface(&surface)?)
}
fn render_document(
&self,
cr: *mut cairo::ffi::cairo_t,
viewport: &cairo::Rectangle,
) -> Result<(), RenderingError> {
let cr = check_cairo_context(cr)?;
let handle = self.get_handle_ref()?;
let renderer = self.make_renderer(&handle);
Ok(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);
Ok(renderer
.geometry_for_layer(id, viewport)
.map(|(i, l)| (RsvgRectangle::from(i), RsvgRectangle::from(l)))?)
}
fn render_layer(
&self,
cr: *mut cairo::ffi::cairo_t,
id: Option<&str>,
viewport: &cairo::Rectangle,
) -> Result<(), RenderingError> {
let cr = check_cairo_context(cr)?;
let handle = self.get_handle_ref()?;
let renderer = self.make_renderer(&handle);
Ok(renderer.render_layer(&cr, id, viewport)?)
}
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);
Ok(renderer
.geometry_for_element(id)
.map(|(i, l)| (RsvgRectangle::from(i), RsvgRectangle::from(l)))?)
}
fn render_element(
&self,
cr: *mut cairo::ffi::cairo_t,
id: Option<&str>,
element_viewport: &cairo::Rectangle,
) -> Result<(), RenderingError> {
let cr = check_cairo_context(cr)?;
let handle = self.get_handle_ref()?;
let renderer = self.make_renderer(&handle);
Ok(renderer.render_element(&cr, id, element_viewport)?)
}
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.imp().inner.borrow_mut();
inner.is_testing = is_testing;
}
}
fn is_rsvg_handle(obj: *const RsvgHandle) -> bool {
unsafe { instance_of::<CHandle>(obj as *const _) }
}
fn is_input_stream(obj: *mut gio::ffi::GInputStream) -> bool {
unsafe { instance_of::<gio::InputStream>(obj as *const _) }
}
fn is_gfile(obj: *const gio::ffi::GFile) -> bool {
unsafe { instance_of::<gio::File>(obj as *const _) }
}
fn is_cancellable(obj: *mut gio::ffi::GCancellable) -> bool {
unsafe { instance_of::<gio::Cancellable>(obj as *const _) }
}
fn get_rust_handle(handle: *const RsvgHandle) -> CHandle {
let handle = unsafe { &*handle };
handle.imp().obj().to_owned()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_get_type() -> glib::ffi::GType {
CHandle::static_type().into_glib()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_error_get_type() -> glib::ffi::GType {
Error::static_type().into_glib()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_flags_get_type() -> glib::ffi::GType {
HandleFlags::static_type().into_glib()
}
#[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_uri(&*uri);
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_set_base_gfile(
handle: *const RsvgHandle,
raw_gfile: *mut gio::ffi::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: gpointer,
destroy_notify: glib::ffi::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::ffi::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));
}
trait IntoGError {
type GlibResult;
fn into_gerror(self, session: &Session, error: *mut *mut glib::ffi::GError)
-> Self::GlibResult;
fn into_g_warning(self) -> Self::GlibResult;
}
impl<E: fmt::Display> IntoGError for Result<(), E> {
type GlibResult = glib::ffi::gboolean;
fn into_gerror(
self,
session: &Session,
error: *mut *mut glib::ffi::GError,
) -> Self::GlibResult {
match self {
Ok(()) => true.into_glib(),
Err(e) => {
set_gerror(session, error, 0, &format!("{e}"));
false.into_glib()
}
}
}
fn into_g_warning(self) -> Self::GlibResult {
match self {
Ok(()) => true.into_glib(),
Err(e) => {
rsvg_g_warning(&format!("{e}"));
false.into_glib()
}
}
}
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_read_stream_sync(
handle: *const RsvgHandle,
stream: *mut gio::ffi::GInputStream,
cancellable: *mut gio::ffi::GCancellable,
error: *mut *mut glib::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_read_stream_sync => false.into_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 session = rhandle.imp().session.clone();
let stream = gio::InputStream::from_glib_none(stream);
let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable);
rhandle
.read_stream_sync(&stream, cancellable.as_ref())
.into_gerror(&session, error)
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_write(
handle: *const RsvgHandle,
buf: *const u8,
count: usize,
error: *mut *mut glib::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_write => false.into_glib();
is_rsvg_handle(handle),
error.is_null() || (*error).is_null(),
!buf.is_null() || count == 0,
}
let rhandle = get_rust_handle(handle);
let buffer = slice::from_raw_parts(buf, count);
rhandle.write(buffer);
true.into_glib()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_close(
handle: *const RsvgHandle,
error: *mut *mut glib::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_close => false.into_glib();
is_rsvg_handle(handle),
error.is_null() || (*error).is_null(),
}
let rhandle = get_rust_handle(handle);
let session = rhandle.imp().session.clone();
rhandle.close().into_gerror(&session, error)
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_has_sub(
handle: *const RsvgHandle,
id: *const libc::c_char,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_has_sub => false.into_glib();
is_rsvg_handle(handle),
}
let rhandle = get_rust_handle(handle);
if id.is_null() {
return false.into_glib();
}
let id: String = from_glib_none(id);
rhandle.has_sub(&id).unwrap_or(false).into_glib()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_render_cairo(
handle: *const RsvgHandle,
cr: *mut cairo::ffi::cairo_t,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_render_cairo => false.into_glib();
is_rsvg_handle(handle),
!cr.is_null(),
}
let rhandle = get_rust_handle(handle);
rhandle.render_cairo_sub(cr, None).into_g_warning()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_render_cairo_sub(
handle: *const RsvgHandle,
cr: *mut cairo::ffi::cairo_t,
id: *const libc::c_char,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_render_cairo_sub => false.into_glib();
is_rsvg_handle(handle),
!cr.is_null(),
}
let rhandle = get_rust_handle(handle);
let id: Option<String> = from_glib_none(id);
rhandle.render_cairo_sub(cr, id.as_deref()).into_g_warning()
}
#[no_mangle]
#[cfg(feature = "pixbuf")]
pub unsafe extern "C" fn rsvg_handle_get_pixbuf(
handle: *const RsvgHandle,
) -> *mut gdk_pixbuf::ffi::GdkPixbuf {
rsvg_return_val_if_fail! {
rsvg_handle_get_pixbuf => ptr::null_mut();
is_rsvg_handle(handle),
}
let mut error = ptr::null_mut();
let pixbuf = rsvg_handle_get_pixbuf_and_error(handle, &mut error);
if !error.is_null() {
let rhandle = get_rust_handle(handle);
let session = &rhandle.imp().session;
let msg = format!("could not render: {:?}", *error);
rsvg_log!(session, "{}", msg);
rsvg_g_warning(&msg);
return ptr::null_mut();
}
pixbuf
}
#[no_mangle]
#[cfg(feature = "pixbuf")]
pub unsafe extern "C" fn rsvg_handle_get_pixbuf_and_error(
handle: *const RsvgHandle,
error: *mut *mut glib::ffi::GError,
) -> *mut gdk_pixbuf::ffi::GdkPixbuf {
rsvg_return_val_if_fail! {
rsvg_handle_get_pixbuf_and_error => ptr::null_mut();
is_rsvg_handle(handle),
error.is_null() || (*error).is_null(),
}
let rhandle = get_rust_handle(handle);
match rhandle.get_pixbuf_sub(None) {
Ok(pixbuf) => pixbuf.to_glib_full(),
Err(e) => {
let session = &rhandle.imp().session;
set_gerror(session, error, 0, &format!("{e}"));
ptr::null_mut()
}
}
}
#[no_mangle]
#[cfg(feature = "pixbuf")]
pub unsafe extern "C" fn rsvg_handle_get_pixbuf_sub(
handle: *const RsvgHandle,
id: *const libc::c_char,
) -> *mut gdk_pixbuf::ffi::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) => {
let session = &rhandle.imp().session;
let msg = format!("could not render: {}", e);
rsvg_log!(session, "{}", msg);
rsvg_g_warning(&msg);
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::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_get_dimensions_sub => false.into_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.into_glib()
}
Err(e) => {
let session = &rhandle.imp().session;
rsvg_log!(session, "could not get dimensions: {}", e);
*dimension_data = RsvgDimensionData::empty();
false.into_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::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_get_position_sub => false.into_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.into_glib()
}
Err(e) => {
let p = &mut *position_data;
p.x = 0;
p.y = 0;
let session = &rhandle.imp().session;
rsvg_log!(session, "could not get position: {}", e);
false.into_glib()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_new() -> *const RsvgHandle {
let obj = glib::Object::new::<CHandle>();
obj.to_glib_full()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_new_with_flags(flags: RsvgHandleFlags) -> *const RsvgHandle {
let obj = glib::Object::builder::<CHandle>()
.property("flags", HandleFlags::from_bits_truncate(flags))
.build();
obj.to_glib_full()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_new_from_file(
filename: *const libc::c_char,
error: *mut *mut glib::ffi::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(p) => p.get_gfile(),
Err(s) => {
let session = Session::default();
set_gerror(&session, error, 0, &s);
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::ffi::GFile,
flags: RsvgHandleFlags,
cancellable: *mut gio::ffi::GCancellable,
error: *mut *mut glib::ffi::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 session = rhandle.imp().session.clone();
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(&session, error, 0, &format!("{e}"));
gobject_ffi::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::ffi::GInputStream,
base_file: *mut gio::ffi::GFile,
flags: RsvgHandleFlags,
cancellable: *mut gio::ffi::GCancellable,
error: *mut *mut glib::ffi::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 session = rhandle.imp().session.clone();
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(&session, error, 0, &format!("{e}"));
gobject_ffi::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::ffi::GError,
) -> *const RsvgHandle {
rsvg_return_val_if_fail! {
rsvg_handle_new_from_data => ptr::null();
!data.is_null() || data_len == 0,
data_len <= std::isize::MAX as usize,
error.is_null() || (*error).is_null(),
}
assert!(data_len <= std::isize::MAX as usize);
let data_len = data_len as isize;
let raw_stream = gio::ffi::g_memory_input_stream_new_from_data(data as *mut u8, data_len, None);
let ret = rsvg_handle_new_from_stream_sync(
raw_stream,
ptr::null_mut(), 0,
ptr::null_mut(), error,
);
gobject_ffi::g_object_unref(raw_stream as *mut _);
ret
}
unsafe fn set_out_param<T: Copy>(
out_has_param: *mut glib::ffi::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.into_glib();
}
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_free(handle: *mut RsvgHandle) {
gobject_ffi::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::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_set_stylesheet => false.into_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 session = rhandle.imp().session.clone();
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(&session, error, 0, &format!("CSS is not valid UTF-8: {e}"));
return false.into_glib();
}
}
}
};
rhandle.set_stylesheet(css).into_gerror(&session, error)
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_get_intrinsic_dimensions(
handle: *const RsvgHandle,
out_has_width: *mut glib::ffi::gboolean,
out_width: *mut Length,
out_has_height: *mut glib::ffi::gboolean,
out_height: *mut Length,
out_has_viewbox: *mut glib::ffi::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, &Into::into(w));
set_out_param(out_has_height, out_height, &Into::into(h));
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::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_get_intrinsic_size_in_pixels => false.into_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().into_glib()
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_render_document(
handle: *const RsvgHandle,
cr: *mut cairo::ffi::cairo_t,
viewport: *const RsvgRectangle,
error: *mut *mut glib::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_render_document => false.into_glib();
is_rsvg_handle(handle),
!cr.is_null(),
!viewport.is_null(),
error.is_null() || (*error).is_null(),
}
let rhandle = get_rust_handle(handle);
let session = rhandle.imp().session.clone();
rhandle
.render_document(cr, &(*viewport).into())
.into_gerror(&session, error)
}
#[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::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_get_geometry_for_layer => false.into_glib();
is_rsvg_handle(handle),
!viewport.is_null(),
error.is_null() || (*error).is_null(),
}
let rhandle = get_rust_handle(handle);
let session = rhandle.imp().session.clone();
let id: Option<String> = from_glib_none(id);
rhandle
.get_geometry_for_layer(id.as_deref(), &(*viewport).into())
.map(|(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;
}
})
.into_gerror(&session, error)
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_render_layer(
handle: *const RsvgHandle,
cr: *mut cairo::ffi::cairo_t,
id: *const libc::c_char,
viewport: *const RsvgRectangle,
error: *mut *mut glib::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_render_layer => false.into_glib();
is_rsvg_handle(handle),
!cr.is_null(),
!viewport.is_null(),
error.is_null() || (*error).is_null(),
}
let rhandle = get_rust_handle(handle);
let session = rhandle.imp().session.clone();
let id: Option<String> = from_glib_none(id);
rhandle
.render_layer(cr, id.as_deref(), &(*viewport).into())
.into_gerror(&session, error)
}
#[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::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_get_geometry_for_element => false.into_glib();
is_rsvg_handle(handle),
error.is_null() || (*error).is_null(),
}
let rhandle = get_rust_handle(handle);
let session = rhandle.imp().session.clone();
let id: Option<String> = from_glib_none(id);
rhandle
.get_geometry_for_element(id.as_deref())
.map(|(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;
}
})
.into_gerror(&session, error)
}
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_render_element(
handle: *const RsvgHandle,
cr: *mut cairo::ffi::cairo_t,
id: *const libc::c_char,
element_viewport: *const RsvgRectangle,
error: *mut *mut glib::ffi::GError,
) -> glib::ffi::gboolean {
rsvg_return_val_if_fail! {
rsvg_handle_render_element => false.into_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 session = rhandle.imp().session.clone();
let id: Option<String> = from_glib_none(id);
rhandle
.render_element(cr, id.as_deref(), &(*element_viewport).into())
.into_gerror(&session, error)
}
#[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() {}
#[derive(Clone, Debug)]
pub enum PathOrUrl {
Path(PathBuf),
Url(Url),
}
impl PathOrUrl {
unsafe fn new(s: *const libc::c_char) -> Result<PathOrUrl, String> {
let cstr = CStr::from_ptr(s);
if cstr.to_bytes().is_empty() {
return Err("invalid empty filename".to_string());
}
Ok(cstr
.to_str()
.map_err(|_| ())
.and_then(Self::try_from_str)
.unwrap_or_else(|_| PathOrUrl::Path(PathBuf::from_glib_none(s))))
}
fn try_from_str(s: &str) -> Result<PathOrUrl, ()> {
assert!(!s.is_empty());
Url::parse(s).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()?))
}
})
}
pub fn from_os_str(osstr: &OsStr) -> Result<PathOrUrl, String> {
if osstr.is_empty() {
return Err("invalid empty filename".to_string());
}
Ok(osstr
.to_str()
.ok_or(())
.and_then(Self::try_from_str)
.unwrap_or_else(|_| PathOrUrl::Path(PathBuf::from(osstr.to_os_string()))))
}
pub fn get_gfile(&self) -> gio::File {
match *self {
PathOrUrl::Path(ref p) => gio::File::for_path(p),
PathOrUrl::Url(ref u) => gio::File::for_uri(u.as_str()),
}
}
pub fn is_stdin_alias(&self) -> bool {
match *self {
PathOrUrl::Path(ref p) => matches!(p.to_str(), Some("-")),
PathOrUrl::Url(ref u) => u.as_str() == "-",
}
}
}
impl fmt::Display for PathOrUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
PathOrUrl::Path(ref p) => p.display().fmt(f),
PathOrUrl::Url(ref u) => u.fmt(f),
}
}
}
fn check_cairo_context(cr: *mut cairo::ffi::cairo_t) -> Result<cairo::Context, RenderingError> {
let status = unsafe { cairo::ffi::cairo_status(cr) };
if status == cairo::ffi::STATUS_SUCCESS {
Ok(unsafe { from_glib_none(cr) })
} else {
let status: cairo::Error = status.into();
let msg = format!("cannot render on a cairo_t with a failure status (status={status:?})");
rsvg_g_warning(&msg);
Err(RenderingError::from(status))
}
}
pub(crate) fn set_gerror(
session: &Session,
err: *mut *mut glib::ffi::GError,
code: u32,
msg: &str,
) {
unsafe {
assert!(code == 0);
rsvg_log!(session, "{}", msg);
glib::ffi::g_set_error_literal(
err,
rsvg_error_quark(),
code as libc::c_int,
msg.to_glib_none().0,
);
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)]
#[repr(u32)]
#[enum_type(name = "RsvgError")]
enum Error {
#[enum_value(name = "RSVG_ERROR_FAILED", nick = "failed")]
Failed = 0,
}
#[derive(Copy, Clone)]
struct RsvgError;
impl ErrorDomain for RsvgError {
fn domain() -> glib::Quark {
glib::Quark::from_str("rsvg-error-quark")
}
fn code(self) -> i32 {
Error::Failed as i32
}
fn from(_code: i32) -> Option<Self> {
Some(RsvgError)
}
}
#[no_mangle]
pub extern "C" fn rsvg_error_quark() -> glib::ffi::GQuark {
RsvgError::domain().into_glib()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn path_or_url_unix() {
unsafe {
match PathOrUrl::new(rsvg_c_str!("/foo/bar")).unwrap() {
PathOrUrl::Path(_) => (),
_ => panic!("unix filename should be a PathOrUrl::Path"),
}
match PathOrUrl::new(rsvg_c_str!("foo/bar")).unwrap() {
PathOrUrl::Path(_) => (),
_ => panic!("unix filename should be a PathOrUrl::Path"),
}
}
}
#[test]
fn path_or_url_windows() {
unsafe {
match PathOrUrl::new(rsvg_c_str!("c:/foo/bar")).unwrap() {
PathOrUrl::Path(_) => (),
_ => panic!("windows filename should be a PathOrUrl::Path"),
}
match PathOrUrl::new(rsvg_c_str!("C:/foo/bar")).unwrap() {
PathOrUrl::Path(_) => (),
_ => panic!("windows filename should be a PathOrUrl::Path"),
}
match PathOrUrl::new(rsvg_c_str!("c:\\foo\\bar")).unwrap() {
PathOrUrl::Path(_) => (),
_ => panic!("windows filename should be a PathOrUrl::Path"),
}
match PathOrUrl::new(rsvg_c_str!("C:\\foo\\bar")).unwrap() {
PathOrUrl::Path(_) => (),
_ => panic!("windows filename should be a PathOrUrl::Path"),
}
}
}
#[test]
fn path_or_url_unix_url() {
unsafe {
match PathOrUrl::new(rsvg_c_str!("file:///foo/bar")).unwrap() {
PathOrUrl::Url(_) => (),
_ => panic!("file:// unix filename should be a PathOrUrl::Url"),
}
}
}
#[test]
fn path_or_url_windows_url() {
unsafe {
match PathOrUrl::new(rsvg_c_str!("file://c:/foo/bar")).unwrap() {
PathOrUrl::Url(_) => (),
_ => panic!("file:// windows filename should be a PathOrUrl::Url"),
}
match PathOrUrl::new(rsvg_c_str!("file://C:/foo/bar")).unwrap() {
PathOrUrl::Url(_) => (),
_ => panic!("file:// windows filename should be a PathOrUrl::Url"),
}
}
}
#[test]
fn path_or_url_empty_str() {
unsafe {
assert!(PathOrUrl::new(rsvg_c_str!("")).is_err());
}
assert!(PathOrUrl::from_os_str(OsStr::new("")).is_err());
}
#[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");
}
}
}