| # Project Proposal: Frankenfonts — Hybrid Rust Font Parsing & FreeType/AGG Rasterization |
| |
| **Author:** Tom Sepez (`tsepez@google.com`) |
| **Target Project:** PDFium (`core/fxge`, standalone, and Chromium integration) |
| **Status:** Proposed Architecture |
| |
| --- |
| |
| ## 1. Executive Summary |
| |
| PDFium historically relies on **FreeType** for both binary font table parsing |
| and glyph pixel rasterization. However, the overwhelming majority of historical |
| vulnerabilities in font engines stem from **parsing untrusted, malformed font |
| tables** (such as embedded TrueType, OpenType, and CFF streams in malicious |
| PDFs). |
| |
| **Project Frankenfonts** proposes a hybrid font architecture: |
| 1. **Frontend (Parsing & Hinting):** Use Google's memory-safe **Fontations** |
| (`read-fonts` + `skrifa`) Rust crates to parse font tables, decode CMaps, |
| compute metrics, and execute TrueType VM / CFF hinting instructions in safe |
| Rust. |
| 2. **Backend (Rasterization):** Pass the extracted vector outlines directly |
| into **FreeType**'s scanline rasterizer (`FT_Render_Glyph` in `ftsmooth.c` / |
| `ftgrays.c`) or PDFium's **AGG** rasterizer to generate anti-aliased bitmap |
| masks. |
| 3. **Decoupling from Skia:** Remove the `pdf_use_skia` requirement from |
| `pdf_enable_fontations`, allowing standalone PDFium (including AGG builds) |
| to benefit from memory-safe font parsing. |
| |
| --- |
| |
| ## 2. Motivation: The FreeType Dilemma |
| |
| ### 2.1. The Vulnerability Surface of Embedded PDF Fonts |
| PDF files frequently embed custom, subsetted, or heavily modified font files. |
| Over the last two decades, embedded font parsing in FreeType has been one of the |
| primary attack vectors for remote code execution and memory corruption in |
| document viewers (integer overflows in table offsets, malformed CMap subtables, |
| corrupt CFF charstrings). |
| |
| ### 2.2. Parsing vs. Rasterization |
| FreeType performs two fundamentally distinct tasks: |
| * **Task A: Binary Font Parsing & Hinting (High Risk):** Reading raw bytes from |
| disk/memory into C structs (`CMap`, `Hmtx`, `OS/2`, `Name`, `Post`, `glyf`, |
| `CFF`) and running Turing-complete bytecode interpreters in C. |
| * **Task B: Scanline Rasterization (Low Risk / High Fidelity):** Taking |
| mathematical Bézier curves and generating anti-aliased 8bpp grayscale pixel |
| masks (`ftgrays.c`) or subpixel LCD masks (`ftsmooth.c`). |
| |
| FreeType's rasterization math is battle-tested and produces the exact |
| anti-aliasing curves PDFium expects. The risk is located in **Task A |
| (Parsing)**. |
| |
| --- |
| |
| ## 3. The Frankenfonts Architecture |
| |
| ``` |
| ┌──────────────────────────────────────────────┐ |
| │ Untrusted PDF Font Data │ |
| └──────────────────────┬───────────────────────┘ |
| │ |
| ▼ |
| ┌──────────────────────────────────────────────┐ |
| │ Fontations Frontend (Pure Safe Rust) │ |
| │ - `read-fonts`: Safe Table Parsing │ |
| │ - `skrifa`: CMaps, Metrics, BBoxes │ |
| │ - `HintingInstance`: TrueType / CFF VM │ |
| │ - `OutlinePen`: Bézier Curve Extraction │ |
| └──────────────────────┬───────────────────────┘ |
| │ |
| ▼ Extracted `FT_Outline` / `CFX_Path` |
| ┌──────────────────────────────────────────────┐ |
| │ Rasterization Backend (FreeType / AGG) │ |
| │ - Feeds safe Bézier curves into rasterizer│ |
| │ - FreeType `FT_Render_Glyph` or AGG AA │ |
| └──────────────────────┬───────────────────────┘ |
| │ |
| ▼ |
| ┌──────────────────────────────────────────────┐ |
| │ CFX_GlyphBitmap │ |
| │ (Anti-Aliased Mask) │ |
| └──────────────────────────────────────────────┘ |
| ``` |
| |
| --- |
| |
| ## 4. Key Architectural Benefits |
| |
| ### 4.1. Complete Elimination of Font Parsing CVEs |
| Because `read-fonts` and `skrifa` are written in 100% memory-safe Rust with |
| strict bounds-checking, zero-copy slicing, and fuzz-tested arithmetic, PDFium |
| becomes completely immune to font-table parsing vulnerabilities. FreeType is |
| never exposed to raw untrusted font byte streams. |
| |
| ### 4.2. High Fidelity & Perceptual Parity |
| By feeding Skrifa's extracted vector outlines into FreeType's existing |
| rasterizer (`ftsmooth.c` / `ftgrays.c`), the resulting bitmap masks maintain visual fidelity |
| with legacy FreeType output. Subpixel hinting variances inherent to independent |
| TrueType VM implementations are handled via windowed MSE fuzzy test matching |
| and expectation suffixes. |
| |
| ### 4.3. Full Standalone Independence (No Skia Required) |
| Currently, `pdf_enable_fontations` is artificially gated on `pdf_use_skia = |
| true`. Frankenfonts decouples Fontations from Skia: |
| * `skrifa` and `read_fonts` are vendored directly via `gnrt` in |
| `//third_party/rust/`. |
| * Standalone PDFium (`pdf_use_agg = true`, `pdf_use_skia = false`) can use |
| Fontations with zero dependencies on Skia. |
| |
| --- |
| |
| ## 5. Implementation Roadmap |
| |
| ### Phase 1: Decouple GN Flags & Public API |
| * Updated `pdfium.gni` to assert that `pdf_enable_fontations` only requires |
| `enable_rust = true`. |
| * Defined `PDF_ENABLE_FONTATIONS` in `BUILD.gn` whenever |
| `pdf_enable_fontations` is enabled. |
| * Exposed `CFX_FontMgr::GetFontBackend()` and `FPDF_InitLibraryWithConfig()` |
| runtime selection in AGG builds. |
| * Wired `--fontations` CLI flag into `pdfium_test`, `pdfium_embeddertests`, |
| and `pdfium_unittests`. |
| |
| ### Phase 2: Route Font Table & Outline Queries to `skrifa` |
| * Implement `hinted_outline()` in `main.rs` using `skrifa::outline::HintingInstance`. |
| * Connect `CFX_Face::RenderGlyph()` to extract outlines through Skrifa at |
| the target display PPEM scale and rasterize through `FT_Render_Glyph()`. |
| |
| --- |
| |
| ## 6. Conclusion |
| |
| Project Frankenfonts delivers **uncompromising memory safety against hostile |
| font exploits in untrusted PDFs** paired with **FreeType scanline |
| rasterization**, keeping standalone PDFium lean, fast, and secure. |