PdfEditor API

The unified fluent API for creating and editing PDF documents. Chain metadata, bookmarks, page manipulation, redaction, stamping, encryption, and compression in a single builder.

Three Entry Points

PdfEditor offers three ways to start an editing session. All three return a PdfEditor instance that supports the same chainable methods.

// 1. Open an existing PDF file
PdfEditor.Open("input.pdf")
    .SetTitle("Updated Title")
    .Save("output.pdf");

// 2. Open from in-memory bytes
byte[] pdfBytes = File.ReadAllBytes("input.pdf");
PdfEditor.Open(pdfBytes)
    .SetAuthor("Engineering")
    .Save("output.pdf");

// 3. Generate a new PDF and chain into editing
PdfEditor.Create(doc =>
{
    doc.Page(page =>
    {
        page.Size(PageSizes.A4);
        page.Margin(50);
        page.Content().Text("Fresh document").FontSize(18);
    });
})
.SetTitle("New Document")
.Encrypt(new Encryption256Bit { OwnerPassword = "secret" })
.Save("new-encrypted.pdf");
Entry pointSignatureDescription
Open(string, string?)File path + optional passwordLoad existing PDF from disk
Open(byte[], string?)PDF bytes + optional passwordLoad existing PDF from memory
Create(Action<IDocumentContainer>)Layout handlerGenerate new PDF via layout engine, then edit

How It Works: PDFium vs. qpdf

Internally, PdfEditor routes each operation to the correct native backend. You never need to know which backend handles what — the routing is automatic.

BackendExecution modelOperations
PDFium Immediate — applied as soon as the method is called Metadata, bookmarks, page deletion, page insertion, merge/append, redaction, form fields, annotations, drawing, rotation, page boxes
qpdf Deferred — collected during chaining, executed at Save() / ToBytes() Encryption, decryption, linearization, overlay/underlay, file attachments, XMP metadata

Why deferred? qpdf operations restructure the PDF object graph and must run as a single atomic pipeline. The PdfEditor collects all qpdf operations during chaining, then executes them in one pass when you call Save() or ToBytes(). PDFium operations modify the in-memory document immediately, so their effects are visible to subsequent calls in the chain.

Output

End every chain with Save() to write to a file or ToBytes() to get the result as a byte array.

// Save to file
PdfEditor.Open("input.pdf")
    .SetTitle("Report")
    .Save("output.pdf");

// Get bytes (e.g., for HTTP response)
byte[] result = PdfEditor.Open(inputBytes)
    .SetTitle("Report")
    .ToBytes();

// ASP.NET example
var bytes = PdfEditor.Open(templateBytes)
    .SetFormField("name", user.Name)
    .SetFormField("date", DateTime.Now.ToShortDateString())
    .FlattenForm()
    .ToBytes();
return File(bytes, "application/pdf", "filled-form.pdf");

Important: PdfEditor implements IDisposable. If you hold a reference beyond a single chain, wrap it in a using statement to release the underlying PDFium document handle.

using var editor = PdfEditor.Open("input.pdf");
editor.SetTitle("Updated");
editor.Save("output.pdf");

Complete Method Reference

Metadata

MethodBackendDescription
SetTitle(string)PDFiumSet document title
SetAuthor(string)PDFiumSet document author
SetSubject(string)PDFiumSet document subject
SetKeywords(string)PDFiumSet document keywords
SetCreator(string)PDFiumSet creator application name
SetProducer(string)PDFiumSet producer application name
SetMetadata(string, string?)PDFiumSet any metadata entry by key
ExtendXmpMetadata(string)qpdfInject additional RDF/XML into XMP stream

Bookmarks

MethodBackendDescription
AddBookmark(string, int, int)PDFiumAdd a top-level bookmark (title, pageIndex, insertIndex)
RemoveBookmark(int)PDFiumRemove a bookmark by index
SetBookmarkTitle(int, string)PDFiumChange a bookmark's title
SetBookmarkDestination(int, int)PDFiumChange a bookmark's target page
MoveBookmark(int, int)PDFiumReorder a bookmark (fromIndex, toIndex)

Page Manipulation

MethodBackendDescription
DeletePage(int)PDFiumDelete a page by zero-based index
DeletePages(params int[])PDFiumDelete multiple pages (processed in descending order)
AddBlankPage(int, double, double)PDFiumInsert a blank page at the given index with optional dimensions
AppendDocument(byte[])PDFiumAppend all pages from another PDF
PrependDocument(byte[])PDFiumPrepend all pages from another PDF
ImportPages(byte[], string?, int)PDFiumImport specific pages from another PDF at a position
MergeFile(string)PDFiumAppend pages from a PDF file path
SetPageRotation(int, int)PDFiumSet page rotation (0, 90, 180, 270 degrees)
SetMediaBox(int, ...)PDFiumSet the full page area (left, bottom, right, top)
SetCropBox(int, ...)PDFiumSet the visible display area

Redaction

MethodBackendDescription
RedactText(string, PdfiumSearchOptions?)PDFiumSearch and redact all occurrences across the document
RedactTextOnPage(int, string, PdfiumSearchOptions?)PDFiumSearch and redact on a specific page
RedactArea(int, RectangleF)PDFiumRedact a rectangular area on a page

Form Fields

MethodBackendDescription
SetFormField(string, string)PDFiumSet a text field value by name
SetFormField(string, bool)PDFiumSet a checkbox or radio button value
RemoveFormField(string)PDFiumRemove a form field
RenameFormField(string, string)PDFiumRename a form field
FlattenForm()PDFiumFlatten all form fields into page content

