├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── COPYRIGHT ├── Cargo.toml ├── LICENSE ├── README.md ├── appveyor.yml ├── build.rs ├── cairo-sys-rs ├── Cargo.toml ├── LICENSE ├── build.rs └── src │ ├── gobject.rs │ └── lib.rs └── src ├── constants.rs ├── context.rs ├── device.rs ├── enums.rs ├── error.rs ├── font ├── font_face.rs ├── font_options.rs ├── mod.rs └── scaled_font.rs ├── image_surface.rs ├── image_surface_png.rs ├── lib.rs ├── matrices.rs ├── paths.rs ├── patterns.rs ├── pdf.rs ├── ps.rs ├── quartz_surface.rs ├── recording_surface.rs ├── rectangle.rs ├── rectangle_int.rs ├── region.rs ├── stream.rs ├── surface.rs ├── surface_macros.rs ├── svg.rs ├── user_data.rs ├── utils.rs ├── win32_surface.rs └── xcb.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: gtk-rs 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/bin/ 2 | target/ 3 | doc/ 4 | configure.in 5 | config.log 6 | config.status 7 | *.dylib 8 | Makefile 9 | .rust 10 | *.so 11 | *.o 12 | *.swp 13 | Cargo.lock 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: rust 3 | os: 4 | - linux 5 | - osx 6 | rust: 7 | - nightly 8 | - beta 9 | - stable 10 | - 1.40.0 11 | env: 12 | - GTK=3.14 FEATURES= 13 | - GTK=3.24 FEATURES= 14 | jobs: 15 | exclude: 16 | - os: osx 17 | env: GTK=3.24 FEATURES= 18 | 19 | addons: 20 | apt: 21 | packages: 22 | - libgtk-3-dev 23 | - libmount-dev 24 | 25 | before_install: 26 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi 27 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink python@2; fi 28 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gtk+3 cairo atk; fi 29 | 30 | script: 31 | - rustc --version 32 | - if [ "$TRAVIS_RUST_VERSION" == "stable" ] && [ "$GTK" == "3.14" ]; then 33 | rustup component add rustfmt; 34 | cargo fmt --all -- --check; 35 | fi 36 | - cargo doc --features "dox,embed-lgpl-docs" 37 | - cargo test --features "$FEATURES,png,pdf,svg,pdf,xcb" 38 | - cargo test --no-default-features --features "$FEATURES,png,pdf,svg,pdf,xcb" 39 | - cd cairo-sys-rs 40 | - cargo rustdoc --no-default-features --features "dox xlib png pdf svg ps xcb use_glib win32-surface" 41 | - cargo rustdoc --no-default-features --features "dox xlib png pdf svg ps xcb use_glib" 42 | - cd .. 43 | # catch any sneaked in lgpl docs 44 | - cargo build --features purge-lgpl-docs 45 | - git diff -R --exit-code 46 | - mkdir .cargo 47 | - echo 'paths = [".", "./cairo-sys-rs"]' > .cargo/config 48 | - git clone -q --depth 50 -b pending https://github.com/gtk-rs/examples _examples 49 | - cd _examples 50 | - ./build_travis.sh 51 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | The Gtk-rs Project is copyright 2013-2016, The Gtk-rs Project Developers: 2 | 3 | Adam Crume 4 | Adolfo Ochagavía 5 | Andre Bogus 6 | Anton Konjahin 7 | Arne Dussin 8 | Boden Garman 9 | Brian Kropf 10 | Bryant Mairs 11 | Chris Greenaway 12 | Chris Palmer 13 | Corey Farwell 14 | Daniel Zalevskiy 15 | David Li 16 | Edward Shaw 17 | Edward Yang 18 | Esption 19 | Evgenii Pashkin 20 | Geoffrey French 21 | Gleb Kozyrev 22 | Glenn Watson 23 | Google Inc. 24 | Guillaume Gomez 25 | Gulshan Singh 26 | Jakob Gillich 27 | James Shepherdson 28 | Jeremy Letang 29 | John Vrbanac 30 | kennytm 31 | Laurence Tratt 32 | Lionel Flandrin 33 | Lucas Werkmeister 34 | Lukas Diekmann 35 | Mathijs Henquet 36 | Maxwell Koo 37 | mitaa 38 | Nick Herman 39 | Nicolas Koch 40 | Oliver Schneider 41 | Ömer Sinan Ağacan 42 | Ralph Giles 43 | Paul Dennis 44 | Paul Hendry 45 | Philipp Brüschweiler 46 | Raphael Nestler 47 | Robertas 48 | Romain Gauthier 49 | S.J.R. van Schaik 50 | Sebastian Schulze 51 | Silvio Fricke 52 | Simon Sapin 53 | Steve Klabnik 54 | Tobias Bales 55 | trolleyman 56 | Umur Gedik 57 | UrKr 58 | Vojtech Kral 59 | Zach Oakes 60 | Zach Ploskey 61 | 62 | The Gtk-rs Project is licensed under the MIT license, see the LICENSE file 63 | or . 64 | 65 | This project provides interoperability with various GNOME libraries but doesn't 66 | distribute any parts of them. Distributing compiled libraries and executables 67 | that link to those libraries may be subject to terms of the GNU LGPL, see the 68 | LGPL file. 69 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cairo-rs" 3 | license = "MIT" 4 | homepage = "https://gtk-rs.org/" 5 | authors = ["The Gtk-rs Project Developers"] 6 | keywords = ["cairo", "gtk-rs", "gnome", "GUI"] 7 | readme = "README.md" 8 | documentation = "https://gtk-rs.org/docs/cairo/" 9 | version = "0.9.0" 10 | description = "Rust bindings for the Cairo library" 11 | repository = "https://github.com/gtk-rs/cairo" 12 | build = "build.rs" 13 | 14 | [badges] 15 | appveyor = { repository = "GuillaumeGomez/cairo", service = "github" } 16 | travis-ci = { repository = "gtk-rs/cairo" } 17 | 18 | [lib] 19 | name = "cairo" 20 | 21 | [features] 22 | purge-lgpl-docs = ["gtk-rs-lgpl-docs"] 23 | png = ["cairo-sys-rs/png"] 24 | pdf = ["cairo-sys-rs/pdf"] 25 | svg = ["cairo-sys-rs/svg"] 26 | ps = ["cairo-sys-rs/ps"] 27 | use_glib = ["glib", "glib-sys", "gobject-sys", "cairo-sys-rs/use_glib"] 28 | embed-lgpl-docs = ["gtk-rs-lgpl-docs"] 29 | v1_14 = ["cairo-sys-rs/v1_14"] 30 | v1_16 = ["v1_14", "cairo-sys-rs/v1_16"] 31 | default = ["use_glib", "freetype"] 32 | freetype = ["cairo-sys-rs/freetype", "freetype-crate"] 33 | script = ["cairo-sys-rs/script"] 34 | xcb = ["cairo-sys-rs/xcb"] 35 | xlib = ["cairo-sys-rs/xlib"] 36 | dox = ["cairo-sys-rs/dox", "glib/dox"] 37 | win32-surface = ["cairo-sys-rs/win32-surface"] 38 | 39 | [package.metadata.docs.rs] 40 | features = ["dox", "embed-lgpl-docs"] 41 | 42 | [build-dependencies.gtk-rs-lgpl-docs] 43 | version = "0.1.8" 44 | optional = true 45 | git = "https://github.com/gtk-rs/lgpl-docs" 46 | 47 | [dependencies.cairo-sys-rs] 48 | path = "cairo-sys-rs" 49 | 50 | [dependencies.glib] 51 | optional = true 52 | git = "https://github.com/gtk-rs/glib" 53 | 54 | [dependencies.glib-sys] 55 | optional = true 56 | git = "https://github.com/gtk-rs/sys" 57 | 58 | [dependencies.gobject-sys] 59 | optional = true 60 | git = "https://github.com/gtk-rs/sys" 61 | 62 | [dependencies.freetype-crate] 63 | package = "freetype" 64 | version = "0.7.0" 65 | optional = true 66 | 67 | [dependencies] 68 | libc = "0.2" 69 | bitflags = "1.0" 70 | thiserror = "1.0.10" 71 | 72 | [dev-dependencies] 73 | tempfile = "3.0" 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2015, The Gtk-rs Project Developers. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cairo [![Build Status](https://travis-ci.org/gtk-rs/cairo.png?branch=master)](https://travis-ci.org/gtk-rs/cairo) [![Build status](https://ci.appveyor.com/api/projects/status/9q998histb9vk2o2?svg=true)](https://ci.appveyor.com/project/GuillaumeGomez/cairo-p0df1) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gtk-rs/gtk) 2 | 3 | Cairo bindings for Rust. 4 | 5 | - [Gtk-rs project site](https://gtk-rs.org/) 6 | 7 | - [Online documentation](https://gtk-rs.org/docs-src/) 8 | 9 | - [Readme](https://github.com/gtk-rs/gtk/blob/master/README.md) in our 10 | [main repo](https://github.com/gtk-rs/gtk) 11 | 12 | ![screenshot](https://guillaume-gomez.fr/image/cairo.png) 13 | 14 | ## License 15 | 16 | MIT 17 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - RUST: stable 4 | BITS: 32 5 | - RUST: stable 6 | BITS: 64 7 | 8 | install: 9 | - IF "%BITS%" == "32" SET ARCH=i686 10 | - IF "%BITS%" == "64" SET ARCH=x86_64 11 | - curl -sSf -o rustup-init.exe https://win.rustup.rs 12 | - rustup-init.exe --default-host "%ARCH%-pc-windows-gnu" --default-toolchain %RUST% -y 13 | # Remove old dll that broke test. See https://github.com/gtk-rs/cairo/pull/160 14 | - IF "%BITS%" == "32" ren C:\Users\appveyor\.rustup\toolchains\stable-i686-pc-windows-gnu\bin\libgcc_s_dw2-1.dll _libgcc_s_dw2-1.dll 15 | - SET PATH=C:\Users\appveyor\.cargo\bin;C:\msys64\mingw%BITS%\bin;%PATH%;C:\msys64\usr\bin 16 | - rustc -Vv 17 | - cargo -Vv 18 | - pacman --noconfirm -S mingw-w64-%ARCH%-gtk3 19 | 20 | build_script: 21 | - cargo doc --features "dox" 22 | - cargo test --features "png,pdf,svg,pdf" 23 | - cargo test --no-default-features --features "png,pdf,svg,pdf" 24 | - mkdir .cargo 25 | - echo paths = [".", "./cairo-sys-rs"] > .cargo\config 26 | - git clone -q --depth 50 -b pending https://github.com/gtk-rs/examples _examples 27 | - cd _examples 28 | - cargo build 29 | - cargo build --features "cairo-rs/png gtk_3_24" 30 | 31 | test: false 32 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | manage_docs(); 3 | } 4 | 5 | #[cfg(all( 6 | any(feature = "embed-lgpl-docs", feature = "purge-lgpl-docs"), 7 | not(all(feature = "embed-lgpl-docs", feature = "purge-lgpl-docs")) 8 | ))] 9 | fn manage_docs() { 10 | extern crate lgpl_docs; 11 | const PATH: &'static str = "src"; 12 | const IGNORES: &'static [&'static str] = &["lib.rs"]; 13 | lgpl_docs::purge(PATH, IGNORES); 14 | if cfg!(feature = "embed-lgpl-docs") { 15 | lgpl_docs::embed(lgpl_docs::Library::Cairo, PATH, IGNORES); 16 | } 17 | } 18 | 19 | #[cfg(any( 20 | all(feature = "embed-lgpl-docs", feature = "purge-lgpl-docs"), 21 | not(any(feature = "embed-lgpl-docs", feature = "purge-lgpl-docs")) 22 | ))] 23 | fn manage_docs() {} 24 | -------------------------------------------------------------------------------- /cairo-sys-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cairo-sys-rs" 3 | license = "MIT" 4 | links = "cairo" 5 | authors = ["The Gtk-rs Project Developers"] 6 | homepage = "https://gtk-rs.org/" 7 | description = "FFI bindings to libcairo" 8 | version = "0.10.0" 9 | keywords = ["cairo", "ffi", "gtk-rs", "gnome"] 10 | repository = "https://github.com/gtk-rs/cairo" 11 | build = "build.rs" 12 | 13 | [package.metadata.system-deps."cairo"] 14 | name = "cairo" 15 | version = "1.12" 16 | 17 | [package.metadata.system-deps."cairo".feature-versions] 18 | v1_14 = "1.14" 19 | v1_16 = "1.16" 20 | 21 | [package.metadata.system-deps."cairo-gobject"] 22 | name = "cairo-gobject" 23 | version = "1.12" 24 | feature = "use_glib" 25 | 26 | [package.metadata.system-deps."cairo-gobject".feature-versions] 27 | v1_14 = "1.14" 28 | v1_16 = "1.16" 29 | 30 | [lib] 31 | name = "cairo_sys" 32 | 33 | [features] 34 | dox = ["x11/dox"] 35 | v1_14 = [] 36 | v1_16 = ["v1_14"] 37 | xlib = ["x11"] 38 | png = [] 39 | pdf = [] 40 | svg = [] 41 | ps = [] 42 | freetype = [] 43 | script = [] 44 | xcb = [] 45 | use_glib = ["glib-sys"] 46 | win32-surface = ["winapi"] 47 | 48 | [dependencies] 49 | libc = "0.2" 50 | 51 | [dependencies.glib-sys] 52 | optional = true 53 | git = "https://github.com/gtk-rs/sys" 54 | 55 | [dependencies.x11] 56 | optional = true 57 | version = "2.16" 58 | features = ["xlib"] 59 | 60 | [target.'cfg(windows)'.dependencies] 61 | winapi = { version = "0.3.2", features = ["windef"], optional = true } 62 | 63 | [build-dependencies] 64 | system-deps = "1.3" 65 | -------------------------------------------------------------------------------- /cairo-sys-rs/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2015, The Gtk-rs Project Developers. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /cairo-sys-rs/build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "dox"))] 2 | extern crate system_deps; 3 | 4 | #[cfg(not(feature = "dox"))] 5 | use std::io; 6 | #[cfg(not(feature = "dox"))] 7 | use std::io::prelude::*; 8 | #[cfg(not(feature = "dox"))] 9 | use std::process; 10 | 11 | #[cfg(feature = "dox")] 12 | fn main() {} // prevent linking libraries to avoid documentation failure 13 | 14 | #[cfg(not(feature = "dox"))] 15 | fn main() { 16 | if let Err(s) = system_deps::Config::new().probe() { 17 | let _ = eprintln!("{}", s); 18 | process::exit(1); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cairo-sys-rs/src/gobject.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use glib_ffi; 6 | 7 | extern "C" { 8 | pub fn cairo_gobject_context_get_type() -> glib_ffi::GType; 9 | pub fn cairo_gobject_device_get_type() -> glib_ffi::GType; 10 | pub fn cairo_gobject_matrix_get_type() -> glib_ffi::GType; 11 | pub fn cairo_gobject_pattern_get_type() -> glib_ffi::GType; 12 | pub fn cairo_gobject_surface_get_type() -> glib_ffi::GType; 13 | pub fn cairo_gobject_rectangle_get_type() -> glib_ffi::GType; 14 | pub fn cairo_gobject_scaled_font_get_type() -> glib_ffi::GType; 15 | pub fn cairo_gobject_font_face_get_type() -> glib_ffi::GType; 16 | pub fn cairo_gobject_font_options_get_type() -> glib_ffi::GType; 17 | pub fn cairo_gobject_rectangle_int_get_type() -> glib_ffi::GType; 18 | pub fn cairo_gobject_region_get_type() -> glib_ffi::GType; 19 | pub fn cairo_gobject_status_get_type() -> glib_ffi::GType; 20 | pub fn cairo_gobject_content_get_type() -> glib_ffi::GType; 21 | pub fn cairo_gobject_operator_get_type() -> glib_ffi::GType; 22 | pub fn cairo_gobject_antialias_get_type() -> glib_ffi::GType; 23 | pub fn cairo_gobject_fill_rule_get_type() -> glib_ffi::GType; 24 | pub fn cairo_gobject_line_cap_get_type() -> glib_ffi::GType; 25 | pub fn cairo_gobject_line_join_get_type() -> glib_ffi::GType; 26 | pub fn cairo_gobject_text_cluster_flags_get_type() -> glib_ffi::GType; 27 | pub fn cairo_gobject_font_slant_get_type() -> glib_ffi::GType; 28 | pub fn cairo_gobject_font_weight_get_type() -> glib_ffi::GType; 29 | pub fn cairo_gobject_subpixel_order_get_type() -> glib_ffi::GType; 30 | pub fn cairo_gobject_hint_style_get_type() -> glib_ffi::GType; 31 | pub fn cairo_gobject_hint_metrics_get_type() -> glib_ffi::GType; 32 | pub fn cairo_gobject_font_type_get_type() -> glib_ffi::GType; 33 | pub fn cairo_gobject_path_data_type_get_type() -> glib_ffi::GType; 34 | pub fn cairo_gobject_device_type_get_type() -> glib_ffi::GType; 35 | pub fn cairo_gobject_surface_type_get_type() -> glib_ffi::GType; 36 | pub fn cairo_gobject_format_get_type() -> glib_ffi::GType; 37 | pub fn cairo_gobject_pattern_type_get_type() -> glib_ffi::GType; 38 | pub fn cairo_gobject_extend_get_type() -> glib_ffi::GType; 39 | pub fn cairo_gobject_filter_get_type() -> glib_ffi::GType; 40 | pub fn cairo_gobject_region_overlap_get_type() -> glib_ffi::GType; 41 | } 42 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2019, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | pub const MIME_TYPE_JPEG: &str = "image/jpeg"; 6 | pub const MIME_TYPE_PNG: &str = "image/png"; 7 | pub const MIME_TYPE_JP2: &str = "image/jp2"; 8 | pub const MIME_TYPE_URI: &str = "text/x-uri"; 9 | pub const MIME_TYPE_UNIQUE_ID: &str = "application/x-cairo.uuid"; 10 | #[cfg(any(feature = "v1_14", feature = "dox"))] 11 | pub const MIME_TYPE_JBIG2: &str = "application/x-cairo.jbig2"; 12 | #[cfg(any(feature = "v1_14", feature = "dox"))] 13 | pub const MIME_TYPE_JBIG2_GLOBAL: &str = "application/x-cairo.jbig2-global"; 14 | #[cfg(any(feature = "v1_14", feature = "dox"))] 15 | pub const MIME_TYPE_JBIG2_GLOBAL_ID: &str = "application/x-cairo.jbig2-global-id"; 16 | #[cfg(any(feature = "v1_16", feature = "dox"))] 17 | pub const MIME_TYPE_CCITT_FAX: &str = "image/g3fax"; 18 | #[cfg(any(feature = "v1_16", feature = "dox"))] 19 | pub const MIME_TYPE_CCITT_FAX_PARAMS: &str = "application/x-cairo.ccitt.params"; 20 | #[cfg(any(feature = "v1_16", feature = "dox"))] 21 | pub const MIME_TYPE_EPS: &str = "application/postscript"; 22 | #[cfg(any(feature = "v1_16", feature = "dox"))] 23 | pub const MIME_TYPE_EPS_PARAMS: &str = "application/x-cairo.eps.params"; 24 | 25 | #[cfg(any(feature = "v1_16", feature = "dox"))] 26 | pub const PDF_OUTLINE_ROOT: i32 = 0; 27 | 28 | #[cfg(any(feature = "v1_16", feature = "dox"))] 29 | pub const CAIRO_TAG_DEST: &str = "cairo.dest"; 30 | #[cfg(any(feature = "v1_16", feature = "dox"))] 31 | pub const CAIRO_TAG_LINK: &str = "Link"; 32 | -------------------------------------------------------------------------------- /src/device.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2016, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use enums::DeviceType; 6 | use error::Error; 7 | use ffi; 8 | use utils::status_to_result; 9 | 10 | use std::fmt; 11 | use std::ptr; 12 | 13 | #[cfg(feature = "use_glib")] 14 | use glib::translate::*; 15 | 16 | #[cfg(any(feature = "script", feature = "dox"))] 17 | use enums::Content; 18 | #[cfg(any(feature = "script", feature = "dox"))] 19 | use enums::ScriptMode; 20 | #[cfg(any(feature = "script", feature = "dox"))] 21 | use recording_surface::RecordingSurface; 22 | #[cfg(any(feature = "script", feature = "dox"))] 23 | use std::ffi::CString; 24 | #[cfg(any(feature = "script", feature = "dox"))] 25 | use std::path::Path; 26 | #[cfg(any(feature = "script", feature = "dox"))] 27 | use surface::Surface; 28 | 29 | #[derive(Debug)] 30 | pub struct DeviceAcquireGuard<'a>(&'a Device); 31 | 32 | impl<'a> Drop for DeviceAcquireGuard<'a> { 33 | fn drop(&mut self) { 34 | self.0.release(); 35 | } 36 | } 37 | 38 | #[derive(Debug)] 39 | pub struct Device(ptr::NonNull); 40 | 41 | impl Device { 42 | pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_device_t) -> Device { 43 | assert!(!ptr.is_null()); 44 | ffi::cairo_device_reference(ptr); 45 | Device(ptr::NonNull::new_unchecked(ptr)) 46 | } 47 | 48 | pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_device_t) -> ::Borrowed { 49 | assert!(!ptr.is_null()); 50 | ::Borrowed::new(Device(ptr::NonNull::new_unchecked(ptr))) 51 | } 52 | 53 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_device_t) -> Device { 54 | assert!(!ptr.is_null()); 55 | Device(ptr::NonNull::new_unchecked(ptr)) 56 | } 57 | 58 | pub fn to_raw_none(&self) -> *mut ffi::cairo_device_t { 59 | self.0.as_ptr() 60 | } 61 | 62 | #[cfg(any(feature = "script", feature = "dox"))] 63 | pub fn create>(filename: P) -> Option { 64 | unsafe { 65 | let filename = filename.as_ref().to_string_lossy().into_owned(); 66 | let filename = CString::new(filename).unwrap(); 67 | let p = ffi::cairo_script_create(filename.as_ptr()); 68 | if p.is_null() { 69 | None 70 | } else { 71 | Some(Self::from_raw_full(p)) 72 | } 73 | } 74 | } 75 | 76 | #[cfg(any(feature = "script", feature = "dox"))] 77 | pub fn from_recording_surface(&self, surface: &RecordingSurface) -> Result<(), Error> { 78 | unsafe { 79 | let status = 80 | ffi::cairo_script_from_recording_surface(self.to_raw_none(), surface.to_raw_none()); 81 | status_to_result(status) 82 | } 83 | } 84 | 85 | #[cfg(any(feature = "script", feature = "dox"))] 86 | pub fn get_mode(&self) -> ScriptMode { 87 | unsafe { ScriptMode::from(ffi::cairo_script_get_mode(self.to_raw_none())) } 88 | } 89 | 90 | #[cfg(any(feature = "script", feature = "dox"))] 91 | pub fn set_mode(&self, mode: ScriptMode) { 92 | unsafe { ffi::cairo_script_set_mode(self.to_raw_none(), mode.into()) } 93 | } 94 | 95 | #[cfg(any(feature = "script", feature = "dox"))] 96 | pub fn surface_create( 97 | &self, 98 | content: Content, 99 | width: f64, 100 | height: f64, 101 | ) -> Result { 102 | unsafe { 103 | Ok(Surface::from_raw_full(ffi::cairo_script_surface_create( 104 | self.to_raw_none(), 105 | content.into(), 106 | width, 107 | height, 108 | ))?) 109 | } 110 | } 111 | 112 | #[cfg(any(feature = "script", feature = "dox"))] 113 | pub fn surface_create_for_target(&self, target: &Surface) -> Result { 114 | unsafe { 115 | Ok(Surface::from_raw_full( 116 | ffi::cairo_script_surface_create_for_target( 117 | self.to_raw_none(), 118 | target.to_raw_none(), 119 | ), 120 | )?) 121 | } 122 | } 123 | 124 | #[cfg(any(feature = "script", feature = "dox"))] 125 | pub fn write_comment(&self, comment: &str) { 126 | unsafe { 127 | let len = comment.len(); 128 | let comment = CString::new(comment).unwrap(); 129 | ffi::cairo_script_write_comment(self.to_raw_none(), comment.as_ptr(), len as i32) 130 | } 131 | } 132 | 133 | pub fn finish(&self) { 134 | unsafe { ffi::cairo_device_finish(self.to_raw_none()) } 135 | } 136 | 137 | pub fn flush(&self) { 138 | unsafe { ffi::cairo_device_flush(self.to_raw_none()) } 139 | } 140 | 141 | pub fn get_type(&self) -> DeviceType { 142 | unsafe { DeviceType::from(ffi::cairo_device_get_type(self.to_raw_none())) } 143 | } 144 | 145 | pub fn acquire(&self) -> Result { 146 | unsafe { 147 | let status = ffi::cairo_device_acquire(self.to_raw_none()); 148 | status_to_result(status)?; 149 | } 150 | Ok(DeviceAcquireGuard { 0: self }) 151 | } 152 | 153 | fn release(&self) { 154 | unsafe { ffi::cairo_device_release(self.to_raw_none()) } 155 | } 156 | 157 | pub fn observer_elapsed(&self) -> f64 { 158 | unsafe { ffi::cairo_device_observer_elapsed(self.to_raw_none()) } 159 | } 160 | 161 | pub fn observer_fill_elapsed(&self) -> f64 { 162 | unsafe { ffi::cairo_device_observer_fill_elapsed(self.to_raw_none()) } 163 | } 164 | 165 | pub fn observer_glyphs_elapsed(&self) -> f64 { 166 | unsafe { ffi::cairo_device_observer_glyphs_elapsed(self.to_raw_none()) } 167 | } 168 | 169 | pub fn observer_mask_elapsed(&self) -> f64 { 170 | unsafe { ffi::cairo_device_observer_mask_elapsed(self.to_raw_none()) } 171 | } 172 | 173 | pub fn observer_paint_elapsed(&self) -> f64 { 174 | unsafe { ffi::cairo_device_observer_paint_elapsed(self.to_raw_none()) } 175 | } 176 | 177 | pub fn observer_stroke_elapsed(&self) -> f64 { 178 | unsafe { ffi::cairo_device_observer_stroke_elapsed(self.to_raw_none()) } 179 | } 180 | 181 | #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))] 182 | pub fn debug_cap_xrender_version(&self, major_version: i32, minor_version: i32) { 183 | unsafe { 184 | match self.get_type() { 185 | DeviceType::Xlib => { 186 | #[cfg(feature = "xlib")] 187 | { 188 | ffi::cairo_xlib_device_debug_cap_xrender_version( 189 | self.to_raw_none(), 190 | major_version, 191 | minor_version, 192 | ) 193 | } 194 | #[cfg(not(feature = "xlib"))] 195 | { 196 | panic!("you need to enable \"xlib\" feature") 197 | } 198 | } 199 | DeviceType::Xcb => { 200 | #[cfg(feature = "xcb")] 201 | { 202 | ffi::cairo_xcb_device_debug_cap_xrender_version( 203 | self.to_raw_none(), 204 | major_version, 205 | minor_version, 206 | ) 207 | } 208 | #[cfg(not(feature = "xcb"))] 209 | { 210 | panic!("you need to enable \"xcb\" feature") 211 | } 212 | } 213 | d => panic!("invalid device type: {}", d), 214 | } 215 | } 216 | } 217 | 218 | #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))] 219 | pub fn debug_get_precision(&self) -> i32 { 220 | unsafe { 221 | match self.get_type() { 222 | DeviceType::Xlib => { 223 | #[cfg(feature = "xlib")] 224 | { 225 | ffi::cairo_xlib_device_debug_get_precision(self.to_raw_none()) 226 | } 227 | #[cfg(not(feature = "xlib"))] 228 | { 229 | panic!("you need to enable \"xlib\" feature") 230 | } 231 | } 232 | DeviceType::Xcb => { 233 | #[cfg(feature = "xcb")] 234 | { 235 | ffi::cairo_xcb_device_debug_get_precision(self.to_raw_none()) 236 | } 237 | #[cfg(not(feature = "xcb"))] 238 | { 239 | panic!("you need to enable \"xcb\" feature") 240 | } 241 | } 242 | d => panic!("invalid device type: {}", d), 243 | } 244 | } 245 | } 246 | 247 | #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))] 248 | pub fn debug_set_precision(&self, precision: i32) { 249 | unsafe { 250 | match self.get_type() { 251 | DeviceType::Xlib => { 252 | #[cfg(feature = "xlib")] 253 | { 254 | ffi::cairo_xlib_device_debug_set_precision(self.to_raw_none(), precision) 255 | } 256 | #[cfg(not(feature = "xlib"))] 257 | { 258 | panic!("you need to enable \"xlib\" feature") 259 | } 260 | } 261 | DeviceType::Xcb => { 262 | #[cfg(feature = "xcb")] 263 | { 264 | ffi::cairo_xcb_device_debug_set_precision(self.to_raw_none(), precision) 265 | } 266 | #[cfg(not(feature = "xcb"))] 267 | { 268 | panic!("you need to enable \"xcb\" feature") 269 | } 270 | } 271 | d => panic!("invalid device type: {}", d), 272 | } 273 | } 274 | } 275 | 276 | user_data_methods! { 277 | ffi::cairo_device_get_user_data, 278 | ffi::cairo_device_set_user_data, 279 | } 280 | } 281 | 282 | #[cfg(feature = "use_glib")] 283 | impl<'a> ToGlibPtr<'a, *mut ffi::cairo_device_t> for Device { 284 | type Storage = &'a Device; 285 | 286 | #[inline] 287 | fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_device_t, Self> { 288 | Stash(self.to_raw_none(), self) 289 | } 290 | 291 | #[inline] 292 | fn to_glib_full(&self) -> *mut ffi::cairo_device_t { 293 | unsafe { ffi::cairo_device_reference(self.to_raw_none()) } 294 | } 295 | } 296 | 297 | #[cfg(feature = "use_glib")] 298 | impl FromGlibPtrNone<*mut ffi::cairo_device_t> for Device { 299 | #[inline] 300 | unsafe fn from_glib_none(ptr: *mut ffi::cairo_device_t) -> Device { 301 | Self::from_raw_none(ptr) 302 | } 303 | } 304 | 305 | #[cfg(feature = "use_glib")] 306 | impl FromGlibPtrBorrow<*mut ffi::cairo_device_t> for Device { 307 | #[inline] 308 | unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_device_t) -> ::Borrowed { 309 | Self::from_raw_borrow(ptr) 310 | } 311 | } 312 | 313 | #[cfg(feature = "use_glib")] 314 | impl FromGlibPtrFull<*mut ffi::cairo_device_t> for Device { 315 | #[inline] 316 | unsafe fn from_glib_full(ptr: *mut ffi::cairo_device_t) -> Device { 317 | Self::from_raw_full(ptr) 318 | } 319 | } 320 | 321 | #[cfg(feature = "use_glib")] 322 | gvalue_impl!( 323 | Device, 324 | ffi::cairo_device_t, 325 | ffi::gobject::cairo_gobject_device_get_type 326 | ); 327 | 328 | impl Clone for Device { 329 | fn clone(&self) -> Device { 330 | unsafe { Self::from_raw_none(ffi::cairo_device_reference(self.0.as_ptr())) } 331 | } 332 | } 333 | 334 | impl Drop for Device { 335 | fn drop(&mut self) { 336 | unsafe { 337 | ffi::cairo_device_destroy(self.0.as_ptr()); 338 | } 339 | } 340 | } 341 | 342 | impl fmt::Display for Device { 343 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 344 | write!(f, "Device") 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | use std::fmt::Debug; 5 | 6 | use std::io; 7 | use thiserror::Error; 8 | 9 | #[derive(Error, Debug, Clone, PartialEq, Copy, Eq)] 10 | #[non_exhaustive] 11 | pub enum Error { 12 | #[error("No Memory")] 13 | NoMemory, 14 | #[error("Invalid Restore")] 15 | InvalidRestore, 16 | #[error("Invalid Pop Group")] 17 | InvalidPopGroup, 18 | #[error("No Current Point")] 19 | NoCurrentPoint, 20 | #[error("Invalid Matrix")] 21 | InvalidMatrix, 22 | #[error("Invalid Status")] 23 | InvalidStatus, 24 | #[error("Null Pointer")] 25 | NullPointer, 26 | #[error("Invalid String")] 27 | InvalidString, 28 | #[error("Invalid Path Data")] 29 | InvalidPathData, 30 | #[error("Cairo : Read Error")] 31 | ReadError, 32 | #[error("Write Error")] 33 | WriteError, 34 | #[error("Surface Finishied")] 35 | SurfaceFinished, 36 | #[error("Surface Type Mismatch")] 37 | SurfaceTypeMismatch, 38 | #[error("Pattern Type Mismatch")] 39 | PatternTypeMismatch, 40 | #[error("Invalid Content")] 41 | InvalidContent, 42 | #[error("Invalid Format")] 43 | InvalidFormat, 44 | #[error("Invalid Visual")] 45 | InvalidVisual, 46 | #[error("File Not Found")] 47 | FileNotFound, 48 | #[error("Invalid Dash")] 49 | InvalidDash, 50 | #[error("Invalid Dash Comment")] 51 | InvalidDscComment, 52 | #[error("Invalid Index")] 53 | InvalidIndex, 54 | #[error("Clip Not Representable")] 55 | ClipNotRepresentable, 56 | #[error("Temp File Error")] 57 | TempFileError, 58 | #[error("Invalid Stride")] 59 | InvalidStride, 60 | #[error("Font Type Mismatch")] 61 | FontTypeMismatch, 62 | #[error("User Font Immutable")] 63 | UserFontImmutable, 64 | #[error("User Font Error")] 65 | UserFontError, 66 | #[error("Negative Count")] 67 | NegativeCount, 68 | #[error("Invalid Clusters")] 69 | InvalidClusters, 70 | #[error("Invalid Slant")] 71 | InvalidSlant, 72 | #[error("Invalid Weight")] 73 | InvalidWeight, 74 | #[error("Ivalid Size")] 75 | InvalidSize, 76 | #[error("User Font Not Implemented")] 77 | UserFontNotImplemented, 78 | #[error("Device Type Mismatch")] 79 | DeviceTypeMismatch, 80 | #[error("Device Error")] 81 | DeviceError, 82 | #[error("Invalid Mesh Construction")] 83 | InvalidMeshConstruction, 84 | #[error("Device Finished")] 85 | DeviceFinished, 86 | #[error("JBig2Global Missing")] 87 | JBig2GlobalMissing, 88 | #[error("PNG Error")] 89 | PngError, 90 | #[error("Freetype Error")] 91 | FreetypeError, 92 | #[error("Win32Gdi Error")] 93 | Win32GdiError, 94 | #[error("LastStatus")] 95 | LastStatus, 96 | #[error("Unknown {0}")] 97 | #[doc(hidden)] 98 | __Unknown(i32), 99 | } 100 | #[doc(hidden)] 101 | impl Into for Error { 102 | fn into(self) -> ffi::cairo_status_t { 103 | match self { 104 | Error::NoMemory => ffi::STATUS_NO_MEMORY, 105 | Error::InvalidRestore => ffi::STATUS_INVALID_RESTORE, 106 | Error::InvalidPopGroup => ffi::STATUS_INVALID_POP_GROUP, 107 | Error::NoCurrentPoint => ffi::STATUS_NO_CURRENT_POINT, 108 | Error::InvalidMatrix => ffi::STATUS_INVALID_MATRIX, 109 | Error::InvalidStatus => ffi::STATUS_INVALID_STATUS, 110 | Error::NullPointer => ffi::STATUS_NULL_POINTER, 111 | Error::InvalidString => ffi::STATUS_INVALID_STRING, 112 | Error::InvalidPathData => ffi::STATUS_INVALID_PATH_DATA, 113 | Error::ReadError => ffi::STATUS_READ_ERROR, 114 | Error::WriteError => ffi::STATUS_WRITE_ERROR, 115 | Error::SurfaceFinished => ffi::STATUS_SURFACE_FINISHED, 116 | Error::SurfaceTypeMismatch => ffi::STATUS_SURFACE_TYPE_MISMATCH, 117 | Error::PatternTypeMismatch => ffi::STATUS_PATTERN_TYPE_MISMATCH, 118 | Error::InvalidContent => ffi::STATUS_INVALID_CONTENT, 119 | Error::InvalidFormat => ffi::STATUS_INVALID_FORMAT, 120 | Error::InvalidVisual => ffi::STATUS_INVALID_VISUAL, 121 | Error::FileNotFound => ffi::STATUS_FILE_NOT_FOUND, 122 | Error::InvalidDash => ffi::STATUS_INVALID_DASH, 123 | Error::InvalidDscComment => ffi::STATUS_INVALID_DSC_COMMENT, 124 | Error::InvalidIndex => ffi::STATUS_INVALID_INDEX, 125 | Error::ClipNotRepresentable => ffi::STATUS_CLIP_NOT_REPRESENTABLE, 126 | Error::TempFileError => ffi::STATUS_TEMP_FILE_ERROR, 127 | Error::InvalidStride => ffi::STATUS_INVALID_STRIDE, 128 | Error::FontTypeMismatch => ffi::STATUS_FONT_TYPE_MISMATCH, 129 | Error::UserFontImmutable => ffi::STATUS_USER_FONT_IMMUTABLE, 130 | Error::UserFontError => ffi::STATUS_USER_FONT_ERROR, 131 | Error::NegativeCount => ffi::STATUS_NEGATIVE_COUNT, 132 | Error::InvalidClusters => ffi::STATUS_INVALID_CLUSTERS, 133 | Error::InvalidSlant => ffi::STATUS_INVALID_SLANT, 134 | Error::InvalidWeight => ffi::STATUS_INVALID_WEIGHT, 135 | Error::InvalidSize => ffi::STATUS_INVALID_SIZE, 136 | Error::UserFontNotImplemented => ffi::STATUS_USER_FONT_NOT_IMPLEMENTED, 137 | Error::DeviceTypeMismatch => ffi::STATUS_DEVICE_TYPE_MISMATCH, 138 | Error::DeviceError => ffi::STATUS_DEVICE_ERROR, 139 | Error::InvalidMeshConstruction => ffi::STATUS_INVALID_MESH_CONSTRUCTION, 140 | Error::DeviceFinished => ffi::STATUS_DEVICE_FINISHED, 141 | Error::JBig2GlobalMissing => ffi::STATUS_J_BIG2_GLOBAL_MISSING, 142 | Error::PngError => ffi::STATUS_PNG_ERROR, 143 | Error::FreetypeError => ffi::STATUS_FREETYPE_ERROR, 144 | Error::Win32GdiError => ffi::STATUS_WIN32_GDI_ERROR, 145 | Error::LastStatus => ffi::STATUS_LAST_STATUS, 146 | Error::__Unknown(value) => value, 147 | } 148 | } 149 | } 150 | 151 | #[doc(hidden)] 152 | impl From for Error { 153 | fn from(value: ffi::cairo_status_t) -> Self { 154 | match value { 155 | ffi::STATUS_NO_MEMORY => Error::NoMemory, 156 | ffi::STATUS_INVALID_RESTORE => Error::InvalidRestore, 157 | ffi::STATUS_INVALID_POP_GROUP => Error::InvalidPopGroup, 158 | ffi::STATUS_NO_CURRENT_POINT => Error::NoCurrentPoint, 159 | ffi::STATUS_INVALID_MATRIX => Error::InvalidMatrix, 160 | ffi::STATUS_INVALID_STATUS => Error::InvalidStatus, 161 | ffi::STATUS_NULL_POINTER => Error::NullPointer, 162 | ffi::STATUS_INVALID_STRING => Error::InvalidString, 163 | ffi::STATUS_INVALID_PATH_DATA => Error::InvalidPathData, 164 | ffi::STATUS_READ_ERROR => Error::ReadError, 165 | ffi::STATUS_WRITE_ERROR => Error::WriteError, 166 | ffi::STATUS_SURFACE_FINISHED => Error::SurfaceFinished, 167 | ffi::STATUS_SURFACE_TYPE_MISMATCH => Error::SurfaceTypeMismatch, 168 | ffi::STATUS_PATTERN_TYPE_MISMATCH => Error::PatternTypeMismatch, 169 | ffi::STATUS_INVALID_CONTENT => Error::InvalidContent, 170 | ffi::STATUS_INVALID_FORMAT => Error::InvalidFormat, 171 | ffi::STATUS_INVALID_VISUAL => Error::InvalidVisual, 172 | ffi::STATUS_FILE_NOT_FOUND => Error::FileNotFound, 173 | ffi::STATUS_INVALID_DASH => Error::InvalidDash, 174 | ffi::STATUS_INVALID_DSC_COMMENT => Error::InvalidDscComment, 175 | ffi::STATUS_INVALID_INDEX => Error::InvalidIndex, 176 | ffi::STATUS_CLIP_NOT_REPRESENTABLE => Error::ClipNotRepresentable, 177 | ffi::STATUS_TEMP_FILE_ERROR => Error::TempFileError, 178 | ffi::STATUS_INVALID_STRIDE => Error::InvalidStride, 179 | ffi::STATUS_FONT_TYPE_MISMATCH => Error::FontTypeMismatch, 180 | ffi::STATUS_USER_FONT_IMMUTABLE => Error::UserFontImmutable, 181 | ffi::STATUS_USER_FONT_ERROR => Error::UserFontError, 182 | ffi::STATUS_NEGATIVE_COUNT => Error::NegativeCount, 183 | ffi::STATUS_INVALID_CLUSTERS => Error::InvalidClusters, 184 | ffi::STATUS_INVALID_SLANT => Error::InvalidSlant, 185 | ffi::STATUS_INVALID_WEIGHT => Error::InvalidWeight, 186 | ffi::STATUS_INVALID_SIZE => Error::InvalidSize, 187 | ffi::STATUS_USER_FONT_NOT_IMPLEMENTED => Error::UserFontNotImplemented, 188 | ffi::STATUS_DEVICE_TYPE_MISMATCH => Error::DeviceTypeMismatch, 189 | ffi::STATUS_DEVICE_ERROR => Error::DeviceError, 190 | ffi::STATUS_INVALID_MESH_CONSTRUCTION => Error::InvalidMeshConstruction, 191 | ffi::STATUS_DEVICE_FINISHED => Error::DeviceFinished, 192 | ffi::STATUS_J_BIG2_GLOBAL_MISSING => Error::JBig2GlobalMissing, 193 | ffi::STATUS_PNG_ERROR => Error::PngError, 194 | ffi::STATUS_FREETYPE_ERROR => Error::FreetypeError, 195 | ffi::STATUS_WIN32_GDI_ERROR => Error::Win32GdiError, 196 | ffi::STATUS_LAST_STATUS => Error::LastStatus, 197 | value => Error::__Unknown(value), 198 | } 199 | } 200 | } 201 | 202 | #[derive(Error, Debug)] 203 | pub enum IoError { 204 | #[error("Cairo error: {0}")] 205 | Cairo(#[from] Error), 206 | #[error("IO error: {0}")] 207 | Io(#[from] io::Error), 208 | } 209 | 210 | #[derive(Error, Debug)] 211 | pub enum BorrowError { 212 | #[error("Failed to borrow with Cairo error: {0}")] 213 | Cairo(#[from] ::Error), 214 | #[error("Can't get exclusive access")] 215 | NonExclusive, 216 | } 217 | -------------------------------------------------------------------------------- /src/font/font_face.rs: -------------------------------------------------------------------------------- 1 | use ffi; 2 | #[cfg(feature = "use_glib")] 3 | use glib::translate::*; 4 | use libc::{c_char, c_int}; 5 | use std::ffi::{CStr, CString}; 6 | #[cfg(not(feature = "use_glib"))] 7 | use std::ptr; 8 | 9 | use enums::{FontSlant, FontType, FontWeight}; 10 | 11 | #[cfg(any(feature = "freetype", feature = "dox"))] 12 | use enums::FtSynthesize; 13 | 14 | use utils::status_to_result; 15 | 16 | #[cfg(feature = "use_glib")] 17 | glib_wrapper! { 18 | #[derive(Debug)] 19 | pub struct FontFace(Shared); 20 | 21 | match fn { 22 | ref => |ptr| ffi::cairo_font_face_reference(ptr), 23 | unref => |ptr| ffi::cairo_font_face_destroy(ptr), 24 | get_type => || ffi::gobject::cairo_gobject_font_face_get_type(), 25 | } 26 | } 27 | 28 | #[cfg(not(feature = "use_glib"))] 29 | #[derive(Debug)] 30 | pub struct FontFace(ptr::NonNull); 31 | 32 | impl FontFace { 33 | pub fn toy_create(family: &str, slant: FontSlant, weight: FontWeight) -> FontFace { 34 | let font_face: FontFace = unsafe { 35 | let family = CString::new(family).unwrap(); 36 | FontFace::from_raw_full(ffi::cairo_toy_font_face_create( 37 | family.as_ptr(), 38 | slant.into(), 39 | weight.into(), 40 | )) 41 | }; 42 | let status = unsafe { ffi::cairo_font_face_status(font_face.to_raw_none()) }; 43 | status_to_result(status).expect("Failed to create a FontFace"); 44 | font_face 45 | } 46 | 47 | // Safety: the FT_Face must be valid and not be freed until the `FontFace` is dropped. 48 | #[cfg(any(feature = "freetype", feature = "dox"))] 49 | pub unsafe fn create_from_ft(face: freetype_crate::freetype::FT_Face) -> FontFace { 50 | let font_face = FontFace::from_raw_full(ffi::cairo_ft_font_face_create_for_ft_face( 51 | face as *mut _, 52 | 0, 53 | )); 54 | let status = unsafe { ffi::cairo_font_face_status(font_face.to_raw_none()) }; 55 | status_to_result(status).expect("Failed to create a FontFace"); 56 | font_face 57 | } 58 | 59 | // Safety: the FT_Face must be valid and not be freed until the `FontFace` is dropped. 60 | #[cfg(any(feature = "freetype", feature = "dox"))] 61 | pub unsafe fn create_from_ft_with_flags( 62 | face: freetype_crate::freetype::FT_Face, 63 | load_flags: c_int, 64 | ) -> FontFace { 65 | let font_face = FontFace::from_raw_full(ffi::cairo_ft_font_face_create_for_ft_face( 66 | face as *mut _, 67 | load_flags, 68 | )); 69 | let status = unsafe { ffi::cairo_font_face_status(font_face.to_raw_none()) }; 70 | status_to_result(status).expect("Failed to create a FontFace"); 71 | font_face 72 | } 73 | 74 | #[cfg(feature = "use_glib")] 75 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_font_face_t) -> FontFace { 76 | from_glib_full(ptr) 77 | } 78 | 79 | #[cfg(not(feature = "use_glib"))] 80 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_font_face_t) -> FontFace { 81 | assert!(!ptr.is_null()); 82 | FontFace(ptr::NonNull::new_unchecked(ptr)) 83 | } 84 | 85 | #[cfg(feature = "use_glib")] 86 | pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_font_face_t) -> FontFace { 87 | from_glib_none(ptr) 88 | } 89 | 90 | #[cfg(not(feature = "use_glib"))] 91 | pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_font_face_t) -> FontFace { 92 | assert!(!ptr.is_null()); 93 | FontFace(ptr::NonNull::new_unchecked(ptr)) 94 | } 95 | 96 | #[cfg(feature = "use_glib")] 97 | pub fn to_raw_none(&self) -> *mut ffi::cairo_font_face_t { 98 | self.to_glib_none().0 99 | } 100 | 101 | #[cfg(not(feature = "use_glib"))] 102 | pub fn to_raw_none(&self) -> *mut ffi::cairo_font_face_t { 103 | self.0.as_ptr() 104 | } 105 | 106 | pub fn toy_get_family(&self) -> Option { 107 | unsafe { to_optional_string(ffi::cairo_toy_font_face_get_family(self.to_raw_none())) } 108 | } 109 | 110 | pub fn toy_get_slant(&self) -> FontSlant { 111 | unsafe { FontSlant::from(ffi::cairo_toy_font_face_get_slant(self.to_raw_none())) } 112 | } 113 | 114 | pub fn toy_get_weight(&self) -> FontWeight { 115 | unsafe { FontWeight::from(ffi::cairo_toy_font_face_get_weight(self.to_raw_none())) } 116 | } 117 | 118 | pub fn get_type(&self) -> FontType { 119 | unsafe { FontType::from(ffi::cairo_font_face_get_type(self.to_raw_none())) } 120 | } 121 | 122 | pub fn get_reference_count(&self) -> usize { 123 | unsafe { ffi::cairo_font_face_get_reference_count(self.to_raw_none()) as usize } 124 | } 125 | 126 | #[cfg(any(feature = "freetype", feature = "dox"))] 127 | pub fn get_synthesize(&self) -> FtSynthesize { 128 | unsafe { FtSynthesize::from(ffi::cairo_ft_font_face_get_synthesize(self.to_raw_none())) } 129 | } 130 | 131 | #[cfg(any(feature = "freetype", feature = "dox"))] 132 | pub fn set_synthesize(&self, synth_flags: FtSynthesize) { 133 | unsafe { ffi::cairo_ft_font_face_set_synthesize(self.to_raw_none(), synth_flags.into()) } 134 | } 135 | 136 | #[cfg(any(feature = "freetype", feature = "dox"))] 137 | pub fn unset_synthesize(&self, synth_flags: FtSynthesize) { 138 | unsafe { ffi::cairo_ft_font_face_unset_synthesize(self.to_raw_none(), synth_flags.into()) } 139 | } 140 | 141 | user_data_methods! { 142 | ffi::cairo_font_face_get_user_data, 143 | ffi::cairo_font_face_set_user_data, 144 | } 145 | } 146 | 147 | #[cfg(not(feature = "use_glib"))] 148 | impl Drop for FontFace { 149 | fn drop(&mut self) { 150 | unsafe { 151 | ffi::cairo_font_face_destroy(self.to_raw_none()); 152 | } 153 | } 154 | } 155 | 156 | #[cfg(not(feature = "use_glib"))] 157 | impl Clone for FontFace { 158 | fn clone(&self) -> FontFace { 159 | unsafe { FontFace::from_raw_none(self.to_raw_none()) } 160 | } 161 | } 162 | 163 | pub(crate) unsafe fn to_optional_string(str: *const c_char) -> Option { 164 | if str.is_null() { 165 | None 166 | } else { 167 | Some(String::from_utf8_lossy(CStr::from_ptr(str).to_bytes()).into_owned()) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/font/font_options.rs: -------------------------------------------------------------------------------- 1 | use ffi; 2 | #[cfg(feature = "use_glib")] 3 | use glib::translate::*; 4 | use std::cmp::PartialEq; 5 | use std::hash; 6 | 7 | #[cfg(any(feature = "v1_16", feature = "dox"))] 8 | use font::font_face::to_optional_string; 9 | #[cfg(any(feature = "v1_16", feature = "dox"))] 10 | use std::ffi::CString; 11 | #[cfg(not(feature = "use_glib"))] 12 | use std::ptr; 13 | 14 | use enums::{Antialias, HintMetrics, HintStyle, SubpixelOrder}; 15 | use utils::status_to_result; 16 | 17 | #[cfg(feature = "use_glib")] 18 | glib_wrapper! { 19 | #[derive(Debug)] 20 | pub struct FontOptions(Boxed); 21 | 22 | match fn { 23 | copy => |ptr| { 24 | let ptr = ffi::cairo_font_options_copy(ptr); 25 | let status = ffi::cairo_font_options_status(ptr); 26 | status_to_result(status).expect("Failed to create a copy of FontOptions"); 27 | ptr 28 | }, 29 | free => |ptr| ffi::cairo_font_options_destroy(ptr), 30 | get_type => || ffi::gobject::cairo_gobject_font_options_get_type(), 31 | } 32 | } 33 | 34 | #[cfg(not(feature = "use_glib"))] 35 | #[derive(Debug)] 36 | pub struct FontOptions(ptr::NonNull); 37 | 38 | impl FontOptions { 39 | pub fn new() -> FontOptions { 40 | let font_options: FontOptions = 41 | unsafe { FontOptions::from_raw_full(ffi::cairo_font_options_create()) }; 42 | 43 | let status = unsafe { ffi::cairo_font_options_status(font_options.to_raw_none()) }; 44 | status_to_result(status).expect("Failed to create a font option"); 45 | 46 | font_options 47 | } 48 | 49 | #[cfg(feature = "use_glib")] 50 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_font_options_t) -> FontOptions { 51 | from_glib_full(ptr) 52 | } 53 | 54 | #[cfg(not(feature = "use_glib"))] 55 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_font_options_t) -> FontOptions { 56 | assert!(!ptr.is_null()); 57 | FontOptions(ptr::NonNull::new_unchecked(ptr)) 58 | } 59 | 60 | #[cfg(feature = "use_glib")] 61 | pub fn to_raw_none(&self) -> *mut ffi::cairo_font_options_t { 62 | mut_override(self.to_glib_none().0) 63 | } 64 | 65 | #[cfg(not(feature = "use_glib"))] 66 | pub fn to_raw_none(&self) -> *mut ffi::cairo_font_options_t { 67 | self.0.as_ptr() 68 | } 69 | 70 | pub fn merge(&mut self, other: &FontOptions) { 71 | unsafe { ffi::cairo_font_options_merge(self.to_raw_none(), other.to_raw_none()) } 72 | } 73 | 74 | pub fn set_antialias(&mut self, antialias: Antialias) { 75 | unsafe { ffi::cairo_font_options_set_antialias(self.to_raw_none(), antialias.into()) } 76 | } 77 | 78 | pub fn get_antialias(&self) -> Antialias { 79 | unsafe { Antialias::from(ffi::cairo_font_options_get_antialias(self.to_raw_none())) } 80 | } 81 | 82 | pub fn set_subpixel_order(&mut self, order: SubpixelOrder) { 83 | unsafe { ffi::cairo_font_options_set_subpixel_order(self.to_raw_none(), order.into()) } 84 | } 85 | 86 | pub fn get_subpixel_order(&self) -> SubpixelOrder { 87 | unsafe { 88 | SubpixelOrder::from(ffi::cairo_font_options_get_subpixel_order( 89 | self.to_raw_none(), 90 | )) 91 | } 92 | } 93 | 94 | pub fn set_hint_style(&mut self, hint_style: HintStyle) { 95 | unsafe { ffi::cairo_font_options_set_hint_style(self.to_raw_none(), hint_style.into()) } 96 | } 97 | 98 | pub fn get_hint_style(&self) -> HintStyle { 99 | unsafe { HintStyle::from(ffi::cairo_font_options_get_hint_style(self.to_raw_none())) } 100 | } 101 | 102 | pub fn set_hint_metrics(&mut self, hint_metrics: HintMetrics) { 103 | unsafe { ffi::cairo_font_options_set_hint_metrics(self.to_raw_none(), hint_metrics.into()) } 104 | } 105 | 106 | pub fn get_hint_metrics(&self) -> HintMetrics { 107 | unsafe { HintMetrics::from(ffi::cairo_font_options_get_hint_metrics(self.to_raw_none())) } 108 | } 109 | 110 | #[cfg(any(feature = "v1_16", feature = "dox"))] 111 | pub fn get_variations(&self) -> Option { 112 | unsafe { to_optional_string(ffi::cairo_font_options_get_variations(self.to_raw_none())) } 113 | } 114 | 115 | #[cfg(any(feature = "v1_16", feature = "dox"))] 116 | pub fn set_variations<'a, T: Into>>(&self, variations: T) { 117 | unsafe { 118 | let variations = variations.into(); 119 | match variations { 120 | Some(ref v) => { 121 | let v = CString::new(*v).unwrap(); 122 | ffi::cairo_font_options_set_variations(self.to_raw_none(), v.as_ptr()) 123 | } 124 | None => ffi::cairo_font_options_set_variations(self.to_raw_none(), 0 as *const _), 125 | } 126 | } 127 | } 128 | } 129 | 130 | impl PartialEq for FontOptions { 131 | fn eq(&self, other: &FontOptions) -> bool { 132 | unsafe { ffi::cairo_font_options_equal(self.to_raw_none(), other.to_raw_none()).as_bool() } 133 | } 134 | } 135 | 136 | impl Eq for FontOptions {} 137 | 138 | impl hash::Hash for FontOptions { 139 | fn hash(&self, state: &mut H) 140 | where 141 | H: hash::Hasher, 142 | { 143 | unsafe { hash::Hash::hash(&ffi::cairo_font_options_hash(self.to_raw_none()), state) } 144 | } 145 | } 146 | 147 | impl Default for FontOptions { 148 | fn default() -> Self { 149 | Self::new() 150 | } 151 | } 152 | 153 | #[cfg(not(feature = "use_glib"))] 154 | impl Drop for FontOptions { 155 | fn drop(&mut self) { 156 | unsafe { 157 | ffi::cairo_font_options_destroy(self.to_raw_none()); 158 | } 159 | } 160 | } 161 | 162 | #[cfg(not(feature = "use_glib"))] 163 | impl Clone for FontOptions { 164 | fn clone(&self) -> FontOptions { 165 | unsafe { FontOptions::from_raw_full(ffi::cairo_font_options_copy(self.to_raw_none())) } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/font/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2015, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | mod font_face; 6 | mod font_options; 7 | mod scaled_font; 8 | 9 | pub use enums::{ 10 | Antialias, FontSlant, FontType, FontWeight, HintMetrics, HintStyle, SubpixelOrder, 11 | TextClusterFlags, 12 | }; 13 | 14 | pub use ffi::{FontExtents, Glyph, TextCluster, TextExtents}; 15 | 16 | /* TODO 17 | Allocates an array of cairo_glyph_t's. This function is only useful in 18 | implementations of cairo_user_scaled_font_text_to_glyphs_func_t where the user 19 | needs to allocate an array of glyphs that cairo will free. For all other uses, 20 | user can use their own allocation method for glyphs. 21 | 22 | 23 | impl Glyph { 24 | 25 | //pub fn cairo_glyph_allocate(num_glyphs: c_int) -> *Glyph; 26 | 27 | //pub fn cairo_glyph_free(glyphs: *Glyph); 28 | } 29 | 30 | Allocates an array of cairo_glyph_t's. This function is only useful in 31 | implementations of cairo_user_scaled_font_text_to_glyphs_func_t where the user 32 | needs to allocate an array of glyphs that cairo will free. For all other uses, 33 | user can use their own allocation method for glyphs. 34 | 35 | impl TextCluster { 36 | //pub fn cairo_text_cluster_allocate(num_clusters: c_int) -> *TextCluster; 37 | 38 | //pub fn cairo_text_cluster_free(clusters: *TextCluster); 39 | } 40 | */ 41 | 42 | pub use self::font_face::FontFace; 43 | pub use self::font_options::FontOptions; 44 | pub use self::scaled_font::ScaledFont; 45 | -------------------------------------------------------------------------------- /src/font/scaled_font.rs: -------------------------------------------------------------------------------- 1 | use ffi; 2 | #[cfg(feature = "use_glib")] 3 | use glib::translate::*; 4 | use std::ffi::CString; 5 | use std::ptr; 6 | 7 | use enums::FontType; 8 | use ffi::{FontExtents, Glyph, TextCluster, TextExtents}; 9 | use matrices::Matrix; 10 | use utils::status_to_result; 11 | 12 | use super::{FontFace, FontOptions}; 13 | 14 | #[cfg(feature = "use_glib")] 15 | glib_wrapper! { 16 | #[derive(Debug)] 17 | pub struct ScaledFont(Shared); 18 | 19 | match fn { 20 | ref => |ptr| ffi::cairo_scaled_font_reference(ptr), 21 | unref => |ptr| ffi::cairo_scaled_font_destroy(ptr), 22 | get_type => || ffi::gobject::cairo_gobject_scaled_font_get_type(), 23 | } 24 | } 25 | 26 | #[cfg(not(feature = "use_glib"))] 27 | #[derive(Debug)] 28 | pub struct ScaledFont(ptr::NonNull); 29 | 30 | impl ScaledFont { 31 | pub fn new( 32 | font_face: &FontFace, 33 | font_matrix: &Matrix, 34 | ctm: &Matrix, 35 | options: &FontOptions, 36 | ) -> ScaledFont { 37 | let scaled_font: ScaledFont = unsafe { 38 | ScaledFont::from_raw_full(ffi::cairo_scaled_font_create( 39 | font_face.to_raw_none(), 40 | font_matrix.ptr(), 41 | ctm.ptr(), 42 | options.to_raw_none(), 43 | )) 44 | }; 45 | let status = unsafe { ffi::cairo_scaled_font_status(scaled_font.to_raw_none()) }; 46 | status_to_result(status).expect("Failed to create a scaled font"); 47 | scaled_font 48 | } 49 | 50 | #[cfg(feature = "use_glib")] 51 | pub fn to_raw_none(&self) -> *mut ffi::cairo_scaled_font_t { 52 | self.to_glib_none().0 53 | } 54 | 55 | #[cfg(not(feature = "use_glib"))] 56 | pub fn to_raw_none(&self) -> *mut ffi::cairo_scaled_font_t { 57 | self.0.as_ptr() 58 | } 59 | 60 | #[cfg(not(feature = "use_glib"))] 61 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_scaled_font_t) -> ScaledFont { 62 | assert!(!ptr.is_null()); 63 | ScaledFont(ptr::NonNull::new_unchecked(ptr)) 64 | } 65 | 66 | #[cfg(feature = "use_glib")] 67 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_scaled_font_t) -> ScaledFont { 68 | from_glib_full(ptr) 69 | } 70 | 71 | #[cfg(feature = "use_glib")] 72 | pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_scaled_font_t) -> ScaledFont { 73 | from_glib_none(ptr) 74 | } 75 | 76 | #[cfg(not(feature = "use_glib"))] 77 | pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_scaled_font_t) -> ScaledFont { 78 | assert!(!ptr.is_null()); 79 | ffi::cairo_scaled_font_reference(ptr); 80 | ScaledFont(ptr::NonNull::new_unchecked(ptr)) 81 | } 82 | 83 | pub fn get_type(&self) -> FontType { 84 | unsafe { FontType::from(ffi::cairo_scaled_font_get_type(self.to_raw_none())) } 85 | } 86 | 87 | pub fn get_reference_count(&self) -> usize { 88 | unsafe { ffi::cairo_scaled_font_get_reference_count(self.to_raw_none()) as usize } 89 | } 90 | 91 | pub fn extents(&self) -> FontExtents { 92 | let mut extents = FontExtents { 93 | ascent: 0.0, 94 | descent: 0.0, 95 | height: 0.0, 96 | max_x_advance: 0.0, 97 | max_y_advance: 0.0, 98 | }; 99 | 100 | unsafe { ffi::cairo_scaled_font_extents(self.to_raw_none(), &mut extents) } 101 | 102 | extents 103 | } 104 | 105 | pub fn text_extents(&self, text: &str) -> TextExtents { 106 | let mut extents = TextExtents { 107 | x_bearing: 0.0, 108 | y_bearing: 0.0, 109 | width: 0.0, 110 | height: 0.0, 111 | x_advance: 0.0, 112 | y_advance: 0.0, 113 | }; 114 | 115 | let text = CString::new(text).unwrap(); 116 | unsafe { 117 | ffi::cairo_scaled_font_text_extents(self.to_raw_none(), text.as_ptr(), &mut extents) 118 | } 119 | 120 | extents 121 | } 122 | 123 | pub fn glyph_extents(&self, glyphs: &[Glyph]) -> TextExtents { 124 | let mut extents = TextExtents { 125 | x_bearing: 0.0, 126 | y_bearing: 0.0, 127 | width: 0.0, 128 | height: 0.0, 129 | x_advance: 0.0, 130 | y_advance: 0.0, 131 | }; 132 | 133 | unsafe { 134 | ffi::cairo_scaled_font_glyph_extents( 135 | self.to_raw_none(), 136 | glyphs.as_ptr(), 137 | glyphs.len() as i32, 138 | &mut extents, 139 | ) 140 | } 141 | 142 | extents 143 | } 144 | 145 | pub fn text_to_glyphs(&self, x: f64, y: f64, text: &str) -> (Vec, Vec) { 146 | // This large unsafe block is due to the FFI function returning two specially allocated 147 | // (cairo_{glyph,text_cluster}_allocate) pointers that need to be copied into Vec 148 | // types before they're of any use to Rust code. 149 | 150 | unsafe { 151 | let mut glyphs_ptr: *mut Glyph = ptr::null_mut(); 152 | let mut glyph_count = 0i32; 153 | let mut clusters_ptr: *mut TextCluster = ptr::null_mut(); 154 | let mut cluster_count = 0i32; 155 | let mut cluster_flags = 0i32; 156 | let text_length = text.len() as i32; 157 | let text = CString::new(text).unwrap(); 158 | 159 | let status = ffi::cairo_scaled_font_text_to_glyphs( 160 | self.to_raw_none(), 161 | x, 162 | y, 163 | text.as_ptr(), 164 | text_length, 165 | &mut glyphs_ptr, 166 | &mut glyph_count, 167 | &mut clusters_ptr, 168 | &mut cluster_count, 169 | &mut cluster_flags, 170 | ); 171 | status_to_result(status).expect("Failed to convert text to glyphs"); 172 | 173 | let glyph_count = glyph_count as usize; 174 | let glyphs: Vec = { 175 | let mut glyphs: Vec = Vec::with_capacity(glyph_count); 176 | 177 | glyphs.set_len(glyph_count); 178 | ptr::copy(glyphs_ptr, glyphs.as_mut_ptr(), glyph_count); 179 | 180 | glyphs 181 | }; 182 | 183 | let cluster_count = cluster_count as usize; 184 | let clusters: Vec = { 185 | let mut clusters = Vec::with_capacity(cluster_count); 186 | 187 | clusters.set_len(cluster_count); 188 | ptr::copy(clusters_ptr, clusters.as_mut_ptr(), cluster_count); 189 | 190 | clusters 191 | }; 192 | 193 | ffi::cairo_glyph_free(glyphs_ptr); 194 | ffi::cairo_text_cluster_free(clusters_ptr); 195 | 196 | (glyphs, clusters) 197 | } 198 | } 199 | 200 | pub fn get_font_face(&self) -> FontFace { 201 | unsafe { FontFace::from_raw_none(ffi::cairo_scaled_font_get_font_face(self.to_raw_none())) } 202 | } 203 | 204 | pub fn get_font_options(&self) -> FontOptions { 205 | let options = FontOptions::new(); 206 | 207 | unsafe { 208 | ffi::cairo_scaled_font_get_font_options(self.to_raw_none(), options.to_raw_none()) 209 | } 210 | 211 | options 212 | } 213 | 214 | pub fn get_font_matrix(&self) -> Matrix { 215 | let mut matrix = Matrix::null(); 216 | 217 | unsafe { ffi::cairo_scaled_font_get_font_matrix(self.to_raw_none(), matrix.mut_ptr()) } 218 | 219 | matrix 220 | } 221 | 222 | pub fn get_ctm(&self) -> Matrix { 223 | let mut matrix = Matrix::null(); 224 | 225 | unsafe { ffi::cairo_scaled_font_get_ctm(self.to_raw_none(), matrix.mut_ptr()) } 226 | 227 | matrix 228 | } 229 | 230 | pub fn get_scale_matrix(&self) -> Matrix { 231 | let mut matrix = Matrix::null(); 232 | 233 | unsafe { ffi::cairo_scaled_font_get_scale_matrix(self.to_raw_none(), matrix.mut_ptr()) } 234 | 235 | matrix 236 | } 237 | 238 | user_data_methods! { 239 | ffi::cairo_scaled_font_get_user_data, 240 | ffi::cairo_scaled_font_set_user_data, 241 | } 242 | } 243 | 244 | #[cfg(not(feature = "use_glib"))] 245 | impl Drop for ScaledFont { 246 | fn drop(&mut self) { 247 | unsafe { 248 | ffi::cairo_scaled_font_destroy(self.to_raw_none()); 249 | } 250 | } 251 | } 252 | 253 | #[cfg(not(feature = "use_glib"))] 254 | impl Clone for ScaledFont { 255 | fn clone(&self) -> ScaledFont { 256 | unsafe { ScaledFont::from_raw_none(self.to_raw_none()) } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/image_surface.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use std::convert::TryFrom; 6 | use std::ops::{Deref, DerefMut}; 7 | use std::rc::Rc; 8 | use std::slice; 9 | 10 | use enums::{Format, SurfaceType}; 11 | use error::Error; 12 | use ffi; 13 | #[cfg(feature = "use_glib")] 14 | use glib::translate::*; 15 | 16 | use std::fmt; 17 | use surface::Surface; 18 | use utils::status_to_result; 19 | use BorrowError; 20 | 21 | declare_surface!(ImageSurface, SurfaceType::Image); 22 | 23 | impl ImageSurface { 24 | pub fn create(format: Format, width: i32, height: i32) -> Result { 25 | unsafe { 26 | Self::from_raw_full(ffi::cairo_image_surface_create( 27 | format.into(), 28 | width, 29 | height, 30 | )) 31 | } 32 | } 33 | 34 | // rustdoc-stripper-ignore-next 35 | /// Creates an image surface for the provided pixel data. 36 | /// - The pointer `data` is the beginning of the underlying slice, 37 | /// and at least `width * stride` succeeding bytes should be allocated. 38 | /// - `data` must live longer than any reference to the returned surface. 39 | /// - You have to free `data` by yourself. 40 | pub unsafe fn create_for_data_unsafe( 41 | data: *mut u8, 42 | format: Format, 43 | width: i32, 44 | height: i32, 45 | stride: i32, 46 | ) -> Result { 47 | ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data( 48 | data, 49 | format.into(), 50 | width, 51 | height, 52 | stride, 53 | )) 54 | } 55 | 56 | pub fn create_for_data + 'static>( 57 | data: D, 58 | format: Format, 59 | width: i32, 60 | height: i32, 61 | stride: i32, 62 | ) -> Result { 63 | let mut data: Box> = Box::new(data); 64 | 65 | let (ptr, len) = { 66 | let data: &mut [u8] = (*data).as_mut(); 67 | 68 | (data.as_mut_ptr(), data.len()) 69 | }; 70 | 71 | assert!(len >= (height * stride) as usize); 72 | let result = unsafe { 73 | ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data( 74 | ptr, 75 | format.into(), 76 | width, 77 | height, 78 | stride, 79 | )) 80 | }; 81 | if let Ok(surface) = &result { 82 | static IMAGE_SURFACE_DATA: crate::UserDataKey>> = 83 | crate::UserDataKey::new(); 84 | surface.set_user_data(&IMAGE_SURFACE_DATA, Rc::new(data)) 85 | } 86 | result 87 | } 88 | 89 | pub fn get_data(&mut self) -> Result { 90 | unsafe { 91 | if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 { 92 | return Err(BorrowError::NonExclusive); 93 | } 94 | 95 | self.flush(); 96 | let status = ffi::cairo_surface_status(self.to_raw_none()); 97 | if let Some(err) = status_to_result(status).err() { 98 | return Err(BorrowError::from(err)); 99 | } 100 | if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(self) 101 | { 102 | return Err(BorrowError::from(Error::SurfaceFinished)); 103 | } 104 | Ok(ImageSurfaceData::new(self)) 105 | } 106 | } 107 | 108 | pub fn with_data(&self, f: F) -> Result<(), BorrowError> { 109 | self.flush(); 110 | unsafe { 111 | let status = ffi::cairo_surface_status(self.to_raw_none()); 112 | if let Some(err) = status_to_result(status).err() { 113 | return Err(BorrowError::from(err)); 114 | } 115 | let ptr = ffi::cairo_image_surface_get_data(self.to_raw_none()); 116 | if ptr.is_null() || is_finished(self) { 117 | return Err(BorrowError::from(Error::SurfaceFinished)); 118 | } 119 | let len = self.get_height() as usize * self.get_stride() as usize; 120 | f(slice::from_raw_parts(ptr, len)); 121 | } 122 | Ok(()) 123 | } 124 | 125 | pub fn get_format(&self) -> Format { 126 | unsafe { Format::from(ffi::cairo_image_surface_get_format(self.to_raw_none())) } 127 | } 128 | 129 | pub fn get_height(&self) -> i32 { 130 | unsafe { ffi::cairo_image_surface_get_height(self.to_raw_none()) } 131 | } 132 | 133 | pub fn get_stride(&self) -> i32 { 134 | unsafe { ffi::cairo_image_surface_get_stride(self.to_raw_none()) } 135 | } 136 | 137 | pub fn get_width(&self) -> i32 { 138 | unsafe { ffi::cairo_image_surface_get_width(self.to_raw_none()) } 139 | } 140 | } 141 | 142 | #[derive(Debug)] 143 | pub struct ImageSurfaceData<'a> { 144 | surface: &'a mut ImageSurface, 145 | slice: &'a mut [u8], 146 | dirty: bool, 147 | } 148 | 149 | impl<'a> ImageSurfaceData<'a> { 150 | fn new(surface: &'a mut ImageSurface) -> ImageSurfaceData<'a> { 151 | unsafe { 152 | let ptr = ffi::cairo_image_surface_get_data(surface.to_raw_none()); 153 | debug_assert!(!ptr.is_null()); 154 | let len = (surface.get_stride() as usize) * (surface.get_height() as usize); 155 | ImageSurfaceData { 156 | surface, 157 | slice: slice::from_raw_parts_mut(ptr, len), 158 | dirty: false, 159 | } 160 | } 161 | } 162 | } 163 | 164 | impl<'a> Drop for ImageSurfaceData<'a> { 165 | fn drop(&mut self) { 166 | if self.dirty { 167 | self.surface.mark_dirty() 168 | } 169 | } 170 | } 171 | 172 | impl<'a> Deref for ImageSurfaceData<'a> { 173 | type Target = [u8]; 174 | 175 | fn deref(&self) -> &[u8] { 176 | self.slice 177 | } 178 | } 179 | 180 | impl<'a> DerefMut for ImageSurfaceData<'a> { 181 | fn deref_mut(&mut self) -> &mut [u8] { 182 | self.dirty = true; 183 | self.slice 184 | } 185 | } 186 | 187 | impl<'a> fmt::Display for ImageSurfaceData<'a> { 188 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 189 | write!(f, "ImageSurfaceData") 190 | } 191 | } 192 | 193 | // Workaround for cairo not having a direct way to check if the surface is finished. 194 | // See: https://gitlab.freedesktop.org/cairo/cairo/-/issues/406 195 | fn is_finished(surface: &ImageSurface) -> bool { 196 | use super::Context; 197 | let ctxt = Context::new(surface); 198 | ctxt.status().is_err() 199 | } 200 | 201 | #[cfg(test)] 202 | mod tests { 203 | use super::*; 204 | 205 | #[test] 206 | fn create_with_invalid_size_yields_error() { 207 | let result = ImageSurface::create(Format::ARgb32, 50000, 50000); 208 | assert!(result.is_err()); 209 | } 210 | 211 | #[test] 212 | fn create_for_data_with_invalid_stride_yields_error() { 213 | let result = ImageSurface::create_for_data(vec![0u8; 10], Format::ARgb32, 1, 2, 5); // unaligned stride 214 | assert!(result.is_err()); 215 | } 216 | 217 | #[test] 218 | fn create_with_valid_size() { 219 | let result = ImageSurface::create(Format::ARgb32, 10, 10); 220 | assert!(result.is_ok()); 221 | 222 | let result = ImageSurface::create_for_data(vec![0u8; 40 * 10], Format::ARgb32, 10, 10, 40); 223 | assert!(result.is_ok()); 224 | } 225 | 226 | #[test] 227 | fn no_crash_after_finish() { 228 | let mut surf = ImageSurface::create(Format::ARgb32, 1024, 1024).unwrap(); 229 | 230 | surf.finish(); 231 | 232 | assert!(surf.get_data().is_err()); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/image_surface_png.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use std::any::Any; 6 | use std::io::{self, Read, Write}; 7 | use std::panic::AssertUnwindSafe; 8 | use std::slice; 9 | use utils::status_to_result; 10 | 11 | use libc::{c_uint, c_void}; 12 | 13 | use error::{Error, IoError}; 14 | use ffi::{self, cairo_status_t}; 15 | use ImageSurface; 16 | 17 | struct ReadEnv<'a, R: 'a + Read> { 18 | reader: &'a mut R, 19 | io_error: Option, 20 | unwind_payload: Option>, 21 | } 22 | 23 | unsafe extern "C" fn read_func( 24 | closure: *mut c_void, 25 | data: *mut u8, 26 | len: c_uint, 27 | ) -> cairo_status_t { 28 | let read_env: &mut ReadEnv = &mut *(closure as *mut ReadEnv); 29 | 30 | // Don’t attempt another read if a previous one errored or panicked: 31 | if read_env.io_error.is_some() || read_env.unwind_payload.is_some() { 32 | return Error::ReadError.into(); 33 | } 34 | 35 | let buffer = slice::from_raw_parts_mut(data, len as usize); 36 | let result = std::panic::catch_unwind(AssertUnwindSafe(|| read_env.reader.read_exact(buffer))); 37 | match result { 38 | Ok(Ok(())) => ffi::STATUS_SUCCESS, 39 | Ok(Err(error)) => { 40 | read_env.io_error = Some(error); 41 | Error::ReadError.into() 42 | } 43 | Err(payload) => { 44 | read_env.unwind_payload = Some(payload); 45 | Error::ReadError.into() 46 | } 47 | } 48 | } 49 | 50 | struct WriteEnv<'a, W: 'a + Write> { 51 | writer: &'a mut W, 52 | io_error: Option, 53 | unwind_payload: Option>, 54 | } 55 | 56 | unsafe extern "C" fn write_func( 57 | closure: *mut c_void, 58 | data: *mut u8, 59 | len: c_uint, 60 | ) -> cairo_status_t { 61 | let write_env: &mut WriteEnv = &mut *(closure as *mut WriteEnv); 62 | 63 | // Don’t attempt another write if a previous one errored or panicked: 64 | if write_env.io_error.is_some() || write_env.unwind_payload.is_some() { 65 | return Error::WriteError.into(); 66 | } 67 | 68 | let buffer = slice::from_raw_parts(data, len as usize); 69 | let result = std::panic::catch_unwind(AssertUnwindSafe(|| write_env.writer.write_all(buffer))); 70 | match result { 71 | Ok(Ok(())) => ffi::STATUS_SUCCESS, 72 | Ok(Err(error)) => { 73 | write_env.io_error = Some(error); 74 | Error::WriteError.into() 75 | } 76 | Err(payload) => { 77 | write_env.unwind_payload = Some(payload); 78 | Error::WriteError.into() 79 | } 80 | } 81 | } 82 | 83 | impl ImageSurface { 84 | pub fn create_from_png(stream: &mut R) -> Result { 85 | let mut env = ReadEnv { 86 | reader: stream, 87 | io_error: None, 88 | unwind_payload: None, 89 | }; 90 | unsafe { 91 | let raw_surface = ffi::cairo_image_surface_create_from_png_stream( 92 | Some(read_func::), 93 | &mut env as *mut ReadEnv as *mut c_void, 94 | ); 95 | 96 | let surface = ImageSurface::from_raw_full(raw_surface)?; 97 | 98 | if let Some(payload) = env.unwind_payload { 99 | std::panic::resume_unwind(payload) 100 | } 101 | 102 | match env.io_error { 103 | None => Ok(surface), 104 | Some(err) => Err(IoError::Io(err)), 105 | } 106 | } 107 | } 108 | 109 | pub fn write_to_png(&self, stream: &mut W) -> Result<(), IoError> { 110 | let mut env = WriteEnv { 111 | writer: stream, 112 | io_error: None, 113 | unwind_payload: None, 114 | }; 115 | let status = unsafe { 116 | ffi::cairo_surface_write_to_png_stream( 117 | self.to_raw_none(), 118 | Some(write_func::), 119 | &mut env as *mut WriteEnv as *mut c_void, 120 | ) 121 | }; 122 | 123 | if let Some(payload) = env.unwind_payload { 124 | std::panic::resume_unwind(payload) 125 | } 126 | 127 | match env.io_error { 128 | None => match status_to_result(status) { 129 | Err(err) => Err(IoError::Cairo(err)), 130 | Ok(_) => Ok(()), 131 | }, 132 | Some(err) => Err(IoError::Io(err)), 133 | } 134 | } 135 | } 136 | 137 | #[cfg(test)] 138 | mod tests { 139 | use super::*; 140 | use enums::Format; 141 | use std::io::ErrorKind; 142 | 143 | struct IoErrorReader; 144 | 145 | // A reader that always returns an error 146 | impl Read for IoErrorReader { 147 | fn read(&mut self, _: &mut [u8]) -> Result { 148 | Err(io::Error::new(ErrorKind::Other, "yikes!")) 149 | } 150 | } 151 | 152 | #[test] 153 | fn valid_png_reads_correctly() { 154 | // A 1x1 PNG, RGB, no alpha, with a single pixel with (42, 42, 42) values 155 | let png_data: Vec = vec![ 156 | 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 157 | 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 158 | 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08, 159 | 0xd7, 0x63, 0xd0, 0xd2, 0xd2, 0x02, 0x00, 0x01, 0x00, 0x00, 0x7f, 0x09, 0xa9, 0x5a, 160 | 0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 161 | ]; 162 | 163 | let r = ImageSurface::create_from_png(&mut &png_data[..]); 164 | assert!(r.is_ok()); 165 | 166 | let mut surface = r.unwrap(); 167 | assert!(surface.get_width() == 1); 168 | assert!(surface.get_height() == 1); 169 | assert!(surface.get_format() == Format::Rgb24); 170 | 171 | let data = surface.get_data().unwrap(); 172 | assert!(data.len() >= 3); 173 | 174 | let slice = &data[0..3]; 175 | assert!(slice[0] == 42); 176 | assert!(slice[1] == 42); 177 | assert!(slice[2] == 42); 178 | } 179 | 180 | #[cfg(not(target_os = "macos"))] 181 | #[test] 182 | fn invalid_png_yields_error() { 183 | let png_data: Vec = vec![ 184 | // v--- this byte is modified 185 | 0x89, 0x40, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 186 | 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 187 | 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08, 188 | 0xd7, 0x63, 0xd0, 0xd2, 0xd2, 0x02, 0x00, 0x01, 0x00, 0x00, 0x7f, 0x09, 0xa9, 0x5a, 189 | 0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 190 | ]; 191 | 192 | match ImageSurface::create_from_png(&mut &png_data[..]) { 193 | Err(IoError::Cairo(_)) => (), 194 | _ => unreachable!(), 195 | } 196 | } 197 | 198 | #[cfg(not(target_os = "macos"))] 199 | #[test] 200 | fn io_error_yields_cairo_read_error() { 201 | let mut r = IoErrorReader; 202 | 203 | match ImageSurface::create_from_png(&mut r) { 204 | Err(IoError::Cairo(Error::ReadError)) => (), 205 | _ => unreachable!(), 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2016, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | //! # Cairo bindings 6 | //! 7 | //! This library contains safe Rust bindings for [Cairo](https://www.cairographics.org/). 8 | //! It is a part of [Gtk-rs](https://gtk-rs.org/). 9 | //! 10 | //! ## Crate features 11 | //! 12 | //! ### Default-on features 13 | //! 14 | //! * **use_glib** - Use with [glib](https://gtk-rs.org/docs/glib/) 15 | //! 16 | //! ### Fileformat features 17 | //! 18 | //! * **png** - Reading and writing PNG images 19 | //! * **pdf** - Rendering PDF documents 20 | //! * **svg** - Rendering SVG documents 21 | //! * **ps** - Rendering PostScript documents 22 | //! 23 | //! ### Cairo API version features 24 | //! 25 | //! * **v1_14** - Use Cairo 1.14 APIs 26 | //! * **v1_16** - Use Cairo 1.16 APIs 27 | //! 28 | //! ### Documentation features 29 | //! 30 | //! * **embed-lgpl-docs** - Embed API docs locally 31 | //! * **purge-lgpl-docs** - Remove API docs again (counterpart to `embed-lgpl-docs`) 32 | //! * **dox** - Used to keep system dependent items in documentation 33 | //! 34 | //! ### X Window features 35 | //! 36 | //! * **xcb** - X Window System rendering using the XCB library 37 | //! * **xlib** - X Window System rendering using XLib 38 | //! 39 | //! ### Windows API features 40 | //! 41 | //! * **win32-surface** - Microsoft Windows surface support 42 | 43 | extern crate cairo_sys as ffi; 44 | extern crate libc; 45 | extern crate thiserror; 46 | 47 | #[macro_use] 48 | extern crate bitflags; 49 | 50 | #[cfg(feature = "use_glib")] 51 | #[macro_use] 52 | extern crate glib; 53 | 54 | #[cfg(feature = "use_glib")] 55 | extern crate glib_sys as glib_ffi; 56 | 57 | #[cfg(feature = "use_glib")] 58 | extern crate gobject_sys as gobject_ffi; 59 | 60 | #[cfg(test)] 61 | extern crate tempfile; 62 | 63 | // Helper macro for our GValue related trait impls 64 | #[cfg(feature = "use_glib")] 65 | macro_rules! gvalue_impl { 66 | ($name:ty, $ffi_name:ty, $get_type:expr) => { 67 | use glib; 68 | #[allow(unused_imports)] 69 | use glib::translate::*; 70 | use glib_ffi; 71 | use gobject_ffi; 72 | 73 | impl glib::types::StaticType for $name { 74 | fn static_type() -> glib::types::Type { 75 | unsafe { from_glib($get_type()) } 76 | } 77 | } 78 | 79 | impl<'a> glib::value::FromValueOptional<'a> for $name { 80 | unsafe fn from_value_optional(v: &'a glib::value::Value) -> Option { 81 | let ptr = gobject_ffi::g_value_get_boxed(v.to_glib_none().0); 82 | assert!(!ptr.is_null()); 83 | from_glib_none(ptr as *mut $ffi_name) 84 | } 85 | } 86 | 87 | impl glib::value::SetValue for $name { 88 | unsafe fn set_value(v: &mut glib::value::Value, s: &Self) { 89 | gobject_ffi::g_value_set_boxed( 90 | v.to_glib_none_mut().0, 91 | s.to_glib_none().0 as glib_ffi::gpointer, 92 | ); 93 | } 94 | } 95 | 96 | impl glib::value::SetValueOptional for $name { 97 | unsafe fn set_value_optional(v: &mut glib::value::Value, s: Option<&Self>) { 98 | if let Some(s) = s { 99 | gobject_ffi::g_value_set_boxed( 100 | v.to_glib_none_mut().0, 101 | s.to_glib_none().0 as glib_ffi::gpointer, 102 | ); 103 | } else { 104 | gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, ::std::ptr::null_mut()); 105 | } 106 | } 107 | } 108 | }; 109 | } 110 | 111 | pub use user_data::UserDataKey; 112 | 113 | pub use context::{Context, RectangleList}; 114 | 115 | pub use paths::{Path, PathSegment, PathSegments}; 116 | 117 | pub use device::Device; 118 | 119 | pub use enums::*; 120 | 121 | pub use error::{BorrowError, Error, IoError}; 122 | 123 | pub use patterns::{ 124 | Gradient, LinearGradient, Mesh, Pattern, RadialGradient, SolidPattern, SurfacePattern, 125 | }; 126 | 127 | pub use font::{ 128 | FontExtents, FontFace, FontOptions, FontSlant, FontType, FontWeight, Glyph, ScaledFont, 129 | TextCluster, TextExtents, 130 | }; 131 | 132 | pub use matrices::Matrix; 133 | 134 | pub use recording_surface::RecordingSurface; 135 | pub use rectangle::Rectangle; 136 | pub use rectangle_int::RectangleInt; 137 | 138 | pub use region::Region; 139 | 140 | pub use surface::{MappedImageSurface, Surface}; 141 | 142 | pub use image_surface::{ImageSurface, ImageSurfaceData}; 143 | 144 | #[cfg(any(feature = "pdf", feature = "svg", feature = "ps", feature = "dox"))] 145 | pub use stream::StreamWithError; 146 | 147 | #[cfg(any(feature = "pdf", feature = "dox"))] 148 | pub use pdf::PdfSurface; 149 | 150 | #[cfg(any(feature = "ps", feature = "dox"))] 151 | pub use ps::PsSurface; 152 | 153 | #[cfg(any(feature = "svg", feature = "dox"))] 154 | pub use svg::SvgSurface; 155 | 156 | #[cfg(any(feature = "xcb", feature = "dox"))] 157 | pub use xcb::{ 158 | XCBConnection, XCBDrawable, XCBPixmap, XCBRenderPictFormInfo, XCBScreen, XCBSurface, 159 | XCBVisualType, 160 | }; 161 | 162 | #[macro_use] 163 | mod surface_macros; 164 | #[macro_use] 165 | mod user_data; 166 | mod constants; 167 | pub use constants::*; 168 | mod utils; 169 | pub use utils::{debug_reset_static_data, get_version_string, Version}; 170 | mod context; 171 | mod device; 172 | mod enums; 173 | mod error; 174 | mod font; 175 | mod image_surface; 176 | #[cfg(any(feature = "png", feature = "dox"))] 177 | mod image_surface_png; 178 | mod matrices; 179 | mod paths; 180 | mod patterns; 181 | mod recording_surface; 182 | mod rectangle; 183 | mod rectangle_int; 184 | mod region; 185 | mod surface; 186 | #[cfg(any(feature = "xcb", feature = "dox"))] 187 | mod xcb; 188 | 189 | #[cfg(any(feature = "pdf", feature = "svg", feature = "ps", feature = "dox"))] 190 | #[macro_use] 191 | mod stream; 192 | #[cfg(any(feature = "pdf", feature = "dox"))] 193 | mod pdf; 194 | #[cfg(any(feature = "ps", feature = "dox"))] 195 | mod ps; 196 | #[cfg(any(feature = "svg", feature = "dox"))] 197 | mod svg; 198 | 199 | #[cfg(any(target_os = "macos", target_os = "ios", feature = "dox"))] 200 | mod quartz_surface; 201 | #[cfg(any(target_os = "macos", target_os = "ios", feature = "dox"))] 202 | pub use quartz_surface::QuartzSurface; 203 | 204 | #[cfg(any(all(windows, feature = "win32-surface"), feature = "dox"))] 205 | mod win32_surface; 206 | 207 | #[cfg(any(all(windows, feature = "win32-surface"), feature = "dox"))] 208 | pub use win32_surface::Win32Surface; 209 | 210 | #[cfg(not(feature = "use_glib"))] 211 | mod borrowed { 212 | use std::mem; 213 | 214 | /// Wrapper around values representing borrowed C memory. 215 | /// 216 | /// This is returned by `from_glib_borrow()` and ensures that the wrapped value 217 | /// is never dropped when going out of scope. 218 | /// 219 | /// Borrowed values must never be passed by value or mutable reference to safe Rust code and must 220 | /// not leave the C scope in which they are valid. 221 | #[derive(Debug)] 222 | pub struct Borrowed(mem::ManuallyDrop); 223 | 224 | impl Borrowed { 225 | /// Creates a new borrowed value. 226 | pub fn new(val: T) -> Self { 227 | Self(mem::ManuallyDrop::new(val)) 228 | } 229 | 230 | /// Extracts the contained value. 231 | /// 232 | /// The returned value must never be dropped and instead has to be passed to `mem::forget()` or 233 | /// be directly wrapped in `mem::ManuallyDrop` or another `Borrowed` wrapper. 234 | pub unsafe fn into_inner(self) -> T { 235 | mem::ManuallyDrop::into_inner(self.0) 236 | } 237 | } 238 | 239 | impl AsRef for Borrowed { 240 | fn as_ref(&self) -> &T { 241 | &*self.0 242 | } 243 | } 244 | 245 | impl std::ops::Deref for Borrowed { 246 | type Target = T; 247 | 248 | fn deref(&self) -> &T { 249 | &*self.0 250 | } 251 | } 252 | } 253 | 254 | #[cfg(not(feature = "use_glib"))] 255 | pub use borrowed::Borrowed; 256 | 257 | #[cfg(feature = "use_glib")] 258 | pub(crate) use glib::translate::Borrowed; 259 | -------------------------------------------------------------------------------- /src/matrices.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2015, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use error::Error; 6 | use ffi; 7 | use libc::c_double; 8 | use utils::status_to_result; 9 | 10 | #[repr(C)] 11 | #[derive(Debug, Clone, Copy, PartialEq)] 12 | pub struct Matrix { 13 | pub xx: c_double, 14 | pub yx: c_double, 15 | 16 | pub xy: c_double, 17 | pub yy: c_double, 18 | 19 | pub x0: c_double, 20 | pub y0: c_double, 21 | } 22 | 23 | impl Default for Matrix { 24 | fn default() -> Matrix { 25 | Matrix::identity() 26 | } 27 | } 28 | 29 | impl Matrix { 30 | pub(crate) fn ptr(&self) -> *const ffi::Matrix { 31 | self as *const Matrix as _ 32 | } 33 | 34 | pub(crate) fn mut_ptr(&mut self) -> *mut ffi::Matrix { 35 | self as *mut Matrix as _ 36 | } 37 | 38 | pub(crate) fn null() -> Matrix { 39 | Matrix { 40 | xx: 0.0, 41 | yx: 0.0, 42 | xy: 0.0, 43 | yy: 0.0, 44 | x0: 0.0, 45 | y0: 0.0, 46 | } 47 | } 48 | 49 | pub fn identity() -> Matrix { 50 | Matrix { 51 | xx: 1.0, 52 | yx: 0.0, 53 | xy: 0.0, 54 | yy: 1.0, 55 | x0: 0.0, 56 | y0: 0.0, 57 | } 58 | } 59 | 60 | pub fn new(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Matrix { 61 | Matrix { 62 | xx, 63 | yx, 64 | xy, 65 | yy, 66 | x0, 67 | y0, 68 | } 69 | } 70 | 71 | pub fn multiply(left: &Matrix, right: &Matrix) -> Matrix { 72 | let mut matrix = Matrix::null(); 73 | unsafe { 74 | ffi::cairo_matrix_multiply(matrix.mut_ptr(), left.ptr(), right.ptr()); 75 | } 76 | matrix 77 | } 78 | 79 | pub fn translate(&mut self, tx: f64, ty: f64) { 80 | unsafe { ffi::cairo_matrix_translate(self.mut_ptr(), tx, ty) } 81 | } 82 | 83 | pub fn scale(&mut self, sx: f64, sy: f64) { 84 | unsafe { ffi::cairo_matrix_scale(self.mut_ptr(), sx, sy) } 85 | } 86 | 87 | pub fn rotate(&mut self, angle: f64) { 88 | unsafe { ffi::cairo_matrix_rotate(self.mut_ptr(), angle) } 89 | } 90 | 91 | pub fn invert(&mut self) { 92 | let status = unsafe { ffi::cairo_matrix_invert(self.mut_ptr()) }; 93 | status_to_result(status).expect("Failed to invert matrix"); 94 | } 95 | 96 | pub fn try_invert(&self) -> Result { 97 | let mut matrix = *self; 98 | 99 | let status = unsafe { ffi::cairo_matrix_invert(matrix.mut_ptr()) }; 100 | status_to_result(status)?; 101 | Ok(matrix) 102 | } 103 | 104 | pub fn transform_distance(&self, _dx: f64, _dy: f64) -> (f64, f64) { 105 | let mut dx = _dx; 106 | let mut dy = _dy; 107 | 108 | unsafe { 109 | ffi::cairo_matrix_transform_distance(self.ptr(), &mut dx, &mut dy); 110 | } 111 | (dx, dy) 112 | } 113 | 114 | pub fn transform_point(&self, _x: f64, _y: f64) -> (f64, f64) { 115 | let mut x = _x; 116 | let mut y = _y; 117 | 118 | unsafe { 119 | ffi::cairo_matrix_transform_point(self.ptr(), &mut x, &mut y); 120 | } 121 | (x, y) 122 | } 123 | } 124 | 125 | #[cfg(test)] 126 | mod tests { 127 | use super::*; 128 | 129 | #[test] 130 | fn memory_layout_is_ffi_equivalent() { 131 | macro_rules! dummy_values { 132 | ($Matrix: ident) => { 133 | $Matrix { 134 | xx: 1.0, 135 | yx: 2.0, 136 | xy: 3.0, 137 | yy: 4.0, 138 | x0: 5.0, 139 | y0: 6.0, 140 | } 141 | }; 142 | } 143 | use ffi::Matrix as FfiMatrix; 144 | let transmuted: Matrix = unsafe { std::mem::transmute(dummy_values!(FfiMatrix)) }; 145 | assert_eq!(transmuted, dummy_values!(Matrix)); 146 | } 147 | 148 | #[test] 149 | fn invalid_matrix_does_not_invert() { 150 | let matrix = Matrix::null(); 151 | assert!(matrix.try_invert().is_err()); 152 | } 153 | 154 | #[test] 155 | #[should_panic] 156 | fn inverting_invalid_matrix_panics() { 157 | let mut matrix = Matrix::null(); 158 | matrix.invert(); 159 | } 160 | 161 | #[test] 162 | fn valid_matrix_try_invert() { 163 | let matrix = Matrix::identity(); 164 | assert!(matrix.try_invert().unwrap() == Matrix::identity()); 165 | } 166 | 167 | #[test] 168 | fn valid_matrix_invert() { 169 | let mut matrix = Matrix::identity(); 170 | matrix.invert(); 171 | assert!(matrix == Matrix::identity()); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/paths.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2015, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use enums::PathDataType; 6 | use ffi; 7 | use ffi::cairo_path_t; 8 | use std::fmt; 9 | use std::iter::Iterator; 10 | use std::ptr; 11 | 12 | #[derive(Debug)] 13 | pub struct Path(ptr::NonNull); 14 | 15 | impl Path { 16 | pub fn as_ptr(&self) -> *mut cairo_path_t { 17 | self.0.as_ptr() 18 | } 19 | 20 | pub unsafe fn from_raw_full(pointer: *mut cairo_path_t) -> Path { 21 | assert!(!pointer.is_null()); 22 | Path(ptr::NonNull::new_unchecked(pointer)) 23 | } 24 | 25 | pub fn iter(&self) -> PathSegments { 26 | use std::slice; 27 | 28 | unsafe { 29 | let ptr: *mut cairo_path_t = self.as_ptr(); 30 | let length = (*ptr).num_data as usize; 31 | let data_ptr = (*ptr).data; 32 | let data_vec = if length != 0 && !data_ptr.is_null() { 33 | slice::from_raw_parts(data_ptr, length) 34 | } else { 35 | &[] 36 | }; 37 | 38 | PathSegments { 39 | data: data_vec, 40 | i: 0, 41 | num_data: length, 42 | } 43 | } 44 | } 45 | } 46 | 47 | impl Drop for Path { 48 | fn drop(&mut self) { 49 | unsafe { 50 | ffi::cairo_path_destroy(self.as_ptr()); 51 | } 52 | } 53 | } 54 | 55 | impl fmt::Display for Path { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | write!(f, "Path") 58 | } 59 | } 60 | 61 | #[derive(Debug, Clone, Copy, PartialEq)] 62 | pub enum PathSegment { 63 | MoveTo((f64, f64)), 64 | LineTo((f64, f64)), 65 | CurveTo((f64, f64), (f64, f64), (f64, f64)), 66 | ClosePath, 67 | } 68 | 69 | impl fmt::Display for PathSegment { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | write!( 72 | f, 73 | "PathSegment::{}", 74 | match *self { 75 | PathSegment::MoveTo(_) => "MoveTo", 76 | PathSegment::LineTo(_) => "LineTo", 77 | PathSegment::CurveTo(_, _, _) => "CurveTo", 78 | PathSegment::ClosePath => "ClosePath", 79 | } 80 | ) 81 | } 82 | } 83 | 84 | pub struct PathSegments<'a> { 85 | data: &'a [ffi::cairo_path_data], 86 | i: usize, 87 | num_data: usize, 88 | } 89 | 90 | impl<'a> Iterator for PathSegments<'a> { 91 | type Item = PathSegment; 92 | 93 | fn next(&mut self) -> Option { 94 | if self.i >= self.num_data { 95 | return None; 96 | } 97 | 98 | unsafe { 99 | let res = match PathDataType::from(self.data[self.i].header.data_type) { 100 | PathDataType::MoveTo => PathSegment::MoveTo(to_tuple(&self.data[self.i + 1].point)), 101 | PathDataType::LineTo => PathSegment::LineTo(to_tuple(&self.data[self.i + 1].point)), 102 | PathDataType::CurveTo => PathSegment::CurveTo( 103 | to_tuple(&self.data[self.i + 1].point), 104 | to_tuple(&self.data[self.i + 2].point), 105 | to_tuple(&self.data[self.i + 3].point), 106 | ), 107 | PathDataType::ClosePath => PathSegment::ClosePath, 108 | PathDataType::__Unknown(x) => panic!("Unknown value: {}", x), 109 | }; 110 | 111 | self.i += self.data[self.i].header.length as usize; 112 | 113 | Some(res) 114 | } 115 | } 116 | } 117 | 118 | impl<'a> fmt::Display for PathSegments<'a> { 119 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 120 | write!(f, "PathSegments") 121 | } 122 | } 123 | 124 | fn to_tuple(pair: &[f64; 2]) -> (f64, f64) { 125 | (pair[0], pair[1]) 126 | } 127 | 128 | #[cfg(test)] 129 | mod tests { 130 | use super::*; 131 | use context::*; 132 | use enums::Format; 133 | use image_surface::*; 134 | 135 | fn make_cr() -> Context { 136 | let surface = ImageSurface::create(Format::Rgb24, 1, 1).unwrap(); 137 | 138 | Context::new(&surface) 139 | } 140 | 141 | fn assert_path_equals_segments(expected: &Path, actual: &Vec) { 142 | // First ensure the lengths are equal 143 | 144 | let expected_iter = expected.iter(); 145 | let actual_iter = actual.iter(); 146 | 147 | assert_eq!(expected_iter.count(), actual_iter.count()); 148 | 149 | // Then actually compare the contents 150 | 151 | let expected_iter = expected.iter(); 152 | let actual_iter = actual.iter(); 153 | 154 | let mut iter = expected_iter.zip(actual_iter); 155 | 156 | while let Some((e, a)) = iter.next() { 157 | assert_eq!(e, *a); 158 | } 159 | } 160 | 161 | #[test] 162 | fn empty_path_doesnt_iter() { 163 | let cr = make_cr(); 164 | 165 | let path = cr.copy_path(); 166 | 167 | assert!(path.iter().next().is_none()); 168 | } 169 | 170 | #[test] 171 | fn moveto() { 172 | let cr = make_cr(); 173 | 174 | cr.move_to(1.0, 2.0); 175 | 176 | let path = cr.copy_path(); 177 | 178 | assert_path_equals_segments(&path, &vec![PathSegment::MoveTo((1.0, 2.0))]); 179 | } 180 | 181 | #[test] 182 | fn moveto_lineto_moveto() { 183 | let cr = make_cr(); 184 | 185 | cr.move_to(1.0, 2.0); 186 | cr.line_to(3.0, 4.0); 187 | cr.move_to(5.0, 6.0); 188 | 189 | let path = cr.copy_path(); 190 | 191 | assert_path_equals_segments( 192 | &path, 193 | &vec![ 194 | PathSegment::MoveTo((1.0, 2.0)), 195 | PathSegment::LineTo((3.0, 4.0)), 196 | PathSegment::MoveTo((5.0, 6.0)), 197 | ], 198 | ); 199 | } 200 | 201 | #[test] 202 | fn moveto_closepath() { 203 | let cr = make_cr(); 204 | 205 | cr.move_to(1.0, 2.0); 206 | cr.close_path(); 207 | 208 | let path = cr.copy_path(); 209 | 210 | // Note that Cairo represents a close_path as closepath+moveto, 211 | // so that the next subpath will have a starting point, 212 | // from the extra moveto. 213 | assert_path_equals_segments( 214 | &path, 215 | &vec![ 216 | PathSegment::MoveTo((1.0, 2.0)), 217 | PathSegment::ClosePath, 218 | PathSegment::MoveTo((1.0, 2.0)), 219 | ], 220 | ); 221 | } 222 | #[test] 223 | fn curveto_closed_subpath_lineto() { 224 | let cr = make_cr(); 225 | 226 | cr.move_to(1.0, 2.0); 227 | cr.curve_to(3.0, 4.0, 5.0, 6.0, 7.0, 8.0); 228 | cr.close_path(); 229 | cr.line_to(9.0, 10.0); 230 | 231 | let path = cr.copy_path(); 232 | 233 | assert_path_equals_segments( 234 | &path, 235 | &vec![ 236 | PathSegment::MoveTo((1.0, 2.0)), 237 | PathSegment::CurveTo((3.0, 4.0), (5.0, 6.0), (7.0, 8.0)), 238 | PathSegment::ClosePath, 239 | PathSegment::MoveTo((1.0, 2.0)), 240 | PathSegment::LineTo((9.0, 10.0)), 241 | ], 242 | ); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/patterns.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2015, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use enums::MeshCorner; 6 | use enums::{Extend, Filter, PatternType}; 7 | use error::Error; 8 | use ffi; 9 | use ffi::{cairo_pattern_t, cairo_surface_t}; 10 | use libc::{c_double, c_int, c_uint}; 11 | use std::convert::TryFrom; 12 | use std::fmt; 13 | use std::ops::Deref; 14 | use std::ptr; 15 | use utils::status_to_result; 16 | use {Matrix, Path, Surface}; 17 | 18 | // See https://cairographics.org/manual/bindings-patterns.html for more info 19 | #[derive(Debug)] 20 | pub struct Pattern { 21 | pointer: *mut cairo_pattern_t, 22 | } 23 | 24 | impl fmt::Display for Pattern { 25 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 26 | write!(f, "Pattern") 27 | } 28 | } 29 | 30 | impl Pattern { 31 | user_data_methods! { 32 | ffi::cairo_pattern_get_user_data, 33 | ffi::cairo_pattern_set_user_data, 34 | } 35 | 36 | pub fn to_raw_none(&self) -> *mut cairo_pattern_t { 37 | self.pointer 38 | } 39 | 40 | pub unsafe fn from_raw_none(pointer: *mut cairo_pattern_t) -> Pattern { 41 | ffi::cairo_pattern_reference(pointer); 42 | Self::from_raw_full(pointer) 43 | } 44 | 45 | pub unsafe fn from_raw_full(pointer: *mut cairo_pattern_t) -> Pattern { 46 | Pattern { pointer } 47 | } 48 | 49 | pub fn get_type(&self) -> PatternType { 50 | unsafe { ffi::cairo_pattern_get_type(self.pointer).into() } 51 | } 52 | 53 | pub fn get_reference_count(&self) -> isize { 54 | unsafe { ffi::cairo_pattern_get_reference_count(self.pointer) as isize } 55 | } 56 | 57 | pub fn set_extend(&self, extend: Extend) { 58 | unsafe { ffi::cairo_pattern_set_extend(self.pointer, extend.into()) } 59 | } 60 | 61 | pub fn get_extend(&self) -> Extend { 62 | unsafe { Extend::from(ffi::cairo_pattern_get_extend(self.pointer)) } 63 | } 64 | 65 | pub fn set_filter(&self, filter: Filter) { 66 | unsafe { ffi::cairo_pattern_set_filter(self.pointer, filter.into()) } 67 | } 68 | 69 | pub fn get_filter(&self) -> Filter { 70 | unsafe { Filter::from(ffi::cairo_pattern_get_filter(self.pointer)) } 71 | } 72 | 73 | pub fn set_matrix(&self, matrix: Matrix) { 74 | unsafe { ffi::cairo_pattern_set_matrix(self.pointer, matrix.ptr()) } 75 | } 76 | 77 | pub fn get_matrix(&self) -> Matrix { 78 | let mut matrix = Matrix::null(); 79 | unsafe { 80 | ffi::cairo_pattern_get_matrix(self.pointer, matrix.mut_ptr()); 81 | } 82 | matrix 83 | } 84 | 85 | fn status(&self) -> Result<(), Error> { 86 | let status = unsafe { ffi::cairo_pattern_status(self.pointer) }; 87 | status_to_result(status) 88 | } 89 | } 90 | 91 | impl Clone for Pattern { 92 | fn clone(&self) -> Self { 93 | Pattern { 94 | pointer: unsafe { ffi::cairo_pattern_reference(self.pointer) }, 95 | } 96 | } 97 | } 98 | 99 | impl Drop for Pattern { 100 | fn drop(&mut self) { 101 | unsafe { ffi::cairo_pattern_destroy(self.pointer) } 102 | } 103 | } 104 | 105 | macro_rules! convert { 106 | ($source: ident => $dest: ident = $( $variant: ident )|+ $( ($intermediate: ident) )*) => { 107 | impl TryFrom<$source> for $dest { 108 | type Error = $source; 109 | 110 | fn try_from(pattern: $source) -> Result { 111 | if $( pattern.get_type() == PatternType::$variant )||+ { 112 | $( 113 | let pattern = $intermediate(pattern); 114 | )* 115 | Ok($dest(pattern)) 116 | } 117 | else { 118 | Err(pattern) 119 | } 120 | } 121 | } 122 | }; 123 | } 124 | 125 | macro_rules! pattern_type( 126 | //Signals without arguments 127 | ($pattern_type:ident $( = $variant: ident)*) => ( 128 | 129 | #[derive(Debug, Clone)] 130 | pub struct $pattern_type(Pattern); 131 | 132 | impl Deref for $pattern_type { 133 | type Target = Pattern; 134 | 135 | fn deref(&self) -> &Pattern { 136 | &self.0 137 | } 138 | } 139 | 140 | impl fmt::Display for $pattern_type { 141 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 142 | write!(f, stringify!($pattern_type)) 143 | } 144 | } 145 | 146 | $( 147 | convert!(Pattern => $pattern_type = $variant); 148 | )* 149 | ); 150 | ); 151 | 152 | pattern_type!(SolidPattern = Solid); 153 | 154 | impl SolidPattern { 155 | pub fn from_rgb(red: f64, green: f64, blue: f64) -> SolidPattern { 156 | unsafe { 157 | SolidPattern(Pattern::from_raw_full(ffi::cairo_pattern_create_rgb( 158 | red, green, blue, 159 | ))) 160 | } 161 | } 162 | 163 | pub fn from_rgba(red: f64, green: f64, blue: f64, alpha: f64) -> SolidPattern { 164 | unsafe { 165 | SolidPattern(Pattern::from_raw_full(ffi::cairo_pattern_create_rgba( 166 | red, green, blue, alpha, 167 | ))) 168 | } 169 | } 170 | 171 | pub fn get_rgba(&self) -> (f64, f64, f64, f64) { 172 | unsafe { 173 | let mut red = 0.0; 174 | let mut green = 0.0; 175 | let mut blue = 0.0; 176 | let mut alpha = 0.0; 177 | 178 | let status = ffi::cairo_pattern_get_rgba( 179 | self.pointer, 180 | &mut red, 181 | &mut green, 182 | &mut blue, 183 | &mut alpha, 184 | ); 185 | status_to_result(status).expect("Failed to get_rgba"); 186 | 187 | (red, green, blue, alpha) 188 | } 189 | } 190 | } 191 | 192 | pattern_type!(Gradient); 193 | convert!(Pattern => Gradient = LinearGradient | RadialGradient); 194 | 195 | impl Gradient { 196 | pub fn add_color_stop_rgb(&self, offset: f64, red: f64, green: f64, blue: f64) { 197 | unsafe { ffi::cairo_pattern_add_color_stop_rgb(self.pointer, offset, red, green, blue) } 198 | } 199 | 200 | pub fn add_color_stop_rgba(&self, offset: f64, red: f64, green: f64, blue: f64, alpha: f64) { 201 | unsafe { 202 | ffi::cairo_pattern_add_color_stop_rgba(self.pointer, offset, red, green, blue, alpha) 203 | } 204 | } 205 | 206 | pub fn get_color_stop_count(&self) -> isize { 207 | unsafe { 208 | let mut count = 0; 209 | let status = ffi::cairo_pattern_get_color_stop_count(self.pointer, &mut count); 210 | 211 | status_to_result(status).expect("Failed to get_color_stop_count"); 212 | count as isize 213 | } 214 | } 215 | 216 | pub fn get_color_stop_rgba(&self, index: isize) -> (f64, f64, f64, f64, f64) { 217 | unsafe { 218 | let mut offset = 0.0; 219 | let mut red = 0.0; 220 | let mut green = 0.0; 221 | let mut blue = 0.0; 222 | let mut alpha = 0.0; 223 | 224 | let status = ffi::cairo_pattern_get_color_stop_rgba( 225 | self.pointer, 226 | index as c_int, 227 | &mut offset, 228 | &mut red, 229 | &mut green, 230 | &mut blue, 231 | &mut alpha, 232 | ); 233 | status_to_result(status).expect("Failed to get_color_stop_rgba"); 234 | (offset, red, green, blue, alpha) 235 | } 236 | } 237 | } 238 | 239 | macro_rules! gradient_type { 240 | ($gradient_type: ident) => { 241 | #[derive(Debug, Clone)] 242 | pub struct $gradient_type(Gradient); 243 | 244 | impl Deref for $gradient_type { 245 | type Target = Gradient; 246 | 247 | fn deref(&self) -> &Gradient { 248 | &self.0 249 | } 250 | } 251 | 252 | impl fmt::Display for $gradient_type { 253 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 254 | write!(f, stringify!($gradient_type)) 255 | } 256 | } 257 | 258 | convert!(Pattern => $gradient_type = $gradient_type (Gradient)); 259 | convert!(Gradient => $gradient_type = $gradient_type); 260 | } 261 | } 262 | 263 | gradient_type!(LinearGradient); 264 | 265 | impl LinearGradient { 266 | pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> LinearGradient { 267 | unsafe { 268 | LinearGradient(Gradient(Pattern::from_raw_full( 269 | ffi::cairo_pattern_create_linear(x0, y0, x1, y1), 270 | ))) 271 | } 272 | } 273 | 274 | pub fn get_linear_points(&self) -> (f64, f64, f64, f64) { 275 | unsafe { 276 | let mut x0 = 0.0; 277 | let mut y0 = 0.0; 278 | let mut x1 = 0.0; 279 | let mut y1 = 0.0; 280 | 281 | let status = ffi::cairo_pattern_get_linear_points( 282 | self.pointer, 283 | &mut x0, 284 | &mut y0, 285 | &mut x1, 286 | &mut y1, 287 | ); 288 | status_to_result(status).expect("Failed to get linear points"); 289 | (x0, y0, x1, y1) 290 | } 291 | } 292 | } 293 | 294 | gradient_type!(RadialGradient); 295 | 296 | impl RadialGradient { 297 | pub fn new(x0: f64, y0: f64, r0: f64, x1: f64, y1: f64, r1: f64) -> RadialGradient { 298 | unsafe { 299 | RadialGradient(Gradient(Pattern::from_raw_full( 300 | ffi::cairo_pattern_create_radial(x0, y0, r0, x1, y1, r1), 301 | ))) 302 | } 303 | } 304 | 305 | pub fn get_radial_circles(&self) -> (f64, f64, f64, f64, f64, f64) { 306 | unsafe { 307 | let mut x0 = 0.0; 308 | let mut y0 = 0.0; 309 | let mut r0 = 0.0; 310 | let mut x1 = 0.0; 311 | let mut y1 = 0.0; 312 | let mut r1 = 0.0; 313 | 314 | let status = ffi::cairo_pattern_get_radial_circles( 315 | self.pointer, 316 | &mut x0, 317 | &mut y0, 318 | &mut r0, 319 | &mut x1, 320 | &mut y1, 321 | &mut r1, 322 | ); 323 | status_to_result(status).expect("Failed to get radial circles"); 324 | (x0, y0, r0, x1, y1, r1) 325 | } 326 | } 327 | } 328 | 329 | pattern_type!(SurfacePattern = Surface); 330 | 331 | impl SurfacePattern { 332 | pub fn create(surface: &Surface) -> SurfacePattern { 333 | unsafe { 334 | SurfacePattern(Pattern::from_raw_full( 335 | ffi::cairo_pattern_create_for_surface(surface.to_raw_none()), 336 | )) 337 | } 338 | } 339 | 340 | pub fn get_surface(&self) -> Surface { 341 | unsafe { 342 | let mut surface_ptr: *mut cairo_surface_t = ptr::null_mut(); 343 | let status = ffi::cairo_pattern_get_surface(self.pointer, &mut surface_ptr); 344 | status_to_result(status).expect("Failed to get the surface"); 345 | Surface::from_raw_none(surface_ptr) 346 | } 347 | } 348 | } 349 | 350 | pattern_type!(Mesh = Mesh); 351 | 352 | impl Mesh { 353 | pub fn new() -> Mesh { 354 | unsafe { Mesh(Pattern::from_raw_full(ffi::cairo_pattern_create_mesh())) } 355 | } 356 | 357 | pub fn begin_patch(&self) { 358 | unsafe { ffi::cairo_mesh_pattern_begin_patch(self.pointer) } 359 | self.status().expect("Failed to begin_patch"); 360 | } 361 | 362 | pub fn end_patch(&self) { 363 | unsafe { ffi::cairo_mesh_pattern_end_patch(self.pointer) } 364 | self.status().expect("Failed to end_patch"); 365 | } 366 | 367 | pub fn move_to(&self, x: f64, y: f64) { 368 | unsafe { ffi::cairo_mesh_pattern_move_to(self.pointer, x, y) } 369 | self.status().expect("Failed to move to"); 370 | } 371 | 372 | pub fn line_to(&self, x: f64, y: f64) { 373 | unsafe { ffi::cairo_mesh_pattern_line_to(self.pointer, x, y) } 374 | self.status().expect("Failed to line to"); 375 | } 376 | 377 | pub fn curve_to(&self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) { 378 | unsafe { ffi::cairo_mesh_pattern_curve_to(self.pointer, x1, y1, x2, y2, x3, y3) } 379 | self.status().expect("Failed to curve to"); 380 | } 381 | 382 | pub fn set_control_point(&self, corner: MeshCorner, x: f64, y: f64) { 383 | unsafe { ffi::cairo_mesh_pattern_set_control_point(self.pointer, corner.into(), x, y) } 384 | self.status().expect("Failed to set control point"); 385 | } 386 | 387 | pub fn get_control_point(&self, patch_num: usize, corner: MeshCorner) -> (f64, f64) { 388 | let mut x: c_double = 0.0; 389 | let mut y: c_double = 0.0; 390 | 391 | let status = unsafe { 392 | ffi::cairo_mesh_pattern_get_control_point( 393 | self.pointer, 394 | patch_num as c_uint, 395 | corner.into(), 396 | &mut x, 397 | &mut y, 398 | ) 399 | }; 400 | status_to_result(status).expect("Failed to get control point"); 401 | (x, y) 402 | } 403 | 404 | pub fn set_corner_color_rgb(&self, corner: MeshCorner, red: f64, green: f64, blue: f64) { 405 | unsafe { 406 | ffi::cairo_mesh_pattern_set_corner_color_rgb( 407 | self.pointer, 408 | corner.into(), 409 | red, 410 | green, 411 | blue, 412 | ) 413 | } 414 | self.status().expect("Failed to set corner color rgb"); 415 | } 416 | 417 | pub fn set_corner_color_rgba( 418 | &self, 419 | corner: MeshCorner, 420 | red: f64, 421 | green: f64, 422 | blue: f64, 423 | alpha: f64, 424 | ) { 425 | unsafe { 426 | ffi::cairo_mesh_pattern_set_corner_color_rgba( 427 | self.pointer, 428 | corner.into(), 429 | red, 430 | green, 431 | blue, 432 | alpha, 433 | ) 434 | } 435 | self.status().expect("Failed to set corner color rgba"); 436 | } 437 | 438 | pub fn get_corner_color_rgba( 439 | &self, 440 | patch_num: usize, 441 | corner: MeshCorner, 442 | ) -> (f64, f64, f64, f64) { 443 | let mut red: c_double = 0.0; 444 | let mut green: c_double = 0.0; 445 | let mut blue: c_double = 0.0; 446 | let mut alpha: c_double = 0.0; 447 | 448 | let status = unsafe { 449 | ffi::cairo_mesh_pattern_get_corner_color_rgba( 450 | self.pointer, 451 | patch_num as c_uint, 452 | corner.into(), 453 | &mut red, 454 | &mut green, 455 | &mut blue, 456 | &mut alpha, 457 | ) 458 | }; 459 | status_to_result(status).expect("Failed to get mesh corner color"); 460 | (red, green, blue, alpha) 461 | } 462 | 463 | pub fn get_patch_count(&self) -> usize { 464 | let mut count: c_uint = 0; 465 | unsafe { 466 | let status = ffi::cairo_mesh_pattern_get_patch_count(self.pointer, &mut count); 467 | status_to_result(status).expect("Failed to get mesh patch count") 468 | } 469 | count as usize 470 | } 471 | 472 | pub fn get_path(&self, patch_num: usize) -> Path { 473 | let path: Path = unsafe { 474 | Path::from_raw_full(ffi::cairo_mesh_pattern_get_path( 475 | self.pointer, 476 | patch_num as c_uint, 477 | )) 478 | }; 479 | let status = unsafe { 480 | let ptr: *mut ffi::cairo_path_t = path.as_ptr(); 481 | (*ptr).status 482 | }; 483 | status_to_result(status).expect("Failed to get the mesh path"); 484 | path 485 | } 486 | } 487 | 488 | impl Default for Mesh { 489 | fn default() -> Mesh { 490 | Mesh::new() 491 | } 492 | } 493 | 494 | #[test] 495 | fn try_from() { 496 | let linear = LinearGradient::new(0., 0., 1., 1.); 497 | let gradient = Gradient::clone(&linear); 498 | let pattern = Pattern::clone(&linear); 499 | assert!(Gradient::try_from(pattern.clone()).is_ok()); 500 | assert!(LinearGradient::try_from(gradient).is_ok()); 501 | assert!(LinearGradient::try_from(pattern).is_ok()); 502 | } 503 | -------------------------------------------------------------------------------- /src/pdf.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2019, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use std::convert::TryFrom; 6 | use std::ffi::{CStr, CString}; 7 | use std::fmt; 8 | use std::io; 9 | use std::mem; 10 | use std::ops::Deref; 11 | use std::path::Path; 12 | use std::ptr; 13 | 14 | #[cfg(any(all(feature = "pdf", feature = "v1_16"), feature = "dox"))] 15 | use enums::{PdfMetadata, PdfOutline}; 16 | use enums::{PdfVersion, SurfaceType}; 17 | use error::Error; 18 | use ffi; 19 | use surface::Surface; 20 | use utils::status_to_result; 21 | 22 | #[cfg(feature = "use_glib")] 23 | use glib::translate::*; 24 | 25 | impl PdfVersion { 26 | pub fn as_str(self) -> Option<&'static str> { 27 | unsafe { 28 | let res = ffi::cairo_pdf_version_to_string(self.into()); 29 | res.as_ref() 30 | .and_then(|cstr| CStr::from_ptr(cstr as _).to_str().ok()) 31 | } 32 | } 33 | } 34 | 35 | declare_surface!(PdfSurface, SurfaceType::Pdf); 36 | 37 | impl PdfSurface { 38 | pub fn new>(width: f64, height: f64, path: P) -> Result { 39 | let path = path.as_ref().to_string_lossy().into_owned(); 40 | let path = CString::new(path).unwrap(); 41 | 42 | unsafe { Self::from_raw_full(ffi::cairo_pdf_surface_create(path.as_ptr(), width, height)) } 43 | } 44 | 45 | for_stream_constructors!(cairo_pdf_surface_create_for_stream); 46 | 47 | pub fn get_versions() -> impl Iterator { 48 | let vers_slice = unsafe { 49 | let mut vers_ptr = ptr::null_mut(); 50 | let mut num_vers = mem::MaybeUninit::uninit(); 51 | ffi::cairo_pdf_get_versions(&mut vers_ptr, num_vers.as_mut_ptr()); 52 | 53 | std::slice::from_raw_parts(vers_ptr, num_vers.assume_init() as _) 54 | }; 55 | vers_slice.iter().map(|v| PdfVersion::from(*v)) 56 | } 57 | 58 | pub fn restrict(&self, version: PdfVersion) -> Result<(), Error> { 59 | unsafe { 60 | ffi::cairo_pdf_surface_restrict_to_version(self.0.to_raw_none(), version.into()); 61 | } 62 | self.status() 63 | } 64 | 65 | pub fn set_size(&self, width: f64, height: f64) -> Result<(), Error> { 66 | unsafe { 67 | ffi::cairo_pdf_surface_set_size(self.0.to_raw_none(), width, height); 68 | } 69 | self.status() 70 | } 71 | 72 | #[cfg(any(all(feature = "pdf", feature = "v1_16"), feature = "dox"))] 73 | pub fn set_metadata(&self, metadata: PdfMetadata, value: &str) -> Result<(), Error> { 74 | let value = CString::new(value).unwrap(); 75 | unsafe { 76 | ffi::cairo_pdf_surface_set_metadata( 77 | self.0.to_raw_none(), 78 | metadata.into(), 79 | value.as_ptr(), 80 | ); 81 | } 82 | self.status() 83 | } 84 | 85 | #[cfg(any(all(feature = "pdf", feature = "v1_16"), feature = "dox"))] 86 | pub fn set_page_label(&self, label: &str) -> Result<(), Error> { 87 | let label = CString::new(label).unwrap(); 88 | unsafe { 89 | ffi::cairo_pdf_surface_set_page_label(self.0.to_raw_none(), label.as_ptr()); 90 | } 91 | self.status() 92 | } 93 | 94 | #[cfg(any(all(feature = "pdf", feature = "v1_16"), feature = "dox"))] 95 | pub fn set_thumbnail_size(&self, width: i32, height: i32) -> Result<(), Error> { 96 | unsafe { 97 | ffi::cairo_pdf_surface_set_thumbnail_size( 98 | self.0.to_raw_none(), 99 | width as _, 100 | height as _, 101 | ); 102 | } 103 | self.status() 104 | } 105 | 106 | #[cfg(any(all(feature = "pdf", feature = "v1_16"), feature = "dox"))] 107 | pub fn add_outline( 108 | &self, 109 | parent_id: i32, 110 | name: &str, 111 | link_attribs: &str, 112 | flags: PdfOutline, 113 | ) -> Result { 114 | let name = CString::new(name).unwrap(); 115 | let link_attribs = CString::new(link_attribs).unwrap(); 116 | 117 | let res = unsafe { 118 | ffi::cairo_pdf_surface_add_outline( 119 | self.0.to_raw_none(), 120 | parent_id, 121 | name.as_ptr(), 122 | link_attribs.as_ptr(), 123 | flags.bits() as _, 124 | ) as _ 125 | }; 126 | 127 | self.status()?; 128 | Ok(res) 129 | } 130 | 131 | fn status(&self) -> Result<(), Error> { 132 | let status = unsafe { ffi::cairo_surface_status(self.to_raw_none()) }; 133 | status_to_result(status) 134 | } 135 | } 136 | 137 | #[cfg(test)] 138 | mod test { 139 | use super::*; 140 | use context::*; 141 | use tempfile::tempfile; 142 | 143 | fn draw(surface: &Surface) { 144 | let cr = Context::new(surface); 145 | 146 | cr.set_line_width(25.0); 147 | 148 | cr.set_source_rgba(1.0, 0.0, 0.0, 0.5); 149 | cr.line_to(0., 0.); 150 | cr.line_to(100., 100.); 151 | cr.stroke(); 152 | 153 | cr.set_source_rgba(0.0, 0.0, 1.0, 0.5); 154 | cr.line_to(0., 100.); 155 | cr.line_to(100., 0.); 156 | cr.stroke(); 157 | } 158 | 159 | fn draw_in_buffer() -> Vec { 160 | let buffer: Vec = vec![]; 161 | 162 | let surface = PdfSurface::for_stream(100., 100., buffer).unwrap(); 163 | draw(&surface); 164 | *surface.finish_output_stream().unwrap().downcast().unwrap() 165 | } 166 | 167 | #[test] 168 | fn versions() { 169 | assert!(PdfSurface::get_versions().any(|v| v == PdfVersion::_1_4)); 170 | } 171 | 172 | #[test] 173 | fn version_string() { 174 | let ver_str = PdfVersion::_1_4.as_str().unwrap(); 175 | assert_eq!(ver_str, "PDF 1.4"); 176 | } 177 | 178 | #[test] 179 | #[cfg(unix)] 180 | fn file() { 181 | let surface = PdfSurface::new(100., 100., "/dev/null").unwrap(); 182 | draw(&surface); 183 | surface.finish(); 184 | } 185 | 186 | #[test] 187 | fn writer() { 188 | let file = tempfile().expect("tempfile failed"); 189 | let surface = PdfSurface::for_stream(100., 100., file).unwrap(); 190 | 191 | draw(&surface); 192 | let stream = surface.finish_output_stream().unwrap(); 193 | let file = stream.downcast::().unwrap(); 194 | 195 | let buffer = draw_in_buffer(); 196 | let file_size = file.metadata().unwrap().len(); 197 | assert_eq!(file_size, buffer.len() as u64); 198 | } 199 | 200 | #[test] 201 | fn ref_writer() { 202 | let mut file = tempfile().expect("tempfile failed"); 203 | let surface = unsafe { PdfSurface::for_raw_stream(100., 100., &mut file).unwrap() }; 204 | 205 | draw(&surface); 206 | surface.finish_output_stream().unwrap(); 207 | drop(file); 208 | } 209 | 210 | #[test] 211 | fn buffer() { 212 | let buffer = draw_in_buffer(); 213 | 214 | let header = b"%PDF-1.5"; 215 | assert_eq!(&buffer[..header.len()], header); 216 | } 217 | 218 | #[test] 219 | fn custom_writer() { 220 | struct CustomWriter(usize); 221 | 222 | impl io::Write for CustomWriter { 223 | fn write(&mut self, buf: &[u8]) -> io::Result { 224 | self.0 += buf.len(); 225 | Ok(buf.len()) 226 | } 227 | 228 | fn flush(&mut self) -> io::Result<()> { 229 | Ok(()) 230 | } 231 | } 232 | 233 | let custom_writer = CustomWriter(0); 234 | 235 | let surface = PdfSurface::for_stream(20., 20., custom_writer).unwrap(); 236 | surface.set_size(100., 100.).unwrap(); 237 | draw(&surface); 238 | let stream = surface.finish_output_stream().unwrap(); 239 | let custom_writer = stream.downcast::().unwrap(); 240 | 241 | let buffer = draw_in_buffer(); 242 | 243 | assert_eq!(custom_writer.0, buffer.len()); 244 | } 245 | 246 | fn with_panicky_stream() -> PdfSurface { 247 | struct PanicWriter; 248 | 249 | impl io::Write for PanicWriter { 250 | fn write(&mut self, _buf: &[u8]) -> io::Result { 251 | panic!("panic in writer"); 252 | } 253 | fn flush(&mut self) -> io::Result<()> { 254 | Ok(()) 255 | } 256 | } 257 | 258 | let surface = PdfSurface::for_stream(20., 20., PanicWriter).unwrap(); 259 | surface.finish(); 260 | surface 261 | } 262 | 263 | #[test] 264 | #[should_panic] 265 | fn finish_stream_propagates_panic() { 266 | let _ = with_panicky_stream().finish_output_stream(); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/ps.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2019, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use std::convert::TryFrom; 6 | use std::ffi::{CStr, CString}; 7 | use std::fmt; 8 | use std::io; 9 | use std::mem; 10 | use std::ops::Deref; 11 | use std::path::Path; 12 | use std::ptr; 13 | 14 | use enums::{PsLevel, SurfaceType}; 15 | use error::Error; 16 | use ffi; 17 | use surface::Surface; 18 | 19 | #[cfg(feature = "use_glib")] 20 | use glib::translate::*; 21 | 22 | impl PsLevel { 23 | pub fn as_str(self) -> Option<&'static str> { 24 | unsafe { 25 | let res = ffi::cairo_ps_level_to_string(self.into()); 26 | res.as_ref() 27 | .and_then(|cstr| CStr::from_ptr(cstr as _).to_str().ok()) 28 | } 29 | } 30 | } 31 | 32 | declare_surface!(PsSurface, SurfaceType::Ps); 33 | 34 | impl PsSurface { 35 | pub fn new>(width: f64, height: f64, path: P) -> Result { 36 | let path = path.as_ref().to_string_lossy().into_owned(); 37 | let path = CString::new(path).unwrap(); 38 | 39 | unsafe { Self::from_raw_full(ffi::cairo_ps_surface_create(path.as_ptr(), width, height)) } 40 | } 41 | 42 | for_stream_constructors!(cairo_ps_surface_create_for_stream); 43 | 44 | pub fn get_levels() -> impl Iterator { 45 | let lvls_slice = unsafe { 46 | let mut vers_ptr = ptr::null_mut(); 47 | let mut num_vers = mem::MaybeUninit::uninit(); 48 | ffi::cairo_ps_get_levels(&mut vers_ptr, num_vers.as_mut_ptr()); 49 | 50 | std::slice::from_raw_parts(vers_ptr, num_vers.assume_init() as _) 51 | }; 52 | 53 | lvls_slice.iter().map(|v| PsLevel::from(*v)) 54 | } 55 | 56 | pub fn restrict(&self, level: PsLevel) { 57 | unsafe { 58 | ffi::cairo_ps_surface_restrict_to_level(self.0.to_raw_none(), level.into()); 59 | } 60 | } 61 | 62 | pub fn get_eps(&self) -> bool { 63 | unsafe { ffi::cairo_ps_surface_get_eps(self.0.to_raw_none()).as_bool() } 64 | } 65 | 66 | pub fn set_eps(&self, eps: bool) { 67 | unsafe { 68 | ffi::cairo_ps_surface_set_eps(self.0.to_raw_none(), eps.into()); 69 | } 70 | } 71 | 72 | pub fn set_size(&self, width: f64, height: f64) { 73 | unsafe { 74 | ffi::cairo_ps_surface_set_size(self.0.to_raw_none(), width, height); 75 | } 76 | } 77 | 78 | pub fn cairo_ps_surface_dsc_begin_setup(&self) { 79 | unsafe { 80 | ffi::cairo_ps_surface_dsc_begin_setup(self.0.to_raw_none()); 81 | } 82 | } 83 | 84 | pub fn cairo_ps_surface_dsc_begin_page_setup(&self) { 85 | unsafe { 86 | ffi::cairo_ps_surface_dsc_begin_page_setup(self.0.to_raw_none()); 87 | } 88 | } 89 | 90 | pub fn cairo_ps_surface_dsc_comment(&self, comment: &str) { 91 | let comment = CString::new(comment).unwrap(); 92 | unsafe { 93 | ffi::cairo_ps_surface_dsc_comment(self.0.to_raw_none(), comment.as_ptr()); 94 | } 95 | } 96 | } 97 | 98 | #[cfg(test)] 99 | mod test { 100 | use super::*; 101 | use context::*; 102 | use tempfile::tempfile; 103 | 104 | fn draw(surface: &Surface) { 105 | let cr = Context::new(surface); 106 | 107 | // Note: Not using RGBA here as PS doesn't natively support 108 | // semi-transparency and Cairo would then embed a rasterized bitmap 109 | 110 | cr.set_line_width(25.0); 111 | 112 | cr.set_source_rgb(1.0, 0.0, 0.0); 113 | cr.line_to(0., 0.); 114 | cr.line_to(100., 100.); 115 | cr.stroke(); 116 | 117 | cr.set_source_rgb(0.0, 0.0, 1.0); 118 | cr.line_to(0., 100.); 119 | cr.line_to(100., 0.); 120 | cr.stroke(); 121 | } 122 | 123 | fn draw_in_buffer() -> Vec { 124 | let buffer: Vec = vec![]; 125 | 126 | let surface = PsSurface::for_stream(100., 100., buffer).unwrap(); 127 | draw(&surface); 128 | *surface.finish_output_stream().unwrap().downcast().unwrap() 129 | } 130 | 131 | #[test] 132 | fn levels() { 133 | assert!(PsSurface::get_levels().any(|v| v == PsLevel::_2)); 134 | } 135 | 136 | #[test] 137 | fn level_string() { 138 | let ver_str = PsLevel::_2.as_str().unwrap(); 139 | assert_eq!(ver_str, "PS Level 2"); 140 | } 141 | 142 | #[test] 143 | fn eps() { 144 | let buffer: Vec = vec![]; 145 | let surface = PsSurface::for_stream(100., 100., buffer).unwrap(); 146 | surface.set_eps(true); 147 | assert_eq!(surface.get_eps(), true); 148 | } 149 | 150 | #[test] 151 | #[cfg(unix)] 152 | fn file() { 153 | let surface = PsSurface::new(100., 100., "/dev/null").unwrap(); 154 | draw(&surface); 155 | surface.finish(); 156 | } 157 | 158 | #[test] 159 | fn writer() { 160 | let file = tempfile().expect("tempfile failed"); 161 | let surface = PsSurface::for_stream(100., 100., file).unwrap(); 162 | 163 | draw(&surface); 164 | let stream = surface.finish_output_stream().unwrap(); 165 | let file = stream.downcast::().unwrap(); 166 | 167 | let buffer = draw_in_buffer(); 168 | let file_size = file.metadata().unwrap().len(); 169 | assert_eq!(file_size, buffer.len() as u64); 170 | } 171 | 172 | #[test] 173 | fn ref_writer() { 174 | let mut file = tempfile().expect("tempfile failed"); 175 | let surface = unsafe { PsSurface::for_raw_stream(100., 100., &mut file).unwrap() }; 176 | 177 | draw(&surface); 178 | surface.finish_output_stream().unwrap(); 179 | } 180 | 181 | #[test] 182 | fn buffer() { 183 | let buffer = draw_in_buffer(); 184 | 185 | let header = b"%!PS-Adobe"; 186 | assert_eq!(&buffer[..header.len()], header); 187 | } 188 | 189 | #[test] 190 | fn custom_writer() { 191 | struct CustomWriter(usize); 192 | 193 | impl io::Write for CustomWriter { 194 | fn write(&mut self, buf: &[u8]) -> io::Result { 195 | self.0 += buf.len(); 196 | Ok(buf.len()) 197 | } 198 | 199 | fn flush(&mut self) -> io::Result<()> { 200 | Ok(()) 201 | } 202 | } 203 | 204 | let custom_writer = CustomWriter(0); 205 | 206 | let surface = PsSurface::for_stream(20., 20., custom_writer).unwrap(); 207 | surface.set_size(100., 100.); 208 | draw(&surface); 209 | let stream = surface.finish_output_stream().unwrap(); 210 | let custom_writer = stream.downcast::().unwrap(); 211 | 212 | let buffer = draw_in_buffer(); 213 | 214 | assert_eq!(custom_writer.0, buffer.len()); 215 | } 216 | 217 | fn with_panicky_stream() -> PsSurface { 218 | struct PanicWriter; 219 | 220 | impl io::Write for PanicWriter { 221 | fn write(&mut self, _buf: &[u8]) -> io::Result { 222 | panic!("panic in writer"); 223 | } 224 | fn flush(&mut self) -> io::Result<()> { 225 | Ok(()) 226 | } 227 | } 228 | 229 | let surface = PsSurface::for_stream(20., 20., PanicWriter).unwrap(); 230 | surface.finish(); 231 | surface 232 | } 233 | 234 | #[test] 235 | #[should_panic] 236 | fn finish_stream_propagates_panic() { 237 | let _ = with_panicky_stream().finish_output_stream(); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/quartz_surface.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use std::convert::TryFrom; 6 | use std::fmt; 7 | use std::ops::Deref; 8 | 9 | use enums::{Format, SurfaceType}; 10 | use error::Error; 11 | use ffi; 12 | #[cfg(feature = "use_glib")] 13 | use glib::translate::*; 14 | use surface::Surface; 15 | 16 | use ffi::CGContextRef; 17 | 18 | declare_surface!(QuartzSurface, SurfaceType::Quartz); 19 | 20 | impl QuartzSurface { 21 | pub fn create(format: Format, width: u32, height: u32) -> Result { 22 | unsafe { 23 | Self::from_raw_full(ffi::cairo_quartz_surface_create( 24 | format.into(), 25 | width, 26 | height, 27 | )) 28 | } 29 | } 30 | 31 | pub fn create_for_cg_context( 32 | cg_context: CGContextRef, 33 | width: u32, 34 | height: u32, 35 | ) -> Result { 36 | unsafe { 37 | Self::from_raw_full(ffi::cairo_quartz_surface_create_for_cg_context( 38 | cg_context, width, height, 39 | )) 40 | } 41 | } 42 | 43 | pub fn get_cg_context(&self) -> CGContextRef { 44 | unsafe { ffi::cairo_quartz_surface_get_cg_context(self.to_raw_none()) } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/recording_surface.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use std::convert::TryFrom; 6 | use std::fmt; 7 | use std::ops::Deref; 8 | 9 | use enums::{Content, SurfaceType}; 10 | use error::Error; 11 | use ffi; 12 | #[cfg(feature = "use_glib")] 13 | use glib::translate::*; 14 | use rectangle::Rectangle; 15 | 16 | use surface::Surface; 17 | 18 | declare_surface!(RecordingSurface, SurfaceType::Recording); 19 | impl RecordingSurface { 20 | pub fn create>>( 21 | content: Content, 22 | extends: T, 23 | ) -> Result { 24 | unsafe { 25 | let extends = extends.into(); 26 | let extends = match extends { 27 | Some(c) => c.to_raw_none(), 28 | None => ::std::ptr::null(), 29 | }; 30 | 31 | Ok(Self::from_raw_full(ffi::cairo_recording_surface_create( 32 | content.into(), 33 | extends, 34 | ))?) 35 | } 36 | } 37 | 38 | pub fn get_extents(&self) -> Option { 39 | unsafe { 40 | let rectangle: Rectangle = ::std::mem::zeroed(); 41 | if ffi::cairo_recording_surface_get_extents(self.to_raw_none(), rectangle.to_raw_none()) 42 | .as_bool() 43 | { 44 | Some(rectangle) 45 | } else { 46 | None 47 | } 48 | } 49 | } 50 | 51 | pub fn ink_extents(&self) -> (f64, f64, f64, f64) { 52 | let mut x0 = 0.; 53 | let mut y0 = 0.; 54 | let mut width = 0.; 55 | let mut height = 0.; 56 | 57 | unsafe { 58 | ffi::cairo_recording_surface_ink_extents( 59 | self.to_raw_none(), 60 | &mut x0, 61 | &mut y0, 62 | &mut width, 63 | &mut height, 64 | ); 65 | } 66 | (x0, y0, width, height) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/rectangle.rs: -------------------------------------------------------------------------------- 1 | use ffi; 2 | #[cfg(feature = "use_glib")] 3 | use glib::translate::*; 4 | use std::fmt; 5 | #[cfg(feature = "use_glib")] 6 | use std::mem; 7 | 8 | #[derive(Clone, Copy, Debug, PartialEq)] 9 | #[repr(C)] 10 | pub struct Rectangle { 11 | pub x: f64, 12 | pub y: f64, 13 | pub width: f64, 14 | pub height: f64, 15 | } 16 | 17 | #[cfg(feature = "use_glib")] 18 | #[doc(hidden)] 19 | impl Uninitialized for Rectangle { 20 | #[inline] 21 | unsafe fn uninitialized() -> Self { 22 | mem::zeroed() 23 | } 24 | } 25 | 26 | #[cfg(feature = "use_glib")] 27 | #[doc(hidden)] 28 | impl<'a> ToGlibPtr<'a, *const ffi::cairo_rectangle_t> for Rectangle { 29 | type Storage = &'a Self; 30 | 31 | #[inline] 32 | fn to_glib_none(&'a self) -> Stash<'a, *const ffi::cairo_rectangle_t, Self> { 33 | let ptr: *const Rectangle = &*self; 34 | Stash(ptr as *const ffi::cairo_rectangle_t, self) 35 | } 36 | } 37 | 38 | #[cfg(feature = "use_glib")] 39 | #[doc(hidden)] 40 | impl<'a> ToGlibPtrMut<'a, *mut ffi::cairo_rectangle_t> for Rectangle { 41 | type Storage = &'a mut Self; 42 | 43 | #[inline] 44 | fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::cairo_rectangle_t, Self> { 45 | let ptr: *mut Rectangle = &mut *self; 46 | StashMut(ptr as *mut ffi::cairo_rectangle_t, self) 47 | } 48 | } 49 | 50 | #[cfg(feature = "use_glib")] 51 | #[doc(hidden)] 52 | impl FromGlibPtrNone<*const ffi::cairo_rectangle_t> for Rectangle { 53 | unsafe fn from_glib_none(ptr: *const ffi::cairo_rectangle_t) -> Self { 54 | *(ptr as *const Rectangle) 55 | } 56 | } 57 | 58 | #[cfg(feature = "use_glib")] 59 | #[doc(hidden)] 60 | impl FromGlibPtrBorrow<*mut ffi::cairo_rectangle_t> for Rectangle { 61 | unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_rectangle_t) -> ::Borrowed { 62 | ::Borrowed::new(*(ptr as *mut Rectangle)) 63 | } 64 | } 65 | 66 | #[cfg(feature = "use_glib")] 67 | #[doc(hidden)] 68 | impl FromGlibPtrNone<*mut ffi::cairo_rectangle_t> for Rectangle { 69 | unsafe fn from_glib_none(ptr: *mut ffi::cairo_rectangle_t) -> Self { 70 | *(ptr as *mut Rectangle) 71 | } 72 | } 73 | 74 | #[cfg(feature = "use_glib")] 75 | gvalue_impl!( 76 | Rectangle, 77 | ffi::cairo_rectangle_t, 78 | ffi::gobject::cairo_gobject_rectangle_get_type 79 | ); 80 | 81 | impl Rectangle { 82 | pub fn to_raw_none(&self) -> *mut ffi::cairo_rectangle_t { 83 | let ptr = &*self as *const Rectangle as usize; 84 | ptr as *mut ffi::cairo_rectangle_t 85 | } 86 | } 87 | 88 | impl fmt::Display for Rectangle { 89 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 90 | write!(f, "Rectangle") 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/rectangle_int.rs: -------------------------------------------------------------------------------- 1 | use ffi; 2 | #[cfg(feature = "use_glib")] 3 | use glib::translate::*; 4 | use std::fmt; 5 | #[cfg(feature = "use_glib")] 6 | use std::mem; 7 | 8 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 9 | #[repr(C)] 10 | pub struct RectangleInt { 11 | pub x: i32, 12 | pub y: i32, 13 | pub width: i32, 14 | pub height: i32, 15 | } 16 | 17 | #[cfg(feature = "use_glib")] 18 | #[doc(hidden)] 19 | impl Uninitialized for RectangleInt { 20 | #[inline] 21 | unsafe fn uninitialized() -> Self { 22 | mem::zeroed() 23 | } 24 | } 25 | 26 | #[cfg(feature = "use_glib")] 27 | #[doc(hidden)] 28 | impl<'a> ToGlibPtr<'a, *const ffi::cairo_rectangle_int_t> for RectangleInt { 29 | type Storage = &'a Self; 30 | 31 | #[inline] 32 | fn to_glib_none(&'a self) -> Stash<'a, *const ffi::cairo_rectangle_int_t, Self> { 33 | let ptr: *const RectangleInt = &*self; 34 | Stash(ptr as *const ffi::cairo_rectangle_int_t, self) 35 | } 36 | } 37 | 38 | #[cfg(feature = "use_glib")] 39 | #[doc(hidden)] 40 | impl<'a> ToGlibPtrMut<'a, *mut ffi::cairo_rectangle_int_t> for RectangleInt { 41 | type Storage = &'a mut Self; 42 | 43 | #[inline] 44 | fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::cairo_rectangle_int_t, Self> { 45 | let ptr: *mut RectangleInt = &mut *self; 46 | StashMut(ptr as *mut ffi::cairo_rectangle_int_t, self) 47 | } 48 | } 49 | 50 | #[cfg(feature = "use_glib")] 51 | #[doc(hidden)] 52 | impl FromGlibPtrNone<*const ffi::cairo_rectangle_int_t> for RectangleInt { 53 | unsafe fn from_glib_none(ptr: *const ffi::cairo_rectangle_int_t) -> Self { 54 | *(ptr as *const RectangleInt) 55 | } 56 | } 57 | 58 | #[cfg(feature = "use_glib")] 59 | #[doc(hidden)] 60 | impl FromGlibPtrBorrow<*mut ffi::cairo_rectangle_int_t> for RectangleInt { 61 | unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_rectangle_int_t) -> ::Borrowed { 62 | ::Borrowed::new(*(ptr as *mut RectangleInt)) 63 | } 64 | } 65 | 66 | #[cfg(feature = "use_glib")] 67 | #[doc(hidden)] 68 | impl FromGlibPtrNone<*mut ffi::cairo_rectangle_int_t> for RectangleInt { 69 | unsafe fn from_glib_none(ptr: *mut ffi::cairo_rectangle_int_t) -> Self { 70 | *(ptr as *mut RectangleInt) 71 | } 72 | } 73 | 74 | #[cfg(feature = "use_glib")] 75 | gvalue_impl!( 76 | RectangleInt, 77 | ffi::cairo_rectangle_int_t, 78 | ffi::gobject::cairo_gobject_rectangle_int_get_type 79 | ); 80 | 81 | impl RectangleInt { 82 | pub fn to_raw_none(&self) -> *mut ffi::cairo_rectangle_int_t { 83 | let ptr = &*self as *const RectangleInt as usize; 84 | ptr as *mut ffi::cairo_rectangle_int_t 85 | } 86 | } 87 | 88 | impl fmt::Display for RectangleInt { 89 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 90 | write!(f, "RectangleInt") 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/region.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2017, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use enums::RegionOverlap; 6 | use error::Error; 7 | use ffi; 8 | #[cfg(feature = "use_glib")] 9 | use glib::translate::*; 10 | use std::fmt; 11 | use std::ptr; 12 | use utils::status_to_result; 13 | use RectangleInt; 14 | 15 | use ffi::cairo_region_t; 16 | 17 | #[derive(Debug)] 18 | pub struct Region(ptr::NonNull); 19 | 20 | #[cfg(feature = "use_glib")] 21 | #[doc(hidden)] 22 | impl<'a> ToGlibPtr<'a, *mut ffi::cairo_region_t> for &'a Region { 23 | type Storage = &'a Region; 24 | 25 | #[inline] 26 | fn to_glib_none(&self) -> Stash<'a, *mut ffi::cairo_region_t, &'a Region> { 27 | Stash(self.0.as_ptr(), *self) 28 | } 29 | 30 | #[inline] 31 | fn to_glib_full(&self) -> *mut ffi::cairo_region_t { 32 | unsafe { ffi::cairo_region_reference(self.0.as_ptr()) } 33 | } 34 | } 35 | 36 | #[cfg(feature = "use_glib")] 37 | #[doc(hidden)] 38 | impl<'a> ToGlibPtrMut<'a, *mut ffi::cairo_region_t> for Region { 39 | type Storage = &'a mut Self; 40 | 41 | // FIXME: This is unsafe: regions are reference counted so we could get multiple mutable 42 | // references here 43 | #[inline] 44 | fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::cairo_region_t, Self> { 45 | StashMut(self.0.as_ptr(), self) 46 | } 47 | } 48 | 49 | #[cfg(feature = "use_glib")] 50 | #[doc(hidden)] 51 | impl FromGlibPtrNone<*mut ffi::cairo_region_t> for Region { 52 | #[inline] 53 | unsafe fn from_glib_none(ptr: *mut ffi::cairo_region_t) -> Region { 54 | Self::from_raw_none(ptr) 55 | } 56 | } 57 | 58 | #[cfg(feature = "use_glib")] 59 | #[doc(hidden)] 60 | impl FromGlibPtrBorrow<*mut ffi::cairo_region_t> for Region { 61 | #[inline] 62 | unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_region_t) -> ::Borrowed { 63 | Self::from_raw_borrow(ptr) 64 | } 65 | } 66 | 67 | #[cfg(feature = "use_glib")] 68 | #[doc(hidden)] 69 | impl FromGlibPtrFull<*mut ffi::cairo_region_t> for Region { 70 | #[inline] 71 | unsafe fn from_glib_full(ptr: *mut ffi::cairo_region_t) -> Region { 72 | Self::from_raw_full(ptr) 73 | } 74 | } 75 | 76 | #[cfg(feature = "use_glib")] 77 | gvalue_impl!( 78 | Region, 79 | ffi::cairo_region_t, 80 | ffi::gobject::cairo_gobject_region_get_type 81 | ); 82 | 83 | impl Clone for Region { 84 | fn clone(&self) -> Region { 85 | unsafe { Self::from_raw_none(self.to_raw_none()) } 86 | } 87 | } 88 | 89 | impl Drop for Region { 90 | fn drop(&mut self) { 91 | unsafe { 92 | ffi::cairo_region_destroy(self.0.as_ptr()); 93 | } 94 | } 95 | } 96 | 97 | impl PartialEq for Region { 98 | fn eq(&self, other: &Region) -> bool { 99 | unsafe { ffi::cairo_region_equal(self.0.as_ptr(), other.0.as_ptr()).as_bool() } 100 | } 101 | } 102 | 103 | impl Eq for Region {} 104 | 105 | impl Region { 106 | #[inline] 107 | pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_region_t) -> Region { 108 | assert!(!ptr.is_null()); 109 | ffi::cairo_region_reference(ptr); 110 | Region(ptr::NonNull::new_unchecked(ptr)) 111 | } 112 | 113 | #[inline] 114 | pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_region_t) -> ::Borrowed { 115 | assert!(!ptr.is_null()); 116 | ::Borrowed::new(Region(ptr::NonNull::new_unchecked(ptr))) 117 | } 118 | 119 | #[inline] 120 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_region_t) -> Region { 121 | assert!(!ptr.is_null()); 122 | Region(ptr::NonNull::new_unchecked(ptr)) 123 | } 124 | 125 | pub fn to_raw_none(&self) -> *mut ffi::cairo_region_t { 126 | self.0.as_ptr() 127 | } 128 | 129 | pub fn create() -> Region { 130 | unsafe { Self::from_raw_full(ffi::cairo_region_create()) } 131 | } 132 | 133 | pub fn create_rectangle(rectangle: &RectangleInt) -> Region { 134 | unsafe { Self::from_raw_full(ffi::cairo_region_create_rectangle(rectangle.to_raw_none())) } 135 | } 136 | 137 | pub fn create_rectangles(rectangles: &[RectangleInt]) -> Region { 138 | unsafe { 139 | Self::from_raw_full(ffi::cairo_region_create_rectangles( 140 | rectangles.as_ptr() as *mut ffi::cairo_rectangle_int_t, 141 | rectangles.len() as i32, 142 | )) 143 | } 144 | } 145 | 146 | pub fn copy(&self) -> Region { 147 | unsafe { Self::from_raw_full(ffi::cairo_region_copy(self.0.as_ptr())) } 148 | } 149 | 150 | pub fn get_extents(&self, rectangle: &mut RectangleInt) { 151 | unsafe { ffi::cairo_region_get_extents(self.0.as_ptr(), rectangle.to_raw_none()) } 152 | } 153 | 154 | pub fn num_rectangles(&self) -> i32 { 155 | unsafe { ffi::cairo_region_num_rectangles(self.0.as_ptr()) } 156 | } 157 | 158 | pub fn get_rectangle(&self, nth: i32) -> RectangleInt { 159 | unsafe { 160 | let rectangle: RectangleInt = ::std::mem::zeroed(); 161 | ffi::cairo_region_get_rectangle(self.0.as_ptr(), nth, rectangle.to_raw_none()); 162 | rectangle 163 | } 164 | } 165 | 166 | pub fn is_empty(&self) -> bool { 167 | unsafe { ffi::cairo_region_is_empty(self.0.as_ptr()).as_bool() } 168 | } 169 | 170 | pub fn contains_point(&self, x: i32, y: i32) -> bool { 171 | unsafe { ffi::cairo_region_contains_point(self.0.as_ptr(), x, y).as_bool() } 172 | } 173 | 174 | pub fn contains_rectangle(&self, rectangle: &RectangleInt) -> RegionOverlap { 175 | unsafe { 176 | RegionOverlap::from(ffi::cairo_region_contains_rectangle( 177 | self.0.as_ptr(), 178 | rectangle.to_raw_none(), 179 | )) 180 | } 181 | } 182 | 183 | pub fn translate(&self, dx: i32, dy: i32) { 184 | unsafe { ffi::cairo_region_translate(self.0.as_ptr(), dx, dy) } 185 | } 186 | 187 | pub fn intersect(&self, other: &Region) -> Result<(), Error> { 188 | unsafe { 189 | let status = ffi::cairo_region_intersect(self.0.as_ptr(), other.0.as_ptr()); 190 | status_to_result(status) 191 | } 192 | } 193 | 194 | pub fn intersect_rectangle(&self, rectangle: &RectangleInt) -> Result<(), Error> { 195 | unsafe { 196 | let status = 197 | ffi::cairo_region_intersect_rectangle(self.0.as_ptr(), rectangle.to_raw_none()); 198 | status_to_result(status) 199 | } 200 | } 201 | 202 | pub fn subtract(&self, other: &Region) -> Result<(), Error> { 203 | unsafe { 204 | let status = ffi::cairo_region_subtract(self.0.as_ptr(), other.0.as_ptr()); 205 | status_to_result(status) 206 | } 207 | } 208 | 209 | pub fn subtract_rectangle(&self, rectangle: &RectangleInt) -> Result<(), Error> { 210 | unsafe { 211 | let status = 212 | ffi::cairo_region_subtract_rectangle(self.0.as_ptr(), rectangle.to_raw_none()); 213 | status_to_result(status) 214 | } 215 | } 216 | 217 | pub fn union(&self, other: &Region) -> Result<(), Error> { 218 | unsafe { 219 | let status = ffi::cairo_region_union(self.0.as_ptr(), other.0.as_ptr()); 220 | status_to_result(status) 221 | } 222 | } 223 | 224 | pub fn union_rectangle(&self, rectangle: &RectangleInt) -> Result<(), Error> { 225 | unsafe { 226 | let status = 227 | ffi::cairo_region_union_rectangle(self.0.as_ptr(), rectangle.to_raw_none()); 228 | status_to_result(status) 229 | } 230 | } 231 | 232 | pub fn xor(&self, other: &Region) -> Result<(), Error> { 233 | unsafe { 234 | let status = ffi::cairo_region_xor(self.0.as_ptr(), other.0.as_ptr()); 235 | status_to_result(status) 236 | } 237 | } 238 | 239 | pub fn xor_rectangle(&self, rectangle: &RectangleInt) -> Result<(), Error> { 240 | unsafe { 241 | let status = ffi::cairo_region_xor_rectangle(self.0.as_ptr(), rectangle.to_raw_none()); 242 | status_to_result(status) 243 | } 244 | } 245 | } 246 | 247 | impl fmt::Display for Region { 248 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 249 | write!(f, "Region") 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/stream.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use error::Error; 6 | use ffi::{self, cairo_status_t}; 7 | use {Surface, UserDataKey}; 8 | 9 | use libc::{c_double, c_uchar, c_uint, c_void}; 10 | use std::any::Any; 11 | use std::cell::{Cell, RefCell}; 12 | use std::io; 13 | use std::panic::AssertUnwindSafe; 14 | use std::ptr; 15 | use std::rc::Rc; 16 | 17 | macro_rules! for_stream_constructors { 18 | ($constructor_ffi: ident) => { 19 | /// Takes full ownership of the output stream, 20 | /// which is not allowed to borrow any lifetime shorter than `'static`. 21 | /// 22 | /// Because the underlying `cairo_surface_t` is reference-counted, 23 | /// a lifetime parameter in a Rust wrapper type would not be enough to track 24 | /// how long it can keep writing to the stream. 25 | pub fn for_stream( 26 | width: f64, 27 | height: f64, 28 | stream: W, 29 | ) -> Result { 30 | Ok(Self(Surface::_for_stream( 31 | ffi::$constructor_ffi, 32 | width, 33 | height, 34 | stream, 35 | )?)) 36 | } 37 | 38 | /// Allows writing to a borrowed stream. The lifetime of the borrow is not tracked. 39 | /// 40 | /// # Safety 41 | /// 42 | /// The value that `stream` points to must live at least until the underlying `cairo_surface_t` 43 | /// (which maybe be longer then the Rust `PdfSurface` wrapper, because of reference-counting), 44 | /// or until the output stream is removed from the surface with [`Surface::take_output_stream`]. 45 | /// 46 | /// Since the former is hard to track for sure, the latter is strongly recommended. 47 | /// The concrete type behind the `Box` value returned by `take_output_stream` 48 | /// is private, so you won’t be able to downcast it. 49 | /// But removing it anyway ensures that later writes do no go through a dangling pointer. 50 | pub unsafe fn for_raw_stream( 51 | width: f64, 52 | height: f64, 53 | stream: *mut W, 54 | ) -> Result { 55 | Ok(Self(Surface::_for_raw_stream( 56 | ffi::$constructor_ffi, 57 | width, 58 | height, 59 | stream, 60 | )?)) 61 | } 62 | }; 63 | } 64 | 65 | impl Surface { 66 | pub(crate) fn _for_stream( 67 | constructor: Constructor, 68 | width: f64, 69 | height: f64, 70 | stream: W, 71 | ) -> Result { 72 | let env_rc = Rc::new(CallbackEnvironment { 73 | mutable: RefCell::new(MutableCallbackEnvironment { 74 | stream: Some((Box::new(stream), None)), 75 | unwind_payload: None, 76 | }), 77 | saw_already_borrowed: Cell::new(false), 78 | }); 79 | let env: *const CallbackEnvironment = &*env_rc; 80 | unsafe { 81 | let ptr = constructor(Some(write_callback::), env as *mut c_void, width, height); 82 | let surface = Surface::from_raw_full(ptr)?; 83 | surface.set_user_data(&STREAM_CALLBACK_ENVIRONMENT, env_rc); 84 | Ok(surface) 85 | } 86 | } 87 | 88 | pub(crate) unsafe fn _for_raw_stream( 89 | constructor: Constructor, 90 | width: f64, 91 | height: f64, 92 | stream: *mut W, 93 | ) -> Result { 94 | Self::_for_stream( 95 | constructor, 96 | width, 97 | height, 98 | RawStream(ptr::NonNull::new(stream).expect("NULL stream passed")), 99 | ) 100 | } 101 | 102 | /// Finish the surface, then remove and return the output stream if any. 103 | /// 104 | /// This calls [`Surface::finish`], to make sure pending writes are done. 105 | /// 106 | /// This is relevant for surfaces created for example with [`PdfSurface::for_stream`]. 107 | /// 108 | /// Use [`Box::downcast`] to recover the concrete stream type. 109 | /// 110 | /// # Panics 111 | /// 112 | /// This method panics if: 113 | /// 114 | /// * This method was already called for this surface, or 115 | /// * This surface was not created with an output stream in the first place, or 116 | /// * A previous write to this surface panicked, or 117 | /// * A previous write happened while another write was ongoing, or 118 | /// * A write is ongoing now. 119 | /// 120 | /// The latter two cases can only occur with a pathological output stream type 121 | /// that accesses the same surface again from `Write::write_all`. 122 | pub fn finish_output_stream(&self) -> Result, StreamWithError> { 123 | self.finish(); 124 | 125 | let env = self 126 | .get_user_data_ptr(&STREAM_CALLBACK_ENVIRONMENT) 127 | .expect("surface without an output stream"); 128 | 129 | // Safety: since `STREAM_CALLBACK_ENVIRONMENT` is private and we never 130 | // call `set_user_data` again or `remove_user_data` with it, 131 | // the contract of `get_user_data_ptr` says that the user data entry 132 | // lives as long as the underlying `cairo_surface_t` 133 | // which is at least as long as `self`. 134 | let env = unsafe { &*env.as_ptr() }; 135 | 136 | if env.saw_already_borrowed.get() { 137 | panic!("The output stream’s RefCell was already borrowed when cairo attempted a write") 138 | } 139 | 140 | let mut mutable = env.mutable.borrow_mut(); 141 | if let Some(payload) = mutable.unwind_payload.take() { 142 | std::panic::resume_unwind(payload) 143 | } 144 | 145 | let (stream, io_error) = mutable 146 | .stream 147 | .take() 148 | .expect("output stream was already taken"); 149 | if let Some(error) = io_error { 150 | Err(StreamWithError { stream, error }) 151 | } else { 152 | Ok(stream) 153 | } 154 | } 155 | } 156 | 157 | pub struct StreamWithError { 158 | pub stream: Box, 159 | pub error: io::Error, 160 | } 161 | 162 | impl std::fmt::Debug for StreamWithError { 163 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 164 | self.error.fmt(f) 165 | } 166 | } 167 | 168 | impl std::fmt::Display for StreamWithError { 169 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 170 | self.error.fmt(f) 171 | } 172 | } 173 | 174 | impl From for io::Error { 175 | fn from(e: StreamWithError) -> Self { 176 | e.error 177 | } 178 | } 179 | 180 | pub(crate) type Constructor = unsafe extern "C" fn( 181 | ffi::cairo_write_func_t, 182 | *mut c_void, 183 | c_double, 184 | c_double, 185 | ) -> *mut ffi::cairo_surface_t; 186 | 187 | static STREAM_CALLBACK_ENVIRONMENT: UserDataKey = UserDataKey::new(); 188 | 189 | struct CallbackEnvironment { 190 | mutable: RefCell, 191 | saw_already_borrowed: Cell, 192 | } 193 | 194 | struct MutableCallbackEnvironment { 195 | stream: Option<(Box, Option)>, 196 | unwind_payload: Option>, 197 | } 198 | 199 | // Safety: unwinding into C is undefined behavior (https://github.com/rust-lang/rust/issues/58794) 200 | // so code outside of the `catch_unwind` call must never panic. 201 | extern "C" fn write_callback( 202 | env: *mut c_void, 203 | data: *mut c_uchar, 204 | length: c_uint, 205 | ) -> cairo_status_t { 206 | // This is consistent with the type of `env` in `Surface::_for_stream`. 207 | let env: *const CallbackEnvironment = env as _; 208 | 209 | // Safety: the user data entry keeps `Rc` alive 210 | // until the surface is destroyed. 211 | // If this is called by cairo, the surface is still alive. 212 | let env: &CallbackEnvironment = unsafe { &*env }; 213 | 214 | if let Ok(mut mutable) = env.mutable.try_borrow_mut() { 215 | if let MutableCallbackEnvironment { 216 | stream: 217 | Some(( 218 | stream, 219 | // Don’t attempt another write if a previous one errored or panicked: 220 | io_error @ None, 221 | )), 222 | unwind_payload: unwind_payload @ None, 223 | } = &mut *mutable 224 | { 225 | // Safety: `write_callback` was instanciated in `Surface::_for_stream` 226 | // with a W parameter consistent with the box that was unsized to `Box`. 227 | let stream = unsafe { stream.downcast_mut_unchecked::() }; 228 | // Safety: this is the callback contract from cairo’s API 229 | let data = unsafe { std::slice::from_raw_parts(data, length as usize) }; 230 | // Because `::write_all` is a generic, 231 | // we must conservatively assume that it can panic. 232 | let result = std::panic::catch_unwind(AssertUnwindSafe(|| stream.write_all(data))); 233 | match result { 234 | Ok(Ok(())) => return ffi::STATUS_SUCCESS, 235 | Ok(Err(error)) => { 236 | *io_error = Some(error); 237 | } 238 | Err(payload) => { 239 | *unwind_payload = Some(payload); 240 | } 241 | } 242 | } 243 | } else { 244 | env.saw_already_borrowed.set(true) 245 | } 246 | Error::WriteError.into() 247 | } 248 | 249 | struct RawStream(ptr::NonNull); 250 | 251 | impl io::Write for RawStream { 252 | fn write(&mut self, buf: &[u8]) -> io::Result { 253 | unsafe { (*self.0.as_ptr()).write(buf) } 254 | } 255 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { 256 | unsafe { (*self.0.as_ptr()).write_all(buf) } 257 | } 258 | fn flush(&mut self) -> io::Result<()> { 259 | unsafe { (*self.0.as_ptr()).flush() } 260 | } 261 | } 262 | 263 | trait AnyExt { 264 | /// Any::downcast_mut, but YOLO 265 | unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { 266 | let ptr = self as *mut Self as *mut T; 267 | &mut *ptr 268 | } 269 | } 270 | impl AnyExt for dyn Any {} 271 | -------------------------------------------------------------------------------- /src/surface.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2016, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use libc::{c_ulong, c_void}; 6 | use std::ffi::CString; 7 | use std::fmt; 8 | use std::ops::Deref; 9 | use std::ptr; 10 | use std::slice; 11 | 12 | use enums::{Content, Format, SurfaceType}; 13 | use error::Error; 14 | use ffi; 15 | #[cfg(feature = "use_glib")] 16 | use glib::translate::*; 17 | use utils::status_to_result; 18 | 19 | use device::Device; 20 | use image_surface::ImageSurface; 21 | use rectangle::Rectangle; 22 | use rectangle_int::RectangleInt; 23 | 24 | #[derive(Debug)] 25 | pub struct Surface(ptr::NonNull); 26 | 27 | impl Surface { 28 | pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface { 29 | assert!(!ptr.is_null()); 30 | ffi::cairo_surface_reference(ptr); 31 | Surface(ptr::NonNull::new_unchecked(ptr)) 32 | } 33 | 34 | pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> ::Borrowed { 35 | assert!(!ptr.is_null()); 36 | ::Borrowed::new(Surface(ptr::NonNull::new_unchecked(ptr))) 37 | } 38 | 39 | pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result { 40 | assert!(!ptr.is_null()); 41 | let status = ffi::cairo_surface_status(ptr); 42 | status_to_result(status)?; 43 | Ok(Surface(ptr::NonNull::new_unchecked(ptr))) 44 | } 45 | 46 | pub fn to_raw_none(&self) -> *mut ffi::cairo_surface_t { 47 | self.0.as_ptr() 48 | } 49 | 50 | pub fn create_similar( 51 | &self, 52 | content: Content, 53 | width: i32, 54 | height: i32, 55 | ) -> Result { 56 | unsafe { 57 | Self::from_raw_full(ffi::cairo_surface_create_similar( 58 | self.0.as_ptr(), 59 | content.into(), 60 | width, 61 | height, 62 | )) 63 | } 64 | } 65 | 66 | pub fn create_for_rectangle(&self, bounds: Rectangle) -> Result { 67 | unsafe { 68 | Self::from_raw_full(ffi::cairo_surface_create_for_rectangle( 69 | self.0.as_ptr(), 70 | bounds.x, 71 | bounds.y, 72 | bounds.width, 73 | bounds.height, 74 | )) 75 | } 76 | } 77 | 78 | pub fn get_mime_data(&self, mime_type: &str) -> Option> { 79 | let data_ptr: *mut u8 = ptr::null_mut(); 80 | let mut length: c_ulong = 0; 81 | unsafe { 82 | let mime_type = CString::new(mime_type).unwrap(); 83 | ffi::cairo_surface_get_mime_data( 84 | self.to_raw_none(), 85 | mime_type.as_ptr(), 86 | &data_ptr, 87 | &mut length, 88 | ); 89 | if !data_ptr.is_null() && length != 0 { 90 | Some(slice::from_raw_parts(data_ptr as *const u8, length as usize).to_vec()) 91 | } else { 92 | None 93 | } 94 | } 95 | } 96 | 97 | pub unsafe fn get_mime_data_raw(&self, mime_type: &str) -> Option<&[u8]> { 98 | let data_ptr: *mut u8 = ptr::null_mut(); 99 | let mut length: c_ulong = 0; 100 | let mime_type = CString::new(mime_type).unwrap(); 101 | ffi::cairo_surface_get_mime_data( 102 | self.to_raw_none(), 103 | mime_type.as_ptr(), 104 | &data_ptr, 105 | &mut length, 106 | ); 107 | if !data_ptr.is_null() && length != 0 { 108 | Some(slice::from_raw_parts( 109 | data_ptr as *const u8, 110 | length as usize, 111 | )) 112 | } else { 113 | None 114 | } 115 | } 116 | 117 | pub fn set_mime_data + 'static>( 118 | &self, 119 | mime_type: &str, 120 | slice: T, 121 | ) -> Result<(), Error> { 122 | let b = Box::new(slice); 123 | let (size, data) = { 124 | let slice = (*b).as_ref(); 125 | (slice.len(), slice.as_ptr()) 126 | }; 127 | 128 | let user_data = Box::into_raw(b); 129 | 130 | unsafe extern "C" fn unbox(data: *mut c_void) { 131 | let data: Box = Box::from_raw(data as *mut T); 132 | drop(data); 133 | } 134 | 135 | let status = unsafe { 136 | let mime_type = CString::new(mime_type).unwrap(); 137 | ffi::cairo_surface_set_mime_data( 138 | self.to_raw_none(), 139 | mime_type.as_ptr(), 140 | data, 141 | size as c_ulong, 142 | Some(unbox::), 143 | user_data as *mut _, 144 | ) 145 | }; 146 | status_to_result(status) 147 | } 148 | 149 | pub fn supports_mime_type(&self, mime_type: &str) -> bool { 150 | unsafe { 151 | let mime_type = CString::new(mime_type).unwrap(); 152 | ffi::cairo_surface_supports_mime_type(self.0.as_ptr(), mime_type.as_ptr()).as_bool() 153 | } 154 | } 155 | 156 | pub fn get_device(&self) -> Option { 157 | unsafe { 158 | let device = ffi::cairo_surface_get_device(self.to_raw_none()); 159 | if device.is_null() { 160 | None 161 | } else { 162 | Some(Device::from_raw_none(device)) 163 | } 164 | } 165 | } 166 | 167 | pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) { 168 | unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) } 169 | } 170 | 171 | pub fn get_device_offset(&self) -> (f64, f64) { 172 | let mut x_offset = 0.0f64; 173 | let mut y_offset = 0.0f64; 174 | unsafe { 175 | ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset); 176 | } 177 | (x_offset, y_offset) 178 | } 179 | 180 | #[cfg(any(feature = "v1_14", feature = "dox"))] 181 | pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) { 182 | unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) } 183 | } 184 | 185 | #[cfg(any(feature = "v1_14", feature = "dox"))] 186 | pub fn get_device_scale(&self) -> (f64, f64) { 187 | let mut x_scale = 0.0f64; 188 | let mut y_scale = 0.0f64; 189 | unsafe { 190 | ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale); 191 | } 192 | (x_scale, y_scale) 193 | } 194 | 195 | pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) { 196 | unsafe { 197 | ffi::cairo_surface_set_fallback_resolution( 198 | self.to_raw_none(), 199 | x_pixels_per_inch, 200 | y_pixels_per_inch, 201 | ) 202 | } 203 | } 204 | 205 | pub fn get_fallback_resolution(&self) -> (f64, f64) { 206 | let mut x_pixels_per_inch = 0.0f64; 207 | let mut y_pixels_per_inch = 0.0f64; 208 | unsafe { 209 | ffi::cairo_surface_get_fallback_resolution( 210 | self.to_raw_none(), 211 | &mut x_pixels_per_inch, 212 | &mut y_pixels_per_inch, 213 | ); 214 | } 215 | (x_pixels_per_inch, y_pixels_per_inch) 216 | } 217 | 218 | pub fn create_similar_image( 219 | &self, 220 | format: Format, 221 | width: i32, 222 | height: i32, 223 | ) -> Result { 224 | unsafe { 225 | Self::from_raw_full(ffi::cairo_surface_create_similar_image( 226 | self.to_raw_none(), 227 | format.into(), 228 | width, 229 | height, 230 | )) 231 | } 232 | } 233 | 234 | pub fn map_to_image(&self, extents: Option) -> Result { 235 | unsafe { 236 | ImageSurface::from_raw_full(match extents { 237 | Some(ref e) => ffi::cairo_surface_map_to_image(self.to_raw_none(), e.to_raw_none()), 238 | None => ffi::cairo_surface_map_to_image(self.to_raw_none(), std::ptr::null()), 239 | }) 240 | .map(|s| MappedImageSurface { 241 | original_surface: self.clone(), 242 | image_surface: s, 243 | }) 244 | } 245 | } 246 | 247 | pub fn mark_dirty(&self) { 248 | unsafe { ffi::cairo_surface_mark_dirty(self.to_raw_none()) } 249 | } 250 | 251 | pub fn mark_dirty_rectangle(&self, x: i32, y: i32, width: i32, height: i32) { 252 | unsafe { ffi::cairo_surface_mark_dirty_rectangle(self.to_raw_none(), x, y, width, height) } 253 | } 254 | 255 | user_data_methods! { 256 | ffi::cairo_surface_get_user_data, 257 | ffi::cairo_surface_set_user_data, 258 | } 259 | } 260 | 261 | #[cfg(feature = "use_glib")] 262 | impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface { 263 | type Storage = &'a Surface; 264 | 265 | #[inline] 266 | fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { 267 | Stash(self.to_raw_none(), self) 268 | } 269 | 270 | #[inline] 271 | fn to_glib_full(&self) -> *mut ffi::cairo_surface_t { 272 | unsafe { ffi::cairo_surface_reference(self.to_raw_none()) } 273 | } 274 | } 275 | 276 | #[cfg(feature = "use_glib")] 277 | impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface { 278 | #[inline] 279 | unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface { 280 | Self::from_raw_none(ptr) 281 | } 282 | } 283 | 284 | #[cfg(feature = "use_glib")] 285 | impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface { 286 | #[inline] 287 | unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> ::Borrowed { 288 | Self::from_raw_borrow(ptr) 289 | } 290 | } 291 | 292 | #[cfg(feature = "use_glib")] 293 | impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface { 294 | #[inline] 295 | unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface { 296 | Self::from_raw_full(ptr).unwrap() 297 | } 298 | } 299 | 300 | #[cfg(feature = "use_glib")] 301 | gvalue_impl!( 302 | Surface, 303 | ffi::cairo_surface_t, 304 | ffi::gobject::cairo_gobject_surface_get_type 305 | ); 306 | 307 | impl Clone for Surface { 308 | fn clone(&self) -> Surface { 309 | unsafe { Self::from_raw_none(self.0.as_ptr()) } 310 | } 311 | } 312 | 313 | impl Drop for Surface { 314 | fn drop(&mut self) { 315 | unsafe { 316 | ffi::cairo_surface_destroy(self.0.as_ptr()); 317 | } 318 | } 319 | } 320 | 321 | impl fmt::Display for Surface { 322 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 323 | write!(f, "Surface") 324 | } 325 | } 326 | 327 | impl Surface { 328 | pub fn flush(&self) { 329 | unsafe { 330 | ffi::cairo_surface_flush(self.0.as_ptr()); 331 | } 332 | } 333 | 334 | pub fn finish(&self) { 335 | unsafe { 336 | ffi::cairo_surface_finish(self.0.as_ptr()); 337 | } 338 | } 339 | 340 | pub fn get_type(&self) -> SurfaceType { 341 | unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.0.as_ptr())) } 342 | } 343 | } 344 | 345 | #[derive(Debug)] 346 | pub struct MappedImageSurface { 347 | original_surface: Surface, 348 | image_surface: ImageSurface, 349 | } 350 | 351 | impl Deref for MappedImageSurface { 352 | type Target = ImageSurface; 353 | 354 | fn deref(&self) -> &ImageSurface { 355 | &self.image_surface 356 | } 357 | } 358 | 359 | impl Drop for MappedImageSurface { 360 | fn drop(&mut self) { 361 | unsafe { 362 | ffi::cairo_surface_unmap_image( 363 | self.original_surface.to_raw_none(), 364 | self.image_surface.to_raw_none(), 365 | ); 366 | ffi::cairo_surface_reference(self.image_surface.to_raw_none()); 367 | } 368 | } 369 | } 370 | 371 | impl fmt::Display for MappedImageSurface { 372 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 373 | write!(f, "MappedImageSurface") 374 | } 375 | } 376 | 377 | #[cfg(test)] 378 | mod tests { 379 | use constants::MIME_TYPE_PNG; 380 | use Format; 381 | use ImageSurface; 382 | 383 | #[test] 384 | fn mime_data() { 385 | let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap(); 386 | let data = surface.get_mime_data(MIME_TYPE_PNG); 387 | /* Initially the data for any mime type has to be none */ 388 | assert!(data.is_none()); 389 | 390 | assert!(surface.set_mime_data(MIME_TYPE_PNG, &[1u8, 10u8]).is_ok()); 391 | let data = surface.get_mime_data(MIME_TYPE_PNG).unwrap(); 392 | assert_eq!(data, &[1u8, 10u8]); 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /src/surface_macros.rs: -------------------------------------------------------------------------------- 1 | // e.g. declare_surface(ImageSurface, SurfaceType::Image) 2 | macro_rules! declare_surface { 3 | ($surf_name:ident, $surf_type:expr) => { 4 | #[derive(Debug)] 5 | pub struct $surf_name(Surface); 6 | 7 | impl TryFrom for $surf_name { 8 | type Error = Surface; 9 | 10 | fn try_from(surface: Surface) -> Result<$surf_name, Surface> { 11 | if surface.get_type() == $surf_type { 12 | Ok($surf_name(surface)) 13 | } else { 14 | Err(surface) 15 | } 16 | } 17 | } 18 | 19 | impl $surf_name { 20 | pub unsafe fn from_raw_full( 21 | ptr: *mut ffi::cairo_surface_t, 22 | ) -> Result<$surf_name, ::error::Error> { 23 | let surface = Surface::from_raw_full(ptr)?; 24 | Self::try_from(surface).map_err(|_| ::error::Error::SurfaceTypeMismatch) 25 | } 26 | } 27 | 28 | #[cfg(feature = "use_glib")] 29 | impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for $surf_name { 30 | type Storage = &'a Surface; 31 | 32 | #[inline] 33 | fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { 34 | let stash = self.0.to_glib_none(); 35 | Stash(stash.0, stash.1) 36 | } 37 | 38 | #[inline] 39 | fn to_glib_full(&self) -> *mut ffi::cairo_surface_t { 40 | unsafe { ffi::cairo_surface_reference(self.to_glib_none().0) } 41 | } 42 | } 43 | 44 | #[cfg(feature = "use_glib")] 45 | impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for $surf_name { 46 | #[inline] 47 | unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> $surf_name { 48 | Self::try_from(from_glib_none::<_, Surface>(ptr)).unwrap() 49 | } 50 | } 51 | 52 | #[cfg(feature = "use_glib")] 53 | impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for $surf_name { 54 | #[inline] 55 | unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> ::Borrowed<$surf_name> { 56 | let surface = from_glib_borrow::<_, Surface>(ptr); 57 | let surface = Self::try_from(surface.into_inner()) 58 | .map_err(std::mem::forget) 59 | .unwrap(); 60 | ::Borrowed::new(surface) 61 | } 62 | } 63 | 64 | #[cfg(feature = "use_glib")] 65 | impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for $surf_name { 66 | #[inline] 67 | unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> $surf_name { 68 | Self::from_raw_full(ptr).unwrap() 69 | } 70 | } 71 | 72 | #[cfg(feature = "use_glib")] 73 | gvalue_impl!( 74 | $surf_name, 75 | ffi::cairo_surface_t, 76 | ffi::gobject::cairo_gobject_surface_get_type 77 | ); 78 | 79 | impl Deref for $surf_name { 80 | type Target = Surface; 81 | 82 | fn deref(&self) -> &Surface { 83 | &self.0 84 | } 85 | } 86 | 87 | impl Clone for $surf_name { 88 | fn clone(&self) -> $surf_name { 89 | $surf_name(self.0.clone()) 90 | } 91 | } 92 | 93 | impl fmt::Display for $surf_name { 94 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 95 | write!(f, "{}", stringify!($surf_name)) 96 | } 97 | } 98 | }; 99 | } 100 | -------------------------------------------------------------------------------- /src/svg.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2019, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | #[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))] 6 | use enums::SvgUnit; 7 | use enums::{SurfaceType, SvgVersion}; 8 | use error::Error; 9 | use ffi; 10 | use std::convert::TryFrom; 11 | use std::ffi::{CStr, CString}; 12 | use std::fmt; 13 | use std::io; 14 | use std::mem; 15 | use std::ops::Deref; 16 | #[cfg(not(windows))] 17 | use std::os::unix::prelude::*; 18 | use std::path::Path; 19 | use std::ptr; 20 | 21 | use surface::Surface; 22 | 23 | #[cfg(feature = "use_glib")] 24 | use glib::translate::*; 25 | 26 | impl SvgVersion { 27 | pub fn as_str(self) -> Option<&'static str> { 28 | unsafe { 29 | let res = ffi::cairo_svg_version_to_string(self.into()); 30 | res.as_ref() 31 | .and_then(|cstr| CStr::from_ptr(cstr as _).to_str().ok()) 32 | } 33 | } 34 | } 35 | 36 | declare_surface!(SvgSurface, SurfaceType::Svg); 37 | 38 | impl SvgSurface { 39 | pub fn new>( 40 | width: f64, 41 | height: f64, 42 | path: Option

