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