diff --git a/Cargo.toml b/Cargo.toml index 39b10dc5..afaceefc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,9 @@ name = "cairo" [features] purge-lgpl-docs = ["gtk-rs-lgpl-docs"] png = ["cairo-sys-rs/png"] +pdf = ["cairo-sys-rs/pdf"] +svg = ["cairo-sys-rs/svg"] +ps = ["cairo-sys-rs/ps"] use_glib = ["glib", "glib-sys", "gobject-sys", "cairo-sys-rs/use_glib"] embed-lgpl-docs = ["gtk-rs-lgpl-docs"] v1_12 = ["cairo-sys-rs/v1_12"] diff --git a/cairo-sys-rs/Cargo.toml b/cairo-sys-rs/Cargo.toml index 221f735a..127c577c 100644 --- a/cairo-sys-rs/Cargo.toml +++ b/cairo-sys-rs/Cargo.toml @@ -19,6 +19,9 @@ v1_12 = [] v1_14 = ["v1_12"] xlib = ["x11"] png = [] +pdf = [] +svg = [] +ps = [] xcb = [] use_glib = ["glib-sys", "gobject-sys", "glib"] diff --git a/cairo-sys-rs/src/enums.rs b/cairo-sys-rs/src/enums.rs index 63d6f3e5..e445822c 100644 --- a/cairo-sys-rs/src/enums.rs +++ b/cairo-sys-rs/src/enums.rs @@ -430,6 +430,28 @@ pub enum RegionOverlap { #[cfg(feature = "use_glib")] gvalue_impl!(RegionOverlap, ::gobject::cairo_gobject_region_overlap_get_type); +#[cfg(any(feature = "pdf", feature = "dox"))] +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PdfVersion { + _1_4, + _1_5, +} +#[cfg(any(feature = "svg", feature = "dox"))] +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SvgVersion { + _1_1, + _1_2, +} +#[cfg(any(feature = "ps", feature = "dox"))] +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PsLevel { + _2, + _3, +} + #[cfg(test)] mod tests { use super::*; diff --git a/cairo-sys-rs/src/lib.rs b/cairo-sys-rs/src/lib.rs index d21ef868..f4735cff 100644 --- a/cairo-sys-rs/src/lib.rs +++ b/cairo-sys-rs/src/lib.rs @@ -64,6 +64,13 @@ use enums::{ Operator, }; +#[cfg(any(feature = "pdf", feature = "dox"))] +use enums::PdfVersion; +#[cfg(any(feature = "svg", feature = "dox"))] +use enums::SvgVersion; +#[cfg(any(feature = "ps", feature = "dox"))] +use enums::PsLevel; + macro_rules! debug_impl { ($name:ty) => { impl ::std::fmt::Debug for $name { @@ -565,6 +572,57 @@ extern "C" { #[cfg(any(feature = "png", feature = "dox"))] pub fn cairo_surface_write_to_png_stream(surface: *mut cairo_surface_t, write_func: cairo_write_func_t, closure: *mut c_void) -> Status; + // CAIRO PDF + #[cfg(any(feature = "pdf", feature = "dox"))] + pub fn cairo_pdf_surface_create (filename: *const c_char, + width_in_points: c_double, + height_in_points: c_double) -> *mut cairo_surface_t; + #[cfg(any(feature = "pdf", feature = "dox"))] + pub fn cairo_pdf_surface_create_for_stream (write_func: cairo_write_func_t, + closure: *mut c_void, + width_in_points: c_double, + height_in_points: c_double) -> *mut cairo_surface_t; + #[cfg(any(feature = "pdf", feature = "dox"))] + pub fn cairo_pdf_surface_restrict_to_version (surface: *mut cairo_surface_t, version: PdfVersion); + // CAIRO SVG + #[cfg(any(feature = "svg", feature = "dox"))] + pub fn cairo_svg_surface_create (filename: *const c_char, + width_in_points: c_double, + height_in_points: c_double) -> *mut cairo_surface_t; + #[cfg(any(feature = "svg", feature = "dox"))] + pub fn cairo_svg_surface_create_for_stream (write_func: cairo_write_func_t, + closure: *mut c_void, + width_in_points: c_double, + height_in_points: c_double) -> *mut cairo_surface_t; + #[cfg(any(feature = "svg", feature = "dox"))] + pub fn cairo_svg_surface_restrict_to_version (surface: *mut cairo_surface_t, version: SvgVersion); + // CAIRO PS + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_create (filename: *const c_char, + width_in_points: c_double, + height_in_points: c_double) -> *mut cairo_surface_t; + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_create_for_stream (write_func: cairo_write_func_t, + closure: *mut c_void, + width_in_points: c_double, + height_in_points: c_double) -> *mut cairo_surface_t; + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_restrict_to_level (surface: *mut cairo_surface_t, version: PsLevel); + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_set_eps (surface: *mut cairo_surface_t, eps: cairo_bool_t); + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_get_eps (surface: *mut cairo_surface_t) -> cairo_bool_t; + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_set_size (surface: *mut cairo_surface_t, + width_in_points: f64, + height_in_points: f64); + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_dsc_begin_setup (surface: *mut cairo_surface_t); + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_dsc_begin_page_setup (surface: *mut cairo_surface_t); + #[cfg(any(feature = "ps", feature = "dox"))] + pub fn cairo_ps_surface_dsc_comment (surface: *mut cairo_surface_t, comment: *const c_char); + // CAIRO XCB #[cfg(any(feature = "xcb", feature = "dox"))] pub fn cairo_xcb_surface_create(connection: *mut xcb_connection_t, diff --git a/src/lib.rs b/src/lib.rs index 70677f34..5b1e31e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,8 +144,6 @@ pub use image_surface::{ ImageSurfaceData, }; -pub use pdf_surface::PDFSurface; - #[cfg(any(feature = "xcb", feature = "dox"))] pub use xcb::{ XCBConnection, @@ -163,7 +161,6 @@ pub mod prelude; mod font; mod context; mod error; -mod pdf_surface; mod image_surface; #[cfg(any(feature = "png", feature = "dox"))] mod image_surface_png; @@ -176,6 +173,15 @@ mod matrices; #[cfg(any(feature = "xcb", feature = "dox"))] mod xcb; +#[cfg(any(feature = "pdf", feature = "svg", feature = "ps", feature = "dox"))] +mod support; +#[cfg(any(feature = "pdf", feature = "dox"))] +pub mod pdf; +#[cfg(any(feature = "svg", feature = "dox"))] +pub mod svg; +#[cfg(any(feature = "ps", feature = "dox"))] +pub mod ps; + #[cfg(any(target_os = "macos", target_os = "ios", feature = "dox"))] mod quartz_surface; #[cfg(any(target_os = "macos", target_os = "ios", feature = "dox"))] diff --git a/src/pdf.rs b/src/pdf.rs new file mode 100644 index 00000000..347dbfdb --- /dev/null +++ b/src/pdf.rs @@ -0,0 +1,307 @@ +// Copyright 2015-2016, The Gtk-rs Project Developers. +// See the COPYRIGHT file at the top-level directory of this distribution. +// Licensed under the MIT license, see the LICENSE file or + +use std::ffi::CString; +use std::ops::Deref; +use std::path::Path; +use std::io; + +use ffi; +use ffi::enums::{SurfaceType, PdfVersion}; +use surface::{Surface, SurfaceExt}; +use support; + +#[cfg(feature = "use_glib")] +use glib::translate::*; + +pub struct File { + inner: Surface +} + +impl File { + #[doc(hidden)] + pub fn from(surface: Surface) -> Result { + if surface.get_type() == SurfaceType::Pdf { + Ok(File { inner: surface }) + } else { + Err(surface) + } + } + + #[doc(hidden)] + pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(Surface::from_raw_full(ptr)).unwrap() + } + + pub fn new>(width: f64, height: f64, path: P) -> File { + let path = path.as_ref().to_string_lossy().into_owned(); + let path = CString::new(path).unwrap(); + + unsafe { + Self::from_raw_full(ffi::cairo_pdf_surface_create(path.as_ptr(), width, height)) + } + } + + pub fn restrict(&self, version: PdfVersion) { + unsafe { + ffi::cairo_pdf_surface_restrict_to_version(self.inner.to_raw_none(), version); + } + } +} + +impl AsRef for File { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl Deref for File { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for File { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(from_glib_none(ptr)).unwrap() + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(from_glib_borrow(ptr)).unwrap() + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from_raw_full(ptr) + } +} + +pub struct Buffer { + inner: Surface, + #[allow(unused)] + support: support::Buffer, +} + +impl Buffer { + pub fn new(width: f64, height: f64) -> Buffer { + let support = support::Buffer::new(ffi::cairo_pdf_surface_create_for_stream, + width, height); + + Buffer { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, version: PdfVersion) { + unsafe { + ffi::cairo_pdf_surface_restrict_to_version(self.inner.to_raw_none(), version); + } + } +} + +impl AsRef<[u8]> for Buffer { + fn as_ref(&self) -> &[u8] { + self.support.as_ref() + } +} + +impl AsRef for Buffer { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl Deref for Buffer { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Buffer { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +pub struct Writer<'a> { + inner: Surface, + #[allow(unused)] + support: support::Writer<'a>, +} + +impl<'a> Writer<'a> { + pub fn new<'b, W: 'b + io::Write>(width: f64, height: f64, w: W) -> Writer<'b> { + let support = support::Writer::new(ffi::cairo_pdf_surface_create_for_stream, + width, height, w); + + Writer { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, version: PdfVersion) { + unsafe { + ffi::cairo_pdf_surface_restrict_to_version(self.inner.to_raw_none(), version); + } + } +} + +impl<'a> AsRef for Writer<'a> { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl<'a> Deref for Writer<'a> { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Writer<'a> { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +pub struct Stream<'a> { + inner: Surface, + #[allow(unused)] + support: support::Stream<'a>, +} + +impl<'a> Stream<'a> { + pub fn new<'b, F>(width: f64, height: f64, func: F) -> Stream<'b> + where F: 'b + FnMut(&[u8]) -> Result<(), ()> + { + let support = support::Stream::new(ffi::cairo_pdf_surface_create_for_stream, + width, height, func); + + Stream { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, version: PdfVersion) { + unsafe { + ffi::cairo_pdf_surface_restrict_to_version(self.inner.to_raw_none(), version); + } + } +} + +impl<'a> AsRef for Stream<'a> { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl<'a> Deref for Stream<'a> { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Stream<'a> { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +#[cfg(test)] +mod test { + use super::*; + use surface::Surface; + use context::*; + + fn draw>(surface: &T) { + let cr = Context::new(surface); + + cr.set_line_width(25.0); + + cr.set_source_rgba(1.0, 0.0, 0.0, 0.5); + cr.line_to(0.,0.); + cr.line_to(100.,100.); + cr.stroke(); + + cr.set_source_rgba(0.0, 0.0, 1.0, 0.5); + cr.line_to(0.,100.); + cr.line_to(100.,0.); + cr.stroke(); + } + + #[test] + fn buffer() { + let surface = Buffer::new(100., 100.); + draw(&surface); + surface.finish(); + } + + #[test] + fn writer() { + let file = ::std::fs::File::create("test1.pdf").unwrap(); + let surface = Writer::new(100., 100., file); + + draw(&surface); + surface.finish(); + } + + #[test] + fn stream() { + let mut vec = Vec::::new(); + let surface = Stream::new(100., 100., |data| { + vec.extend(data); + Ok(()) + }); + + draw(&surface); + surface.finish(); + } +} diff --git a/src/ps.rs b/src/ps.rs new file mode 100644 index 00000000..9edc352f --- /dev/null +++ b/src/ps.rs @@ -0,0 +1,303 @@ +// Copyright 2015-2016, The Gtk-rs Project Developers. +// See the COPYRIGHT file at the top-level directory of this distribution. +// Licensed under the MIT license, see the LICENSE file or + +use std::ffi::CString; +use std::ops::Deref; +use std::path::Path; +use std::io; + +use ffi; +use ffi::enums::{SurfaceType, PsLevel}; +use surface::{Surface, SurfaceExt}; +use support; + +#[cfg(feature = "use_glib")] +use glib::translate::*; + +pub struct File { + inner: Surface +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for File { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(from_glib_none(ptr)).unwrap() + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(from_glib_borrow(ptr)).unwrap() + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from_raw_full(ptr) + } +} + +impl File { + #[doc(hidden)] + pub fn from(surface: Surface) -> Result { + if surface.get_type() == SurfaceType::Ps { + Ok(File { inner: surface }) + } else { + Err(surface) + } + } + + #[doc(hidden)] + pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(Surface::from_raw_full(ptr)).unwrap() + } + + pub fn new>(width: f64, height: f64, path: P) -> File { + let path = path.as_ref().to_string_lossy().into_owned(); + let path = CString::new(path).unwrap(); + + unsafe { + Self::from_raw_full(ffi::cairo_ps_surface_create(path.as_ptr(), width, height)) + } + } + + pub fn restrict(&self, level: PsLevel) { + unsafe { + ffi::cairo_ps_surface_restrict_to_level(self.inner.to_raw_none(), level); + } + } +} + +impl AsRef for File { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl Deref for File { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +pub struct Buffer { + inner: Surface, + #[allow(unused)] + support: support::Buffer, +} + +impl Buffer { + pub fn new(width: f64, height: f64) -> Buffer { + let support = support::Buffer::new(ffi::cairo_ps_surface_create_for_stream, width, height); + + Buffer { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, level: PsLevel) { + unsafe { + ffi::cairo_ps_surface_restrict_to_level(self.inner.to_raw_none(), level); + } + } +} + +impl AsRef<[u8]> for Buffer { + fn as_ref(&self) -> &[u8] { + self.support.as_ref() + } +} + +impl AsRef for Buffer { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl Deref for Buffer { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Buffer { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +pub struct Writer<'a> { + inner: Surface, + #[allow(unused)] + support: support::Writer<'a>, +} + +impl<'a> Writer<'a> { + pub fn new<'b, W: 'b + io::Write>(width: f64, height: f64, w: W) -> Writer<'b> { + let support = support::Writer::new(ffi::cairo_ps_surface_create_for_stream, width, height, w); + + Writer { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, level: PsLevel) { + unsafe { + ffi::cairo_ps_surface_restrict_to_level(self.inner.to_raw_none(), level); + } + } +} + +impl<'a> AsRef for Writer<'a> { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl<'a> Deref for Writer<'a> { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Writer<'a> { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +pub struct Stream<'a> { + inner: Surface, + #[allow(unused)] + support: support::Stream<'a>, +} + +impl<'a> Stream<'a> { + pub fn new<'b, F>(width: f64, height: f64, func: F) -> Stream<'b> + where F: 'b + FnMut(&[u8]) -> Result<(), ()> + { + let support = support::Stream::new(ffi::cairo_ps_surface_create_for_stream, width, height, func); + + Stream { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, level: PsLevel) { + unsafe { + ffi::cairo_ps_surface_restrict_to_level(self.inner.to_raw_none(), level); + } + } +} + +impl<'a> AsRef for Stream<'a> { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl<'a> Deref for Stream<'a> { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Stream<'a> { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +#[cfg(test)] +mod test { + use super::*; + use surface::Surface; + use context::*; + + fn draw>(surface: &T) { + let cr = Context::new(surface); + + cr.set_line_width(25.0); + + cr.set_source_rgba(1.0, 0.0, 0.0, 0.5); + cr.line_to(0.,0.); + cr.line_to(100.,100.); + cr.stroke(); + + cr.set_source_rgba(0.0, 0.0, 1.0, 0.5); + cr.line_to(0.,100.); + cr.line_to(100.,0.); + cr.stroke(); + } + + #[test] + fn buffer() { + let surface = Buffer::new(100., 100.); + draw(&surface); + surface.finish(); + } + + #[test] + fn writer() { + let file = ::std::fs::File::create("test1.ps").unwrap(); + let surface = Writer::new(100., 100., file); + + draw(&surface); + surface.finish(); + } + + #[test] + fn stream() { + let mut vec = Vec::::new(); + let surface = Stream::new(100., 100., |data| { + vec.extend(data); + Ok(()) + }); + + draw(&surface); + surface.finish(); + } +} diff --git a/src/support.rs b/src/support.rs new file mode 100644 index 00000000..a95331d7 --- /dev/null +++ b/src/support.rs @@ -0,0 +1,133 @@ +// Copyright 2015-2016, The Gtk-rs Project Developers. +// See the COPYRIGHT file at the top-level directory of this distribution. +// Licensed under the MIT license, see the LICENSE file or + +use std::io; +use std::slice; + +use ffi; +use libc::{c_void, c_uchar, c_uint, c_double}; +use ffi::enums::Status; + +pub type Constructor = unsafe extern fn (ffi::cairo_write_func_t, *mut c_void, c_double, c_double) -> *mut ffi::cairo_surface_t; + +pub struct Stream<'a> { + ptr: *mut ffi::cairo_surface_t, + func: *mut Box<'a + FnMut(&[u8]) -> Result<(), ()>>, +} + +impl<'a> Stream<'a> { + pub fn new<'b, F>(constructor: Constructor, width: f64, height: f64, func: F) -> Stream<'b> + where F: 'b + FnMut(&[u8]) -> Result<(), ()> + { + unsafe { + unsafe extern fn write_to(func: *mut c_void, data: *mut c_uchar, length: c_uint) -> Status + { + // This is perfectly fine, lifetimes don't really exist. + let mut func: Box Result<(), ()>>> = Box::from_raw(func as *mut _); + + let data = slice::from_raw_parts(data, length as usize); + let result = match func(data) { + Ok(_) => Status::Success, + Err(_) => Status::WriteError, + }; + + // We don't want to actually drop the closure, send it back + // into limbo. + Box::into_raw(func); + + result + } + + // Type inference can't into this. + let func: *mut Box<'b + FnMut(&[u8]) -> Result<(), ()>> = Box::into_raw(Box::new(Box::new(func))); + let surface = constructor(Some(write_to), func as *mut _, width, height); + + Stream { + ptr: surface, + func: func, + } + } + } + + pub fn as_ptr(&self) -> *mut ffi::cairo_surface_t { + self.ptr + } +} + +impl<'a> Drop for Stream<'a> { + fn drop(&mut self) { + unsafe { + Box::from_raw(self.func); + } + } +} + +pub struct Writer<'a> { + inner: Stream<'a>, +} + +impl<'a> Writer<'a> { + pub fn new<'b, W: 'b + io::Write>(constructor: Constructor, width: f64, height: f64, mut w: W) -> Writer<'b> { + Writer { + inner: Stream::new(constructor, width, height, move |data| + match w.write_all(data) { + Ok(_) => Ok(()), + Err(_) => Err(()), + }), + } + } + + pub fn as_ptr(&self) -> *mut ffi::cairo_surface_t { + self.inner.as_ptr() + } +} + +pub struct Buffer { + buffer: *mut Vec, + inner: Stream<'static>, +} + +impl Buffer { + pub fn new(constructor: Constructor, width: f64, height: f64) -> Buffer { + let buffer = Box::into_raw(Box::new(Vec::new())); + + Buffer { + buffer: buffer, + inner: Stream::new(constructor, width, height, move |data| { + unsafe { + let mut out = Box::from_raw(buffer); + out.extend(data); + Box::into_raw(out); + } + + Ok(()) + }), + } + } + + pub fn as_ptr(&self) -> *mut ffi::cairo_surface_t { + self.inner.as_ptr() + } +} + +impl AsRef<[u8]> for Buffer { + fn as_ref(&self) -> &[u8] { + unsafe { + let vec = Box::from_raw(self.buffer); + let ptr = vec.as_ptr(); + let len = vec.len(); + Box::into_raw(vec); + + slice::from_raw_parts(ptr, len) + } + } +} + +impl Drop for Buffer { + fn drop(&mut self) { + unsafe { + Box::from_raw(self.buffer); + } + } +} diff --git a/src/svg.rs b/src/svg.rs new file mode 100644 index 00000000..e29cc1cb --- /dev/null +++ b/src/svg.rs @@ -0,0 +1,303 @@ +// Copyright 2015-2016, The Gtk-rs Project Developers. +// See the COPYRIGHT file at the top-level directory of this distribution. +// Licensed under the MIT license, see the LICENSE file or + +use std::ffi::CString; +use std::ops::Deref; +use std::path::Path; +use std::io; + +use ffi; +use ffi::enums::{SurfaceType, SvgVersion}; +use surface::{Surface, SurfaceExt}; +use support; + +#[cfg(feature = "use_glib")] +use glib::translate::*; + +pub struct File { + inner: Surface +} + +impl File { + #[doc(hidden)] + pub fn from(surface: Surface) -> Result { + if surface.get_type() == SurfaceType::Svg { + Ok(File { inner: surface }) + } else { + Err(surface) + } + } + + #[doc(hidden)] + pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(Surface::from_raw_full(ptr)).unwrap() + } + + pub fn new>(width: f64, height: f64, path: P) -> File { + let path = path.as_ref().to_string_lossy().into_owned(); + let path = CString::new(path).unwrap(); + + unsafe { + Self::from_raw_full(ffi::cairo_svg_surface_create(path.as_ptr(), width, height)) + } + } + + pub fn restrict(&self, version: SvgVersion) { + unsafe { + ffi::cairo_svg_surface_restrict_to_version(self.inner.to_raw_none(), version); + } + } +} + +impl AsRef for File { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl Deref for File { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for File { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(from_glib_none(ptr)).unwrap() + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from(from_glib_borrow(ptr)).unwrap() + } +} + +#[cfg(feature = "use_glib")] +impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for File { + #[inline] + unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> File { + Self::from_raw_full(ptr) + } +} + +pub struct Buffer { + inner: Surface, + #[allow(unused)] + support: support::Buffer, +} + +impl Buffer { + pub fn new(width: f64, height: f64) -> Buffer { + let support = support::Buffer::new(ffi::cairo_svg_surface_create_for_stream, width, height); + + Buffer { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, version: SvgVersion) { + unsafe { + ffi::cairo_svg_surface_restrict_to_version(self.inner.to_raw_none(), version); + } + } +} + +impl AsRef<[u8]> for Buffer { + fn as_ref(&self) -> &[u8] { + self.support.as_ref() + } +} + +impl AsRef for Buffer { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl Deref for Buffer { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Buffer { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +pub struct Writer<'a> { + inner: Surface, + #[allow(unused)] + support: support::Writer<'a>, +} + +impl<'a> Writer<'a> { + pub fn new<'b, W: 'b + io::Write>(width: f64, height: f64, w: W) -> Writer<'b> { + let support = support::Writer::new(ffi::cairo_svg_surface_create_for_stream, width, height, w); + + Writer { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, version: SvgVersion) { + unsafe { + ffi::cairo_svg_surface_restrict_to_version(self.inner.to_raw_none(), version); + } + } +} + +impl<'a> AsRef for Writer<'a> { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl<'a> Deref for Writer<'a> { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Writer<'a> { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +pub struct Stream<'a> { + inner: Surface, + #[allow(unused)] + support: support::Stream<'a>, +} + +impl<'a> Stream<'a> { + pub fn new<'b, F>(width: f64, height: f64, func: F) -> Stream<'b> + where F: 'b + FnMut(&[u8]) -> Result<(), ()> + { + let support = support::Stream::new(ffi::cairo_svg_surface_create_for_stream, width, height, func); + + Stream { + inner: unsafe { Surface::from_raw_full(support.as_ptr()) }, + support: support, + } + } + + pub fn restrict(&self, version: SvgVersion) { + unsafe { + ffi::cairo_svg_surface_restrict_to_version(self.inner.to_raw_none(), version); + } + } +} + +impl<'a> AsRef for Stream<'a> { + fn as_ref(&self) -> &Surface { + &self.inner + } +} + +impl<'a> Deref for Stream<'a> { + type Target = Surface; + + fn deref(&self) -> &Surface { + &self.inner + } +} + +#[cfg(feature = "use_glib")] +impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Stream<'a> { + type Storage = &'a Surface; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { + let stash = self.inner.to_glib_none(); + Stash(stash.0, stash.1) + } +} + +#[cfg(test)] +mod test { + use super::*; + use surface::Surface; + use context::*; + + fn draw>(surface: &T) { + let cr = Context::new(surface); + + cr.set_line_width(25.0); + + cr.set_source_rgba(1.0, 0.0, 0.0, 0.5); + cr.line_to(0.,0.); + cr.line_to(100.,100.); + cr.stroke(); + + cr.set_source_rgba(0.0, 0.0, 1.0, 0.5); + cr.line_to(0.,100.); + cr.line_to(100.,0.); + cr.stroke(); + } + + #[test] + fn buffer() { + let surface = Buffer::new(100., 100.); + draw(&surface); + surface.finish(); + } + + #[test] + fn writer() { + let file = ::std::fs::File::create("test1.svg").unwrap(); + let surface = Writer::new(100., 100., file); + + draw(&surface); + surface.finish(); + } + + #[test] + fn stream() { + let mut vec = Vec::::new(); + let surface = Stream::new(100., 100., |data| { + vec.extend(data); + Ok(()) + }); + + draw(&surface); + surface.finish(); + } +}