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_8125",
180        feature = "pdfium_7881",
181        feature = "pdfium_7763",
182        feature = "pdfium_7543",
183        feature = "pdfium_7350",
184        feature = "pdfium_7215",
185        feature = "pdfium_7123",
186        feature = "pdfium_6996",
187        feature = "pdfium_6721",
188        feature = "pdfium_6666"
189    ))]
190    /// Returns the name of this [PdfFont].
191    pub fn name(&self) -> String {
192        // Retrieving the font name from Pdfium is a two-step operation. First, we call
193        // FPDFFont_GetBaseFontName() with a null buffer; this will retrieve the length of
194        // the font name in bytes. If the length is zero, then there is no font name.
195
196        // If the length is non-zero, then we reserve a byte buffer of the given
197        // length and call FPDFFont_GetBaseFontName() again with a pointer to the buffer;
198        // this will write the font name into the buffer. Unlike most text handling in
199        // Pdfium, font names are returned in UTF-8 format.
200
201        let buffer_length = unsafe {
202            self.bindings()
203                .FPDFFont_GetBaseFontName(self.handle, std::ptr::null_mut(), 0)
204        };
205
206        if buffer_length == 0 {
207            // The font name is not present.
208
209            return String::new();
210        }
211
212        let mut buffer = create_byte_buffer(buffer_length as usize);
213
214        let result = unsafe {
215            self.bindings().FPDFFont_GetBaseFontName(
216                self.handle,
217                buffer.as_mut_ptr() as *mut c_char,
218                buffer_length,
219            )
220        };
221
222        assert_eq!(result, buffer_length);
223
224        String::from_utf8(buffer)
225            // Trim any trailing nulls. All strings returned from Pdfium are generally terminated
226            // by one null byte.
227            .map(|str| str.trim_end_matches(char::from(0)).to_owned())
228            .unwrap_or_else(|_| String::new())
229    }
230
231    /// Returns the family of this [PdfFont].
232    pub fn family(&self) -> String {
233        // Retrieving the family name from Pdfium is a two-step operation. First, we call
234        // FPDFFont_GetFamilyName() with a null buffer; this will retrieve the length of
235        // the font name in bytes. If the length is zero, then there is no font name.
236
237        // If the length is non-zero, then we reserve a byte buffer of the given
238        // length and call FPDFFont_GetFamilyName() again with a pointer to the buffer;
239        // this will write the font name into the buffer. Unlike most text handling in
240        // Pdfium, font names are returned in UTF-8 format.
241
242        #[cfg(any(
243            feature = "pdfium_future",
244            feature = "pdfium_8125",
245            feature = "pdfium_7881",
246            feature = "pdfium_7763",
247            feature = "pdfium_7543",
248            feature = "pdfium_7350",
249            feature = "pdfium_7215",
250            feature = "pdfium_7123",
251            feature = "pdfium_6996",
252            feature = "pdfium_6721",
253            feature = "pdfium_6666",
254            feature = "pdfium_6611"
255        ))]
256        let buffer_length = unsafe {
257            self.bindings()
258                .FPDFFont_GetFamilyName(self.handle, std::ptr::null_mut(), 0)
259        };
260
261        #[cfg(any(
262            feature = "pdfium_6569",
263            feature = "pdfium_6555",
264            feature = "pdfium_6490",
265            feature = "pdfium_6406",
266            feature = "pdfium_6337",
267            feature = "pdfium_6295",
268            feature = "pdfium_6259",
269            feature = "pdfium_6164",
270            feature = "pdfium_6124",
271            feature = "pdfium_6110",
272            feature = "pdfium_6084",
273            feature = "pdfium_6043",
274            feature = "pdfium_6015",
275            feature = "pdfium_5961"
276        ))]
277        let buffer_length = unsafe {
278            self.bindings()
279                .FPDFFont_GetFontName(self.handle, std::ptr::null_mut(), 0)
280        };
281
282        if buffer_length == 0 {
283            // The font name is not present.
284
285            return String::new();
286        }
287
288        let mut buffer = create_byte_buffer(buffer_length as usize);
289
290        #[cfg(any(
291            feature = "pdfium_future",
292            feature = "pdfium_8125",
293            feature = "pdfium_7881",
294            feature = "pdfium_7763",
295            feature = "pdfium_7543",
296            feature = "pdfium_7350",
297            feature = "pdfium_7215",
298            feature = "pdfium_7123",
299            feature = "pdfium_6996",
300            feature = "pdfium_6721",
301            feature = "pdfium_6666",
302            feature = "pdfium_6611"
303        ))]
304        let result = unsafe {
305            self.bindings().FPDFFont_GetFamilyName(
306                self.handle,
307                buffer.as_mut_ptr() as *mut c_char,
308                buffer_length,
309            )
310        };
311
312        #[cfg(any(
313            feature = "pdfium_6569",
314            feature = "pdfium_6555",
315            feature = "pdfium_6490",
316            feature = "pdfium_6406",
317            feature = "pdfium_6337",
318            feature = "pdfium_6295",
319            feature = "pdfium_6259",
320            feature = "pdfium_6164",
321            feature = "pdfium_6124",
322            feature = "pdfium_6110",
323            feature = "pdfium_6084",
324            feature = "pdfium_6043",
325            feature = "pdfium_6015",
326            feature = "pdfium_5961"
327        ))]
328        let result = unsafe {
329            self.bindings().FPDFFont_GetFontName(
330                self.handle,
331                buffer.as_mut_ptr() as *mut c_char,
332                buffer_length,
333            )
334        };
335
336        assert_eq!(result, buffer_length);
337
338        String::from_utf8(buffer)
339            // Trim any trailing nulls. All strings returned from Pdfium are generally terminated
340            // by one null byte.
341            .map(|str| str.trim_end_matches(char::from(0)).to_owned())
342            .unwrap_or_else(|_| String::new())
343    }
344
345    /// Returns the weight of this [PdfFont].
346    ///
347    /// Pdfium may not reliably return the correct value of this property for built-in fonts.
348    pub fn weight(&self) -> Result<PdfFontWeight, PdfiumError> {
349        PdfFontWeight::from_pdfium(unsafe { self.bindings().FPDFFont_GetWeight(self.handle) })
350            .ok_or(PdfiumError::PdfiumLibraryInternalError(
351                PdfiumInternalError::Unknown,
352            ))
353    }
354
355    /// Returns the italic angle of this [PdfFont]. The italic angle is the angle,
356    /// expressed in degrees counter-clockwise from the vertical, of the dominant vertical
357    /// strokes of the font. The value is zero for non-italic fonts, and negative for fonts
358    /// that slope to the right (as almost all italic fonts do).
359    ///
360    /// Pdfium may not reliably return the correct value of this property for built-in fonts.
361    pub fn italic_angle(&self) -> Result<i32, PdfiumError> {
362        let mut angle = 0;
363
364        if self.bindings().is_true(unsafe {
365            self.bindings()
366                .FPDFFont_GetItalicAngle(self.handle, &mut angle)
367        }) {
368            Ok(angle)
369        } else {
370            Err(PdfiumError::PdfiumLibraryInternalError(
371                PdfiumInternalError::Unknown,
372            ))
373        }
374    }
375
376    /// Returns the ascent of this [PdfFont] for the given font size. The ascent is the maximum
377    /// height above the baseline reached by glyphs in this font, excluding the height of glyphs
378    /// for accented characters.
379    pub fn ascent(&self, font_size: PdfPoints) -> Result<PdfPoints, PdfiumError> {
380        let mut ascent = 0.0;
381
382        if self.bindings().is_true(unsafe {
383            self.bindings()
384                .FPDFFont_GetAscent(self.handle, font_size.value, &mut ascent)
385        }) {
386            Ok(PdfPoints::new(ascent))
387        } else {
388            Err(PdfiumError::PdfiumLibraryInternalError(
389                PdfiumInternalError::Unknown,
390            ))
391        }
392    }
393
394    /// Returns the descent of this [PdfFont] for the given font size. The descent is the
395    /// maximum distance below the baseline reached by glyphs in this font, expressed as a
396    /// negative points value.
397    pub fn descent(&self, font_size: PdfPoints) -> Result<PdfPoints, PdfiumError> {
398        let mut descent = 0.0;
399
400        if self.bindings().is_true(unsafe {
401            self.bindings()
402                .FPDFFont_GetDescent(self.handle, font_size.value, &mut descent)
403        }) {
404            Ok(PdfPoints::new(descent))
405        } else {
406            Err(PdfiumError::PdfiumLibraryInternalError(
407                PdfiumInternalError::Unknown,
408            ))
409        }
410    }
411
412    /// Returns the raw font descriptor bitflags for the containing [PdfFont].
413    #[inline]
414    fn get_flags_bits(&self) -> FpdfFontDescriptorFlags {
415        FpdfFontDescriptorFlags::from_bits_truncate(unsafe {
416            self.bindings().FPDFFont_GetFlags(self.handle)
417        } as u32)
418    }
419
420    /// Returns `true` if all the glyphs in this [PdfFont] have the same width.
421    ///
422    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
423    pub fn is_fixed_pitch(&self) -> bool {
424        self.get_flags_bits()
425            .contains(FpdfFontDescriptorFlags::FIXED_PITCH_BIT_1)
426    }
427
428    /// Returns `true` if the glyphs in this [PdfFont] have variable widths.
429    ///
430    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
431    #[inline]
432    pub fn is_proportional_pitch(&self) -> bool {
433        !self.is_fixed_pitch()
434    }
435
436    /// Returns `true` if one or more glyphs in this [PdfFont] have serifs - short strokes
437    /// drawn at an angle on the top or bottom of glyph stems to decorate the glyphs.
438    /// For example, Times New Roman is a serif font.
439    ///
440    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
441    pub fn is_serif(&self) -> bool {
442        self.get_flags_bits()
443            .contains(FpdfFontDescriptorFlags::SERIF_BIT_2)
444    }
445
446    /// Returns `true` if no glyphs in this [PdfFont] have serifs - short strokes
447    /// drawn at an angle on the top or bottom of glyph stems to decorate the glyphs.
448    /// For example, Helvetica is a sans-serif font.
449    ///
450    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
451    #[inline]
452    pub fn is_sans_serif(&self) -> bool {
453        !self.is_serif()
454    }
455
456    /// Returns `true` if this [PdfFont] contains glyphs outside the Adobe standard Latin
457    /// character set.
458    ///
459    /// This classification of non-symbolic and symbolic fonts is peculiar to PDF. A font may
460    /// contain additional characters that are used in Latin writing systems but are outside the
461    /// Adobe standard Latin character set; PDF considers such a font to be symbolic.
462    ///
463    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
464    pub fn is_symbolic(&self) -> bool {
465        // This flag bit and the non-symbolic flag bit cannot both be set or both be clear.
466
467        self.get_flags_bits()
468            .contains(FpdfFontDescriptorFlags::SYMBOLIC_BIT_3)
469    }
470
471    /// Returns `true` if this [PdfFont] does not contain glyphs outside the Adobe standard
472    /// Latin character set.
473    ///
474    /// This classification of non-symbolic and symbolic fonts is peculiar to PDF. A font may
475    /// contain additional characters that are used in Latin writing systems but are outside the
476    /// Adobe standard Latin character set; PDF considers such a font to be symbolic.
477    ///
478    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
479    pub fn is_non_symbolic(&self) -> bool {
480        // This flag bit and the symbolic flag bit cannot both be set or both be clear.
481
482        self.get_flags_bits()
483            .contains(FpdfFontDescriptorFlags::NON_SYMBOLIC_BIT_6)
484    }
485
486    /// Returns `true` if the glyphs in this [PdfFont] are designed to resemble cursive handwriting.
487    ///
488    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
489    pub fn is_cursive(&self) -> bool {
490        self.get_flags_bits()
491            .contains(FpdfFontDescriptorFlags::SCRIPT_BIT_4)
492    }
493
494    /// Returns `true` if the glyphs in this [PdfFont] include dominant vertical strokes
495    /// that are slanted.
496    ///
497    /// The designed vertical stroke angle can be retrieved using the [PdfFont::italic_angle()] function.
498    ///
499    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
500    pub fn is_italic(&self) -> bool {
501        self.get_flags_bits()
502            .contains(FpdfFontDescriptorFlags::ITALIC_BIT_7)
503    }
504
505    /// Returns `true` if this [PdfFont] contains no lowercase letters by design.
506    ///
507    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
508    pub fn is_all_caps(&self) -> bool {
509        self.get_flags_bits()
510            .contains(FpdfFontDescriptorFlags::ALL_CAP_BIT_17)
511    }
512
513    /// Returns `true` if the lowercase letters in this [PdfFont] have the same shapes as the
514    /// corresponding uppercase letters but are sized proportionally so they have the same size
515    /// and stroke weight as lowercase glyphs in the same typeface family.
516    ///
517    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
518    pub fn is_small_caps(&self) -> bool {
519        self.get_flags_bits()
520            .contains(FpdfFontDescriptorFlags::SMALL_CAP_BIT_18)
521    }
522
523    /// Returns `true` if bold glyphs in this [PdfFont] are painted with extra pixels
524    /// at very small font sizes.
525    ///
526    /// Typically when glyphs are painted at small sizes on low-resolution devices, individual strokes
527    /// of bold glyphs may appear only one pixel wide. Because this is the minimum width of a pixel
528    /// based device, individual strokes of non-bold glyphs may also appear as one pixel wide
529    /// and therefore cannot be distinguished from bold glyphs. If this flag is set, individual
530    /// strokes of bold glyphs may be thickened at small font sizes.
531    ///
532    /// Pdfium may not reliably return the correct value of this flag for built-in fonts.
533    pub fn is_bold_reenforced(&self) -> bool {
534        self.get_flags_bits()
535            .contains(FpdfFontDescriptorFlags::FORCE_BOLD_BIT_19)
536    }
537
538    /// Returns `true` if this [PdfFont] is an instance of one of the 14 built-in fonts
539    /// provided as part of the PDF specification.
540    #[inline]
541    pub fn is_built_in(&self) -> bool {
542        self.built_in.is_some()
543    }
544
545    /// Returns the [PdfFontBuiltin] type of this built-in font, or `None` if this font is
546    /// not one of the 14 built-in fonts provided as part of the PDF specification.
547    #[inline]
548    pub fn built_in(&self) -> Option<PdfFontBuiltin> {
549        self.built_in
550    }
551
552    /// Returns `true` if the data for this [PdfFont] is embedded in the containing [PdfDocument].
553    pub fn is_embedded(&self) -> Result<bool, PdfiumError> {
554        let result = unsafe { self.bindings().FPDFFont_GetIsEmbedded(self.handle) };
555
556        match result {
557            1 => Ok(true),
558            0 => Ok(false),
559            _ => Err(PdfiumError::PdfiumLibraryInternalError(
560                PdfiumInternalError::Unknown,
561            )),
562        }
563    }
564
565    /// Writes this [PdfFont] to a new byte buffer, returning the byte buffer.
566    ///
567    /// If this [PdfFont] is not embedded in the containing [PdfDocument], then the data
568    /// returned will be for the substitution font instead.
569    pub fn data(&self) -> Result<Vec<u8>, PdfiumError> {
570        // Retrieving the font data from Pdfium is a two-step operation. First, we call
571        // FPDFFont_GetFontData() with a null buffer; this will retrieve the length of
572        // the data in bytes. If the length is zero, then there is no data associated
573        // with this font.
574
575        // If the length is non-zero, then we reserve a byte buffer of the given
576        // length and call FPDFFont_GetFontData() again with a pointer to the buffer;
577        // this will write the font data to the buffer.
578
579        let mut out_buflen: usize = 0;
580
581        if self.bindings().is_true(unsafe {
582            self.bindings().FPDFFont_GetFontData(
583                self.handle,
584                std::ptr::null_mut(),
585                0,
586                &mut out_buflen,
587            )
588        }) {
589            // out_buflen now contains the length of the font data.
590
591            let buffer_length = out_buflen;
592
593            let mut buffer = create_byte_buffer(buffer_length);
594
595            let result = unsafe {
596                self.bindings().FPDFFont_GetFontData(
597                    self.handle,
598                    buffer.as_mut_ptr(),
599                    buffer_length,
600                    &mut out_buflen,
601                )
602            };
603
604            assert!(self.bindings().is_true(result));
605            assert_eq!(buffer_length, out_buflen);
606
607            Ok(buffer)
608        } else {
609            Err(PdfiumError::PdfiumLibraryInternalError(
610                PdfiumInternalError::Unknown,
611            ))
612        }
613    }
614
615    /// Returns a collection of all the [PdfFontGlyphs] defined for this [PdfFont] in the containing
616    /// `PdfDocument`.
617    ///
618    /// Note that documents typically include only the specific glyphs they need from any given font,
619    /// not the entire font glyphset. This is a PDF feature known as font subsetting. The collection
620    /// of glyphs returned by this function may therefore not cover the entire font glyphset.
621    #[inline]
622    pub fn glyphs(&self) -> &PdfFontGlyphs<'_> {
623        self.glyphs.initialize_len();
624        &self.glyphs
625    }
626}
627
628impl<'a> Drop for PdfFont<'a> {
629    /// Closes this [PdfFont], releasing held memory.
630    #[inline]
631    fn drop(&mut self) {
632        // The documentation for FPDFText_LoadFont() and FPDFText_LoadStandardFont() both state
633        // that the font loaded by the function can be closed by calling FPDFFont_Close().
634        // I had taken this to mean that _any_ FPDF_Font handle returned from a Pdfium function
635        // should be closed via FPDFFont_Close(), but testing suggests this is not the case;
636        // rather, it is only fonts specifically loaded by calling FPDFText_LoadFont() or
637        // FPDFText_LoadStandardFont() that need to be actively closed.
638
639        // In other words, retrieving a handle to a font that already exists in a document evidently
640        // does not allocate any additional resources, so we don't need to free anything.
641        // (Indeed, if we try to, Pdfium segfaults.)
642
643        if self.is_font_memory_loaded {
644            unsafe {
645                self.bindings().FPDFFont_Close(self.handle);
646            }
647        }
648    }
649}
650
651impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfFont<'a> {}
652
653#[cfg(feature = "thread_safe")]
654unsafe impl<'a> Send for PdfFont<'a> {}
655
656#[cfg(feature = "thread_safe")]
657unsafe impl<'a> Sync for PdfFont<'a> {}