pdfium_render/pdf/document/fonts.rs
1//! Defines the [PdfFonts] struct, a collection of all the `PdfFont` objects in a
2//! `PdfDocument`.
3
4use crate::bindgen::{FPDF_DOCUMENT, FPDF_FONT, FPDF_FONT_TRUETYPE, FPDF_FONT_TYPE1};
5use crate::bindings::PdfiumLibraryBindings;
6use crate::error::{PdfiumError, PdfiumInternalError};
7use crate::pdf::font::PdfFont;
8use std::collections::HashMap;
9use std::io::Read;
10use std::os::raw::{c_int, c_uint};
11
12#[cfg(not(target_arch = "wasm32"))]
13use std::fs::File;
14
15#[cfg(not(target_arch = "wasm32"))]
16use std::path::Path;
17
18#[cfg(target_arch = "wasm32")]
19use wasm_bindgen::JsCast;
20
21#[cfg(target_arch = "wasm32")]
22use wasm_bindgen_futures::JsFuture;
23
24#[cfg(target_arch = "wasm32")]
25use js_sys::{ArrayBuffer, Uint8Array};
26
27#[cfg(target_arch = "wasm32")]
28use web_sys::{window, Blob, Response};
29
30// The following dummy declaration is used only when running cargo doc.
31// It allows documentation of WASM-specific functionality to be included
32// in documentation generated on non-WASM targets.
33
34#[cfg(doc)]
35struct Blob;
36
37/// The 14 built-in fonts provided as part of the PDF specification.
38#[derive(Copy, Clone, Debug, PartialEq)]
39pub enum PdfFontBuiltin {
40 TimesRoman,
41 TimesBold,
42 TimesItalic,
43 TimesBoldItalic,
44 Helvetica,
45 HelveticaBold,
46 HelveticaOblique,
47 HelveticaBoldOblique,
48 Courier,
49 CourierBold,
50 CourierOblique,
51 CourierBoldOblique,
52 Symbol,
53 ZapfDingbats,
54}
55
56impl PdfFontBuiltin {
57 /// Returns the PostScript name of this built-in PDF font, as listed on page 416
58 /// of the PDF 1.7 specification.
59 pub fn to_pdf_font_name(&self) -> &str {
60 match self {
61 PdfFontBuiltin::TimesRoman => "Times-Roman",
62 PdfFontBuiltin::TimesBold => "Times-Bold",
63 PdfFontBuiltin::TimesItalic => "Times-Italic",
64 PdfFontBuiltin::TimesBoldItalic => "Times-BoldItalic",
65 PdfFontBuiltin::Helvetica => "Helvetica",
66 PdfFontBuiltin::HelveticaBold => "Helvetica-Bold",
67 PdfFontBuiltin::HelveticaOblique => "Helvetica-Oblique",
68 PdfFontBuiltin::HelveticaBoldOblique => "Helvetica-BoldOblique",
69 PdfFontBuiltin::Courier => "Courier",
70 PdfFontBuiltin::CourierBold => "Courier-Bold",
71 PdfFontBuiltin::CourierOblique => "Courier-Oblique",
72 PdfFontBuiltin::CourierBoldOblique => "Courier-BoldOblique",
73 PdfFontBuiltin::Symbol => "Symbol",
74 PdfFontBuiltin::ZapfDingbats => "ZapfDingbats",
75 }
76 }
77}
78
79/// A reusable token referencing a [PdfFont] previously added to the [PdfFonts] collection
80/// of a `PdfDocument`.
81#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
82pub struct PdfFontToken(FPDF_FONT);
83
84impl PdfFontToken {
85 #[inline]
86 pub(crate) fn from_pdfium(handle: FPDF_FONT) -> Self {
87 Self(handle)
88 }
89
90 #[inline]
91 pub(crate) fn from_font(font: &PdfFont) -> Self {
92 Self::from_pdfium(font.handle())
93 }
94
95 #[inline]
96 pub(crate) fn handle(&self) -> FPDF_FONT {
97 self.0
98 }
99}
100
101/// Allows font-handling functions to take either a [PdfFont] owned instance, a [PdfFont] reference,
102/// or a [PdfFontToken].
103pub trait ToPdfFontToken {
104 fn token(&self) -> PdfFontToken;
105}
106
107impl ToPdfFontToken for PdfFontToken {
108 #[inline]
109 fn token(&self) -> PdfFontToken {
110 *self
111 }
112}
113
114impl<'a> ToPdfFontToken for PdfFont<'a> {
115 #[inline]
116 fn token(&self) -> PdfFontToken {
117 PdfFontToken::from_font(self)
118 }
119}
120
121impl<'a> ToPdfFontToken for &'a PdfFont<'a> {
122 #[inline]
123 fn token(&self) -> PdfFontToken {
124 PdfFontToken::from_font(self)
125 }
126}
127
128/// A collection of all the `PdfFont` objects in a `PdfDocument`.
129pub struct PdfFonts<'a> {
130 document_handle: FPDF_DOCUMENT,
131 fonts: HashMap<PdfFontToken, PdfFont<'a>>,
132 bindings: &'a dyn PdfiumLibraryBindings,
133}
134
135impl<'a> PdfFonts<'a> {
136 #[inline]
137 pub(crate) fn from_pdfium(
138 document_handle: FPDF_DOCUMENT,
139 bindings: &'a dyn PdfiumLibraryBindings,
140 ) -> Self {
141 PdfFonts {
142 document_handle,
143 fonts: HashMap::new(),
144 bindings,
145 }
146 }
147
148 /// Returns the [PdfiumLibraryBindings] used by this [PdfFonts] collection.
149 #[inline]
150 pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
151 self.bindings
152 }
153
154 /// Returns a reusable [PdfFontToken] for the given built-in font.
155 #[inline]
156 pub fn new_built_in(&mut self, font: PdfFontBuiltin) -> PdfFontToken {
157 let font = PdfFont::from_pdfium(
158 self.bindings
159 .FPDFText_LoadStandardFont(self.document_handle, font.to_pdf_font_name()),
160 self.bindings,
161 Some(font),
162 true,
163 );
164
165 let token = PdfFontToken(font.handle());
166
167 self.fonts.insert(token, font);
168
169 token
170 }
171
172 /// Returns a reusable [PdfFontToken] for the built-in "Times-Roman" font.
173 #[inline]
174 pub fn times_roman(&mut self) -> PdfFontToken {
175 self.new_built_in(PdfFontBuiltin::TimesRoman)
176 }
177
178 /// Returns a reusable [PdfFontToken] for the built-in "Times-Bold" font.
179 #[inline]
180 pub fn times_bold(&mut self) -> PdfFontToken {
181 self.new_built_in(PdfFontBuiltin::TimesBold)
182 }
183
184 /// Returns a reusable [PdfFontToken] for the built-in "Times-Italic" font.
185 #[inline]
186 pub fn times_italic(&mut self) -> PdfFontToken {
187 self.new_built_in(PdfFontBuiltin::TimesItalic)
188 }
189
190 /// Returns a reusable [PdfFontToken] for the built-in "Times-BoldItalic" font.
191 #[inline]
192 pub fn times_bold_italic(&mut self) -> PdfFontToken {
193 self.new_built_in(PdfFontBuiltin::TimesBoldItalic)
194 }
195
196 /// Returns a reusable [PdfFontToken] for the built-in "Helvetica" font.
197 #[inline]
198 pub fn helvetica(&mut self) -> PdfFontToken {
199 self.new_built_in(PdfFontBuiltin::Helvetica)
200 }
201
202 /// Returns a reusable [PdfFontToken] for the built-in "Helvetica-Bold" font.
203 #[inline]
204 pub fn helvetica_bold(&mut self) -> PdfFontToken {
205 self.new_built_in(PdfFontBuiltin::HelveticaBold)
206 }
207
208 /// Returns a reusable [PdfFontToken] for the built-in "Helvetica-Oblique" font.
209 #[inline]
210 pub fn helvetica_oblique(&mut self) -> PdfFontToken {
211 self.new_built_in(PdfFontBuiltin::HelveticaOblique)
212 }
213
214 /// Returns a reusable [PdfFontToken] for the built-in "Helvetica-BoldOblique" font.
215 #[inline]
216 pub fn helvetica_bold_oblique(&mut self) -> PdfFontToken {
217 self.new_built_in(PdfFontBuiltin::HelveticaBoldOblique)
218 }
219
220 /// Returns a reusable [PdfFontToken] for the built-in "Courier" font.
221 #[inline]
222 pub fn courier(&mut self) -> PdfFontToken {
223 self.new_built_in(PdfFontBuiltin::Courier)
224 }
225
226 /// Returns a reusable [PdfFontToken] for the built-in "Courier-Bold" font.
227 #[inline]
228 pub fn courier_bold(&mut self) -> PdfFontToken {
229 self.new_built_in(PdfFontBuiltin::CourierBold)
230 }
231
232 /// Returns a reusable [PdfFontToken] for the built-in "Courier-Oblique" font.
233 #[inline]
234 pub fn courier_oblique(&mut self) -> PdfFontToken {
235 self.new_built_in(PdfFontBuiltin::CourierOblique)
236 }
237
238 /// Returns a reusable [PdfFontToken] for the built-in "Courier-BoldOblique" font.
239 #[inline]
240 pub fn courier_bold_oblique(&mut self) -> PdfFontToken {
241 self.new_built_in(PdfFontBuiltin::CourierBoldOblique)
242 }
243
244 /// Returns a reusable [PdfFontToken] for the built-in "Symbol" font.
245 #[inline]
246 pub fn symbol(&mut self) -> PdfFontToken {
247 self.new_built_in(PdfFontBuiltin::Symbol)
248 }
249
250 /// Returns a reusable [PdfFontToken] for the built-in "ZapfDingbats" font.
251 #[inline]
252 pub fn zapf_dingbats(&mut self) -> PdfFontToken {
253 self.new_built_in(PdfFontBuiltin::ZapfDingbats)
254 }
255
256 /// Attempts to load a Type 1 font file from the given file path, returning a reusable
257 /// [PdfFontToken] if the font was successfully loaded.
258 ///
259 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
260 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
261 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
262 /// or right-to-left languages.
263 ///
264 /// This function is not available when compiling to WASM. You have several options for
265 /// loading font data in WASM:
266 /// * Use the [PdfFont::load_type1_from_fetch()] function to download font data from a
267 /// URL using the browser's built-in `fetch()` API. This function is only available when
268 /// compiling to WASM.
269 /// * Use the [PdfFont::load_type1_from_blob()] function to load font data from a
270 /// Javascript File or Blob object (such as a File object returned from an HTML
271 /// `<input type="file">` element). This function is only available when compiling to WASM.
272 /// * Use the [PdfFont::load_type1_from_reader()] function to load font data from any
273 /// valid Rust reader.
274 /// * Use another method to retrieve the bytes of the target font over the network,
275 /// then load those bytes into Pdfium using the [PdfFont::new_type1_from_bytes()] function.
276 /// * Embed the bytes of the desired font directly into the compiled WASM module
277 /// using the `include_bytes!()` macro.
278 #[cfg(not(target_arch = "wasm32"))]
279 pub fn load_type1_from_file(
280 &mut self,
281 path: &(impl AsRef<Path> + ?Sized),
282 is_cid_font: bool,
283 ) -> Result<PdfFontToken, PdfiumError> {
284 self.load_type1_from_reader(File::open(path).map_err(PdfiumError::IoError)?, is_cid_font)
285 }
286
287 /// Attempts to load a Type 1 font file from the given reader, returning a reusable
288 /// [PdfFontToken] if the font was successfully loaded.
289 ///
290 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
291 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
292 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
293 /// or right-to-left languages.
294 pub fn load_type1_from_reader(
295 &mut self,
296 mut reader: impl Read,
297 is_cid_font: bool,
298 ) -> Result<PdfFontToken, PdfiumError> {
299 let mut bytes = Vec::new();
300
301 reader
302 .read_to_end(&mut bytes)
303 .map_err(PdfiumError::IoError)?;
304
305 self.load_type1_from_bytes(bytes.as_slice(), is_cid_font)
306 }
307
308 /// Attempts to load a Type 1 font file from the given URL, returning a reusable
309 /// [PdfFontToken] if the font was successfully loaded.
310 ///
311 /// The Javascript `fetch()` API is used to download data over the network.
312 ///
313 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
314 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
315 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
316 /// or right-to-left languages.
317 ///
318 /// This function is only available when compiling to WASM.
319 #[cfg(any(doc, target_arch = "wasm32"))]
320 pub async fn load_type1_from_fetch(
321 &mut self,
322 url: impl ToString,
323 is_cid_font: bool,
324 ) -> Result<PdfFontToken, PdfiumError> {
325 if let Some(window) = window() {
326 let fetch_result = JsFuture::from(window.fetch_with_str(url.to_string().as_str()))
327 .await
328 .map_err(PdfiumError::WebSysFetchError)?;
329
330 debug_assert!(fetch_result.is_instance_of::<Response>());
331
332 let response: Response = fetch_result
333 .dyn_into()
334 .map_err(|_| PdfiumError::WebSysInvalidResponseError)?;
335
336 let blob: Blob =
337 JsFuture::from(response.blob().map_err(PdfiumError::WebSysFetchError)?)
338 .await
339 .map_err(PdfiumError::WebSysFetchError)?
340 .into();
341
342 self.load_type1_from_blob(blob, is_cid_font).await
343 } else {
344 Err(PdfiumError::WebSysWindowObjectNotAvailable)
345 }
346 }
347
348 /// Attempts to load a Type 1 font from the given Blob, returning a reusable
349 /// [PdfFontToken] if the font was successfully loaded.
350 ///
351 /// A File object returned from a FileList is a suitable Blob:
352 ///
353 /// ```text
354 /// <input id="filePicker" type="file">
355 ///
356 /// const file = document.getElementById('filePicker').files[0];
357 /// ```
358 ///
359 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
360 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
361 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
362 /// or right-to-left languages.
363 ///
364 /// This function is only available when compiling to WASM.
365 #[cfg(any(doc, target_arch = "wasm32"))]
366 pub async fn load_type1_from_blob(
367 &mut self,
368 blob: Blob,
369 is_cid_font: bool,
370 ) -> Result<PdfFontToken, PdfiumError> {
371 let array_buffer: ArrayBuffer = JsFuture::from(blob.array_buffer())
372 .await
373 .map_err(PdfiumError::WebSysFetchError)?
374 .into();
375
376 let u8_array: Uint8Array = Uint8Array::new(&array_buffer);
377
378 let bytes: Vec<u8> = u8_array.to_vec();
379
380 self.load_type1_from_bytes(bytes.as_slice(), is_cid_font)
381 }
382
383 /// Attempts to load the given byte data as a Type 1 font file, returning a reusable
384 /// [PdfFontToken] if the font was successfully loaded.
385 ///
386 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
387 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
388 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
389 /// or right-to-left languages.
390 pub fn load_type1_from_bytes(
391 &mut self,
392 font_data: &[u8],
393 is_cid_font: bool,
394 ) -> Result<PdfFontToken, PdfiumError> {
395 self.new_font_from_bytes(font_data, FPDF_FONT_TYPE1, is_cid_font)
396 }
397
398 /// Attempts to load a TrueType font file from the given file path, returning a reusable
399 /// [PdfFontToken] if the font was successfully loaded.
400 ///
401 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
402 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
403 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
404 /// or right-to-left languages.
405 ///
406 /// This function is not available when compiling to WASM. You have several options for
407 /// loading font data in WASM:
408 /// * Use the [PdfFont::load_true_type_from_fetch()] function to download font data from a
409 /// URL using the browser's built-in `fetch()` API. This function is only available when
410 /// compiling to WASM.
411 /// * Use the [PdfFont::load_true_type_from_blob()] function to load font data from a
412 /// Javascript `File` or `Blob` object (such as a `File` object returned from an HTML
413 /// `<input type="file">` element). This function is only available when compiling to WASM.
414 /// * Use the [PdfFont::load_true_type_from_reader()] function to load font data from any
415 /// valid Rust reader.
416 /// * Use another method to retrieve the bytes of the target font over the network,
417 /// then load those bytes into Pdfium using the [PdfFont::new_true_type_from_bytes()] function.
418 /// * Embed the bytes of the desired font directly into the compiled WASM module
419 /// using the `include_bytes!()` macro.
420 #[cfg(not(target_arch = "wasm32"))]
421 pub fn load_true_type_from_file(
422 &mut self,
423 path: &(impl AsRef<Path> + ?Sized),
424 is_cid_font: bool,
425 ) -> Result<PdfFontToken, PdfiumError> {
426 self.load_true_type_from_reader(
427 File::open(path).map_err(PdfiumError::IoError)?,
428 is_cid_font,
429 )
430 }
431
432 /// Attempts to load a TrueType font file from the given reader, returning a reusable
433 /// [PdfFontToken] if the font was successfully loaded.
434 ///
435 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
436 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
437 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
438 /// or right-to-left languages.
439 pub fn load_true_type_from_reader(
440 &mut self,
441 mut reader: impl Read,
442 is_cid_font: bool,
443 ) -> Result<PdfFontToken, PdfiumError> {
444 let mut bytes = Vec::new();
445
446 reader
447 .read_to_end(&mut bytes)
448 .map_err(PdfiumError::IoError)?;
449
450 self.load_true_type_from_bytes(bytes.as_slice(), is_cid_font)
451 }
452
453 /// Attempts to load a TrueType font file from the given URL, returning a reusable
454 /// [PdfFontToken] if the font was successfully loaded.
455 ///
456 /// The Javascript `fetch()` API is used to download data over the network.
457 ///
458 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
459 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
460 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
461 /// or right-to-left languages.
462 ///
463 /// This function is only available when compiling to WASM.
464 #[cfg(any(doc, target_arch = "wasm32"))]
465 pub async fn load_true_type_from_fetch(
466 &mut self,
467 url: impl ToString,
468 is_cid_font: bool,
469 ) -> Result<PdfFontToken, PdfiumError> {
470 if let Some(window) = window() {
471 let fetch_result = JsFuture::from(window.fetch_with_str(url.to_string().as_str()))
472 .await
473 .map_err(PdfiumError::WebSysFetchError)?;
474
475 debug_assert!(fetch_result.is_instance_of::<Response>());
476
477 let response: Response = fetch_result
478 .dyn_into()
479 .map_err(|_| PdfiumError::WebSysInvalidResponseError)?;
480
481 let blob: Blob =
482 JsFuture::from(response.blob().map_err(PdfiumError::WebSysFetchError)?)
483 .await
484 .map_err(PdfiumError::WebSysFetchError)?
485 .into();
486
487 self.load_true_type_from_blob(blob, is_cid_font).await
488 } else {
489 Err(PdfiumError::WebSysWindowObjectNotAvailable)
490 }
491 }
492
493 /// Attempts to load a TrueType font from the given `Blob`, returning a reusable
494 /// [PdfFontToken] if the font was successfully loaded.
495 ///
496 /// A `File` object returned from a `FileList` is a suitable `Blob`:
497 ///
498 /// ```text
499 /// <input id="filePicker" type="file">
500 ///
501 /// const file = document.getElementById('filePicker').files[0];
502 /// ```
503 ///
504 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
505 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
506 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
507 /// or right-to-left languages.
508 ///
509 /// This function is only available when compiling to WASM.
510 #[cfg(any(doc, target_arch = "wasm32"))]
511 pub async fn load_true_type_from_blob(
512 &mut self,
513 blob: Blob,
514 is_cid_font: bool,
515 ) -> Result<PdfFontToken, PdfiumError> {
516 let array_buffer: ArrayBuffer = JsFuture::from(blob.array_buffer())
517 .await
518 .map_err(PdfiumError::WebSysFetchError)?
519 .into();
520
521 let u8_array: Uint8Array = Uint8Array::new(&array_buffer);
522
523 let bytes: Vec<u8> = u8_array.to_vec();
524
525 self.load_true_type_from_bytes(bytes.as_slice(), is_cid_font)
526 }
527
528 /// Attempts to load the given byte data as a TrueType font file, returning a reusable
529 /// [PdfFontToken] if the font was successfully loaded.
530 ///
531 /// Set the `is_cid_font` parameter to `true` if the given font is keyed by
532 /// 16-bit character ID (CID), indicating that it supports an extended glyphset of
533 /// 65,535 glyphs. This is typically the case with fonts that support Asian character sets
534 /// or right-to-left languages.
535 pub fn load_true_type_from_bytes(
536 &mut self,
537 font_data: &[u8],
538 is_cid_font: bool,
539 ) -> Result<PdfFontToken, PdfiumError> {
540 self.new_font_from_bytes(font_data, FPDF_FONT_TRUETYPE, is_cid_font)
541 }
542
543 #[inline]
544 pub(crate) fn new_font_from_bytes(
545 &mut self,
546 font_data: &[u8],
547 font_type: c_uint,
548 is_cid_font: bool,
549 ) -> Result<PdfFontToken, PdfiumError> {
550 let handle = self.bindings.FPDFText_LoadFont(
551 self.document_handle,
552 font_data.as_ptr(),
553 font_data.len() as c_uint,
554 font_type as c_int,
555 self.bindings.bool_to_pdfium(is_cid_font),
556 );
557
558 if handle.is_null() {
559 Err(PdfiumError::PdfiumLibraryInternalError(
560 PdfiumInternalError::Unknown,
561 ))
562 } else {
563 let font = PdfFont::from_pdfium(handle, self.bindings, None, true);
564
565 let token = PdfFontToken::from_font(&font);
566
567 self.fonts.insert(token, font);
568
569 Ok(token)
570 }
571 }
572
573 /// Returns a reference to the [PdfFont] associated with the given [PdfFontToken], if any.
574 #[inline]
575 pub fn get(&self, token: PdfFontToken) -> Option<&PdfFont> {
576 self.fonts.get(&token)
577 }
578}