, 43 | ) -> Result { 44 | #[cfg(not(windows))] 45 | let path = path.map(|p| { 46 | CString::new(p.as_ref().as_os_str().as_bytes()).expect("Invalid path with NULL bytes") 47 | }); 48 | #[cfg(windows)] 49 | let path = path.map(|p| { 50 | let path_str = p 51 | .as_ref() 52 | .to_str() 53 | .expect("Path can't be represented as UTF-8") 54 | .to_owned(); 55 | if path_str.starts_with("\\\\?\\") { 56 | CString::new(path_str[4..].as_bytes()) 57 | } else { 58 | CString::new(path_str.as_bytes()) 59 | } 60 | .expect("Invalid path with NUL bytes") 61 | }); 62 | 63 | unsafe { 64 | Ok(Self(Surface::from_raw_full( 65 | ffi::cairo_svg_surface_create( 66 | path.as_ref().map(|p| p.as_ptr()).unwrap_or(ptr::null()), 67 | width, 68 | height, 69 | ), 70 | )?)) 71 | } 72 | } 73 | 74 | for_stream_constructors!(cairo_svg_surface_create_for_stream); 75 | 76 | pub fn get_versions() -> impl Iterator { 77 | let vers_slice = unsafe { 78 | let mut vers_ptr = ptr::null_mut(); 79 | let mut num_vers = mem::MaybeUninit::uninit(); 80 | ffi::cairo_svg_get_versions(&mut vers_ptr, num_vers.as_mut_ptr()); 81 | 82 | std::slice::from_raw_parts(vers_ptr, num_vers.assume_init() as _) 83 | }; 84 | 85 | vers_slice.iter().map(|v| SvgVersion::from(*v)) 86 | } 87 | 88 | pub fn restrict(&self, version: SvgVersion) { 89 | unsafe { 90 | ffi::cairo_svg_surface_restrict_to_version(self.0.to_raw_none(), version.into()); 91 | } 92 | } 93 | 94 | #[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))] 95 | pub fn set_document_unit(&mut self, unit: SvgUnit) { 96 | unsafe { 97 | ffi::cairo_svg_surface_set_document_unit(self.0.to_raw_none(), unit.into()); 98 | } 99 | } 100 | 101 | #[cfg(any(all(feature = "svg", feature = "v1_16"), feature = "dox"))] 102 | pub fn get_document_unit(&self) -> SvgUnit { 103 | unsafe { 104 | SvgUnit::from(ffi::cairo_svg_surface_get_document_unit( 105 | self.0.to_raw_none(), 106 | )) 107 | } 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod test { 113 | use super::*; 114 | use context::*; 115 | use tempfile::{tempfile, NamedTempFile}; 116 | 117 | fn draw(surface: &Surface) { 118 | let cr = Context::new(surface); 119 | 120 | cr.set_line_width(25.0); 121 | 122 | cr.set_source_rgba(1.0, 0.0, 0.0, 0.5); 123 | cr.line_to(0., 0.); 124 | cr.line_to(100., 100.); 125 | cr.stroke(); 126 | 127 | cr.set_source_rgba(0.0, 0.0, 1.0, 0.5); 128 | cr.line_to(0., 100.); 129 | cr.line_to(100., 0.); 130 | cr.stroke(); 131 | } 132 | 133 | fn draw_in_buffer() -> Vec { 134 | let buffer: Vec = vec![]; 135 | 136 | let surface = SvgSurface::for_stream(100., 100., buffer).unwrap(); 137 | draw(&surface); 138 | *surface.finish_output_stream().unwrap().downcast().unwrap() 139 | } 140 | 141 | fn assert_len_close_enough(len_a: usize, len_b: usize) { 142 | // It seems cairo randomizies some element IDs which might make one svg slightly 143 | // larger than the other. Here we make sure the difference is within ~10%. 144 | let len_diff = (len_a as isize - len_b as isize).abs() as usize; 145 | assert!(len_diff < len_b / 10); 146 | } 147 | 148 | #[test] 149 | fn versions() { 150 | assert!(SvgSurface::get_versions().any(|v| v == SvgVersion::_1_1)); 151 | } 152 | 153 | #[test] 154 | fn version_string() { 155 | let ver_str = SvgVersion::_1_1.as_str().unwrap(); 156 | assert_eq!(ver_str, "SVG 1.1"); 157 | } 158 | 159 | #[test] 160 | fn without_file() { 161 | let surface = SvgSurface::new(100., 100., None::<&Path>).unwrap(); 162 | draw(&surface); 163 | surface.finish(); 164 | } 165 | 166 | #[test] 167 | fn file() { 168 | let file = NamedTempFile::new().expect("tempfile failed"); 169 | let surface = SvgSurface::new(100., 100., Some(&file.path())).unwrap(); 170 | draw(&surface); 171 | surface.finish(); 172 | } 173 | 174 | #[test] 175 | fn writer() { 176 | let file = tempfile().expect("tempfile failed"); 177 | let surface = SvgSurface::for_stream(100., 100., file).unwrap(); 178 | 179 | draw(&surface); 180 | let stream = surface.finish_output_stream().unwrap(); 181 | let file = stream.downcast::().unwrap(); 182 | 183 | let buffer = draw_in_buffer(); 184 | let file_size = file.metadata().unwrap().len(); 185 | 186 | assert_len_close_enough(file_size as usize, buffer.len()); 187 | } 188 | 189 | #[test] 190 | fn ref_writer() { 191 | let mut file = tempfile().expect("tempfile failed"); 192 | let surface = unsafe { SvgSurface::for_raw_stream(100., 100., &mut file).unwrap() }; 193 | 194 | draw(&surface); 195 | surface.finish_output_stream().unwrap(); 196 | } 197 | 198 | #[test] 199 | fn buffer() { 200 | let buffer = draw_in_buffer(); 201 | 202 | let header = b" io::Result { 214 | self.1.write(buf)?; 215 | 216 | self.0 += buf.len(); 217 | Ok(buf.len()) 218 | } 219 | 220 | fn flush(&mut self) -> io::Result<()> { 221 | Ok(()) 222 | } 223 | } 224 | 225 | let file = tempfile().expect("tempfile failed"); 226 | let custom_writer = CustomWriter(0, file); 227 | 228 | let surface = SvgSurface::for_stream(100., 100., custom_writer).unwrap(); 229 | draw(&surface); 230 | let stream = surface.finish_output_stream().unwrap(); 231 | let custom_writer = stream.downcast::().unwrap(); 232 | 233 | let buffer = draw_in_buffer(); 234 | 235 | assert_len_close_enough(custom_writer.0, buffer.len()); 236 | } 237 | 238 | fn with_panicky_stream() -> SvgSurface { 239 | struct PanicWriter; 240 | 241 | impl io::Write for PanicWriter { 242 | fn write(&mut self, _buf: &[u8]) -> io::Result { 243 | panic!("panic in writer"); 244 | } 245 | fn flush(&mut self) -> io::Result<()> { 246 | Ok(()) 247 | } 248 | } 249 | 250 | let surface = SvgSurface::for_stream(20., 20., PanicWriter).unwrap(); 251 | surface.finish(); 252 | surface 253 | } 254 | 255 | #[test] 256 | #[should_panic] 257 | fn finish_stream_propagates_panic() { 258 | let _ = with_panicky_stream().finish_output_stream(); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/user_data.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use ffi::cairo_user_data_key_t; 4 | 5 | pub struct UserDataKey { 6 | pub(crate) ffi: cairo_user_data_key_t, 7 | marker: PhantomData<*const T>, 8 | } 9 | 10 | unsafe impl Sync for UserDataKey {} 11 | 12 | impl UserDataKey { 13 | pub const fn new() -> Self { 14 | UserDataKey { 15 | ffi: cairo_user_data_key_t { unused: 0 }, 16 | marker: PhantomData, 17 | } 18 | } 19 | } 20 | 21 | // In a safe API for user data we can’t make `get_user_data` 22 | // transfer full ownership of the value to the caller (e.g. by returning `Box`) 23 | // because `self` still has a pointer to that value 24 | // and `get_user_data` could be called again with the same key. 25 | // 26 | // We also can’t return a `&T` reference that borrows from `self` 27 | // because the value could be removed with `remove_user_data` or replaced with `set_user_data` 28 | // while the borrow still needs to be valid. 29 | // (Borrowing with `&mut self` would not help as `Self` can be itself reference-counted.) 30 | // 31 | // Therefore the value must be reference-counted. 32 | // 33 | // We use `Rc` over `Arc` because the types implementing these methods are `!Send` and `!Sync`. 34 | // See 35 | 36 | macro_rules! user_data_methods { 37 | ($ffi_get_user_data: path, $ffi_set_user_data: path,) => { 38 | /// Attach user data to `self` for the given `key`. 39 | pub fn set_user_data( 40 | &self, 41 | key: &'static crate::UserDataKey, 42 | value: std::rc::Rc, 43 | ) { 44 | unsafe extern "C" fn destructor(ptr: *mut libc::c_void) { 45 | let ptr: *const T = ptr as _; 46 | drop(std::rc::Rc::from_raw(ptr)) 47 | } 48 | // Safety: 49 | // 50 | // The destructor’s cast and `from_raw` are symetric 51 | // with the `into_raw` and cast below. 52 | // They both transfer ownership of one strong reference: 53 | // neither of them touches the reference count. 54 | let ptr: *const T = std::rc::Rc::into_raw(value); 55 | let ptr = ptr as *mut T as *mut libc::c_void; 56 | let status = unsafe { 57 | $ffi_set_user_data(self.to_raw_none(), &key.ffi, ptr, Some(destructor::)) 58 | }; 59 | crate::utils::status_to_result(status).expect("Failed to set user data"); 60 | } 61 | 62 | /// Return the user data previously attached to `self` with the given `key`, if any. 63 | pub fn get_user_data( 64 | &self, 65 | key: &'static crate::UserDataKey, 66 | ) -> Option> { 67 | let ptr = self.get_user_data_ptr(key)?.as_ptr(); 68 | 69 | // Safety: 70 | // 71 | // `Rc::from_raw` would normally take ownership of a strong reference for this pointer. 72 | // But `self` still has a copy of that pointer and `get_user_data` can be called again 73 | // with the same key. 74 | // We use `ManuallyDrop` to avoid running the destructor of that first `Rc`, 75 | // and return a cloned one (which increments the reference count). 76 | unsafe { 77 | let rc = std::mem::ManuallyDrop::new(std::rc::Rc::from_raw(ptr)); 78 | Some(std::rc::Rc::clone(&rc)) 79 | } 80 | } 81 | 82 | /// Return the user data previously attached to `self` with the given `key`, if any, 83 | /// without incrementing the reference count. 84 | /// 85 | /// The pointer is valid when it is returned from this method, 86 | /// until the cairo object that `self` represents is destroyed 87 | /// or `remove_user_data` or `set_user_data` is called with the same key. 88 | pub fn get_user_data_ptr( 89 | &self, 90 | key: &'static crate::UserDataKey, 91 | ) -> Option> { 92 | // Safety: 93 | // 94 | // If `ffi_get_user_data` returns a non-null pointer, 95 | // there was a previous call to `ffi_set_user_data` with a key with the same address. 96 | // Either: 97 | // 98 | // * This was a call to a Rust `Self::set_user_data` method. 99 | // Because that method takes a `&'static` reference, 100 | // the key used then must live at that address until the end of the process. 101 | // Because `UserDataKey` has a non-zero size regardless of `T`, 102 | // no other `UserDataKey` value can have the same address. 103 | // Therefore the `T` type was the same then at it is now and `cast` is type-safe. 104 | // 105 | // * Or, it is technically possible that the `set` call was to the C function directly, 106 | // with a `cairo_user_data_key_t` in heap-allocated memory that was then freed, 107 | // then `Box::new(UserDataKey::new()).leak()` was used to create a `&'static` 108 | // that happens to have the same address because the allocator for `Box` 109 | // reused that memory region. 110 | // Since this involves a C (or FFI) call *and* is so far out of “typical” use 111 | // of the user data functionality, we consider this a misuse of an unsafe API. 112 | unsafe { 113 | let ptr = $ffi_get_user_data(self.to_raw_none(), &key.ffi); 114 | Some(std::ptr::NonNull::new(ptr)?.cast()) 115 | } 116 | } 117 | 118 | /// Unattach from `self` the user data associated with `key`, if any. 119 | /// If there is no other `Rc` strong reference, the data is destroyed. 120 | pub fn remove_user_data(&self, key: &'static crate::UserDataKey) { 121 | let status = unsafe { 122 | $ffi_set_user_data(self.to_raw_none(), &key.ffi, std::ptr::null_mut(), None) 123 | }; 124 | crate::utils::status_to_result(status).expect("Failed to remove user data"); 125 | } 126 | }; 127 | } 128 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2018, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use error::Error; 6 | use ffi; 7 | use std::ffi::CStr; 8 | use std::fmt; 9 | 10 | pub unsafe fn debug_reset_static_data() { 11 | ffi::cairo_debug_reset_static_data() 12 | } 13 | 14 | pub fn status_to_result(status: ffi::cairo_status_t) -> Result<(), Error> { 15 | match status { 16 | ffi::STATUS_SUCCESS => Ok(()), 17 | err => Err(err.into()), 18 | } 19 | } 20 | 21 | pub fn get_version_string() -> &'static str { 22 | unsafe { 23 | let ptr = ffi::cairo_version_string(); 24 | CStr::from_ptr(ptr) 25 | .to_str() 26 | .expect("invalid version string") 27 | } 28 | } 29 | 30 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] 31 | pub struct Version { 32 | pub major: u8, 33 | pub minor: u8, 34 | pub micro: u8, 35 | } 36 | 37 | impl Version { 38 | pub fn get_version() -> Version { 39 | let version = unsafe { ffi::cairo_version() }; 40 | Version { 41 | major: (version / 10_000 % 100) as _, 42 | minor: (version / 100 % 100) as _, 43 | micro: (version % 100) as _, 44 | } 45 | } 46 | } 47 | 48 | impl fmt::Display for Version { 49 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 50 | write!(f, "{}.{}.{}", self.major, self.minor, self.micro) 51 | } 52 | } 53 | 54 | #[cfg(test)] 55 | mod tests { 56 | use super::*; 57 | 58 | #[test] 59 | fn check_versions() { 60 | assert_eq!(get_version_string(), Version::get_version().to_string()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/win32_surface.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | pub use ffi::winapi; 6 | 7 | use std::convert::TryFrom; 8 | use std::fmt; 9 | use std::ops::Deref; 10 | 11 | use enums::{Format, SurfaceType}; 12 | use error::Error; 13 | use ffi; 14 | #[cfg(feature = "use_glib")] 15 | use glib::translate::*; 16 | use surface::Surface; 17 | 18 | declare_surface!(Win32Surface, SurfaceType::Win32); 19 | 20 | impl Win32Surface { 21 | pub fn create(hdc: winapi::HDC) -> Result { 22 | unsafe { Self::from_raw_full(ffi::cairo_win32_surface_create(hdc)) } 23 | } 24 | 25 | #[cfg(any(all(windows, feature = "v1_14"), feature = "dox"))] 26 | pub fn create_with_format(hdc: winapi::HDC, format: Format) -> Result { 27 | unsafe { 28 | Self::from_raw_full(ffi::cairo_win32_surface_create_with_format( 29 | hdc, 30 | format.into(), 31 | )) 32 | } 33 | } 34 | 35 | pub fn create_with_dib(format: Format, width: i32, height: i32) -> Result { 36 | unsafe { 37 | Self::from_raw_full(ffi::cairo_win32_surface_create_with_dib( 38 | format.into(), 39 | width, 40 | height, 41 | )) 42 | } 43 | } 44 | 45 | pub fn create_with_ddb( 46 | hdc: winapi::HDC, 47 | format: Format, 48 | width: i32, 49 | height: i32, 50 | ) -> Result { 51 | unsafe { 52 | Self::from_raw_full(ffi::cairo_win32_surface_create_with_ddb( 53 | hdc, 54 | format.into(), 55 | width, 56 | height, 57 | )) 58 | } 59 | } 60 | 61 | pub fn printing_surface_create(hdc: winapi::HDC) -> Result { 62 | unsafe { Self::from_raw_full(ffi::cairo_win32_printing_surface_create(hdc)) } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/xcb.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2015, The Gtk-rs Project Developers. 2 | // See the COPYRIGHT file at the top-level directory of this distribution. 3 | // Licensed under the MIT license, see the LICENSE file or 4 | 5 | use enums::SurfaceType; 6 | use ffi; 7 | #[cfg(feature = "use_glib")] 8 | use glib::translate::*; 9 | use std::convert::TryFrom; 10 | use std::fmt; 11 | use std::ops::Deref; 12 | use std::ptr; 13 | 14 | use error::Error; 15 | use surface::Surface; 16 | use utils::status_to_result; 17 | 18 | #[derive(Debug)] 19 | pub struct XCBDrawable(pub u32); 20 | 21 | impl XCBDrawable { 22 | fn to_raw_none(&self) -> u32 { 23 | self.0 24 | } 25 | } 26 | 27 | impl fmt::Display for XCBDrawable { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | write!(f, "XCBDrawable") 30 | } 31 | } 32 | 33 | #[derive(Debug)] 34 | pub struct XCBPixmap(pub u32); 35 | 36 | impl XCBPixmap { 37 | fn to_raw_none(&self) -> u32 { 38 | self.0 39 | } 40 | } 41 | 42 | impl fmt::Display for XCBPixmap { 43 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 44 | write!(f, "XCBPixmap") 45 | } 46 | } 47 | 48 | #[derive(Debug)] 49 | pub struct XCBConnection(pub ptr::NonNull); 50 | 51 | impl XCBConnection { 52 | pub fn to_raw_none(&self) -> *mut ffi::xcb_connection_t { 53 | self.0.as_ptr() 54 | } 55 | 56 | pub unsafe fn from_raw_none(ptr: *mut ffi::xcb_connection_t) -> XCBConnection { 57 | assert!(!ptr.is_null()); 58 | XCBConnection(ptr::NonNull::new_unchecked(ptr)) 59 | } 60 | 61 | pub unsafe fn from_raw_borrow(ptr: *mut ffi::xcb_connection_t) -> ::Borrowed { 62 | assert!(!ptr.is_null()); 63 | ::Borrowed::new(XCBConnection(ptr::NonNull::new_unchecked(ptr))) 64 | } 65 | 66 | pub unsafe fn from_raw_full(ptr: *mut ffi::xcb_connection_t) -> XCBConnection { 67 | assert!(!ptr.is_null()); 68 | XCBConnection(ptr::NonNull::new_unchecked(ptr)) 69 | } 70 | } 71 | 72 | #[cfg(feature = "use_glib")] 73 | impl<'a> ToGlibPtr<'a, *mut ffi::xcb_connection_t> for &'a XCBConnection { 74 | type Storage = &'a XCBConnection; 75 | 76 | #[inline] 77 | fn to_glib_none(&self) -> Stash<'a, *mut ffi::xcb_connection_t, &'a XCBConnection> { 78 | Stash(self.to_raw_none(), *self) 79 | } 80 | } 81 | 82 | #[cfg(feature = "use_glib")] 83 | impl FromGlibPtrNone<*mut ffi::xcb_connection_t> for XCBConnection { 84 | #[inline] 85 | unsafe fn from_glib_none(ptr: *mut ffi::xcb_connection_t) -> XCBConnection { 86 | Self::from_raw_none(ptr) 87 | } 88 | } 89 | 90 | #[cfg(feature = "use_glib")] 91 | impl FromGlibPtrBorrow<*mut ffi::xcb_connection_t> for XCBConnection { 92 | #[inline] 93 | unsafe fn from_glib_borrow(ptr: *mut ffi::xcb_connection_t) -> ::Borrowed { 94 | Self::from_raw_borrow(ptr) 95 | } 96 | } 97 | 98 | #[cfg(feature = "use_glib")] 99 | impl FromGlibPtrFull<*mut ffi::xcb_connection_t> for XCBConnection { 100 | #[inline] 101 | unsafe fn from_glib_full(ptr: *mut ffi::xcb_connection_t) -> XCBConnection { 102 | Self::from_raw_full(ptr) 103 | } 104 | } 105 | 106 | impl Clone for XCBConnection { 107 | fn clone(&self) -> XCBConnection { 108 | unsafe { Self::from_raw_none(self.to_raw_none()) } 109 | } 110 | } 111 | 112 | impl fmt::Display for XCBConnection { 113 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 114 | write!(f, "XCBConnection") 115 | } 116 | } 117 | 118 | #[derive(Debug)] 119 | pub struct XCBRenderPictFormInfo(pub ptr::NonNull); 120 | 121 | impl XCBRenderPictFormInfo { 122 | pub fn to_raw_none(&self) -> *mut ffi::xcb_render_pictforminfo_t { 123 | self.0.as_ptr() 124 | } 125 | 126 | pub unsafe fn from_raw_none(ptr: *mut ffi::xcb_render_pictforminfo_t) -> XCBRenderPictFormInfo { 127 | assert!(!ptr.is_null()); 128 | XCBRenderPictFormInfo(ptr::NonNull::new_unchecked(ptr)) 129 | } 130 | 131 | pub unsafe fn from_raw_borrow( 132 | ptr: *mut ffi::xcb_render_pictforminfo_t, 133 | ) -> ::Borrowed { 134 | assert!(!ptr.is_null()); 135 | ::Borrowed::new(XCBRenderPictFormInfo(ptr::NonNull::new_unchecked(ptr))) 136 | } 137 | 138 | pub unsafe fn from_raw_full(ptr: *mut ffi::xcb_render_pictforminfo_t) -> XCBRenderPictFormInfo { 139 | assert!(!ptr.is_null()); 140 | XCBRenderPictFormInfo(ptr::NonNull::new_unchecked(ptr)) 141 | } 142 | } 143 | 144 | #[cfg(feature = "use_glib")] 145 | impl<'a> ToGlibPtr<'a, *mut ffi::xcb_render_pictforminfo_t> for &'a XCBRenderPictFormInfo { 146 | type Storage = &'a XCBRenderPictFormInfo; 147 | 148 | #[inline] 149 | fn to_glib_none( 150 | &self, 151 | ) -> Stash<'a, *mut ffi::xcb_render_pictforminfo_t, &'a XCBRenderPictFormInfo> { 152 | Stash(self.to_raw_none(), *self) 153 | } 154 | } 155 | 156 | #[cfg(feature = "use_glib")] 157 | impl FromGlibPtrNone<*mut ffi::xcb_render_pictforminfo_t> for XCBRenderPictFormInfo { 158 | #[inline] 159 | unsafe fn from_glib_none(ptr: *mut ffi::xcb_render_pictforminfo_t) -> XCBRenderPictFormInfo { 160 | Self::from_raw_none(ptr) 161 | } 162 | } 163 | 164 | #[cfg(feature = "use_glib")] 165 | impl FromGlibPtrBorrow<*mut ffi::xcb_render_pictforminfo_t> for XCBRenderPictFormInfo { 166 | #[inline] 167 | unsafe fn from_glib_borrow( 168 | ptr: *mut ffi::xcb_render_pictforminfo_t, 169 | ) -> ::Borrowed { 170 | Self::from_raw_borrow(ptr) 171 | } 172 | } 173 | 174 | #[cfg(feature = "use_glib")] 175 | impl FromGlibPtrFull<*mut ffi::xcb_render_pictforminfo_t> for XCBRenderPictFormInfo { 176 | #[inline] 177 | unsafe fn from_glib_full(ptr: *mut ffi::xcb_render_pictforminfo_t) -> XCBRenderPictFormInfo { 178 | Self::from_raw_full(ptr) 179 | } 180 | } 181 | 182 | impl Clone for XCBRenderPictFormInfo { 183 | fn clone(&self) -> XCBRenderPictFormInfo { 184 | unsafe { Self::from_raw_none(self.to_raw_none()) } 185 | } 186 | } 187 | 188 | impl fmt::Display for XCBRenderPictFormInfo { 189 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 190 | write!(f, "XCBRenderPictFormInfo") 191 | } 192 | } 193 | 194 | #[derive(Debug)] 195 | pub struct XCBScreen(pub ptr::NonNull); 196 | 197 | impl XCBScreen { 198 | pub fn to_raw_none(&self) -> *mut ffi::xcb_screen_t { 199 | self.0.as_ptr() 200 | } 201 | 202 | pub unsafe fn from_raw_none(ptr: *mut ffi::xcb_screen_t) -> XCBScreen { 203 | assert!(!ptr.is_null()); 204 | XCBScreen(ptr::NonNull::new_unchecked(ptr)) 205 | } 206 | 207 | pub unsafe fn from_raw_borrow(ptr: *mut ffi::xcb_screen_t) -> ::Borrowed { 208 | assert!(!ptr.is_null()); 209 | ::Borrowed::new(XCBScreen(ptr::NonNull::new_unchecked(ptr))) 210 | } 211 | 212 | pub unsafe fn from_raw_full(ptr: *mut ffi::xcb_screen_t) -> XCBScreen { 213 | assert!(!ptr.is_null()); 214 | XCBScreen(ptr::NonNull::new_unchecked(ptr)) 215 | } 216 | } 217 | 218 | #[cfg(feature = "use_glib")] 219 | impl<'a> ToGlibPtr<'a, *mut ffi::xcb_screen_t> for &'a XCBScreen { 220 | type Storage = &'a XCBScreen; 221 | 222 | #[inline] 223 | fn to_glib_none(&self) -> Stash<'a, *mut ffi::xcb_screen_t, &'a XCBScreen> { 224 | Stash(self.to_raw_none(), *self) 225 | } 226 | } 227 | 228 | #[cfg(feature = "use_glib")] 229 | impl FromGlibPtrNone<*mut ffi::xcb_screen_t> for XCBScreen { 230 | #[inline] 231 | unsafe fn from_glib_none(ptr: *mut ffi::xcb_screen_t) -> XCBScreen { 232 | Self::from_raw_none(ptr) 233 | } 234 | } 235 | 236 | #[cfg(feature = "use_glib")] 237 | impl FromGlibPtrBorrow<*mut ffi::xcb_screen_t> for XCBScreen { 238 | #[inline] 239 | unsafe fn from_glib_borrow(ptr: *mut ffi::xcb_screen_t) -> ::Borrowed { 240 | Self::from_raw_borrow(ptr) 241 | } 242 | } 243 | 244 | #[cfg(feature = "use_glib")] 245 | impl FromGlibPtrFull<*mut ffi::xcb_screen_t> for XCBScreen { 246 | #[inline] 247 | unsafe fn from_glib_full(ptr: *mut ffi::xcb_screen_t) -> XCBScreen { 248 | Self::from_raw_full(ptr) 249 | } 250 | } 251 | 252 | impl Clone for XCBScreen { 253 | fn clone(&self) -> XCBScreen { 254 | unsafe { Self::from_raw_none(self.to_raw_none()) } 255 | } 256 | } 257 | 258 | impl fmt::Display for XCBScreen { 259 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 260 | write!(f, "XCBScreen") 261 | } 262 | } 263 | 264 | declare_surface!(XCBSurface, SurfaceType::Xcb); 265 | 266 | impl XCBSurface { 267 | pub fn create( 268 | connection: &XCBConnection, 269 | drawable: &XCBDrawable, 270 | visual: &XCBVisualType, 271 | width: i32, 272 | height: i32, 273 | ) -> Result { 274 | unsafe { 275 | Ok(Self::from_raw_full(ffi::cairo_xcb_surface_create( 276 | connection.to_raw_none(), 277 | drawable.to_raw_none(), 278 | visual.to_raw_none(), 279 | width, 280 | height, 281 | ))?) 282 | } 283 | } 284 | 285 | pub fn create_for_bitmap( 286 | connection: &XCBConnection, 287 | screen: &XCBScreen, 288 | bitmap: &XCBPixmap, 289 | width: i32, 290 | height: i32, 291 | ) -> Result { 292 | unsafe { 293 | Ok(Self(Surface::from_raw_full( 294 | ffi::cairo_xcb_surface_create_for_bitmap( 295 | connection.to_raw_none(), 296 | screen.to_raw_none(), 297 | bitmap.to_raw_none(), 298 | width, 299 | height, 300 | ), 301 | )?)) 302 | } 303 | } 304 | 305 | pub fn create_with_xrender_format( 306 | connection: &XCBConnection, 307 | screen: &XCBScreen, 308 | bitmap: &XCBPixmap, 309 | format: &XCBRenderPictFormInfo, 310 | width: i32, 311 | height: i32, 312 | ) -> Result { 313 | unsafe { 314 | Ok(Self(Surface::from_raw_full( 315 | ffi::cairo_xcb_surface_create_with_xrender_format( 316 | connection.to_raw_none(), 317 | screen.to_raw_none(), 318 | bitmap.to_raw_none(), 319 | format.to_raw_none(), 320 | width, 321 | height, 322 | ), 323 | )?)) 324 | } 325 | } 326 | 327 | fn status(&self) -> Result<(), Error> { 328 | let status = unsafe { ffi::cairo_surface_status(self.to_raw_none()) }; 329 | status_to_result(status) 330 | } 331 | 332 | pub fn set_size(&self, width: i32, height: i32) -> Result<(), Error> { 333 | unsafe { ffi::cairo_xcb_surface_set_size(self.to_raw_none(), width, height) } 334 | self.status() 335 | } 336 | 337 | pub fn set_drawable( 338 | &self, 339 | drawable: &XCBDrawable, 340 | width: i32, 341 | height: i32, 342 | ) -> Result<(), Error> { 343 | unsafe { 344 | ffi::cairo_xcb_surface_set_drawable( 345 | self.to_raw_none(), 346 | drawable.to_raw_none(), 347 | width, 348 | height, 349 | ) 350 | } 351 | self.status() 352 | } 353 | } 354 | 355 | #[derive(Debug)] 356 | pub struct XCBVisualType(pub ptr::NonNull); 357 | 358 | impl XCBVisualType { 359 | pub fn to_raw_none(&self) -> *mut ffi::xcb_visualtype_t { 360 | self.0.as_ptr() 361 | } 362 | 363 | pub unsafe fn from_raw_none(ptr: *mut ffi::xcb_visualtype_t) -> XCBVisualType { 364 | assert!(!ptr.is_null()); 365 | XCBVisualType(ptr::NonNull::new_unchecked(ptr)) 366 | } 367 | 368 | pub unsafe fn from_raw_borrow(ptr: *mut ffi::xcb_visualtype_t) -> ::Borrowed { 369 | assert!(!ptr.is_null()); 370 | ::Borrowed::new(XCBVisualType(ptr::NonNull::new_unchecked(ptr))) 371 | } 372 | 373 | pub unsafe fn from_raw_full(ptr: *mut ffi::xcb_visualtype_t) -> XCBVisualType { 374 | assert!(!ptr.is_null()); 375 | XCBVisualType(ptr::NonNull::new_unchecked(ptr)) 376 | } 377 | } 378 | 379 | #[cfg(feature = "use_glib")] 380 | impl<'a> ToGlibPtr<'a, *mut ffi::xcb_visualtype_t> for &'a XCBVisualType { 381 | type Storage = &'a XCBVisualType; 382 | 383 | #[inline] 384 | fn to_glib_none(&self) -> Stash<'a, *mut ffi::xcb_visualtype_t, &'a XCBVisualType> { 385 | Stash(self.to_raw_none(), *self) 386 | } 387 | } 388 | 389 | #[cfg(feature = "use_glib")] 390 | impl FromGlibPtrNone<*mut ffi::xcb_visualtype_t> for XCBVisualType { 391 | #[inline] 392 | unsafe fn from_glib_none(ptr: *mut ffi::xcb_visualtype_t) -> XCBVisualType { 393 | Self::from_raw_none(ptr) 394 | } 395 | } 396 | 397 | #[cfg(feature = "use_glib")] 398 | impl FromGlibPtrBorrow<*mut ffi::xcb_visualtype_t> for XCBVisualType { 399 | #[inline] 400 | unsafe fn from_glib_borrow(ptr: *mut ffi::xcb_visualtype_t) -> ::Borrowed { 401 | Self::from_raw_borrow(ptr) 402 | } 403 | } 404 | 405 | #[cfg(feature = "use_glib")] 406 | impl FromGlibPtrFull<*mut ffi::xcb_visualtype_t> for XCBVisualType { 407 | #[inline] 408 | unsafe fn from_glib_full(ptr: *mut ffi::xcb_visualtype_t) -> XCBVisualType { 409 | Self::from_raw_full(ptr) 410 | } 411 | } 412 | 413 | impl Clone for XCBVisualType { 414 | fn clone(&self) -> XCBVisualType { 415 | unsafe { Self::from_raw_none(self.to_raw_none()) } 416 | } 417 | } 418 | 419 | impl fmt::Display for XCBVisualType { 420 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 421 | write!(f, "XCBVisualType") 422 | } 423 | } 424 | 425 | impl ::device::Device { 426 | pub fn get_connection(&self) -> XCBConnection { 427 | unsafe { 428 | XCBConnection::from_raw_full(ffi::cairo_xcb_device_get_connection(self.to_raw_none())) 429 | } 430 | } 431 | 432 | pub fn debug_cap_xshm_version(&self, major_version: i32, minor_version: i32) { 433 | unsafe { 434 | ffi::cairo_xcb_device_debug_cap_xshm_version( 435 | self.to_raw_none(), 436 | major_version, 437 | minor_version, 438 | ) 439 | } 440 | } 441 | } 442 | --------------------------------------------------------------------------------