Skip to content

Editing Overview

PDF Oxide provides two levels of API for editing existing PDFs: the high-level Pdf class (recommended) and the lower-level DocumentEditor. Both allow you to open a PDF, modify its content and metadata, track changes, and save the result.

Binding coverage for editing. The richest editing surface is in Python, Rust, and WASM — they expose page operations, text/image editing, annotation work, encryption, and redaction. Go exposes a substantial editor API (metadata, page rotate/move/delete, erase-region, form fill/flatten, crop, merge, save-encrypted). C# currently exposes metadata edit (Title/Author/Subject), form fill (SetFormFieldValue), FlattenForms, and Save/SaveAsync on DocumentEditor; page-level operations, encryption, redaction, and text/image editing are not yet surfaced in the C# public API — use the CLI, Go, Python, or Rust bindings for those paths.

Opening a PDF for Editing

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("input.pdf")

The editor is initialized lazily on the first modification. You can start reading immediately and the editor activates when you call any mutating method such as set_title() or page().

WASM

import { WasmPdfDocument } from "pdf-oxide-wasm";

const bytes = new Uint8Array(/* file bytes */);
const doc = new WasmPdfDocument(bytes);

Rust

Use the unified Pdf API:

use pdf_oxide::api::Pdf;

let mut doc = Pdf::open("input.pdf")?;

Or use DocumentEditor directly for lower-level control:

use pdf_oxide::editor::DocumentEditor;

let mut editor = DocumentEditor::open("input.pdf")?;

Go

package main

import (
    "log"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    editor, err := pdfoxide.OpenEditor("input.pdf")
    if err != nil { log.Fatal(err) }
    defer editor.Close()
}

C#

using PdfOxide;

using var editor = DocumentEditor.Open("input.pdf");

Checking for Modifications

Before saving, you can check whether any changes have been made:

Python

doc = PdfDocument("input.pdf")
print(doc.is_modified)  # False -- no changes yet

doc.set_title("Updated Title")
print(doc.is_modified)  # True

Rust

let mut doc = Pdf::open("input.pdf")?;
assert!(!doc.is_modified());

doc.editor().unwrap().set_title("Updated Title");
assert!(doc.is_modified());

Go

editor, _ := pdfoxide.OpenEditor("input.pdf")
defer editor.Close()

modified, _ := editor.IsModified()
fmt.Println(modified) // false

_ = editor.SetTitle("Updated Title")
modified, _ = editor.IsModified()
fmt.Println(modified) // true

C#

using var editor = DocumentEditor.Open("input.pdf");
Console.WriteLine(editor.IsModified); // false

editor.Title = "Updated Title";
Console.WriteLine(editor.IsModified); // true

Saving

Python

doc = PdfDocument("input.pdf")
doc.set_title("New Title")
doc.save("output.pdf")

WASM

import { WasmPdfDocument } from "pdf-oxide-wasm";

const bytes = new Uint8Array(/* file bytes */);
const doc = new WasmPdfDocument(bytes);
doc.setTitle("New Title");
const output = doc.save();
doc.free();

Rust

let mut doc = Pdf::open("input.pdf")?;
doc.editor().unwrap().set_title("New Title");
doc.save("output.pdf")?;

// Or save to a new path
doc.save_as("copy.pdf")?;

Go

editor, _ := pdfoxide.OpenEditor("input.pdf")
defer editor.Close()

_ = editor.SetTitle("New Title")
_ = editor.Save("output.pdf")

C#

using var editor = DocumentEditor.Open("input.pdf");
editor.Title = "New Title";
editor.Save("output.pdf");

The save() method performs a full rewrite of the PDF by default. For advanced save options (incremental updates, encryption), see Encryption & Security.

Document Metadata

Read and write the standard PDF metadata fields: title, author, subject, and keywords.

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("input.pdf")

# Set metadata
doc.set_title("Quarterly Report")
doc.set_author("Jane Smith")
doc.set_subject("Q4 2025 Financial Results")
doc.set_keywords("finance, quarterly, 2025")

doc.save("output.pdf")

WASM

import { WasmPdfDocument } from "pdf-oxide-wasm";

const bytes = new Uint8Array(/* file bytes */);
const doc = new WasmPdfDocument(bytes);

// Set metadata
doc.setTitle("Quarterly Report");
doc.setAuthor("Jane Smith");
doc.setSubject("Q4 2025 Financial Results");
doc.setKeywords("finance, quarterly, 2025");

