Skip to main content

stet_fonts/
lib.rs

1// stet - A PostScript Interpreter
2// Copyright (c) 2026 Scott Bowman
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! Pure-Rust parsers for Type 1, CFF / Type 2, and TrueType fonts, plus
6//! the shared geometry primitives they produce.
7//!
8//! This crate has **no stet-specific dependencies**, so it is usable on
9//! its own for font-parsing workflows that don't need a PostScript
10//! interpreter or a PDF renderer. The rest of the stet workspace uses it
11//! as the foundational font/geometry layer.
12//!
13//! # What's here
14//!
15//! - [`geometry`] — `Matrix` (affine transforms), `PathSegment`, `PsPath`
16//! - [`type1_parser`] / [`charstring`] — Adobe Type 1 parsing and
17//!   charstring interpretation (eexec decryption included)
18//! - [`cff_parser`] / [`type2_charstring`] — Compact Font Format parser
19//!   and Type 2 charstring interpreter
20//! - [`truetype`] — TrueType table accessors, simple + composite glyph
21//!   resolution, `glyf` → [`PsPath`] conversion
22//! - [`encoding`] — StandardEncoding, ISOLatin1Encoding, SymbolEncoding,
23//!   MacRoman, etc.
24//! - [`agl`] — Adobe Glyph List (glyph name ↔ Unicode)
25//! - [`system_fonts`] — platform font directory discovery and 35-standard
26//!   PostScript → URW substitution
27//!
28//! # Quick example
29//!
30//! Parse a Type 1 font and inspect its glyph dictionary:
31//!
32//! ```no_run
33//! use stet_fonts::type1_parser::parse_type1;
34//!
35//! let data = std::fs::read("NimbusRoman-Regular.t1")?;
36//! let font = parse_type1(&data)?;
37//! println!("font: {} ({} glyphs)", font.font_name, font.charstrings.len());
38//! # Ok::<(), Box<dyn std::error::Error>>(())
39//! ```
40//!
41//! # Scope and non-goals
42//!
43//! The surface is shaped around what stet needs to render PDFs and run a
44//! PostScript Level 3 interpreter — whole-font parsing with glyph
45//! outlines as `PsPath`. There is no text layout, no shaping, no colour
46//! fonts, no variable-font interpolation, and no OpenType
47//! GSUB/GPOS/BASE/GDEF tables. For a general-purpose font-layout
48//! library, consider `ttf-parser`, `rustybuzz`, or `skrifa`.
49
50pub mod agl;
51pub mod cff_parser;
52pub mod charstring;
53pub mod encoding;
54pub mod geometry;
55pub mod system_fonts;
56pub mod truetype;
57pub mod type1_parser;
58pub mod type2_charstring;
59
60// Re-export core geometry types at crate root for convenience.
61pub use geometry::{Matrix, PathSegment, PsPath};
62
63/// Standard PostScript font name → URW replacement filename.
64pub const FONT_SUBSTITUTIONS: &[(&str, &str)] = &[
65    // Times family → Nimbus Roman
66    ("Times-Roman", "NimbusRoman-Regular"),
67    ("Times-Bold", "NimbusRoman-Bold"),
68    ("Times-Italic", "NimbusRoman-Italic"),
69    ("Times-BoldItalic", "NimbusRoman-BoldItalic"),
70    // Helvetica family → Nimbus Sans
71    ("Helvetica", "NimbusSans-Regular"),
72    ("Helvetica-Bold", "NimbusSans-Bold"),
73    ("Helvetica-Oblique", "NimbusSans-Italic"),
74    ("Helvetica-BoldOblique", "NimbusSans-BoldItalic"),
75    // Helvetica Narrow → Nimbus Sans Narrow
76    ("Helvetica-Narrow", "NimbusSansNarrow-Regular"),
77    ("Helvetica-Narrow-Bold", "NimbusSansNarrow-Bold"),
78    ("Helvetica-Narrow-Oblique", "NimbusSansNarrow-Oblique"),
79    (
80        "Helvetica-Narrow-BoldOblique",
81        "NimbusSansNarrow-BoldOblique",
82    ),
83    // Courier family → Nimbus Mono PS
84    ("Courier", "NimbusMonoPS-Regular"),
85    ("Courier-Bold", "NimbusMonoPS-Bold"),
86    ("Courier-Oblique", "NimbusMonoPS-Italic"),
87    ("Courier-BoldOblique", "NimbusMonoPS-BoldItalic"),
88    // Arial family → Nimbus Sans (metric-compatible)
89    ("ArialMT", "NimbusSans-Regular"),
90    ("Arial-BoldMT", "NimbusSans-Bold"),
91    ("Arial-ItalicMT", "NimbusSans-Italic"),
92    ("Arial-BoldItalicMT", "NimbusSans-BoldItalic"),
93    ("Arial", "NimbusSans-Regular"),
94    ("Arial-Bold", "NimbusSans-Bold"),
95    ("Arial-Italic", "NimbusSans-Italic"),
96    ("Arial-BoldItalic", "NimbusSans-BoldItalic"),
97    // Symbol fonts
98    ("Symbol", "StandardSymbolsPS"),
99    ("ZapfDingbats", "D050000L"),
100    // Palatino → P052
101    ("Palatino-Roman", "P052-Roman"),
102    ("Palatino-Bold", "P052-Bold"),
103    ("Palatino-Italic", "P052-Italic"),
104    ("Palatino-BoldItalic", "P052-BoldItalic"),
105    // New Century Schoolbook → C059
106    ("NewCenturySchlbk-Roman", "C059-Roman"),
107    ("NewCenturySchlbk-Bold", "C059-Bold"),
108    ("NewCenturySchlbk-Italic", "C059-Italic"),
109    ("NewCenturySchlbk-BoldItalic", "C059-BdIta"),
110    // Bookman → URWBookman
111    ("Bookman-Light", "URWBookman-Light"),
112    ("Bookman-LightItalic", "URWBookman-LightItalic"),
113    ("Bookman-Demi", "URWBookman-Demi"),
114    ("Bookman-DemiItalic", "URWBookman-DemiItalic"),
115    // AvantGarde → URWGothic
116    ("AvantGarde-Book", "URWGothic-Book"),
117    ("AvantGarde-BookOblique", "URWGothic-BookOblique"),
118    ("AvantGarde-Demi", "URWGothic-Demi"),
119    ("AvantGarde-DemiOblique", "URWGothic-DemiOblique"),
120    // TimesNewRoman variants (common in Windows-generated PDFs)
121    ("TimesNewRoman", "NimbusRoman-Regular"),
122    ("TimesNewRomanPS", "NimbusRoman-Regular"),
123    ("TimesNewRomanPSMT", "NimbusRoman-Regular"),
124    ("TimesNewRoman-Bold", "NimbusRoman-Bold"),
125    ("TimesNewRoman,Bold", "NimbusRoman-Bold"),
126    ("TimesNewRomanPS-Bold", "NimbusRoman-Bold"),
127    ("TimesNewRomanPS-BoldMT", "NimbusRoman-Bold"),
128    ("TimesNewRoman-Italic", "NimbusRoman-Italic"),
129    ("TimesNewRoman,Italic", "NimbusRoman-Italic"),
130    ("TimesNewRomanPS-Italic", "NimbusRoman-Italic"),
131    ("TimesNewRomanPS-ItalicMT", "NimbusRoman-Italic"),
132    ("TimesNewRoman-BoldItalic", "NimbusRoman-BoldItalic"),
133    ("TimesNewRomanPS-BoldItalic", "NimbusRoman-BoldItalic"),
134    ("TimesNewRomanPS-BoldItalicMT", "NimbusRoman-BoldItalic"),
135    // ZapfChancery
136    ("ZapfChancery-MediumItalic", "Z003-MediumItalic"),
137];
138
139/// Look up a font substitution: "Helvetica" → "NimbusSans-Regular".
140pub fn find_substitution(name: &str) -> Option<&'static str> {
141    for &(ps_name, urw_name) in FONT_SUBSTITUTIONS {
142        if ps_name == name {
143            return Some(urw_name);
144        }
145    }
146    None
147}