1use 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}