mkwebfont_hb-subset 0.5.0

A wrapper for HarfBuzz font subsetting API
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
use std::{ffi::c_char, marker::PhantomData, ops::Deref, ptr::null_mut};

use crate::{
    map::Map, set::CharSet, sys, AllocationError, Blob, FontFaceExtractionError, Language,
};

/// A font face is an object that represents a single face from within a font family.
///
/// More precisely, a font face represents a single face in a binary font file. Font faces are typically built from a
/// binary blob and a face index. Font faces are used to create fonts.
#[repr(transparent)]
pub struct FontFace<'a>(*mut sys::hb_face_t, PhantomData<Blob<'a>>);

impl<'a> FontFace<'a> {
    /// Constructs a new face object from the specified blob.
    ///
    /// This defaults to taking the first face in the blob. If you need to specify which font face to load, you can use
    /// [`new_with_index`] instead.
    ///
    /// [`new_with_index`]: Self::new_with_index
    #[doc(alias = "hb_face_create")]
    pub fn new(blob: Blob<'a>) -> Result<Self, FontFaceExtractionError> {
        Self::new_with_index(blob, 0)
    }

    /// Constructs a new face object from the specified blob and a face index into that blob.
    ///
    /// The face index is used for blobs of file formats such as TTC and DFont that can contain more than one face. Face
    /// indices within such collections are zero-based.
    #[doc(alias = "hb_face_create")]
    pub fn new_with_index(blob: Blob<'a>, index: u32) -> Result<Self, FontFaceExtractionError> {
        let face = unsafe { sys::hb_face_create(blob.as_raw(), index) };
        if face.is_null() {
            return Err(FontFaceExtractionError);
        }
        Ok(Self(face, PhantomData))
    }

    /// Gets the blob underlying this font face.
    ///
    /// Useful when you want to output the font face to a file.
    ///
    /// Returns an empty blob if referencing face data is not possible.
    #[doc(alias = "hb_face_reference_blob")]
    pub fn underlying_blob(&self) -> Blob<'_> {
        unsafe { Blob::from_raw(sys::hb_face_reference_blob(self.as_raw())) }
    }

    /// Fetches the glyph-count value of the specified face object.
    #[doc(alias = "hb_face_get_glyph_count")]
    pub fn glyph_count(&self) -> usize {
        (unsafe { sys::hb_face_get_glyph_count(self.as_raw()) }) as usize
    }

    /// Collects all of the Unicode characters covered by the font face.
    #[doc(alias = "hb_face_collect_unicodes")]
    pub fn covered_codepoints(&self) -> Result<CharSet, AllocationError> {
        let set = CharSet::new()?;
        unsafe { sys::hb_face_collect_unicodes(self.as_raw(), set.as_raw()) };
        Ok(set)
    }

    /// Collects the mapping from Unicode characters to nominal glyphs of the face.
    #[doc(alias = "hb_face_collect_nominal_glyph_mapping")]
    pub fn nominal_glyph_mapping(&self) -> Result<Map<'static, char, u32>, AllocationError> {
        let map = Map::new()?;
        unsafe {
            sys::hb_face_collect_nominal_glyph_mapping(self.as_raw(), map.as_raw(), null_mut())
        };
        Ok(map)
    }

    /// Preprocesses the face and attaches data that will be needed by the subsetter.
    ///
    /// Future subsetting operations can use the precomputed data to speed up the subsetting operation. The
    /// preprocessing operation may take longer than the time it takes to produce a subset from the source font. Thus
    /// the main performance gains are made when a preprocessed face is reused for multiple subsetting operations.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn subsets() -> impl IntoIterator<Item = SubsetInput> { [SubsetInput::new().unwrap()] }
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// let processed = font.preprocess_for_subsetting();
    /// for subset in subsets() {
    ///     subset.subset_font(&processed)?;
    /// }
    /// # Ok(())
    /// # }
    /// ```
    pub fn preprocess_for_subsetting(&self) -> PreprocessedFontFace<'a> {
        PreprocessedFontFace(unsafe { sys::hb_subset_preprocess(self.0) }, PhantomData)
    }
}

impl<'a> FontFace<'a> {
    /// Converts the font face into raw [`sys::hb_face_t`] pointer.
    ///
    /// This method transfers the ownership of the font face to the caller. It is up to the caller to call
    /// [`sys::hb_face_destroy`] to free the pointer, or call [`Self::from_raw`] to convert it back into [`FontFace`].
    pub fn into_raw(self) -> *mut sys::hb_face_t {
        let ptr = self.0;
        std::mem::forget(self);
        ptr
    }

    /// Exposes the raw inner pointer without transferring the ownership.
    ///
    /// Unlike [`Self::into_raw`], this method does not transfer the ownership of the pointer to the caller.
    pub fn as_raw(&self) -> *mut sys::hb_face_t {
        self.0
    }

