This guide covers how to create and edit PDF documents using pdf_oxide's creation API.
- Overview
- Quick Start
- High-Level API
- PdfBuilder Options
- Low-Level API
- Image Embedding
- PDF Editing
- Advanced Features
- Python Bindings
pdf_oxide provides multiple APIs for PDF creation:
| API Level | Use Case | Flexibility |
|---|---|---|
Pdf::from_markdown() |
Quick document creation | Simple |
PdfBuilder |
Customized documents | Medium |
DocumentBuilder |
Full control over layout | High |
PdfWriter |
Low-level PDF structure | Maximum |
use pdf_oxide::api::Pdf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let pdf = Pdf::from_markdown("# Hello World\n\nThis is my PDF.")?;
pdf.save("output.pdf")?;
Ok(())
}use pdf_oxide::api::Pdf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let pdf = Pdf::from_html("<h1>Hello</h1><p>World</p>")?;
pdf.save("output.pdf")?;
Ok(())
}use pdf_oxide::api::Pdf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let pdf = Pdf::from_text("Line 1\nLine 2\nLine 3")?;
pdf.save("output.pdf")?;
Ok(())
}The Pdf type is the simplest way to create PDFs:
use pdf_oxide::api::Pdf;
// Create from various sources
let pdf1 = Pdf::from_markdown("# Title")?;
let pdf2 = Pdf::from_html("<h1>Title</h1>")?;
let pdf3 = Pdf::from_text("Plain text")?;
// Get the PDF bytes
let bytes = pdf1.as_bytes();
// Save to file
pdf1.save("document.pdf")?;- Headings:
# H1,## H2,### H3,#### H4 - Paragraphs: Blank lines separate paragraphs
- Bold/Italic:
**bold**,*italic*,__bold__,_italic_ - Lists:
- itemor* item - Blockquotes:
> quoted text - Code Blocks: Triple backticks for code
- Tables: GFM-style tables with column alignment
Tables use the GitHub-Flavored Markdown syntax:
| Name | Age | City |
|:-----|:---:|-----:|
| Alice | 30 | NYC |
| Bob | 25 | LA |Column alignment is specified in the separator row:
:---or---= left-aligned:---:= center-aligned---:= right-aligned
For more control, use PdfBuilder:
use pdf_oxide::api::PdfBuilder;
use pdf_oxide::writer::PageSize;
let pdf = PdfBuilder::new()
// Document metadata
.title("My Document")
.author("John Doe")
.subject("Important Report")
.keywords("rust, pdf, document")
// Page settings
.page_size(PageSize::A4) // A4, Letter, Legal, A3, or Custom
.margin(72.0) // 1 inch margins (all sides)
.margins(50.0, 50.0, 72.0, 72.0) // left, right, top, bottom
// Typography
.font_size(11.0) // Base font size
.line_height(1.5) // Line spacing multiplier
// Build from content
.from_markdown("# Content")?;
pdf.save("customized.pdf")?;| Size | Dimensions (points) | Dimensions (inches) |
|---|---|---|
PageSize::Letter |
612 × 792 | 8.5 × 11 |
PageSize::A4 |
595 × 842 | 8.27 × 11.69 |
PageSize::Legal |
612 × 1008 | 8.5 × 14 |
PageSize::A3 |
842 × 1190 | 11.69 × 16.54 |
PageSize::Custom(w, h) |
w × h | - |
PDF uses points as the unit of measurement: 72 points = 1 inch
For full control over page layout:
use pdf_oxide::writer::{DocumentBuilder, PageSize, DocumentMetadata};
let mut builder = DocumentBuilder::new();
// Set metadata
builder = builder.metadata(
DocumentMetadata::new()
.title("My Document")
.author("Author Name")
);
// Add pages with content
builder.page(PageSize::Letter)
.at(72.0, 720.0) // Position cursor
.heading(1, "Chapter 1") // Add heading
.space(20.0) // Vertical space
.paragraph("This is a paragraph with automatic word wrapping.")
.horizontal_rule()
.font("Helvetica-Bold", 14.0)
.text("Bold text")
.done();
// Build the PDF
let pdf_bytes = builder.build()?;For maximum control over PDF structure:
use pdf_oxide::writer::{PdfWriter, PdfWriterConfig};
let mut writer = PdfWriter::with_config(
PdfWriterConfig::default()
.with_title("Document")
.with_compress(true) // Enable FlateDecode compression
);
let mut page = writer.add_letter_page();
page.add_text("Hello, World!", 72.0, 720.0);
page.finish();
let pdf_bytes = writer.finish()?;use pdf_oxide::writer::{ColorSpace, ImageData, ImageManager};
// Create an image from raw pixel data
let width = 100;
let height = 100;
let pixels = vec![255u8; width * height * 3]; // RGB data
let image = ImageData::new(
width as u32,
height as u32,
ColorSpace::DeviceRGB,
pixels,
);
// Register with image manager
let mut images = ImageManager::new();
let img_id = images.register("my_image", image);
// Use img_id when drawinguse pdf_oxide::writer::ImageData;
// Load JPEG
let jpeg_data = std::fs::read("photo.jpg")?;
let jpeg_image = ImageData::from_jpeg(jpeg_data)?;
// Load PNG
let png_data = std::fs::read("graphic.png")?;
let png_image = ImageData::from_png(&png_data)?;
// Auto-detect format
let image = ImageData::from_file("image.png")?;use pdf_oxide::writer::ContentStreamBuilder;
let mut content = ContentStreamBuilder::new();
// Draw image at position with size
content.draw_image(
&img_id, // Resource ID from ImageManager
72.0, // X position (left edge)
500.0, // Y position (bottom edge)
200.0, // Width
150.0, // Height
);| Color Space | Components | Use Case |
|---|---|---|
DeviceRGB |
3 | Photos, graphics |
DeviceGray |
1 | Grayscale images |
DeviceCMYK |
4 | Print documents |
use pdf_oxide::api::Pdf;
use pdf_oxide::editor::{DocumentEditor, EditableDocument};
// Open existing PDF
let mut editor = DocumentEditor::open("existing.pdf")?;
// Read metadata
let info = editor.get_info()?;
println!("Current title: {:?}", info.title);
// Modify metadata
editor.set_title("New Title");
editor.set_author("New Author");
editor.set_subject("Updated subject");
editor.set_keywords("new, keywords");
// Page operations
let page_count = editor.page_count()?;
println!("Pages: {}", page_count);
// Get page info
let page_info = editor.get_page_info(0)?;
println!("Page 1: {}x{} points", page_info.width, page_info.height);
// Duplicate a page
let new_idx = editor.duplicate_page(0)?;
// Move a page
editor.move_page(new_idx, 1)?;
// Remove a page
editor.remove_page(2)?;
// Save
editor.save("modified.pdf")?;use pdf_oxide::editor::SaveOptions;
// Full rewrite (recommended for most cases)
editor.save_with_options("output.pdf", SaveOptions::full_rewrite())?;
// Incremental update (preserves signatures)
editor.save_with_options("output.pdf", SaveOptions::incremental())?;
// Custom options
let options = SaveOptions {
incremental: false,
compress: true,
linearize: false,
garbage_collect: true,
};
editor.save_with_options("output.pdf", options)?;use pdf_oxide::writer::{OutlineBuilder, OutlineItem, OutlineDestination, FitMode};
let mut outline = OutlineBuilder::new();
outline.add(OutlineItem::new("Chapter 1")
.destination(OutlineDestination::page(0, FitMode::Fit))
.children(vec![
OutlineItem::new("Section 1.1")
.destination(OutlineDestination::page(0, FitMode::FitH(500.0))),
OutlineItem::new("Section 1.2")
.destination(OutlineDestination::page(1, FitMode::Fit)),
]));use pdf_oxide::writer::{Table, TableRow, TableCell, TableStyle, ColumnWidth};
let table = Table::new()
.style(TableStyle::default())
.columns(vec![
ColumnWidth::Fixed(100.0),
ColumnWidth::Auto,
ColumnWidth::Percent(30.0),
])
.header(TableRow::new(vec![
TableCell::text("Name"),
TableCell::text("Description"),
TableCell::text("Price"),
]))
.row(TableRow::new(vec![
TableCell::text("Item 1"),
TableCell::text("First item"),
TableCell::text("$10.00"),
]));use pdf_oxide::writer::{LinearGradientBuilder, GradientStop};
let gradient = LinearGradientBuilder::new()
.start(0.0, 0.0)
.end(100.0, 100.0)
.add_stop(GradientStop::new(0.0, [1.0, 0.0, 0.0])) // Red
.add_stop(GradientStop::new(1.0, [0.0, 0.0, 1.0])); // Blueuse pdf_oxide::writer::{ExtGStateBuilder, BlendMode};
let state = ExtGStateBuilder::new()
.fill_alpha(0.5)
.stroke_alpha(0.8)
.blend_mode(BlendMode::Multiply);pdf_oxide provides Python bindings for PDF creation:
from pdf_oxide import Pdf, PdfBuilder, PageSize
# Simple creation
pdf = Pdf.from_markdown("# Hello World")
pdf.save("output.pdf")
# With options
pdf = (PdfBuilder()
.title("My Document")
.author("Author")
.page_size(PageSize.A4)
.from_markdown("# Content"))
# Save
pdf.save("document.pdf")
# Get bytes
data = pdf.to_bytes()-
Use the simplest API that meets your needs
- Start with
Pdf::from_markdown()for simple documents - Move to
PdfBuilderfor customization - Use
DocumentBuilderorPdfWriteronly when necessary
- Start with
-
Enable compression for smaller files
PdfWriterConfig::default().with_compress(true)
-
Set metadata for searchability
.title("Document Title") .author("Author Name") .keywords("relevant, keywords")
-
Use appropriate page sizes
Letterfor US documentsA4for international documents
-
Handle errors properly
let pdf = Pdf::from_markdown(content)?; // Use ? for error propagation
See the examples/ directory for complete working examples:
examples/create_pdf_from_markdown.rs- Markdown to PDFexamples/create_pdf_with_images.rs- Embedding imagesexamples/edit_existing_pdf.rs- Modifying PDFs
Run examples with:
cargo run --example create_pdf_from_markdown
cargo run --example create_pdf_with_images
cargo run --example edit_existing_pdf