pivot_pdf/fonts.rs
1/// Index into the document's TrueType font list.
2#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
3pub struct TrueTypeFontId(pub usize);
4
5/// Unified font reference: either a builtin PDF font or a loaded
6/// TrueType font.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
8pub enum FontRef {
9 /// One of the 14 standard PDF Type1 fonts, guaranteed available in all viewers.
10 Builtin(BuiltinFont),
11 /// A TrueType font loaded via [`crate::PdfDocument::load_font_file`] or
12 /// [`crate::PdfDocument::load_font_bytes`].
13 TrueType(TrueTypeFontId),
14}
15
16impl From<BuiltinFont> for FontRef {
17 fn from(font: BuiltinFont) -> Self {
18 FontRef::Builtin(font)
19 }
20}
21
22/// Font identifier for the 14 standard PDF fonts.
23/// These fonts are guaranteed available in all PDF viewers
24/// without embedding.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub enum BuiltinFont {
27 /// Helvetica (sans-serif, regular).
28 Helvetica,
29 /// Helvetica Bold.
30 HelveticaBold,
31 /// Helvetica Oblique (italic).
32 HelveticaOblique,
33 /// Helvetica Bold Oblique.
34 HelveticaBoldOblique,
35 /// Times Roman (serif, regular).
36 TimesRoman,
37 /// Times Bold.
38 TimesBold,
39 /// Times Italic.
40 TimesItalic,
41 /// Times Bold Italic.
42 TimesBoldItalic,
43 /// Courier (monospace, regular).
44 Courier,
45 /// Courier Bold.
46 CourierBold,
47 /// Courier Oblique (italic).
48 CourierOblique,
49 /// Courier Bold Oblique.
50 CourierBoldOblique,
51 /// Symbol (mathematical and special characters).
52 Symbol,
53 /// Zapf Dingbats (decorative symbols).
54 ZapfDingbats,
55}
56
57impl BuiltinFont {
58 /// Returns the PDF resource name used in content streams
59 /// (e.g. "F1"). Fixed mapping by variant order.
60 pub fn pdf_name(&self) -> &'static str {
61 match self {
62 BuiltinFont::Helvetica => "F1",
63 BuiltinFont::HelveticaBold => "F2",
64 BuiltinFont::HelveticaOblique => "F3",
65 BuiltinFont::HelveticaBoldOblique => "F4",
66 BuiltinFont::TimesRoman => "F5",
67 BuiltinFont::TimesBold => "F6",
68 BuiltinFont::TimesItalic => "F7",
69 BuiltinFont::TimesBoldItalic => "F8",
70 BuiltinFont::Courier => "F9",
71 BuiltinFont::CourierBold => "F10",
72 BuiltinFont::CourierOblique => "F11",
73 BuiltinFont::CourierBoldOblique => "F12",
74 BuiltinFont::Symbol => "F13",
75 BuiltinFont::ZapfDingbats => "F14",
76 }
77 }
78
79 /// Returns the PDF BaseFont name (e.g. "Helvetica",
80 /// "Times-Roman").
81 pub fn pdf_base_name(&self) -> &'static str {
82 match self {
83 BuiltinFont::Helvetica => "Helvetica",
84 BuiltinFont::HelveticaBold => "Helvetica-Bold",
85 BuiltinFont::HelveticaOblique => "Helvetica-Oblique",
86 BuiltinFont::HelveticaBoldOblique => "Helvetica-BoldOblique",
87 BuiltinFont::TimesRoman => "Times-Roman",
88 BuiltinFont::TimesBold => "Times-Bold",
89 BuiltinFont::TimesItalic => "Times-Italic",
90 BuiltinFont::TimesBoldItalic => "Times-BoldItalic",
91 BuiltinFont::Courier => "Courier",
92 BuiltinFont::CourierBold => "Courier-Bold",
93 BuiltinFont::CourierOblique => "Courier-Oblique",
94 BuiltinFont::CourierBoldOblique => "Courier-BoldOblique",
95 BuiltinFont::Symbol => "Symbol",
96 BuiltinFont::ZapfDingbats => "ZapfDingbats",
97 }
98 }
99
100 /// Look up a BuiltinFont by its PDF base name string.
101 /// Returns None if the name doesn't match any variant.
102 pub fn from_name(name: &str) -> Option<BuiltinFont> {
103 match name {
104 "Helvetica" => Some(BuiltinFont::Helvetica),
105 "Helvetica-Bold" => Some(BuiltinFont::HelveticaBold),
106 "Helvetica-Oblique" => Some(BuiltinFont::HelveticaOblique),
107 "Helvetica-BoldOblique" => Some(BuiltinFont::HelveticaBoldOblique),
108 "Times-Roman" => Some(BuiltinFont::TimesRoman),
109 "Times-Bold" => Some(BuiltinFont::TimesBold),
110 "Times-Italic" => Some(BuiltinFont::TimesItalic),
111 "Times-BoldItalic" => Some(BuiltinFont::TimesBoldItalic),
112 "Courier" => Some(BuiltinFont::Courier),
113 "Courier-Bold" => Some(BuiltinFont::CourierBold),
114 "Courier-Oblique" => Some(BuiltinFont::CourierOblique),
115 "Courier-BoldOblique" => Some(BuiltinFont::CourierBoldOblique),
116 "Symbol" => Some(BuiltinFont::Symbol),
117 "ZapfDingbats" => Some(BuiltinFont::ZapfDingbats),
118 _ => None,
119 }
120 }
121}
122
123/// Character widths for Helvetica (ASCII 32..=126) in units of 1/1000 em.
124/// Source: Adobe Helvetica AFM data.
125const HELVETICA_WIDTHS: [u16; 95] = [
126 278, // 32 space
127 278, // 33 !
128 355, // 34 "
129 556, // 35 #
130 556, // 36 $
131 889, // 37 %
132 667, // 38 &
133 191, // 39 '
134 333, // 40 (
135 333, // 41 )
136 389, // 42 *
137 584, // 43 +
138 278, // 44 ,
139 333, // 45 -
140 278, // 46 .
141 278, // 47 /
142 556, // 48 0
143 556, // 49 1
144 556, // 50 2
145 556, // 51 3
146 556, // 52 4
147 556, // 53 5
148 556, // 54 6
149 556, // 55 7
150 556, // 56 8
151 556, // 57 9
152 278, // 58 :
153 278, // 59 ;
154 584, // 60 <
155 584, // 61 =
156 584, // 62 >
157 556, // 63 ?
158 1015, // 64 @
159 667, // 65 A
160 667, // 66 B
161 722, // 67 C
162 722, // 68 D
163 667, // 69 E
164 611, // 70 F
165 778, // 71 G
166 722, // 72 H
167 278, // 73 I
168 500, // 74 J
169 667, // 75 K
170 556, // 76 L
171 833, // 77 M
172 722, // 78 N
173 778, // 79 O
174 667, // 80 P
175 778, // 81 Q
176 722, // 82 R
177 667, // 83 S
178 611, // 84 T
179 722, // 85 U
180 667, // 86 V
181 944, // 87 W
182 667, // 88 X
183 667, // 89 Y
184 611, // 90 Z
185 278, // 91 [
186 278, // 92 backslash
187 278, // 93 ]
188 469, // 94 ^
189 556, // 95 _
190 333, // 96 `
191 556, // 97 a
192 556, // 98 b
193 500, // 99 c
194 556, // 100 d
195 556, // 101 e
196 278, // 102 f
197 556, // 103 g
198 556, // 104 h
199 222, // 105 i
200 222, // 106 j
201 500, // 107 k
202 222, // 108 l
203 833, // 109 m
204 556, // 110 n
205 556, // 111 o
206 556, // 112 p
207 556, // 113 q
208 333, // 114 r
209 500, // 115 s
210 278, // 116 t
211 556, // 117 u
212 500, // 118 v
213 722, // 119 w
214 500, // 120 x
215 500, // 121 y
216 500, // 122 z
217 334, // 123 {
218 260, // 124 |
219 334, // 125 }
220 584, // 126 ~
221];
222
223/// Character widths for Helvetica-Bold (ASCII 32..=126) in 1/1000 em.
224/// Source: Adobe Helvetica-Bold AFM data.
225const HELVETICA_BOLD_WIDTHS: [u16; 95] = [
226 278, // 32 space
227 333, // 33 !
228 474, // 34 "
229 556, // 35 #
230 556, // 36 $
231 889, // 37 %
232 722, // 38 &
233 238, // 39 '
234 333, // 40 (
235 333, // 41 )
236 389, // 42 *
237 584, // 43 +
238 278, // 44 ,
239 333, // 45 -
240 278, // 46 .
241 278, // 47 /
242 556, // 48 0
243 556, // 49 1
244 556, // 50 2
245 556, // 51 3
246 556, // 52 4
247 556, // 53 5
248 556, // 54 6
249 556, // 55 7
250 556, // 56 8
251 556, // 57 9
252 333, // 58 :
253 333, // 59 ;
254 584, // 60 <
255 584, // 61 =
256 584, // 62 >
257 611, // 63 ?
258 975, // 64 @
259 722, // 65 A
260 722, // 66 B
261 722, // 67 C
262 722, // 68 D
263 667, // 69 E
264 611, // 70 F
265 778, // 71 G
266 722, // 72 H
267 278, // 73 I
268 556, // 74 J
269 722, // 75 K
270 611, // 76 L
271 833, // 77 M
272 722, // 78 N
273 778, // 79 O
274 667, // 80 P
275 778, // 81 Q
276 722, // 82 R
277 667, // 83 S
278 611, // 84 T
279 722, // 85 U
280 667, // 86 V
281 944, // 87 W
282 667, // 88 X
283 667, // 89 Y
284 611, // 90 Z
285 333, // 91 [
286 278, // 92 backslash
287 333, // 93 ]
288 584, // 94 ^
289 556, // 95 _
290 333, // 96 `
291 556, // 97 a
292 611, // 98 b
293 556, // 99 c
294 611, // 100 d
295 556, // 101 e
296 333, // 102 f
297 611, // 103 g
298 611, // 104 h
299 278, // 105 i
300 278, // 106 j
301 556, // 107 k
302 278, // 108 l
303 889, // 109 m
304 611, // 110 n
305 611, // 111 o
306 611, // 112 p
307 611, // 113 q
308 389, // 114 r
309 556, // 115 s
310 333, // 116 t
311 611, // 117 u
312 556, // 118 v
313 778, // 119 w
314 556, // 120 x
315 556, // 121 y
316 500, // 122 z
317 389, // 123 {
318 280, // 124 |
319 389, // 125 }
320 584, // 126 ~
321];
322
323/// Character widths for Times-Roman (ASCII 32..=126) in 1/1000 em.
324/// Source: Adobe Times-Roman AFM data.
325const TIMES_ROMAN_WIDTHS: [u16; 95] = [
326 250, // 32 space
327 333, // 33 !
328 408, // 34 "
329 500, // 35 #
330 500, // 36 $
331 833, // 37 %
332 778, // 38 &
333 180, // 39 '
334 333, // 40 (
335 333, // 41 )
336 500, // 42 *
337 564, // 43 +
338 250, // 44 ,
339 333, // 45 -
340 250, // 46 .
341 278, // 47 /
342 500, // 48 0
343 500, // 49 1
344 500, // 50 2
345 500, // 51 3
346 500, // 52 4
347 500, // 53 5
348 500, // 54 6
349 500, // 55 7
350 500, // 56 8
351 500, // 57 9
352 278, // 58 :
353 278, // 59 ;
354 564, // 60 <
355 564, // 61 =
356 564, // 62 >
357 444, // 63 ?
358 921, // 64 @
359 722, // 65 A
360 667, // 66 B
361 667, // 67 C
362 722, // 68 D
363 611, // 69 E
364 556, // 70 F
365 722, // 71 G
366 722, // 72 H
367 333, // 73 I
368 389, // 74 J
369 722, // 75 K
370 611, // 76 L
371 889, // 77 M
372 722, // 78 N
373 722, // 79 O
374 556, // 80 P
375 722, // 81 Q
376 667, // 82 R
377 556, // 83 S
378 611, // 84 T
379 722, // 85 U
380 722, // 86 V
381 944, // 87 W
382 722, // 88 X
383 722, // 89 Y
384 611, // 90 Z
385 333, // 91 [
386 278, // 92 backslash
387 333, // 93 ]
388 469, // 94 ^
389 500, // 95 _
390 333, // 96 `
391 444, // 97 a
392 500, // 98 b
393 444, // 99 c
394 500, // 100 d
395 444, // 101 e
396 333, // 102 f
397 500, // 103 g
398 500, // 104 h
399 278, // 105 i
400 278, // 106 j
401 500, // 107 k
402 278, // 108 l
403 778, // 109 m
404 500, // 110 n
405 500, // 111 o
406 500, // 112 p
407 500, // 113 q
408 333, // 114 r
409 389, // 115 s
410 278, // 116 t
411 500, // 117 u
412 500, // 118 v
413 722, // 119 w
414 500, // 120 x
415 500, // 121 y
416 444, // 122 z
417 480, // 123 {
418 200, // 124 |
419 480, // 125 }
420 541, // 126 ~
421];
422
423/// Character widths for Times-Bold (ASCII 32..=126) in 1/1000 em.
424/// Source: Adobe Times-Bold AFM data.
425const TIMES_BOLD_WIDTHS: [u16; 95] = [
426 250, // 32 space
427 333, // 33 !
428 555, // 34 "
429 500, // 35 #
430 500, // 36 $
431 1000, // 37 %
432 833, // 38 &
433 278, // 39 '
434 333, // 40 (
435 333, // 41 )
436 500, // 42 *
437 570, // 43 +
438 250, // 44 ,
439 333, // 45 -
440 250, // 46 .
441 278, // 47 /
442 500, // 48 0
443 500, // 49 1
444 500, // 50 2
445 500, // 51 3
446 500, // 52 4
447 500, // 53 5
448 500, // 54 6
449 500, // 55 7
450 500, // 56 8
451 500, // 57 9
452 333, // 58 :
453 333, // 59 ;
454 570, // 60 <
455 570, // 61 =
456 570, // 62 >
457 500, // 63 ?
458 930, // 64 @
459 722, // 65 A
460 667, // 66 B
461 722, // 67 C
462 722, // 68 D
463 667, // 69 E
464 611, // 70 F
465 778, // 71 G
466 778, // 72 H
467 389, // 73 I
468 500, // 74 J
469 778, // 75 K
470 667, // 76 L
471 944, // 77 M
472 722, // 78 N
473 778, // 79 O
474 611, // 80 P
475 778, // 81 Q
476 722, // 82 R
477 556, // 83 S
478 667, // 84 T
479 722, // 85 U
480 722, // 86 V
481 1000, // 87 W
482 722, // 88 X
483 722, // 89 Y
484 667, // 90 Z
485 333, // 91 [
486 278, // 92 backslash
487 333, // 93 ]
488 581, // 94 ^
489 500, // 95 _
490 333, // 96 `
491 500, // 97 a
492 556, // 98 b
493 444, // 99 c
494 556, // 100 d
495 444, // 101 e
496 333, // 102 f
497 500, // 103 g
498 556, // 104 h
499 278, // 105 i
500 333, // 106 j
501 556, // 107 k
502 278, // 108 l
503 833, // 109 m
504 556, // 110 n
505 500, // 111 o
506 556, // 112 p
507 556, // 113 q
508 444, // 114 r
509 389, // 115 s
510 333, // 116 t
511 556, // 117 u
512 500, // 118 v
513 722, // 119 w
514 500, // 120 x
515 500, // 121 y
516 444, // 122 z
517 394, // 123 {
518 220, // 124 |
519 394, // 125 }
520 520, // 126 ~
521];
522
523/// Character widths for Times-Italic (ASCII 32..=126) in 1/1000 em.
524/// Source: Adobe Times-Italic AFM data.
525const TIMES_ITALIC_WIDTHS: [u16; 95] = [
526 250, // 32 space
527 333, // 33 !
528 420, // 34 "
529 500, // 35 #
530 500, // 36 $
531 833, // 37 %
532 778, // 38 &
533 214, // 39 '
534 333, // 40 (
535 333, // 41 )
536 500, // 42 *
537 675, // 43 +
538 250, // 44 ,
539 333, // 45 -
540 250, // 46 .
541 278, // 47 /
542 500, // 48 0
543 500, // 49 1
544 500, // 50 2
545 500, // 51 3
546 500, // 52 4
547 500, // 53 5
548 500, // 54 6
549 500, // 55 7
550 500, // 56 8
551 500, // 57 9
552 333, // 58 :
553 333, // 59 ;
554 675, // 60 <
555 675, // 61 =
556 675, // 62 >
557 500, // 63 ?
558 920, // 64 @
559 611, // 65 A
560 611, // 66 B
561 667, // 67 C
562 722, // 68 D
563 611, // 69 E
564 611, // 70 F
565 722, // 71 G
566 722, // 72 H
567 333, // 73 I
568 444, // 74 J
569 667, // 75 K
570 556, // 76 L
571 833, // 77 M
572 667, // 78 N
573 722, // 79 O
574 611, // 80 P
575 722, // 81 Q
576 611, // 82 R
577 500, // 83 S
578 556, // 84 T
579 722, // 85 U
580 611, // 86 V
581 833, // 87 W
582 611, // 88 X
583 556, // 89 Y
584 556, // 90 Z
585 389, // 91 [
586 278, // 92 backslash
587 389, // 93 ]
588 422, // 94 ^
589 500, // 95 _
590 333, // 96 `
591 500, // 97 a
592 500, // 98 b
593 444, // 99 c
594 500, // 100 d
595 444, // 101 e
596 278, // 102 f
597 500, // 103 g
598 500, // 104 h
599 278, // 105 i
600 278, // 106 j
601 444, // 107 k
602 278, // 108 l
603 722, // 109 m
604 500, // 110 n
605 500, // 111 o
606 500, // 112 p
607 500, // 113 q
608 389, // 114 r
609 389, // 115 s
610 278, // 116 t
611 500, // 117 u
612 444, // 118 v
613 667, // 119 w
614 444, // 120 x
615 444, // 121 y
616 389, // 122 z
617 400, // 123 {
618 275, // 124 |
619 400, // 125 }
620 541, // 126 ~
621];
622
623/// Character widths for Times-BoldItalic (ASCII 32..=126) in 1/1000 em.
624/// Source: Adobe Times-BoldItalic AFM data.
625const TIMES_BOLD_ITALIC_WIDTHS: [u16; 95] = [
626 250, // 32 space
627 389, // 33 !
628 555, // 34 "
629 500, // 35 #
630 500, // 36 $
631 833, // 37 %
632 778, // 38 &
633 278, // 39 '
634 333, // 40 (
635 333, // 41 )
636 500, // 42 *
637 570, // 43 +
638 250, // 44 ,
639 333, // 45 -
640 250, // 46 .
641 278, // 47 /
642 500, // 48 0
643 500, // 49 1
644 500, // 50 2
645 500, // 51 3
646 500, // 52 4
647 500, // 53 5
648 500, // 54 6
649 500, // 55 7
650 500, // 56 8
651 500, // 57 9
652 333, // 58 :
653 333, // 59 ;
654 570, // 60 <
655 570, // 61 =
656 570, // 62 >
657 500, // 63 ?
658 832, // 64 @
659 667, // 65 A
660 667, // 66 B
661 667, // 67 C
662 722, // 68 D
663 667, // 69 E
664 667, // 70 F
665 722, // 71 G
666 778, // 72 H
667 389, // 73 I
668 500, // 74 J
669 667, // 75 K
670 611, // 76 L
671 889, // 77 M
672 722, // 78 N
673 722, // 79 O
674 611, // 80 P
675 722, // 81 Q
676 667, // 82 R
677 556, // 83 S
678 611, // 84 T
679 722, // 85 U
680 667, // 86 V
681 889, // 87 W
682 667, // 88 X
683 611, // 89 Y
684 611, // 90 Z
685 333, // 91 [
686 278, // 92 backslash
687 333, // 93 ]
688 570, // 94 ^
689 500, // 95 _
690 333, // 96 `
691 500, // 97 a
692 500, // 98 b
693 444, // 99 c
694 500, // 100 d
695 444, // 101 e
696 333, // 102 f
697 500, // 103 g
698 556, // 104 h
699 278, // 105 i
700 278, // 106 j
701 500, // 107 k
702 278, // 108 l
703 778, // 109 m
704 556, // 110 n
705 500, // 111 o
706 556, // 112 p
707 556, // 113 q
708 389, // 114 r
709 389, // 115 s
710 278, // 116 t
711 556, // 117 u
712 444, // 118 v
713 667, // 119 w
714 500, // 120 x
715 444, // 121 y
716 389, // 122 z
717 348, // 123 {
718 220, // 124 |
719 348, // 125 }
720 570, // 126 ~
721];
722
723/// Courier uses a uniform width of 600 for all characters.
724const COURIER_WIDTH: u16 = 600;
725
726/// Default width for characters outside the mapped range (1/1000 em).
727const DEFAULT_WIDTH: u16 = 278;
728
729/// Font metrics for built-in PDF fonts.
730pub struct FontMetrics;
731
732impl FontMetrics {
733 /// Returns the width of a character in 1/1000 em units.
734 pub fn char_width(font: BuiltinFont, ch: char) -> u16 {
735 // Courier variants are monospaced
736 match font {
737 BuiltinFont::Courier
738 | BuiltinFont::CourierBold
739 | BuiltinFont::CourierOblique
740 | BuiltinFont::CourierBoldOblique => {
741 return COURIER_WIDTH;
742 }
743 // Symbol/ZapfDingbats use default width (Phase 1)
744 BuiltinFont::Symbol | BuiltinFont::ZapfDingbats => {
745 return DEFAULT_WIDTH;
746 }
747 _ => {}
748 }
749
750 let code = ch as u32;
751 if code < 32 || code > 126 {
752 return DEFAULT_WIDTH;
753 }
754 let index = (code - 32) as usize;
755 match font {
756 BuiltinFont::Helvetica | BuiltinFont::HelveticaOblique => HELVETICA_WIDTHS[index],
757 BuiltinFont::HelveticaBold | BuiltinFont::HelveticaBoldOblique => {
758 HELVETICA_BOLD_WIDTHS[index]
759 }
760 BuiltinFont::TimesRoman => TIMES_ROMAN_WIDTHS[index],
761 BuiltinFont::TimesBold => TIMES_BOLD_WIDTHS[index],
762 BuiltinFont::TimesItalic => TIMES_ITALIC_WIDTHS[index],
763 BuiltinFont::TimesBoldItalic => TIMES_BOLD_ITALIC_WIDTHS[index],
764 // Already handled above
765 _ => DEFAULT_WIDTH,
766 }
767 }
768
769 /// Measures the width of a text string in points.
770 pub fn measure_text(text: &str, font: BuiltinFont, font_size: f64) -> f64 {
771 let total: u32 = text
772 .chars()
773 .map(|ch| Self::char_width(font, ch) as u32)
774 .sum();
775 total as f64 * font_size / 1000.0
776 }
777
778 /// Returns the line height for a given font size
779 /// (1.2x multiplier).
780 pub fn line_height(_font: BuiltinFont, font_size: f64) -> f64 {
781 font_size * 1.2
782 }
783}