    /// Constructs a font face from raw [`sys::hb_face_t`] pointer.
    ///
    /// # Safety
    /// The given `font_face` pointer must either be constructed by some Harfbuzz function, or be returned from
    /// [`Self::into_raw`].
    pub unsafe fn from_raw(font_face: *mut sys::hb_face_t) -> Self {
        Self(font_face, PhantomData)
    }
}

/// Functions for fetching name strings from OpenType fonts.
///
/// See [OpenType spec](https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids) for more information
/// on these strings.
impl<'a> FontFace<'a> {
    /// Gets value from OpenType name table for given language.
    ///
    /// Instead of using this method directly, consider using one of the convenience methods for getting the correct
    /// string directly.
    ///
    /// If `language` is `null()`, English is assumed.
    #[doc(alias = "hb_ot_name_get_utf8")]
    #[doc(alias = "hb_ot_name_get_utf16")]
    #[doc(alias = "hb_ot_name_get_utf32")]
    pub fn ot_name(&self, name: impl Into<sys::hb_ot_name_id_t>, language: Language) -> String {
        let name = name.into();
        let mut len = unsafe {
            sys::hb_ot_name_get_utf8(self.as_raw(), name, language.as_raw(), null_mut(), null_mut())
        };
        len += 1; // Reserve space for NUL termination
        let mut buf = vec![0; len as usize];
        let full_len = unsafe {
            sys::hb_ot_name_get_utf8(
                self.as_raw(),
                name,
                language.as_raw(),
                &mut len as *mut u32,
                buf.as_mut_ptr() as *mut c_char,
            )
        };
        assert!(len <= full_len);
        buf.truncate(len as usize);

        String::from_utf8(buf).expect("Output is promised to be valid UTF-8")
    }

    /// Gets copyright notice.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.copyright(), "Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic)");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_COPYRIGHT")]
    pub fn copyright(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::COPYRIGHT, Language::default())
    }

    /// Gets font family name.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.font_family(), "Noto Sans");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_FONT_FAMILY")]
    pub fn font_family(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::FONT_FAMILY, Language::default())
    }

    /// Gets font subfamily name.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.font_subfamily(), "Regular");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_FONT_SUBFAMILY")]
    pub fn font_subfamily(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::FONT_SUBFAMILY, Language::default())
    }

    /// Gets unique font identifier.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.unique_id(), "2.013;GOOG;NotoSans-Regular");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_UNIQUE_ID")]
    pub fn unique_id(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::UNIQUE_ID, Language::default())
    }

    /// Gets full font name that reflects all family and relevant subfamily descriptors.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.full_name(), "Noto Sans Regular");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_FULL_NAME")]
    pub fn full_name(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::FULL_NAME, Language::default())
    }

    /// Gets version string.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.version_string(), "Version 2.013; ttfautohint (v1.8.4.7-5d5b)");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_VERSION_STRING")]
    pub fn version_string(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::VERSION_STRING, Language::default())
    }

    /// Gets PostScript name for the font.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.postscript_name(), "NotoSans-Regular");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_POSTSCRIPT_NAME")]
    pub fn postscript_name(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::POSTSCRIPT_NAME, Language::default())
    }

    /// Gets trademark information.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.trademark(), "Noto is a trademark of Google LLC.");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_TRADEMARK")]
    pub fn trademark(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::TRADEMARK, Language::default())
    }

    /// Gets manufacturer name.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.manufacturer(), "Monotype Imaging Inc.");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_MANUFACTURER")]
    pub fn manufacturer(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::MANUFACTURER, Language::default())
    }

    /// Gets designer name.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.designer(), "Monotype Design Team");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_DESIGNER")]
    pub fn designer(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::DESIGNER, Language::default())
    }

    /// Gets description.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.description(), "Designed by Monotype design team, Irene Vlachou.");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_DESCRIPTION")]
    pub fn description(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::DESCRIPTION, Language::default())
    }

    /// Gets URL of font vendor.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.vendor_url(), "http://www.google.com/get/noto/");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_VENDOR_URL")]
    pub fn vendor_url(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::VENDOR_URL, Language::default())
    }

    /// Gets URL of typeface designer.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.designer_url(), "http://www.monotype.com/studio");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_DESIGNER_URL")]
    pub fn designer_url(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::DESIGNER_URL, Language::default())
    }

    /// Gets license description.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.license(), "This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: https://scripts.sil.org/OFL");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_LICENSE")]
    pub fn license(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::LICENSE, Language::default())
    }

    /// Gets URL where additional licensing information can be found.
    ///
    /// # Example
    /// ```
    /// # use hb_subset::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let font = FontFace::new(Blob::from_file("tests/fonts/NotoSans.ttf")?)?;
    /// assert_eq!(font.license_url(), "https://scripts.sil.org/OFL");
    /// # Ok(())
    /// # }
    /// ```
    #[doc(alias = "HB_OT_NAME_ID_LICENSE_URL")]
    pub fn license_url(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::LICENSE_URL, Language::default())
    }

    /// Gets typographic family name.
    #[doc(alias = "HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY")]
    pub fn typographic_family(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::TYPOGRAPHIC_FAMILY, Language::default())
    }

    /// Gets typographic subfamily name.
    #[doc(alias = "HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY")]
    pub fn typographic_subfamily(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::TYPOGRAPHIC_SUBFAMILY, Language::default())
    }

    /// Gets compatible full name for MacOS.
    #[doc(alias = "HB_OT_NAME_ID_MAC_FULL_NAME")]
    pub fn mac_full_name(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::MAC_FULL_NAME, Language::default())
    }

    /// Gets sample text.
    #[doc(alias = "HB_OT_NAME_ID_SAMPLE_TEXT")]
    pub fn sample_text(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::SAMPLE_TEXT, Language::default())
    }

    /// Gets PostScript CID findfont name.
    #[doc(alias = "HB_OT_NAME_ID_CID_FINDFONT_NAME")]
    pub fn cid_findfont_name(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::CID_FINDFONT_NAME, Language::default())
    }

    /// Gets WWS family Name.
    #[doc(alias = "HB_OT_NAME_ID_WWS_FAMILY")]
    pub fn wws_family(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::WWS_FAMILY, Language::default())
    }

    /// Gets WWS subfamily Name.
    #[doc(alias = "HB_OT_NAME_ID_WWS_SUBFAMILY")]
    pub fn wws_subfamily(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::WWS_SUBFAMILY, Language::default())
    }

    /// Gets light background palette.
    #[doc(alias = "HB_OT_NAME_ID_LIGHT_BACKGROUND")]
    pub fn light_background(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::LIGHT_BACKGROUND, Language::default())
    }

    /// Gets dark background palette.
    #[doc(alias = "HB_OT_NAME_ID_DARK_BACKGROUND")]
    pub fn dark_background(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::DARK_BACKGROUND, Language::default())
    }

    /// Gets variations PostScript name prefix.
    #[doc(alias = "HB_OT_NAME_ID_VARIATIONS_PS_PREFIX")]
    pub fn variations_ps_prefix(&self) -> String {
        self.ot_name(sys::hb_ot_name_id_predefined_t::VARIATIONS_PS_PREFIX, Language::default())
    }
}

