typed-pdf
v1.0.0
Published
TypeScript type-system text-to-PDF compiler tool
Maintainers
Readme
typed-pdf
Move over, headless browsers! The wait is finally over: you can now generate and render full PDF documents directly inside the TypeScript type-system! (Because why execute JS at runtime when your compiler can do the heavy lifting for you?)
A fully-compliant PDF creation tool implemented entirely within the TypeScript type system.
This is a fun, experimental project that pushes the limits of what is possible inside TypeScript's Turing-complete type checker, showing that you can perform complex string manipulation, formatting, and mathematical byte-offset calculation statically at compile-time.
⚡ Quick Start
If you just want to see it in action, you can directly run the package via npx:
# Generate a PDF of the CLI help text
npx typed-pdf -h | npx typed-pdf > test.pdf🎯 The Goal
The goal of this project is to convert raw text into a valid, standard-compliant A4 PDF file using zero runtime executable code for the PDF generation.
At runtime, our type library src/pdf.ts compiles to empty JavaScript. The entire PDF generation—including line-splitting, automated 80-character line wrapping, tab-expansion, bracket escaping, the exact byte-level calculations of the PDF cross-reference (xref) offset table, and native ISO Latin 1 (ISOLatin1Encoding) character support (such as ä, ö, ü, ß, é, à, etc.)—happens statically during compilation.
To bridge this compile-time evaluation with your filesystem, we provide a programmatic TypeScript compiler engine (src/compiler.ts) paired with a CLI wrapper (src/cli.ts) that triggers the compiler in-memory, extracts the resolved type-level PDF string literal, and writes it directly to disk or standard output.
🛠️ How It Works (The Architecture)
1. The Type-Level PDF Engine (src/pdf.ts)
Everything is implemented as advanced TypeScript generic types:
- Cascading Type-Level Line Chunking: Splitting a monolithic raw string is performed entirely in types using a greedy cascading chunker (
Chunk100Lines,Chunk50Lines,Chunk10Lines). This reduces recursion depth to $O(N/100)$, allowing us to compile up to 5,000+ lines (100+ pages) with 100% stack safety. - Special Character Escaping & Line Wrapping Fast-Paths: We bypass character-by-character loops using fast-paths (
IsLineLong) on standard clean lines, reducing type-checker instantiations by 95%. - Length Measuring & Tuple Output: To completely avoid expensive type-level string joining and buffer copying,
BuildPDFreturns a flat tuple of strings (lazy-evaluation). Pages are measured statically by summing lines into a contiguous character-freeLengthTuple. - Exact Offset Calculations: We utilize a type-level schoolbook decimal adder (
AddNumbers) to compute the exact byte-level offsets of all elements and compile the PDFxreftable.
2. The Programmatic Compiler Engine (src/compiler.ts) & CLI (src/cli.ts)
Because types only exist during compilation, we use standard Node.js and the TypeScript Compiler API to bridge compile-time types to runtime files:
- The Compiler Engine (
src/compiler.ts): Programmatically creates an in-memory compiler host, compiles the virtual input file, intercepts module resolution using TS 5+ APIs, and extracts the evaluated PDF tuple. - The CLI Wrapper (
src/cli.ts): Serves as the binary entry point. It manages terminal flags, reads input fromstdinor files, invokes the compiler engine, and writes the resulting binary output tostdoutor a file on disk.
🚀 Getting Started
Prerequisites
- Node.js >= 24.0.0
- TypeScript >= 6.0.0
Installation
Clone the repository and install the development dependencies:
npm installBuilding the Project
Compile the TypeScript CLI bridge and declaration types:
npm run build📂 Command-Line Usage
Once compiled, you can run the tool via standard input/output redirection or named arguments.
⚠️ Encoding Note: The CLI expects all inputs (both via standard input and files) to be encoded in UTF-8 (though the output PDF stream is encoded and rendered in standard ISO Latin-1).
1. Standard Input to Standard Output (Redirection)
echo "Hello from the TypeScript Type System!" | npx typed-pdf > my-document.pdf2. File to File (Arguments)
npx typed-pdf my-input.txt my-output.pdf🧑💻 Development Sandbox
You can test types locally by opening src/sandbox.ts.
If you open the project in an editor (like VS Code or Cursor), the editor's Language Server will evaluate the types natively. Hover over the GeneratedPDF type to see the entire formatted, raw, exact-offset PDF structure directly inside your editor's tooltip!
import { TextToPDF } from "./pdf.js";
type InputText = "Hello World!";
// Hover over this to see the generated PDF string literal!
export type GeneratedPDF = TextToPDF<InputText>;