Skip to main content

pdfium_render/pdf/
font.rs

1//! Defines the [PdfFont] struct, exposing functionality related to a single font used to
2//! render text in a `PdfDocument`.
3
4pub mod glyph;
5pub mod glyphs;
6pub mod provider;
7
8use crate::bindgen::{
9    FPDF_FONT, FXFONT_ANSI_CHARSET, FXFONT_ARABIC_CHARSET, FXFONT_CHINESEBIG5_CHARSET,
10    FXFONT_CYRILLIC_CHARSET, FXFONT_DEFAULT_CHARSET, FXFONT_EASTERNEUROPEAN_CHARSET,
11    FXFONT_GB2312_CHARSET, FXFONT_GREEK_CHARSET, FXFONT_HANGEUL_CHARSET, FXFONT_HEBREW_CHARSET,
12    FXFONT_SHIFTJIS_CHARSET, FXFONT_SYMBOL_CHARSET, FXFONT_THAI_CHARSET, FXFONT_VIETNAMESE_CHARSET,
13};
14use crate::error::{PdfiumError, PdfiumInternalError};
15use crate::pdf::document::fonts::PdfFontBuiltin;
16use crate::pdf::font::glyphs::PdfFontGlyphs;
17use crate::pdf::points::PdfPoints;
18use crate::pdfium::PdfiumLibraryBindingsAccessor;
19use crate::utils::mem::create_byte_buffer;
20use bitflags::bitflags;
21use std::marker::PhantomData;
22use std::os::raw::{c_char, c_int};
23
24#[cfg(doc)]
25use crate::pdf::document::PdfDocument;
26
27// The following dummy declaration is used only when running cargo doc.
28// It allows documentation of WASM-specific functionality to be included
29// in documentation generated on non-WASM targets.
30
31#[cfg(doc)]
32struct Blob;
33
34bitflags! {
35    pub(crate) struct FpdfFontDescriptorFlags: u32 {
36        const FIXED_PITCH_BIT_1 =  0b00000000000000000000000000000001;
37        const SERIF_BIT_2 =        0b00000000000000000000000000000010;
38        const SYMBOLIC_BIT_3 =     0b00000000000000000000000000000100;
39        const SCRIPT_BIT_4 =       0b00000000000000000000000000001000;
40        const NON_SYMBOLIC_BIT_6 = 0b00000000000000000000000000100000;
41        const ITALIC_BIT_7 =       0b00000000000000000000000001000000;
42        const ALL_CAP_BIT_17 =     0b00000000000000010000000000000000;
43        const SMALL_CAP_BIT_18 =   0b00000000000000100000000000000000;
44        const FORCE_BOLD_BIT_19 =  0b00000000000001000000000000000000;
45    }
46}
47
48/// The weight of a [PdfFont]. Typical values are 400 (normal) and 700 (bold).
49#[derive(Copy, Clone, Debug, PartialEq)]
50pub enum PdfFontWeight {
51    Weight100,
52    Weight200,
53    Weight300,
54    Weight400Normal,
55    Weight500,
56    Weight600,
57    Weight700Bold,
58    Weight800,
59    Weight900,
60
61    /// Any font weight value that falls outside the typical 100 - 900 value range.
62    Custom(u32),
63}
64
65impl PdfFontWeight {
66    pub(crate) fn from_pdfium(value: c_int) -> Option<PdfFontWeight> {
67        match value {
68            -1 => None,
69            100 => Some(PdfFontWeight::Weight100),
70            200 => Some(PdfFontWeight::Weight200),
71            300 => Some(PdfFontWeight::Weight300),
72            400 => Some(PdfFontWeight::Weight400Normal),
73            500 => Some(PdfFontWeight::Weight500),
74            600 => Some(PdfFontWeight::Weight600),
75            700 => Some(PdfFontWeight::Weight700Bold),
76            800 => Some(PdfFontWeight::Weight800),
77            900 => Some(PdfFontWeight::Weight900),
78            other => Some(PdfFontWeight::Custom(other as u32)),
79        }
80    }
81}
82
83/// The character set of a [PdfFont].
84pub enum PdfFontCharacterSet {
85    Ansi,
86    Default,
87    Symbol,
88    JapaneseShiftJis,
89    KoreanHangul,
90    ChineseGb2312,
91    ChineseBig5,
92    Greek,
93    Vietnamese,
94    Hebrew,
95    Arabic,
96    Cyrillic,
97    Thai,
98    EasternEuropean,
99}
100
101impl PdfFontCharacterSet {
102    pub(crate) fn from_pdfium(value: c_int) -> Option<PdfFontCharacterSet> {
103        match value as u32 {
104            FXFONT_ANSI_CHARSET => Some(PdfFontCharacterSet::Ansi),
105            FXFONT_DEFAULT_CHARSET => Some(PdfFontCharacterSet::Default),
106            FXFONT_SYMBOL_CHARSET => Some(PdfFontCharacterSet::Symbol),
107            FXFONT_SHIFTJIS_CHARSET => Some(PdfFontCharacterSet::JapaneseShiftJis),
108            FXFONT_HANGEUL_CHARSET => Some(PdfFontCharacterSet::KoreanHangul),
109            FXFONT_GB2312_CHARSET => Some(PdfFontCharacterSet::ChineseGb2312),
110            FXFONT_CHINESEBIG5_CHARSET => Some(PdfFontCharacterSet::ChineseBig5),
111            FXFONT_GREEK_CHARSET => Some(PdfFontCharacterSet::Greek),
112            FXFONT_VIETNAMESE_CHARSET => Some(PdfFontCharacterSet::Vietnamese),
113            FXFONT_HEBREW_CHARSET => Some(PdfFontCharacterSet::Hebrew),
114            FXFONT_ARABIC_CHARSET => Some(PdfFontCharacterSet::Arabic),
115            FXFONT_CYRILLIC_CHARSET => Some(PdfFontCharacterSet::Cyrillic),
116            FXFONT_THAI_CHARSET => Some(PdfFontCharacterSet::Thai),
117            FXFONT_EASTERNEUROPEAN_CHARSET => Some(PdfFontCharacterSet::EasternEuropean),
118            _ => None,
119        }
120    }
121
122    pub(crate) fn as_pdfium(&self) -> c_int {
123        (match self {
124            PdfFontCharacterSet::Ansi => FXFONT_ANSI_CHARSET,
125            PdfFontCharacterSet::Default => FXFONT_DEFAULT_CHARSET,
126            PdfFontCharacterSet::Symbol => FXFONT_SYMBOL_CHARSET,
127            PdfFontCharacterSet::JapaneseShiftJis => FXFONT_SHIFTJIS_CHARSET,
128            PdfFontCharacterSet::KoreanHangul => FXFONT_HANGEUL_CHARSET,
129            PdfFontCharacterSet::ChineseGb2312 => FXFONT_GB2312_CHARSET,
130            PdfFontCharacterSet::ChineseBig5 => FXFONT_CHINESEBIG5_CHARSET,
131            PdfFontCharacterSet::Greek => FXFONT_GREEK_CHARSET,
132            PdfFontCharacterSet::Vietnamese => FXFONT_VIETNAMESE_CHARSET,
133            PdfFontCharacterSet::Hebrew => FXFONT_HEBREW_CHARSET,
134            PdfFontCharacterSet::Arabic => FXFONT_ARABIC_CHARSET,
135            PdfFontCharacterSet::Cyrillic => FXFONT_CYRILLIC_CHARSET,
136            PdfFontCharacterSet::Thai => FXFONT_THAI_CHARSET,
137            PdfFontCharacterSet::EasternEuropean => FXFONT_EASTERNEUROPEAN_CHARSET,
138        }) as c_int
139    }
140}
141
142/// A single font used to render text in a [PdfDocument].
143///
144/// The PDF specification defines 14 built-in fonts that can be used in any PDF file without
145/// font embedding. Additionally, custom fonts can be directly embedded into any PDF file as
146/// a data stream.
147pub struct PdfFont<'a> {
148    built_in: Option<PdfFontBuiltin>,
149    handle: FPDF_FONT,
150    glyphs: PdfFontGlyphs<'a>,
151    is_font_memory_loaded: bool,
152    lifetime: PhantomData<&'a FPDF_FONT>,
153}
154
155impl<'a> PdfFont<'a> {
156    #[inline]
157    pub(crate) fn from_pdfium(
158        handle: FPDF_FONT,
159        built_in: Option<PdfFontBuiltin>,
160        is_font_memory_loaded: bool,
161    ) -> Self {
162        PdfFont {
163            built_in,
164            handle,
165            glyphs: PdfFontGlyphs::from_pdfium(handle),
166            is_font_memory_loaded,
167            lifetime: PhantomData,
168        }
169    }
170
171    /// Returns the internal `FPDF_FONT` handle for this [PdfFont].
172    #[inline]
173    pub(crate) fn handle(&self) -> FPDF_FONT {
174        self.handle
175    }
176
177    #[cfg(any(
178        feature = "pdfium_future",
179        feature = "pdfium_7763",
180        feature = "pdfium_7543",
181        feature = "pdfium_7350",
182        feature = "pdfium_7215",
183        feature = "pdfium_7123",
184        feature = "pdfium_6996",
185        feature = "pdfium_6721",
186        feature = "pdfium_6666"
187    ))]
188    /// Returns the name of this [PdfFont].
189    pub fn name(&self) -> String {
190        // Retrieving the font name from Pdfium is a two-step operation. First, we call
191        // FPDFFont_GetBaseFontName() with a null buffer; this will retrieve the length of
192        // the font name in bytes. If the length is zero, then there is no font name.
193
194        // If the length is non-zero, then we reserve a byte buffer of the given
195        // length and call FPDFFont_GetBaseFontName() again with a pointer to the buffer;
196        // this will write the font name into the buffer. Unlike most text handling in
197        // Pdfium, font names are returned in UTF-8 format.
198
199        let buffer_length = unsafe {
200            self.bindings()
201                .FPDFFont_GetBaseFontName(self.handle, std::ptr::null_mut(), 0)
202        };
203
204        if buffer_length == 0 {
205            // The font name is not present.
206
207            return String::new();
208        }
209
210        let mut buffer = create_byte_buffer(buffer_length as usize);
211
212        let result = unsafe {
213            self.bindings().FPDFFont_GetBaseFontName(
214                self.handle,
215                buffer.as_mut_ptr() as *mut c_char,
216                buffer_length,
217            )
218        };
219
220        assert_eq!(result, buffer_length);
221
222        String::from_utf8(buffer)
223            // Trim any trailing nulls. All strings returned from Pdfium are generally terminated
224            // by one null byte.
225            .map(|str| str.trim_end_matches(char::from(0)).to_owned())
226            .unwrap_or_else(|_| String::new())
227    }
228
229    /// Returns the family of this [PdfFont].
230    pub fn family(&self) -> String {
231        // Retrieving the family name from Pdfium is a two-step operation. First, we call
232        // FPDFFont_GetFamilyName() with a null buffer; this will retrieve the length of
233        // the font name in bytes. If the length is zero, then there is no font name.
234
235        // If the length is non-zero, then we reserve a byte buffer of the given
236        // length and call FPDFFont_GetFamilyName() again with a pointer to the buffer;
237        // this will write the font name into the buffer. Unlike most text handling in
238        // Pdfium, font names are returned in UTF-8 format.
239
240        #[cfg(any(
241            feature = "pdfium_future",
242            feature = "pdfium_7763",
243            feature = "pdfium_7543",
244            feature = "pdfium_7350",
245            feature = "pdfium_7215",
246            feature = "pdfium_7123",
247            feature = "pdfium_6996",
248            feature = "pdfium_6721",
249            feature = "pdfium_6666",
250            feature = "pdfium_6611"
251        ))]
252        let buffer_length = unsafe {
253            self.bindings()
254                .FPDFFont_GetFamilyName(self.handle, std::ptr::null_mut(), 0)
255        };
256
257        #[cfg(any(
258            feature = "pdfium_6569",
259            feature = "pdfium_6555",
260            feature = "pdfium_6490",
261            feature = "pdfium_6406",
262            feature = "pdfium_6337",
263            feature = "pdfium_6295",
264            feature = "pdfium_6259",
265            feature = "pdfium_6164",
266            feature = "pdfium_6124",
267            feature = "pdfium_6110",
268            feature = "pdfium_6084",
269            feature = "pdfium_6043",
270            feature = "pdfium_6015",
271            feature = "pdfium_5961"
272        ))]
273        let buffer_length = unsafe {
274            self.bindings()
275                .FPDFFont_GetFontName(self.handle, std::ptr::null_mut(), 0)
276        };
277
278        if buffer_length == 0 {
279            // The font name is not present.
280
281            return String::new();
282        }
283
284        let mut buffer = create_byte_buffer(buffer_length as usize);
285
286        #[cfg(any(
287            feature = "pdfium_future",
288            feature = "pdfium_7763",
289            feature = "pdfium_7543",
290            feature = "pdfium_7350",
291            feature = "pdfium_7215",
292            feature = "pdfium_7123",
293            feature = "pdfium_6996",
294            feature = "pdfium_6721",
295            feature = "pdfium_6666",
296            feature = "pdfium_6611"
297        ))]
298        let result = unsafe {
299            self.bindings().FPDFFont_GetFamilyName(
300                self.handle,
301                buffer.as_mut_ptr() as *mut c_char,
302                buffer_length,
303            )
304        };
305
306        #[cfg(any(
307            feature = "pdfium_6569",
308            feature = "pdfium_6555",
309            feature = "pdfium_6490",
310            feature = "pdfium_6406",
311            feature = "pdfium_6337",
312            feature = "pdfium_6295",
313            feature = "pdfium_6259",
314            feature = "pdfium_6164",
315            feature = "pdfium_6124",
316            feature = "pdfium_6110",
317            feature = "pdfium_6084",
318            feature = "pdfium_6043",
319            feature = "pdfium_6015",
320            feature = "pdfium_5961"
321        ))]
322        let result = unsafe {
323            self.bindings().FPDFFont_GetFontName(
324                self.handle,
325                buffer.as_mut_ptr() as *mut c_char,
326                buffer_length,
327            )
328        };
329
330        assert_eq!(result, buffer_length);
331
332        String::from_utf8(buffer)
333            // Trim any trailing nulls. All strings returned from Pdfium are generally terminated
334            // by one null byte.
335            .map(|str| str.trim_end_matches(char::from(0)).to_owned())
336            .unwrap_or_else(|_| String::new())
337    }
338
339    /// Returns the weight of this [PdfFont].
340    ///
341    /// Pdfium may not reliably return the correct value of this property for built-in fonts.
342    pub fn weight(&self) -> Result<PdfFontWeight, PdfiumError> {
343        PdfFontWeight::from_pdfium(unsafe { self.bindings().FPDFFont_GetWeight(self.handle) })
344            .ok_or(PdfiumError::PdfiumLibraryInternalError(
345                PdfiumInternalError::Unknown,
346            ))
347    }
348
349    /// Returns the italic angle of this [PdfFont]. The italic angle is the angle,
350    /// expressed in degrees counter-clockwise from the vertical, of the dominant vertical
351    /// strokes of the font. The value is zero for non-italic fonts, and negative for fonts
352    /// that slope to the right (as almost all italic fonts do).
353    ///
354    /// Pdfium may not reliably return the correct value of this property for built-in fonts.
355    pub fn italic_angle(&self) -> Result<i32, PdfiumError> {
356        let mut angle = 0;
357
358        if self.bindings().is_true(unsafe {
359            self.bindings()
360                .FPDFFont_GetItalicAngle(self.handle, &mut angle)
361        }) {
362            Ok(angle)
363        } else {
364            Err(PdfiumError::PdfiumLibraryInternalError(
365                PdfiumInternalError::Unknown,
366            ))
367        }
368    }
369
370    /// Returns the ascent of this [PdfFont] for the given font size. The ascent is the maximum
371    /// height above the baseline reached by glyphs in this font, excluding the height of glyphs
372    /// for accented characters.
373    pub fn ascent(&self, font_size: PdfPoints) -> Result<PdfPoints, PdfiumError> {
374        let mut ascent = 0.0;
375
376        if self.bindings().is_true(unsafe {
377            self.bindings()
378                .FPDFFont_GetAscent(self.handle, font_size.value, &mut ascent)
379        }) {
380            Ok(PdfPoints::new(ascent))
381        } else {
382            Err(PdfiumError::PdfiumLibraryInternalError(
383                PdfiumInternalError::Unknown,
384            ))
385        }
386    }
387
388    /// Returns the descent of this [PdfFont] for the given font size. The descent is the
389    /// maximum distance below the baseline reached by glyphs in this font, expressed as a
390    /// negative points value.
391    pub fn descent(&self, font_size: PdfPoints) -> Result<PdfPoints, PdfiumError> {
392        let mut descent = 0.0;
393
394        if self.bindings().is_true(unsafe {
395            self.bindings()
396                .FPDFFont_GetDescent(self.handle, font_size.value, &mut descent)
397        }) {
398            Ok(PdfPoints::new(descent))
399        } else {
400            Err(PdfiumError::PdfiumLibraryInternalError(
401                PdfiumInternalError::Unknown,
402            ))
403        }
404    }
405
406    /// Returns the raw font descriptor bitflags for the containing [PdfFont].
407    #[inline]
408    fn get_flags_bits(&self) -> FpdfFontDescriptorFlags {
409        FpdfFontDescriptorFlags::from_bits_truncate(unsafe {
410            self.bindings().FPDFFont_GetFlags(self.handle)
411        } as u32)
412    }
413
414    /// Returns `true` if all the glyphs in this [PdfFont] have the same width.
415    ///
416    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
417    pub fn is_fixed_pitch(&self) -> bool {
418        self.get_flags_bits()
419            .contains(FpdfFontDescriptorFlags::FIXED_PITCH_BIT_1)
420    }
421
422    /// Returns `true` if the glyphs in this [PdfFont] have variable widths.
423    ///
424    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
425    #[inline]
426    pub fn is_proportional_pitch(&self) -> bool {
427        !self.is_fixed_pitch()
428    }
429
430    /// Returns `true` if one or more glyphs in this [PdfFont] have serifs - short strokes
431    /// drawn at an angle on the top or bottom of glyph stems to decorate the glyphs.
432    /// For example, Times New Roman is a serif font.
433    ///
434    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
435    pub fn is_serif(&self) -> bool {
436        self.get_flags_bits()
437            .contains(FpdfFontDescriptorFlags::SERIF_BIT_2)
438    }
439
440    /// Returns `true` if no glyphs in this [PdfFont] have serifs - short strokes
441    /// drawn at an angle on the top or bottom of glyph stems to decorate the glyphs.
442    /// For example, Helvetica is a sans-serif font.
443    ///
444    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
445    #[inline]
446    pub fn is_sans_serif(&self) -> bool {
447        !self.is_serif()
448    }
449
450    /// Returns `true` if this [PdfFont] contains glyphs outside the Adobe standard Latin
451    /// character set.
452    ///
453    /// This classification of non-symbolic and symbolic fonts is peculiar to PDF. A font may
454    /// contain additional characters that are used in Latin writing systems but are outside the
455    /// Adobe standard Latin character set; PDF considers such a font to be symbolic.
456    ///
457    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
458    pub fn is_symbolic(&self) -> bool {
459        // This flag bit and the non-symbolic flag bit cannot both be set or both be clear.
460
461        self.get_flags_bits()
462            .contains(FpdfFontDescriptorFlags::SYMBOLIC_BIT_3)
463    }
464
465    /// Returns `true` if this [PdfFont] does not contain glyphs outside the Adobe standard
466    /// Latin character set.
467    ///
468    /// This classification of non-symbolic and symbolic fonts is peculiar to PDF. A font may
469    /// contain additional characters that are used in Latin writing systems but are outside the
470    /// Adobe standard Latin character set; PDF considers such a font to be symbolic.
471    ///
472    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
473    pub fn is_non_symbolic(&self) -> bool {
474        // This flag bit and the symbolic flag bit cannot both be set or both be clear.
475
476        self.get_flags_bits()
477            .contains(FpdfFontDescriptorFlags::NON_SYMBOLIC_BIT_6)
478    }
479
480    /// Returns `true` if the glyphs in this [PdfFont] are designed to resemble cursive handwriting.
481    ///
482    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
483    pub fn is_cursive(&self) -> bool {
484        self.get_flags_bits()
485            .contains(FpdfFontDescriptorFlags::SCRIPT_BIT_4)
486    }
487
488    /// Returns `true` if the glyphs in this [PdfFont] include dominant vertical strokes
489    /// that are slanted.
490    ///
491    /// The designed vertical stroke angle can be retrieved using the [PdfFont::italic_angle()] function.
492    ///
493    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
494    pub fn is_italic(&self) -> bool {
495        self.get_flags_bits()
496            .contains(FpdfFontDescriptorFlags::ITALIC_BIT_7)
497    }
498
499    /// Returns `true` if this [PdfFont] contains no lowercase letters by design.
500    ///
501    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
502    pub fn is_all_caps(&self) -> bool {
503        self.get_flags_bits()
504            .contains(FpdfFontDescriptorFlags::ALL_CAP_BIT_17)
505    }
506
507    /// Returns `true` if the lowercase letters in this [PdfFont] have the same shapes as the
508    /// corresponding uppercase letters but are sized proportionally so they have the same size
509    /// and stroke weight as lowercase glyphs in the same typeface family.
510    ///
511    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
512    pub fn is_small_caps(&self) -> bool {
513        self.get_flags_bits()
514            .contains(FpdfFontDescriptorFlags::SMALL_CAP_BIT_18)
515    }
516
517    /// Returns `true` if bold glyphs in this [PdfFont] are painted with extra pixels
518    /// at very small font sizes.
519    ///
520    /// Typically when glyphs are painted at small sizes on low-resolution devices, individual strokes
521    /// of bold glyphs may appear only one pixel wide. Because this is the minimum width of a pixel
522    /// based device, individual strokes of non-bold glyphs may also appear as one pixel wide
523    /// and therefore cannot be distinguished from bold glyphs. If this flag is set, individual
524    /// strokes of bold glyphs may be thickened at small font sizes.
525    ///
526    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
527    pub fn is_bold_reenforced(&self) -> bool {
528        self.get_flags_bits()
529            .contains(FpdfFontDescriptorFlags::FORCE_BOLD_BIT_19)
530    }
531
532    /// Returns `true` if this [PdfFont] is an instance of one of the 14 built-in fonts
533    /// provided as part of the PDF specification.
534    #[inline]
535    pub fn is_built_in(&self) -> bool {
536        self.built_in.is_some()
537    }
538
539    /// Returns the [PdfFontBuiltin] type of this built-in font, or `None` if this font is
540    /// not one of the 14 built-in fonts provided as part of the PDF specification.
541    #[inline]
542    pub fn built_in(&self) -> Option<PdfFontBuiltin> {
543        self.built_in
544    }
545
546    /// Returns `true` if the data for this [PdfFont] is embedded in the containing [PdfDocument].
547    pub fn is_embedded(&self) -> Result<bool, PdfiumError> {
548        let result = unsafe { self.bindings().FPDFFont_GetIsEmbedded(self.handle) };
549
550        match result {
551            1 => Ok(true),
552            0 => Ok(false),
553            _ => Err(PdfiumError::PdfiumLibraryInternalError(
554                PdfiumInternalError::Unknown,
555            )),
556        }
557    }
558
559    /// Writes this [PdfFont] to a new byte buffer, returning the byte buffer.
560    ///
561    /// If this [PdfFont] is not embedded in the containing [PdfDocument], then the data
562    /// returned will be for the substitution font instead.
563    pub fn data(&self) -> Result<Vec<u8>, PdfiumError> {
564        // Retrieving the font data from Pdfium is a two-step operation. First, we call
565        // FPDFFont_GetFontData() with a null buffer; this will retrieve the length of
566        // the data in bytes. If the length is zero, then there is no data associated
567        // with this font.
568
569        // If the length is non-zero, then we reserve a byte buffer of the given
570        // length and call FPDFFont_GetFontData() again with a pointer to the buffer;
571        // this will write the font data to the buffer.
572
573        let mut out_buflen: usize = 0;
574
575        if self.bindings().is_true(unsafe {
576            self.bindings().FPDFFont_GetFontData(
577                self.handle,
578                std::ptr::null_mut(),
579                0,
580                &mut out_buflen,
581            )
582        }) {
583            // out_buflen now contains the length of the font data.
584
585            let buffer_length = out_buflen;
586
587            let mut buffer = create_byte_buffer(buffer_length);
588
589            let result = unsafe {
590                self.bindings().FPDFFont_GetFontData(
591                    self.handle,
592                    buffer.as_mut_ptr(),
593                    buffer_length,
594                    &mut out_buflen,
595                )
596            };
597
598            assert!(self.bindings().is_true(result));
599            assert_eq!(buffer_length, out_buflen);
600
601            Ok(buffer)
602        } else {
603            Err(PdfiumError::PdfiumLibraryInternalError(
604                PdfiumInternalError::Unknown,
605            ))
606        }
607    }
608
609    /// Returns a collection of all the [PdfFontGlyphs] defined for this [PdfFont] in the containing
610    /// `PdfDocument`.
611    ///
612    /// Note that documents typically include only the specific glyphs they need from any given font,
613    /// not the entire font glyphset. This is a PDF feature known as font subsetting. The collection
614    /// of glyphs returned by this function may therefore not cover the entire font glyphset.
615    #[inline]
616    pub fn glyphs(&self) -> &PdfFontGlyphs<'_> {
617        self.glyphs.initialize_len();
618        &self.glyphs
619    }
620}
621
622impl<'a> Drop for PdfFont<'a> {
623    /// Closes this [PdfFont], releasing held memory.
624    #[inline]
625    fn drop(&mut self) {
626        // The documentation for FPDFText_LoadFont() and FPDFText_LoadStandardFont() both state
627        // that the font loaded by the function can be closed by calling FPDFFont_Close().
628        // I had taken this to mean that _any_ FPDF_Font handle returned from a Pdfium function
629        // should be closed via FPDFFont_Close(), but testing suggests this is not the case;
630        // rather, it is only fonts specifically loaded by calling FPDFText_LoadFont() or
631        // FPDFText_LoadStandardFont() that need to be actively closed.
632
633        // In other words, retrieving a handle to a font that already exists in a document evidently
634        // does not allocate any additional resources, so we don't need to free anything.
635        // (Indeed, if we try to, Pdfium segfaults.)
636
637        if self.is_font_memory_loaded {
638            unsafe {
639                self.bindings().FPDFFont_Close(self.handle);
640            }
641        }
642    }
643}
644
645impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfFont<'a> {}
646
647#[cfg(feature = "thread_safe")]
648unsafe impl<'a> Send for PdfFont<'a> {}
649
650#[cfg(feature = "thread_safe")]
651unsafe impl<'a> Sync for PdfFont<'a> {}