Annotations

MethodBackendDescription
FlattenAnnotations()PDFiumFlatten all annotations on all pages into content

Drawing

MethodBackendDescription
DrawText(int, string, float, float, ...)PDFiumDraw text at a position on a page
DrawLine(int, float, float, float, float, ...)PDFiumDraw a line on a page
DrawRectangle(int, float, float, float, float, ...)PDFiumDraw a rectangle (stroke and/or fill) on a page

Stamping

MethodBackendDescription
StampText(TextStamp)Skia + qpdfStamp text onto every page
StampImage(ImageStamp)Skia + qpdfStamp an image onto every page

Compression

MethodBackendDescription
Compress(CompressionOptions?)PDFium + qpdfRecompress images and optimize streams

Encryption and Decryption

MethodBackendDescription
Encrypt(Encryption256Bit)qpdfAES-256 encryption (PDF 2.0, recommended)
Encrypt(Encryption128Bit)qpdfAES-128/RC4 encryption (PDF 1.6 era)
Encrypt(Encryption40Bit)qpdfRC4 40-bit encryption (legacy PDF 1.1)
Decrypt()qpdfRemove encryption from the document
RemoveRestrictions()qpdfRemove security restrictions (also decrypts)

Linearization

MethodBackendDescription
Linearize()qpdfOptimize for fast web viewing (progressive download)

Overlay and Underlay

MethodBackendDescription
OverlayFile(LayerConfiguration)qpdfDraw another PDF on top of pages
UnderlayFile(LayerConfiguration)qpdfDraw another PDF beneath pages

Attachments

MethodBackendDescription
AddAttachment(DocumentAttachment)qpdfEmbed a file inside the PDF

Examples

Post-Process a Generated Report

PdfEditor.Create(doc =>
{
    doc.Page(page =>
    {
        page.Size(PageSizes.A4);
        page.Margin(50);
        page.Header().Text("Q1 Financial Report").FontSize(10);
        page.Content().Column(col =>
        {
            col.Spacing(10);
            col.Item().Text("Revenue Summary").FontSize(20).Bold();
            col.Item().Text("Total revenue: $2.4M (+12% YoY)");
            // ... more content
        });
        page.Footer().AlignCenter().Text(t =>
        {
            t.CurrentPageNumber();
            t.Span(" / ");
            t.TotalPages();
        });
    });
})
.SetTitle("Q1 2026 Financial Report")
.SetAuthor("Finance Department")
.AddBookmark("Revenue Summary", pageIndex: 0)
.StampText(new TextStamp("CONFIDENTIAL")
{
    FontSize = 48,
    Opacity = 0.15f,
    Rotation = -45
})
.Encrypt(new Encryption256Bit
{
    OwnerPassword = "finance-2026",
    AllowPrinting = true,
    AllowContentExtraction = false
})
.Linearize()
.Save("q1-report-final.pdf");

Fill and Flatten a Form Template

var result = PdfEditor.Open("template.pdf")
    .SetFormField("firstName", "Alice")
    .SetFormField("lastName", "Chen")
    .SetFormField("email", "alice@example.com")
    .SetFormField("agreeToTerms", true)
    .FlattenForm()
    .ToBytes();

Merge Multiple Documents

PdfEditor.Open("cover.pdf")
    .MergeFile("chapter1.pdf")
    .MergeFile("chapter2.pdf")
    .AppendDocument(appendixBytes)
    .SetTitle("Complete Manual")
    .AddBookmark("Cover", pageIndex: 0)
    .AddBookmark("Chapter 1", pageIndex: 1)
    .AddBookmark("Chapter 2", pageIndex: 15)
    .Save("manual.pdf");

Redact Sensitive Information

PdfEditor.Open("contract.pdf")
    .RedactText("SSN 123-45-6789")
    .RedactText("Account #9876543210")
    .RedactArea(pageIndex: 2, new RectangleF(50, 600, 200, 30))
    .SetTitle("Redacted Contract")
    .Save("contract-redacted.pdf");

Watermark and Compress

PdfEditor.Open("photos.pdf")
    .StampImage(new ImageStamp(File.ReadAllBytes("watermark.png"))
    {
        Width = 200,
        Opacity = 0.2f,
        HorizontalAlignment = StampAlignment.Center,
        VerticalAlignment = StampAlignment.Center
    })
    .Compress(new CompressionOptions { ImageQuality = 65 })
    .Save("photos-watermarked.pdf");

Add File Attachments (PDF/A-3)

PdfEditor.Create(doc =>
{
    doc.Page(page =>
    {
        page.Size(PageSizes.A4);
        page.Content().Text("Invoice with attached XML data");
    });
})
.AddAttachment(new DocumentAttachment
{
    FilePath = "invoice-data.xml",
    AttachmentName = "invoice.xml",
    MimeType = "application/xml",
    Description = "Machine-readable invoice data",
    Relationship = DocumentAttachmentRelationship.Alternative
})
.Save("invoice-with-attachment.pdf");

Note: Method chaining order generally does not matter — PDFium operations execute immediately, and qpdf operations are collected and executed as a batch at Save() / ToBytes(). The one exception is that operations that reload the document (like RedactText, StampText, and Compress) flush the current PDFium state, so any PDFium operations before and after a reload are applied to different document snapshots.