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 point | Signature | Description |
Open(string, string?) | File path + optional password | Load existing PDF from disk |
Open(byte[], string?) | PDF bytes + optional password | Load existing PDF from memory |
Create(Action<IDocumentContainer>) | Layout handler | Generate 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.
| Backend | Execution model | Operations |
| 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
| Method | Backend | Description |
SetTitle(string) | PDFium | Set document title |
SetAuthor(string) | PDFium | Set document author |
SetSubject(string) | PDFium | Set document subject |
SetKeywords(string) | PDFium | Set document keywords |
SetCreator(string) | PDFium | Set creator application name |
SetProducer(string) | PDFium | Set producer application name |
SetMetadata(string, string?) | PDFium | Set any metadata entry by key |
ExtendXmpMetadata(string) | qpdf | Inject additional RDF/XML into XMP stream |
Bookmarks
| Method | Backend | Description |
AddBookmark(string, int, int) | PDFium | Add a top-level bookmark (title, pageIndex, insertIndex) |
RemoveBookmark(int) | PDFium | Remove a bookmark by index |
SetBookmarkTitle(int, string) | PDFium | Change a bookmark's title |
SetBookmarkDestination(int, int) | PDFium | Change a bookmark's target page |
MoveBookmark(int, int) | PDFium | Reorder a bookmark (fromIndex, toIndex) |
Page Manipulation
| Method | Backend | Description |
DeletePage(int) | PDFium | Delete a page by zero-based index |
DeletePages(params int[]) | PDFium | Delete multiple pages (processed in descending order) |
AddBlankPage(int, double, double) | PDFium | Insert a blank page at the given index with optional dimensions |
AppendDocument(byte[]) | PDFium | Append all pages from another PDF |
PrependDocument(byte[]) | PDFium | Prepend all pages from another PDF |
ImportPages(byte[], string?, int) | PDFium | Import specific pages from another PDF at a position |
MergeFile(string) | PDFium | Append pages from a PDF file path |
SetPageRotation(int, int) | PDFium | Set page rotation (0, 90, 180, 270 degrees) |
SetMediaBox(int, ...) | PDFium | Set the full page area (left, bottom, right, top) |
SetCropBox(int, ...) | PDFium | Set the visible display area |
Redaction
| Method | Backend | Description |
RedactText(string, PdfiumSearchOptions?) | PDFium | Search and redact all occurrences across the document |
RedactTextOnPage(int, string, PdfiumSearchOptions?) | PDFium | Search and redact on a specific page |
RedactArea(int, RectangleF) | PDFium | Redact a rectangular area on a page |
Form Fields
| Method | Backend | Description |
SetFormField(string, string) | PDFium | Set a text field value by name |
SetFormField(string, bool) | PDFium | Set a checkbox or radio button value |
RemoveFormField(string) | PDFium | Remove a form field |
RenameFormField(string, string) | PDFium | Rename a form field |
FlattenForm() | PDFium | Flatten all form fields into page content |
Annotations
| Method | Backend | Description |
FlattenAnnotations() | PDFium | Flatten all annotations on all pages into content |
Drawing
| Method | Backend | Description |
DrawText(int, string, float, float, ...) | PDFium | Draw text at a position on a page |
DrawLine(int, float, float, float, float, ...) | PDFium | Draw a line on a page |
DrawRectangle(int, float, float, float, float, ...) | PDFium | Draw a rectangle (stroke and/or fill) on a page |
Stamping
| Method | Backend | Description |
StampText(TextStamp) | Skia + qpdf | Stamp text onto every page |
StampImage(ImageStamp) | Skia + qpdf | Stamp an image onto every page |
Compression
| Method | Backend | Description |
Compress(CompressionOptions?) | PDFium + qpdf | Recompress images and optimize streams |
Encryption and Decryption
| Method | Backend | Description |
Encrypt(Encryption256Bit) | qpdf | AES-256 encryption (PDF 2.0, recommended) |
Encrypt(Encryption128Bit) | qpdf | AES-128/RC4 encryption (PDF 1.6 era) |
Encrypt(Encryption40Bit) | qpdf | RC4 40-bit encryption (legacy PDF 1.1) |
Decrypt() | qpdf | Remove encryption from the document |
RemoveRestrictions() | qpdf | Remove security restrictions (also decrypts) |
Linearization
| Method | Backend | Description |
Linearize() | qpdf | Optimize for fast web viewing (progressive download) |
Overlay and Underlay
| Method | Backend | Description |
OverlayFile(LayerConfiguration) | qpdf | Draw another PDF on top of pages |
UnderlayFile(LayerConfiguration) | qpdf | Draw another PDF beneath pages |
Attachments
| Method | Backend | Description |
AddAttachment(DocumentAttachment) | qpdf | Embed 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.