const output = doc.save();
doc.free();

Rust

use pdf_oxide::editor::DocumentEditor;

let mut editor = DocumentEditor::open("input.pdf")?;

// Read metadata
if let Some(title) = editor.title()? {
    println!("Current title: {}", title);
}
if let Some(author) = editor.author()? {
    println!("Current author: {}", author);
}
if let Some(subject) = editor.subject()? {
    println!("Current subject: {}", subject);
}
if let Some(keywords) = editor.keywords()? {
    println!("Current keywords: {}", keywords);
}

// Set metadata
editor.set_title("Quarterly Report");
editor.set_author("Jane Smith");
editor.set_subject("Q4 2025 Financial Results");
editor.set_keywords("finance, quarterly, 2025");

editor.save("output.pdf")?;

Go

editor, _ := pdfoxide.OpenEditor("input.pdf")
defer editor.Close()

if t, err := editor.Title(); err == nil { fmt.Println("Current title:", t) }
if a, err := editor.Author(); err == nil { fmt.Println("Current author:", a) }

_ = editor.SetTitle("Quarterly Report")
_ = editor.SetAuthor("Jane Smith")
_ = editor.SetSubject("Q4 2025 Financial Results")

_ = editor.Save("output.pdf")

C#

using var editor = DocumentEditor.Open("input.pdf");

if (editor.Title is { } t) Console.WriteLine($"Current title: {t}");
if (editor.Author is { } a) Console.WriteLine($"Current author: {a}");
if (editor.Subject is { } s) Console.WriteLine($"Current subject: {s}");

editor.Title = "Quarterly Report";
editor.Author = "Jane Smith";
editor.Subject = "Q4 2025 Financial Results";

editor.Save("output.pdf");

Document Information

Source Path and Version

use pdf_oxide::editor::DocumentEditor;

let editor = DocumentEditor::open("input.pdf")?;

// Path to the original file
println!("Source: {}", editor.source_path());

// PDF version as (major, minor)
let (major, minor) = editor.version();
println!("PDF version: {}.{}", major, minor);

// Number of pages
println!("Pages: {}", editor.current_page_count());

Full API Reference

DocumentEditor

Method Returns Description
open(path) Result<DocumentEditor> Open a PDF for editing
is_modified() bool Check if any changes have been made
source_path() &str Path to the source PDF
source() &PdfDocument Read-only access to the source document
version() (u8, u8) PDF version (major, minor)
current_page_count() usize Number of pages in the document
title() Result<Option<String>> Get document title
set_title(title) () Set document title
author() Result<Option<String>> Get document author
set_author(author) () Set document author
subject() Result<Option<String>> Get document subject
set_subject(subject) () Set document subject
keywords() Result<Option<String>> Get document keywords
set_keywords(keywords) () Set document keywords
save(path) Result<()> Save with full rewrite
save_with_options(path, options) Result<()> Save with custom options

Pdf (Unified API)

Method Returns Description
Pdf::open(path) Result<Pdf> Open a PDF for editing
Pdf::open_editor(path) Result<DocumentEditor> Open directly as DocumentEditor
is_modified() bool Check if changes exist
save(path) Result<()> Save the document
save_as(path) Result<()> Save to a new path
page(index) Result<PdfPage> Get a page for DOM editing
save_page(page) Result<()> Save a modified page back
editor() Option<&mut DocumentEditor> Access the underlying editor

EditableDocument Trait

The EditableDocument trait defines the core editing contract:

pub trait EditableDocument {
    fn get_info(&mut self) -> Result<DocumentInfo>;
    fn set_info(&mut self, info: DocumentInfo) -> Result<()>;
    fn page_count(&mut self) -> Result<usize>;
    fn get_page_info(&mut self, index: usize) -> Result<PageInfo>;
    fn remove_page(&mut self, index: usize) -> Result<()>;
    fn move_page(&mut self, from: usize, to: usize) -> Result<()>;
    fn duplicate_page(&mut self, index: usize) -> Result<usize>;
    fn save(&mut self, path: impl AsRef<Path>) -> Result<()>;
    fn save_with_options(&mut self, path: impl AsRef<Path>, options: SaveOptions) -> Result<()>;
}

Complete DocumentEditor Method Surface

