To create a PDF from scratch, use Document::with_version(version) to initialize a new document. You can then build the document structure by adding objects (like fonts, resources, and pages) using doc.add_object() or doc.new_object_id().
Key concepts for manual construction:
- Dictionaries: Use the
dictionary! macro to create key-value relationships for PDF objects (e.g., Font, Page, Catalog). - Streams: Use
Stream::new(dictionary, bytes) to wrap content like text operations. - Content: Use
Content and Operation to define the actual drawing/text instructions (e.g., BT for Begin Text, Tj for text strings). - Hierarchy: A standard PDF requires a
Catalog (the root) which points to a Pages tree, which in turn contains individual Page objects.
use lopdf::dictionary;
use lopdf::{Document, Object, Stream};
use lopdf::content::{Content, Operation};
// `with_version` specifes the PDF version this document compliess with.
let mut doc = Document::with_version("1.5");
// "Pages" is the root node of the page tree.
let pages_id = doc.new_object_id();
// Fonts are dictionaries.
let font_id = doc.add_object(dictionary! {
"Type" => "Font",
"Subtype" => "Type1",
"BaseFont" => "Courier",
});
// Resource dictionaries contain fonts used by pages.
let resources_id = doc.add_object(dictionary! {
"Font" => dictionary! {
"F1" => font_id,
},
});
// Content defines the operations (operators and operands).
let content = Content {
operations: vec![
Operation::new("BT", vec![]),
Operation::new("Tf", vec!["F1".into(), 48.into()]),
Operation::new("Td", vec![100.into(), 600.into()]),
Operation::new("Tj", vec![Object::string_literal("Hello World!")]),
Operation::new("ET", vec![]),
],
};
let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
// Page dictionary
let page_id = doc.add_object(dictionary! {
"Type" => "Page",
"Parent" => pages_id,
"Contents" => content_id,
});
// Pages tree root
let pages = dictionary! {
"Type" => "Pages",
"Kids" => vec![page_id.into()],
"Count" => 1,
"Resources" => resources_id,
"MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
};
doc.objects.insert(pages_id, Object::Dictionary(pages));
// Catalog
let catalog_id = doc.add_object(dictionary! {
"Type" => "Catalog",
"Pages" => pages_id,
});
doc.trailer.set("Root", catalog_id);
doc.compress();
// Save
doc.save("example.pdf").unwrap();