printpdf
repository·master·Indexed 22 days ago
https://github.com/fschutt/printpdfA Rust library for creating, reading, writing, and rendering PDF documents. It supports advanced typography, graphics, layers, bookmarks, and embedded fonts. The library includes an experimental HTML-to-PDF rendering engine for layouts like reports and books with automatic page-breaking, as well as a WebAssembly (WASM) module for JavaScript integration.
What's inside printpdf
- printpdf is a Rust library designed for creating, reading, writing, and rendering PDF documents. It supports advanced features like layers, bookmarks, link annotations, and embedded fonts. It also provides an experimental HTML-to-PDF rendering engine for basic layouts like reports and books with automatic page-breaking.
Understand PdfResources and shared assets
masterThe
PdfResourcesstructure contains assets that are shared across multiple pages of a document to optimize file size and consistency.Serialization Note:
- Fonts: Parsed fonts are serialized as base64-encoded data URL strings, mapped by a
FontId(UUID). - XObjects: Images and other XObjects are also serialized as base64-encoded data URL strings, mapped by an
XObjectId(UUID).
interface PdfResources { fonts: { [uuid: string]: string }; xobjects: { [uuid: string]: XObject }; extgstates: { [uuid: string]: ExtendedGraphicsState }; layers: { [uuid: string]: Layer }; }- Fonts: Parsed fonts are serialized as base64-encoded data URL strings, mapped by a
Understand the PdfDocument structure
masterThe
PdfDocumentinterface is the primary data structure representing a parsed PDF document in the printpdf.js API. It aggregates metadata, shared resources, bookmarks, and the individual pages that make up the document.interface PdfDocument { metadata: PdfMetadata; resources: PdfResources; bookmarks: { [uuid: string]: PageAnnotation }; pages: PdfPage[]; }Understand the printpdf XML structure
masterThe
printpdfXML syntax uses a structure similar to HTML to define PDF documents. The<html title="...">node is the root, containing a<head>for metadata, styles, and recurring elements, and a<body>for the main content.Key Structural Elements
<head>: Contains the<style>block,<header>, and<footer>.<header>: Defines content to be rendered at the top of pages. Use theexclude-pagesattribute to skip rendering on specific pages (e.g.,<header exclude-pages="1">skips the first page).<footer>: Defines content to be rendered at the bottom of pages.<style>: Used for global CSS rules. Note: Styles defined here are applied after inline styles to allow overriding preconfigured components.<body>: Contains the document content. The<body>node automatically handles page breaks based on content size.
Layout and Sizing
- Page Dimensions:
100%width or height corresponds to the actual page width or height. - Image Scaling: In the PDF output,
1pxis equivalent to1mm. - Flexbox Layout: The layout system behaves similarly to CSS Flexbox. Elements are first expanded to their minimum size, then space is distributed to elements with
flex-grow > 0up to theirmax-widthormax-heightlimits.
<html title="Document title"> <head> <header exclude-pages="1"> <p style="color:red">Header content</p> </header> <footer style="color:black"> <p>Footer content</p> </footer> <style> * { color: red; } </style> </head> <body style="padding:10mm"> <div style="width:100%; height:100%;"> <h1>Title Page</h1> </div> <div style="display:flex; flex-grow:1;"> <!-- Flexbox content --> </div> </body> </html>Understand XObject types and structures
masterIn
printpdf.js, anXObjectis an external object referenced outside the main PDF content stream. It is a tagged enum that can take one of three forms:- image: Contains Base64-encoded image data.
- form: A reusable content stream (Note: this is a PDF Form XObject, not a PDF interactive form).
- external: An external stream of graphics operations.
Use
XObjectwhen you need to define reusable graphics or embedded images that are called via the/Dooperator.type XObject = | { type: "image"; data: string } | { type: "form"; data: FormXObject } | { type: "external"; data: ExternalXObject };Understand PdfMetadata and PdfDocumentInfo
masterMetadata in printpdf.js is managed via
PdfMetadata, which synchronizes standard document information (PdfDocumentInfo) and XMP metadata.Note on Data Types: While the underlying Rust implementation uses specific unit types, the JSON API serializes dates as ISO format strings and the PDF conformance as a kebab-case string.
interface PdfMetadata { info: PdfDocumentInfo; xmp: XmpMetadata | null; } interface PdfDocumentInfo { trapped: boolean; version: number; creationDate: string; modificationDate: string; metadataDate: string; conformance: string; documentTitle: string; author: string; creator: string; producer: string; keywords: string[]; subject: string; identifier: string; }Understand the PdfPage structure
masterA
PdfPagerepresents a single page within a PDF. It defines the physical and clipping boundaries of the page using boxes (mediaBox, trimBox, cropBox) measured in points (pt), and contains a list of operations (ops) used to render the page content.interface PdfPage { mediaBox: Rect; trimBox: Rect; cropBox: Rect; ops: Op[]; }Understand the printpdf.js WASM API response format
masterAll functions in the WASM API take a stringified JSON object as input and return a stringified JSON object as output. When parsed, the output follows this structure:
{ status: number, // 0 = success, non-zero = error data: T | string // The actual data (of type T) or an error string }Serialization Rules:
- Enums: Rust enums are converted to
kebab-case. Tagged enums usetypefor the variant anddatafor the payload (e.g.,MyEnum::VariantType { data }becomes{ type: "variant-type", data: ... }). - Structs: Rust struct fields are converted to
camelCase(e.g.,page_heightbecomespageHeight).
- Enums: Rust enums are converted to
When to choose printpdf
masterSelect
printpdfif your project requires:- Both reading and writing capabilities in a single library.
- Strong graphics and SVG support.
- Font embedding with Unicode support.
- Direct control over PDF structures combined with convenience functions.
- HTML conversion capabilities (currently experimental).
Understand PDF page operations (Op type)
masterIn
printpdf.js, a PDF page is defined by a stream of operations of typeOp. This is a tagged enum where each variant represents a specific PDF command (e.g., drawing, text, color settings, or state management). Developers interacting with the low-level data structures must use these operation types to construct or manipulate page content.Common operation categories include:
- Graphics State:
save-graphics-state,restore-graphics-state,load-graphics-state. - Text Operations:
start-text-section,end-text-section,set-font,show-text,set-text-cursor. - Drawing:
draw-line,draw-rectangle,draw-polygon. - Styling:
set-fill-color,set-outline-color,set-line-dash-pattern,set-text-rendering-mode. - Layers & Content:
begin-layer,end-layer,begin-marked-content.
type Op = | { type: "marker"; data: Marker } | { type: "set-color-space-stroke"; data: SetColorSpace } | { type: "set-color-space-fill"; data: SetColorSpace } | { type: "begin-layer"; data: BeginLayer } | { type: "end-layer"; data: EndLayer } | { type: "save-graphics-state" } | { type: "restore-graphics-state" } | { type: "load-graphics-state"; data: LoadGraphicsState } | { type: "start-text-section" } | { type: "end-text-section" } | { type: "set-font"; data: SetFont } | { type: "show-text"; data: ShowText } | { type: "add-line-break" } | { type: "set-line-height"; data: SetLineHeight } | { type: "set-word-spacing"; data: SetWordSpacing } | { type: "set-text-cursor"; data: SetTextCursor } | { type: "set-fill-color"; data: SetFillColor } | { type: "set-outline-color"; data: SetOutlineColor } | { type: "set-outline-thickness"; data: SetOutlineThickness } | { type: "set-line-dash-pattern"; data: SetLineDashPattern } | { type: "set-line-join-style"; data: SetLineJoinStyle } | { type: "set-line-cap-style"; data: SetLineCapStyle } | { type: "set-miter-limit"; data: SetMiterLimit } | { type: "set-text-rendering-mode"; data: SetTextRenderingMode } | { type: "set-character-spacing"; data: SetCharacterSpacing } | { type: "set-line-offset"; data: SetLineOffset } | { type: "draw-line"; data: DrawLine } | { type: "draw-rectangle"; data: DrawRectangle } | { type: "draw-polygon"; data: DrawPolygon } | { type: "set-transformation-matrix"; data: SetTransformationMatrix } | { type: "set-text-matrix"; data: SetTextMatrix } | { type: "link-annotation"; data: LinkAnnotationOp } | { type: "use-xobject"; data: UseXobject } | { type: "move-text-cursor-and-set-leading"; data: MoveTextCursorAndSetLeading } | { type: "set-rendering-intent"; data: SetRenderingIntent } | { type: "set-horizontal-scaling"; data: SetHorizontalScaling } | { type: "begin-inline-image" } | { type: "begin-inline-image-data" } | { type: "end-inline-image" } | { type: "begin-marked-content"; data: BeginMarkedContent } | { type: "begin-marked-content-with-properties"; data: BeginMarkedContentWithProperties } | { type: "begin-optional-content"; data: BeginOptionalContent } | { type: "define-marked-content-point"; data: DefineMarkedContentPoint } | { type: "end-marked-content" } | { type: "end-marked-content-with-properties" } | { type: "end-optional-content" } | { type: "begin-compatibility-section" } | { type: "end-compatibility-section" } | { type: "move-to-next-line-show-text"; data: MoveToNextLineShowText } | { type: "set-spacing-move-and-show-text"; data: SetSpacingMoveAndShowText } | { type: "paint-shading"; data: PaintShading } | { type: "unknown"; data: Unknown };- Graphics State:
Initialize the printpdf.js WASM module
masterBefore calling any PDF functions, you must initialize the WebAssembly module using the
initfunction. Ensure thatprintpdf_bg.wasmis located in the same directory asprintpdf.js.import init, { Pdf_HtmlToDocument, Pdf_BytesToDocument, Pdf_ResourcesForPage, Pdf_PageToSvg, Pdf_DocumentToBytes, } from './pkg/printpdf.js'; async function main() { // Initialize the WASM await init(); // Now we can safely call our PDF functions // ... } main().catch(console.error);Create a basic PDF document
masterTo create a minimal PDF, initialize a
PdfDocument, define aPdfPagewith specific dimensions (using units likeMm), and provide a list ofOp(operations). Finally, call.save()withPdfSaveOptionsto generate the PDF bytes.use printpdf::*; fn main() { let mut doc = PdfDocument::new("My first PDF"); let page1_contents = vec![Op::Marker { id: "debugging-marker".to_string() }]; let page1 = PdfPage::new(Mm(210.0), Mm(297.0), page1_contents); let mut warnings = Vec::new(); let pdf_bytes: Vec<u8> = doc .with_pages(vec![page1]) .save(&PdfSaveOptions::default(), &mut warnings); }