The tables below enumerate the full public DocumentEditor / Pdf-editor method surface in Rust. Each row links to the deep-dive guide where applicable. Binding availability is noted; see the individual pages for Go / C# examples.

Metadata

Method Rust Python WASM Go C# Page
title / set_title Overview
author / set_author Overview
subject / set_subject Overview
keywords / set_keywords Overview
producer / set_producer Overview
creation_date / set_creation_date Overview
apply_metadata(info) Overview

Page operations

Method Rust Python WASM Go C# Page
remove_page / delete_page Pages
move_page(from, to) Pages
duplicate_page(i) Pages
get_page_rotation / set_page_rotation Pages
rotate_page_by(i, deg) Pages
rotate_all_pages(deg) Pages
get_page_media_box / set_page_media_box Pages
get_page_crop_box / set_page_crop_box Pages
crop_margins(l, r, t, b) Pages
erase_region(page, rect) Pages
erase_regions(page, rects) Pages
clear_erase_regions(page) Pages

Merging / splitting

Method Rust Python WASM Go C# Page
merge_from(path) Merge & Split
merge_pages_from(path, pages) Merge & Split
extract_pages(pages, output) Merge & Split
Merge([]paths) top-level Merge & Split

Forms

Method Rust Python WASM Go C# Page
get_form_fields() Forms
get_form_field_value(name) Forms
has_form_field(name) Forms
set_form_field_value(name, value) Forms
add_form_field(widget, page) Form Creation
add_parent_field / add_child_field Form Creation
remove_form_field(name) Forms
set_form_field_* (readonly, required, tooltip, rect, max_length, alignment, colors, flags) Forms
flatten_forms_on_page(page) Forms
flatten_forms() Forms
export_form_data_fdf(path) Forms
export_form_data_xfdf(path) Forms

Annotations

Method Rust Python WASM Go C# Page
flatten_page_annotations(page) Annotations
flatten_all_annotations() Annotations
is_page_marked_for_annotation_flatten(page) Annotations
unmark_page_for_annotation_flatten(page) Annotations

Redaction

Method Rust Python WASM Go C# Page
apply_page_redactions(page) Redaction
apply_all_redactions() Redaction
is_page_marked_for_redaction(page) Redaction
unmark_page_for_redaction(page) Redaction

XFA

Method Rust Python WASM Go C# Page
has_xfa() XFA Forms
analyze_xfa() XFA Forms
convert_xfa_to_acroform(opts) XFA Forms

Save

Method Rust Python WASM Go C# Page
save(path) Overview
save_with_options(path, opts) Encryption
save_encrypted(path, user, owner) Encryption
save_with_encryption(path, cfg) Encryption
save_async / SaveAsync Async

Housekeeping (Go convenience)

Methods exposed as DocumentEditor methods in Go, equivalents via editor.source() in Rust:

Method Rust Python WASM Go C#
IsModified is_modified() is_modified IsModified() IsModified
SourcePath source_path() source_path SourcePath() SourcePath
Version version() version() Version()
PageCount current_page_count() page_count() pageCount() PageCount() PageCount
RemoveHeaders / RemoveFooters / RemoveArtifacts
Close / Dispose Drop context manager free() Close() Dispose()

Complete Edit Workflow

This example demonstrates a complete editing session: open, inspect, modify metadata, edit content, and save.

Python

from pdf_oxide import PdfDocument

# Open the document
doc = PdfDocument("report.pdf")
print(f"Pages: {doc.page_count()}")

# Update metadata
doc.set_title("Annual Report 2025")
doc.set_author("Finance Team")

# Edit text on page 0
page = doc.page(0)
for text in page.find_text_containing("DRAFT"):
    page.set_text(text.id, "FINAL")
doc.save_page(page)

# Save
doc.save("report-final.pdf")

Rust

use pdf_oxide::api::Pdf;

let mut doc = Pdf::open("report.pdf")?;
println!("Pages: {}", doc.page_count()?);

// Update metadata
{
    let editor = doc.editor().unwrap();
    editor.set_title("Annual Report 2025");
    editor.set_author("Finance Team");
}

// Edit text on page 0
let mut page = doc.page(0)?;
let drafts = page.find_text_containing("DRAFT");
for t in &drafts {
    page.set_text(t.id(), "FINAL")?;
}
doc.save_page(page)?;

// Save
doc.save("report-final.pdf")?;