cairo/font/
user_fonts.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::sync::OnceLock;
4
5use super::{FontExtents, FontFace, ScaledFont, TextCluster, TextClusterFlags, TextExtents};
6use crate::{Context, Error, Glyph, ffi, utils::status_to_result};
7
8type BoxInitFunc =
9    Box<dyn Fn(&ScaledFont, &Context, &mut FontExtents) -> Result<(), Error> + Send + Sync>;
10type BoxRenderGlyphFunc = Box<
11    dyn Fn(&ScaledFont, libc::c_ulong, &Context, &mut TextExtents) -> Result<(), Error>
12        + Send
13        + Sync,
14>;
15type BoxUnicodeToGlyphFunc =
16    Box<dyn Fn(&ScaledFont, libc::c_ulong) -> Result<libc::c_ulong, Error> + Send + Sync>;
17type BoxTextToGlyphsFunc = Box<
18    dyn Fn(&ScaledFont, &str) -> Result<(Vec<Glyph>, Vec<TextCluster>, TextClusterFlags), Error>
19        + Send
20        + Sync,
21>;
22
23pub struct UserFontFace(FontFace);
24
25impl UserFontFace {
26    #[doc(alias = "cairo_user_font_face_create")]
27    pub fn create() -> Result<Self, Error> {
28        let font_face = unsafe { FontFace::from_raw_full(ffi::cairo_user_font_face_create()) };
29        let status = unsafe { ffi::cairo_font_face_status(font_face.to_raw_none()) };
30        status_to_result(status)?;
31        Ok(Self(font_face))
32    }
33
34    #[doc(alias = "cairo_user_font_face_set_init_func")]
35    pub fn set_init_func<F>(&self, func: F)
36    where
37        F: Fn(&ScaledFont, &Context, &mut FontExtents) -> Result<(), Error> + Send + Sync + 'static,
38    {
39        static INIT_FUNC: OnceLock<BoxInitFunc> = OnceLock::new();
40        if INIT_FUNC.set(Box::new(func)).is_err() {
41            panic!("Init func can only be set once")
42        }
43        unsafe extern "C" fn init_trampoline(
44            scaled_font: *mut ffi::cairo_scaled_font_t,
45            cr: *mut ffi::cairo_t,
46            extents: *mut ffi::cairo_font_extents_t,
47        ) -> ffi::cairo_status_t {
48            unsafe {
49                let font_extents = &mut *(extents as *mut FontExtents);
50                let init_func = INIT_FUNC.get().unwrap();
51                match init_func(
52                    &ScaledFont::from_raw_none(scaled_font),
53                    &Context::from_raw_none(cr),
54                    font_extents,
55                ) {
56                    Err(err) => err.into(),
57                    _ => ffi::STATUS_SUCCESS,
58                }
59            }
60        }
61        unsafe {
62            ffi::cairo_user_font_face_set_init_func(self.to_raw_none(), Some(init_trampoline));
63        }
64    }
65
66    #[doc(alias = "cairo_user_font_face_set_render_glyph_func")]
67    pub fn set_render_glyph_func<F>(&self, func: F)
68    where
69        F: Fn(&ScaledFont, libc::c_ulong, &Context, &mut TextExtents) -> Result<(), Error>
70            + Send
71            + Sync
72            + 'static,
73    {
74        static RENDER_GLYPH_FUNC: OnceLock<BoxRenderGlyphFunc> = OnceLock::new();
75        if RENDER_GLYPH_FUNC.set(Box::new(func)).is_err() {
76            panic!("RenderGlyph func can only be set once")
77        }
78        unsafe extern "C" fn render_glyph_trampoline(
79            scaled_font: *mut ffi::cairo_scaled_font_t,
80            glyph: libc::c_ulong,
81            cr: *mut ffi::cairo_t,
82            extents: *mut ffi::cairo_text_extents_t,
83        ) -> ffi::cairo_status_t {
84            unsafe {
85                let text_extents = &mut *(extents as *mut TextExtents);
86                let render_glyph_func = RENDER_GLYPH_FUNC.get().unwrap();
87                match render_glyph_func(
88                    &ScaledFont::from_raw_none(scaled_font),
89                    glyph,
90                    &Context::from_raw_none(cr),
91                    text_extents,
92                ) {
93                    Err(err) => err.into(),
94                    _ => ffi::STATUS_SUCCESS,
95                }
96            }
97        }
98        unsafe {
99            ffi::cairo_user_font_face_set_render_glyph_func(
100                self.to_raw_none(),
101                Some(render_glyph_trampoline),
102            );
103        }
104    }
105
106    #[doc(alias = "cairo_user_font_face_set_render_color_glyph_func")]
107    pub fn set_render_color_glyph_func<F>(&self, func: F)
108    where
109        F: Fn(&ScaledFont, libc::c_ulong, &Context, &mut TextExtents) -> Result<(), Error>
110            + Send
111            + Sync
112            + 'static,
113    {
114        static RENDER_COLOR_GLYPH_FUNC: OnceLock<BoxRenderGlyphFunc> = OnceLock::new();
115        if RENDER_COLOR_GLYPH_FUNC.set(Box::new(func)).is_err() {
116            panic!("RenderColorGlyph func can only be set once")
117        }
118        unsafe extern "C" fn render_glyph_trampoline(
119            scaled_font: *mut ffi::cairo_scaled_font_t,
120            glyph: libc::c_ulong,
121            cr: *mut ffi::cairo_t,
122            extents: *mut ffi::cairo_text_extents_t,
123        ) -> ffi::cairo_status_t {
124            unsafe {
125                let text_extents = &mut *(extents as *mut TextExtents);
126                let render_glyph_func = RENDER_COLOR_GLYPH_FUNC.get().unwrap();
127                match render_glyph_func(
128                    &ScaledFont::from_raw_none(scaled_font),
129                    glyph,
130                    &Context::from_raw_none(cr),
131                    text_extents,
132                ) {
133                    Err(err) => err.into(),
134                    _ => ffi::STATUS_SUCCESS,
135                }
136            }
137        }
138        unsafe {
139            ffi::cairo_user_font_face_set_render_glyph_func(
140                self.to_raw_none(),
141                Some(render_glyph_trampoline),
142            );
143        }
144    }
145
146    #[doc(alias = "cairo_user_font_face_set_unicode_to_glyph_func")]
147    pub fn set_unicode_to_glyph_func<F>(&self, func: F)
148    where
149        F: Fn(&ScaledFont, libc::c_ulong) -> Result<libc::c_ulong, Error> + Send + Sync + 'static,
150    {
151        static UNICODE_TO_GLYPH_FUNC: OnceLock<BoxUnicodeToGlyphFunc> = OnceLock::new();
152        if UNICODE_TO_GLYPH_FUNC.set(Box::new(func)).is_err() {
153            panic!("UnicodeToGlyph func can only be set once")
154        }
155        unsafe extern "C" fn unicode_to_glyph_trampoline(
156            scaled_font: *mut ffi::cairo_scaled_font_t,
157            unicode: libc::c_ulong,
158            glyph_index: *mut libc::c_ulong,
159        ) -> ffi::cairo_status_t {
160            unsafe {
161                let unicode_to_glyph_func = UNICODE_TO_GLYPH_FUNC.get().unwrap();
162                match unicode_to_glyph_func(&ScaledFont::from_raw_none(scaled_font), unicode) {
163                    Err(err) => err.into(),
164                    Ok(glyph) => {
165                        *glyph_index = glyph;
166                        ffi::STATUS_SUCCESS
167                    }
168                }
169            }
170        }
171        unsafe {
172            ffi::cairo_user_font_face_set_unicode_to_glyph_func(
173                self.to_raw_none(),
174                Some(unicode_to_glyph_trampoline),
175            );
176        }
177    }
178
179    #[doc(alias = "cairo_user_font_face_set_text_to_glyphs_func")]
180    pub fn set_text_to_glyphs_func<F>(&self, func: F)
181    where
182        F: Fn(&ScaledFont, &str) -> Result<(Vec<Glyph>, Vec<TextCluster>, TextClusterFlags), Error>
183            + Send
184            + Sync
185            + 'static,
186    {
187        static TEXT_TO_GLYPHS_FUNC: OnceLock<BoxTextToGlyphsFunc> = OnceLock::new();
188        if TEXT_TO_GLYPHS_FUNC.set(Box::new(func)).is_err() {
189            panic!("TextToGlyphs func can only be set once")
190        }
191        unsafe extern "C" fn text_to_glyphs_trampoline(
192            scaled_font: *mut ffi::cairo_scaled_font_t,
193            utf8: *const libc::c_char,
194            utf8_len: libc::c_int,
195            glyphs: *mut *mut ffi::cairo_glyph_t,
196            num_glyphs: *mut libc::c_int,
197            clusters: *mut *mut ffi::cairo_text_cluster_t,
198            num_clusters: *mut libc::c_int,
199            cluster_flags: *mut ffi::cairo_text_cluster_flags_t,
200        ) -> ffi::cairo_status_t {
201            unsafe {
202                let text_to_glyphs_func = TEXT_TO_GLYPHS_FUNC.get().unwrap();
203                let text = if utf8_len > 0 {
204                    let bytes = std::slice::from_raw_parts(utf8 as *const u8, utf8_len as usize);
205                    std::str::from_utf8_unchecked(bytes)
206                } else {
207                    std::ffi::CStr::from_ptr(utf8).to_str().unwrap()
208                };
209                match text_to_glyphs_func(&ScaledFont::from_raw_none(scaled_font), text) {
210                    Err(err) => err.into(),
211                    Ok((glyphs_, clusters_, flags)) => {
212                        *num_glyphs = glyphs_.len() as _;
213                        let c_glyphs = ffi::cairo_glyph_allocate(*num_glyphs);
214                        std::ptr::copy_nonoverlapping(
215                            glyphs_.as_ptr(),
216                            c_glyphs as *mut _,
217                            glyphs_.len(),
218                        );
219                        *glyphs = c_glyphs;
220
221                        *num_clusters = clusters_.len() as _;
222                        let c_clusters = ffi::cairo_text_cluster_allocate(*num_clusters);
223                        std::ptr::copy_nonoverlapping(
224                            clusters_.as_ptr(),
225                            c_clusters as *mut _,
226                            clusters_.len(),
227                        );
228                        *clusters = c_clusters;
229
230                        *cluster_flags = flags.into();
231
232                        ffi::STATUS_SUCCESS
233                    }
234                }
235            }
236        }
237        unsafe {
238            ffi::cairo_user_font_face_set_text_to_glyphs_func(
239                self.to_raw_none(),
240                Some(text_to_glyphs_trampoline),
241            );
242        }
243    }
244}
245
246impl std::ops::Deref for UserFontFace {
247    type Target = FontFace;
248
249    #[inline]
250    fn deref(&self) -> &Self::Target {
251        &self.0
252    }
253}