impl<'a> Drop for FontFace<'a> {
    #[doc(alias = "hb_face_destroy")]
    fn drop(&mut self) {
        unsafe { sys::hb_face_destroy(self.0) }
    }
}

/// Font face that has been preprocessed for subsetting.
///
/// See [FontFace::preprocess_for_subsetting()].
#[repr(transparent)]
pub struct PreprocessedFontFace<'a>(*mut sys::hb_face_t, PhantomData<Blob<'a>>);

impl<'a> Deref for PreprocessedFontFace<'a> {
    type Target = FontFace<'a>;

    fn deref(&self) -> &Self::Target {
        unsafe { std::mem::transmute(self) }
    }
}

impl<'a> Drop for PreprocessedFontFace<'a> {
    #[doc(alias = "hb_face_destroy")]
    fn drop(&mut self) {
        unsafe { sys::hb_face_destroy(self.0) }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::NOTO_SANS;

    #[test]
    fn loaded_font_contains_correct_number_of_codepoints_and_glyphs() {
        let font_face = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
        assert_eq!(font_face.covered_codepoints().unwrap().len(), 3094);
        assert_eq!(font_face.glyph_count(), 4671);
    }

    #[test]
    fn underlying_blob_works() {
        let blob = Blob::from_file(NOTO_SANS).unwrap();
        let font_face = FontFace::new(blob.clone()).unwrap();
        assert_eq!(&*font_face.underlying_blob(), &*blob);
    }

    #[test]
    fn nominal_glyph_mapping_works() {
        let font_face = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
        let map = font_face.nominal_glyph_mapping().unwrap();
        assert_eq!(map.get('a').unwrap(), 68);
        assert_eq!(map.get('b').unwrap(), 69);
        assert_eq!(map.get('c').unwrap(), 70);
        assert_eq!(map.get('d').unwrap(), 71);
        assert_eq!(map.get('e').unwrap(), 72);
        assert_eq!(map.get('f').unwrap(), 73);
        assert_eq!(map.get('i').unwrap(), 76);
        assert_eq!(map.get('').unwrap(), 1656);
    }

    #[test]
    fn convert_into_raw_and_back() {
        let font_face = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
        let font_face_ptr = font_face.into_raw();
        let font_face = unsafe { FontFace::from_raw(font_face_ptr) };
        drop(font_face);
    }
}