skia_safe/core/
font.rs

1use std::{fmt, ptr};
2
3use skia_bindings::{self as sb, SkFont, SkFont_PrivFlags};
4
5use crate::{
6    interop::VecSink, prelude::*, scalar, EncodedText, FontHinting, FontMetrics, GlyphId, Paint,
7    Path, Point, Rect, Typeface, Unichar,
8};
9
10pub use skia_bindings::SkFont_Edging as Edging;
11variant_name!(Edging::Alias);
12
13pub type Font = Handle<SkFont>;
14unsafe_send_sync!(Font);
15
16impl NativeDrop for SkFont {
17    fn drop(&mut self) {
18        unsafe { sb::C_SkFont_destruct(self) }
19    }
20}
21
22impl NativeClone for SkFont {
23    fn clone(&self) -> Self {
24        construct(|f| unsafe { sb::C_SkFont_CopyConstruct(f, self) })
25    }
26}
27
28impl NativePartialEq for SkFont {
29    fn eq(&self, rhs: &Self) -> bool {
30        unsafe { sb::C_SkFont_Equals(self, rhs) }
31    }
32}
33
34impl Default for Font {
35    fn default() -> Self {
36        Self::from_native_c(unsafe { SkFont::new() })
37    }
38}
39
40impl fmt::Debug for Font {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        f.debug_struct("Font")
43            .field("is_force_auto_hinting", &self.is_force_auto_hinting())
44            .field("is_embedded_bitmaps", &self.is_embedded_bitmaps())
45            .field("is_subpixel", &self.is_subpixel())
46            .field("is_linear_metrics", &self.is_linear_metrics())
47            .field("is_embolden", &self.is_embolden())
48            .field("is_baseline_snap", &self.is_baseline_snap())
49            .field("edging", &self.edging())
50            .field("hinting", &self.hinting())
51            .field("typeface", &self.typeface())
52            .field("size", &self.size())
53            .field("scale_x", &self.scale_x())
54            .field("skew_x", &self.skew_x())
55            .field("metrics", &self.metrics())
56            .field("spacing", &self.spacing())
57            .finish()
58    }
59}
60
61impl Font {
62    pub fn new(typeface: impl Into<Typeface>, size: impl Into<Option<scalar>>) -> Self {
63        Self::from_typeface(typeface, size)
64    }
65
66    pub fn from_typeface(typeface: impl Into<Typeface>, size: impl Into<Option<scalar>>) -> Self {
67        match size.into() {
68            None => Self::construct(|font| unsafe {
69                sb::C_SkFont_ConstructFromTypeface(font, typeface.into().into_ptr())
70            }),
71            Some(size) => Self::construct(|font| unsafe {
72                sb::C_SkFont_ConstructFromTypefaceWithSize(font, typeface.into().into_ptr(), size)
73            }),
74        }
75    }
76
77    pub fn from_typeface_with_params(
78        typeface: impl Into<Typeface>,
79        size: scalar,
80        scale: scalar,
81        skew: scalar,
82    ) -> Self {
83        Self::construct(|font| unsafe {
84            sb::C_SkFont_ConstructFromTypefaceWithSizeScaleAndSkew(
85                font,
86                typeface.into().into_ptr(),
87                size,
88                scale,
89                skew,
90            )
91        })
92    }
93
94    pub fn is_force_auto_hinting(&self) -> bool {
95        self.has_flag(sb::SkFont_PrivFlags_kForceAutoHinting_PrivFlag)
96    }
97
98    pub fn is_embedded_bitmaps(&self) -> bool {
99        self.has_flag(sb::SkFont_PrivFlags_kEmbeddedBitmaps_PrivFlag)
100    }
101
102    pub fn is_subpixel(&self) -> bool {
103        self.has_flag(sb::SkFont_PrivFlags_kSubpixel_PrivFlag)
104    }
105
106    pub fn is_linear_metrics(&self) -> bool {
107        self.has_flag(sb::SkFont_PrivFlags_kLinearMetrics_PrivFlag)
108    }
109
110    pub fn is_embolden(&self) -> bool {
111        self.has_flag(sb::SkFont_PrivFlags_kEmbolden_PrivFlag)
112    }
113
114    pub fn is_baseline_snap(&self) -> bool {
115        self.has_flag(sb::SkFont_PrivFlags_kBaselineSnap_PrivFlag)
116    }
117
118    fn has_flag(&self, flag: SkFont_PrivFlags) -> bool {
119        (SkFont_PrivFlags::from(self.native().fFlags) & flag) != 0
120    }
121
122    pub fn set_force_auto_hinting(&mut self, force_auto_hinting: bool) -> &mut Self {
123        unsafe { self.native_mut().setForceAutoHinting(force_auto_hinting) }
124        self
125    }
126
127    pub fn set_embedded_bitmaps(&mut self, embedded_bitmaps: bool) -> &mut Self {
128        unsafe { self.native_mut().setEmbeddedBitmaps(embedded_bitmaps) }
129        self
130    }
131
132    pub fn set_subpixel(&mut self, subpixel: bool) -> &mut Self {
133        unsafe { self.native_mut().setSubpixel(subpixel) }
134        self
135    }
136
137    pub fn set_linear_metrics(&mut self, linear_metrics: bool) -> &mut Self {
138        unsafe { self.native_mut().setLinearMetrics(linear_metrics) }
139        self
140    }
141
142    pub fn set_embolden(&mut self, embolden: bool) -> &mut Self {
143        unsafe { self.native_mut().setEmbolden(embolden) }
144        self
145    }
146
147    pub fn set_baseline_snap(&mut self, baseline_snap: bool) -> &mut Self {
148        unsafe { self.native_mut().setBaselineSnap(baseline_snap) }
149        self
150    }
151
152    pub fn edging(&self) -> Edging {
153        unsafe { sb::C_SkFont_getEdging(self.native()) }
154    }
155
156    pub fn set_edging(&mut self, edging: Edging) -> &mut Self {
157        unsafe { self.native_mut().setEdging(edging) }
158        self
159    }
160
161    pub fn set_hinting(&mut self, hinting: FontHinting) -> &mut Self {
162        unsafe { self.native_mut().setHinting(hinting) }
163        self
164    }
165
166    pub fn hinting(&self) -> FontHinting {
167        unsafe { sb::C_SkFont_getHinting(self.native()) }
168    }
169
170    #[must_use]
171    pub fn with_size(&self, size: scalar) -> Option<Self> {
172        if size >= 0.0 && !size.is_infinite() && !size.is_nan() {
173            let mut font = unsafe { SkFont::new() };
174            unsafe { sb::C_SkFont_makeWithSize(self.native(), size, &mut font) }
175            Some(Self::from_native_c(font))
176        } else {
177            None
178        }
179    }
180
181    pub fn typeface(&self) -> Typeface {
182        Typeface::from_unshared_ptr(self.native().fTypeface.fPtr)
183            .expect("typeface is expected to be non-null")
184    }
185
186    pub fn size(&self) -> scalar {
187        self.native().fSize
188    }
189
190    pub fn scale_x(&self) -> scalar {
191        self.native().fScaleX
192    }
193
194    pub fn skew_x(&self) -> scalar {
195        self.native().fSkewX
196    }
197
198    pub fn set_typeface(&mut self, tf: impl Into<Typeface>) -> &mut Self {
199        unsafe { sb::C_SkFont_setTypeface(self.native_mut(), tf.into().into_ptr()) }
200        self
201    }
202
203    pub fn set_size(&mut self, size: scalar) -> &mut Self {
204        unsafe { self.native_mut().setSize(size) }
205        self
206    }
207
208    pub fn set_scale_x(&mut self, scale_x: scalar) -> &mut Self {
209        unsafe { self.native_mut().setScaleX(scale_x) }
210        self
211    }
212
213    pub fn set_skew_x(&mut self, skew_x: scalar) -> &mut Self {
214        unsafe { self.native_mut().setSkewX(skew_x) }
215        self
216    }
217
218    pub fn str_to_glyphs(&self, str: impl AsRef<str>, glyphs: &mut [GlyphId]) -> usize {
219        self.text_to_glyphs(str.as_ref(), glyphs)
220    }
221
222    pub fn text_to_glyphs(&self, text: impl EncodedText, glyphs: &mut [GlyphId]) -> usize {
223        let (ptr, size, encoding) = text.as_raw();
224        unsafe {
225            sb::C_SkFont_textToGlyphs(
226                self.native(),
227                ptr,
228                size,
229                encoding.into_native(),
230                glyphs.as_mut_ptr(),
231                glyphs.len(),
232            )
233        }
234    }
235
236    pub fn count_str(&self, str: impl AsRef<str>) -> usize {
237        self.count_text(str.as_ref())
238    }
239
240    pub fn count_text(&self, text: impl EncodedText) -> usize {
241        let (ptr, size, encoding) = text.as_raw();
242        unsafe {
243            sb::C_SkFont_textToGlyphs(
244                self.native(),
245                ptr,
246                size,
247                encoding.into_native(),
248                ptr::null_mut(),
249                0usize,
250            )
251        }
252    }
253
254    // convenience function
255    pub fn str_to_glyphs_vec(&self, str: impl AsRef<str>) -> Vec<GlyphId> {
256        self.text_to_glyphs_vec(str.as_ref())
257    }
258
259    // convenience function
260    pub fn text_to_glyphs_vec(&self, text: impl EncodedText) -> Vec<GlyphId> {
261        let count = self.count_text(&text);
262        let mut glyphs: Vec<GlyphId> = vec![Default::default(); count];
263        let resulting_count = self.text_to_glyphs(text, glyphs.as_mut_slice());
264        assert_eq!(count, resulting_count);
265        glyphs
266    }
267
268    pub fn measure_str(&self, str: impl AsRef<str>, paint: Option<&Paint>) -> (scalar, Rect) {
269        self.measure_text(str.as_ref(), paint)
270    }
271
272    pub fn measure_text(&self, text: impl EncodedText, paint: Option<&Paint>) -> (scalar, Rect) {
273        let mut bounds = Rect::default();
274        let (ptr, size, encoding) = text.as_raw();
275        let width = unsafe {
276            self.native().measureText(
277                ptr,
278                size,
279                encoding.into_native(),
280                bounds.native_mut(),
281                paint.native_ptr_or_null(),
282            )
283        };
284
285        (width, bounds)
286    }
287
288    pub fn unichar_to_glyph(&self, uni: Unichar) -> GlyphId {
289        unsafe { self.native().unicharToGlyph(uni) }
290    }
291
292    pub fn unichar_to_glyphs(&self, uni: &[Unichar], glyphs: &mut [GlyphId]) {
293        assert_eq!(uni.len(), glyphs.len());
294        unsafe {
295            sb::C_SkFont_unicharsToGlyphs(
296                self.native(),
297                uni.as_ptr(),
298                uni.len(),
299                glyphs.as_mut_ptr(),
300            )
301        }
302    }
303
304    pub fn get_widths(&self, glyphs: &[GlyphId], widths: &mut [scalar]) {
305        self.get_widths_bounds(glyphs, Some(widths), None, None)
306    }
307
308    pub fn get_widths_bounds(
309        &self,
310        glyphs: &[GlyphId],
311        mut widths: Option<&mut [scalar]>,
312        mut bounds: Option<&mut [Rect]>,
313        paint: Option<&Paint>,
314    ) {
315        let count = glyphs.len();
316
317        {
318            if let Some(slice) = &widths {
319                assert_eq!(count, slice.len())
320            };
321            if let Some(slice) = &bounds {
322                assert_eq!(count, slice.len())
323            };
324        }
325
326        let bounds_ptr = bounds.native_mut().as_ptr_or_null_mut();
327        let widths_ptr = widths.as_ptr_or_null_mut();
328        let paint_ptr = paint.native_ptr_or_null();
329
330        unsafe {
331            sb::C_SkFont_getWidthBounds(
332                self.native(),
333                glyphs.as_ptr(),
334                count,
335                widths_ptr,
336                bounds_ptr,
337                paint_ptr,
338            )
339        }
340    }
341
342    pub fn get_bounds(&self, glyphs: &[GlyphId], bounds: &mut [Rect], paint: Option<&Paint>) {
343        self.get_widths_bounds(glyphs, None, Some(bounds), paint)
344    }
345
346    pub fn get_pos(&self, glyphs: &[GlyphId], pos: &mut [Point], origin: Option<Point>) {
347        let count = glyphs.len();
348        assert_eq!(count, pos.len());
349
350        let origin = origin.unwrap_or_default();
351
352        unsafe {
353            sb::C_SkFont_getPos(
354                self.native(),
355                glyphs.as_ptr(),
356                count,
357                pos.native_mut().as_mut_ptr(),
358                *origin.native(),
359            )
360        }
361    }
362
363    pub fn get_x_pos(&self, glyphs: &[GlyphId], x_pos: &mut [scalar], origin: Option<scalar>) {
364        let count = glyphs.len();
365        assert_eq!(count, x_pos.len());
366        let origin = origin.unwrap_or_default();
367
368        unsafe {
369            sb::C_SkFont_getXPos(
370                self.native(),
371                glyphs.as_ptr(),
372                count,
373                x_pos.as_mut_ptr(),
374                origin,
375            )
376        }
377    }
378
379    pub fn get_intercepts<'a>(
380        &self,
381        glyphs: &[GlyphId],
382        pos: &[Point],
383        (top, bottom): (scalar, scalar),
384        paint: impl Into<Option<&'a Paint>>,
385    ) -> Vec<scalar> {
386        assert_eq!(glyphs.len(), pos.len());
387        let count = glyphs.len();
388        let mut r: Vec<scalar> = Vec::new();
389        let mut set = |scalars: &[scalar]| r = scalars.to_vec();
390        unsafe {
391            sb::C_SkFont_getIntercepts(
392                self.native(),
393                glyphs.as_ptr(),
394                count,
395                pos.native().as_ptr(),
396                top,
397                bottom,
398                paint.into().native_ptr_or_null(),
399                VecSink::new(&mut set).native_mut(),
400            );
401        }
402        r
403    }
404
405    pub fn get_path(&self, glyph_id: GlyphId) -> Option<Path> {
406        let mut path = Path::default();
407        unsafe { sb::C_SkFont_getPath(self.native(), glyph_id, path.native_mut()) }.then_some(path)
408    }
409
410    // TODO: getPaths() (needs a function to be passed, but supports a context).
411
412    pub fn metrics(&self) -> (scalar, FontMetrics) {
413        let mut line_spacing = 0.0;
414        let fm =
415            FontMetrics::construct(|fm| line_spacing = unsafe { self.native().getMetrics(fm) });
416        (line_spacing, fm)
417    }
418
419    pub fn spacing(&self) -> scalar {
420        unsafe { self.native().getMetrics(ptr::null_mut()) }
421    }
422}
423
424#[cfg(test)]
425mod tests {
426    use crate::{FontMgr, FontStyle};
427
428    use super::*;
429
430    #[test]
431    fn test_flags() {
432        let font_mgr = FontMgr::new();
433        let typeface = font_mgr
434            .legacy_make_typeface(None, FontStyle::normal())
435            .unwrap();
436        let mut font = Font::new(typeface, 10.0);
437
438        font.set_force_auto_hinting(true);
439        assert!(font.is_force_auto_hinting());
440        font.set_force_auto_hinting(false);
441        assert!(!font.is_force_auto_hinting());
442
443        font.set_embolden(true);
444        assert!(font.is_embolden());
445        font.set_embolden(false);
446        assert!(!font.is_embolden());
447    }
448}