Metadata & Bookmarks
Read and write document properties (title, author, keywords, custom fields) and manage the bookmark outline tree that PDF viewers display as a table of contents.
Setting Metadata
Use PdfEditor to set standard PDF info dictionary entries. Every setter returns the editor instance so calls can be chained:
PdfEditor.Open("report.pdf")
.SetTitle("Q1 Financial Report")
.SetAuthor("Jane Smith")
.SetSubject("Quarterly financials for FY2026")
.SetKeywords("finance, quarterly, 2026, revenue")
.SetCreator("Acme Reporting Tool")
.SetProducer("FolioPDF 2026.4")
.Save("report-tagged.pdf");
Standard Metadata Fields
| Method | PDF Key | Description |
|---|---|---|
SetTitle(string) | /Title | The document's title, shown in viewer title bars and search results. |
SetAuthor(string) | /Author | The person or organisation that created the content. |
SetSubject(string) | /Subject | A brief description of the document's topic. |
SetKeywords(string) | /Keywords | Comma-separated keywords for search and categorisation. |
SetCreator(string) | /Creator | The application that authored the original document (e.g. "Microsoft Word"). |
SetProducer(string) | /Producer | The application that converted the document to PDF. |
Custom Metadata
The SetMetadata method writes arbitrary key-value pairs into the PDF's /Info dictionary. Use it for application-specific properties that aren't covered by the standard fields:
PdfEditor.Open("contract.pdf")
.SetMetadata("Department", "Legal")
.SetMetadata("ProjectCode", "PROJ-4821")
.SetMetadata("ReviewStatus", "Approved")
.Save("contract-tagged.pdf");
Pass null as the value to remove a custom property:
PdfEditor.Open("contract-tagged.pdf")
.SetMetadata("ReviewStatus", null) // removes the entry
.Save("contract-clean.pdf");
Setting Metadata on Generated Documents
Use PdfEditor.Create to generate a PDF and tag it with metadata in a single chain — no intermediate file needed:
PdfEditor.Create(doc =>
{
doc.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(50);
page.Content().Column(col =>
{
col.Item().Text("Annual Report 2026").FontSize(28).Bold();
col.Item().Text("TrueSpar Holdings Ltd.").FontSize(14);
});
});
})
.SetTitle("Annual Report 2026")
.SetAuthor("TrueSpar Holdings Ltd.")
.SetSubject("Full-year financial statements and board commentary")
.SetKeywords("annual report, 2026, financials, TrueSpar")
.Save("annual-report-2026.pdf");
Reading Metadata
Use PdfiumDocument.Load to inspect a PDF's metadata without modifying it:
using var doc = PdfiumDocument.Load(File.ReadAllBytes("annual-report-2026.pdf"));
var meta = doc.GetMetadata();
Console.WriteLine($"Title: {meta.Title}");
Console.WriteLine($"Author: {meta.Author}");
Console.WriteLine($"Subject: {meta.Subject}");
Console.WriteLine($"Keywords: {meta.Keywords}");
Console.WriteLine($"Creator: {meta.Creator}");
Console.WriteLine($"Producer: {meta.Producer}");
Console.WriteLine($"Created: {meta.CreationDate}");
Console.WriteLine($"Modified: {meta.ModDate}");
PdfiumDocumentMetadata Properties
| Property | Type | Description |
|---|---|---|
Title | string? | Document title, or null when not set. |
Author | string? | Document author. |
Subject | string? | Document subject. |
Keywords | string? | Keywords as a single string (comma-separated by convention). |
Creator | string? | Originating application name. |
Producer | string? | PDF generation application name. |
CreationDate | string? | Raw PDF date string (e.g. D:20260407120000+02'00'). |
ModDate | string? | Raw PDF modification date string. |
Date format. PDF dates are returned as raw strings because real-world files frequently contain malformed dates. The format specified by ISO 32000-1 is D:YYYYMMDDHHmmSSOHH'mm' where everything after the year is optional and O is the UTC offset sign (+, -, or Z).
Reading Metadata from Encrypted Documents
Encrypted PDFs require a password before metadata can be read:
using var doc = PdfiumDocument.Load(
File.ReadAllBytes("encrypted.pdf"),
password: "my-secret");
var meta = doc.GetMetadata();
Console.WriteLine($"Title: {meta.Title}");
Bookmarks
Bookmarks (also called the document outline) provide a hierarchical table of contents that PDF viewers display in a navigation panel. FolioPDF supports adding, removing, renaming, reordering, and reading bookmarks.
Adding Bookmarks
PdfEditor.Open("manual.pdf")
.AddBookmark("Introduction", pageIndex: 0)
.AddBookmark("Getting Started", pageIndex: 2)
.AddBookmark("Configuration", pageIndex: 5)
.AddBookmark("API Reference", pageIndex: 12)
.AddBookmark("Troubleshooting", pageIndex: 30)
.Save("manual-bookmarked.pdf");
AddBookmark Parameters
| Parameter | Type | Description |
|---|---|---|
title | string | The text displayed in the viewer's bookmark panel. |
pageIndex | int | Zero-based page number the bookmark jumps to. Pass -1 for no destination. |
insertIndex | int | Position among siblings. Pass -1 (default) to append at the end. |
Removing Bookmarks
Remove a top-level bookmark by its zero-based index:
PdfEditor.Open("manual-bookmarked.pdf")
.RemoveBookmark(4) // removes "Troubleshooting"
.RemoveBookmark(3) // removes "API Reference"
.Save("manual-trimmed.pdf");
Index order matters. When removing multiple bookmarks, process them from highest index to lowest so that earlier removals don't shift the indices of later ones. The example above removes index 4 first, then index 3.
Renaming and Reordering Bookmarks
PdfEditor.Open("manual-bookmarked.pdf")
.SetBookmarkTitle(0, "Welcome") // rename "Introduction" to "Welcome"
.SetBookmarkDestination(1, pageIndex: 3) // change target page
.MoveBookmark(fromIndex: 4, toIndex: 1) // move "Troubleshooting" to second position
.Save("manual-reordered.pdf");
Reading Bookmarks
Use PdfiumDocument.GetBookmarks() to read the full outline tree. Each bookmark has a title, a target page index, and a list of children:
using var doc = PdfiumDocument.Load(File.ReadAllBytes("manual-bookmarked.pdf"));
var bookmarks = doc.GetBookmarks();
foreach (var bookmark in bookmarks)
{
Console.WriteLine($" {bookmark.Title} -> page {bookmark.PageIndex}");
foreach (var child in bookmark.Children)
{
Console.WriteLine($" {child.Title} -> page {child.PageIndex}");
}
}
PdfiumBookmark Properties
| Property | Type | Description |
|---|---|---|
Title | string | Display title shown in the viewer's navigation panel. |
PageIndex | int | Zero-based target page, or -1 if the destination could not be resolved. |
Children | IReadOnlyList<PdfiumBookmark> | Nested child bookmarks. Empty for leaf entries. |
Practical Example: Auto-Generated Table of Contents
Combine document generation with bookmark tagging to produce a fully navigable PDF:
var chapters = new[]
{
("Executive Summary", 0),
("Market Analysis", 3),
("Product Roadmap", 8),
("Financial Outlook", 14),
("Risk Factors", 20),
("Appendix", 25),
};
var editor = PdfEditor.Create(doc =>
{
foreach (var (title, _) in chapters)
{
doc.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(50);
page.Content().Column(col =>
{
col.Item().Text(title).FontSize(28).Bold();
col.Item().PaddingTop(20).Text(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
);
});
});
}
});
// Tag each chapter as a bookmark
foreach (var (title, pageIndex) in chapters)
editor.AddBookmark(title, pageIndex);
editor
.SetTitle("Annual Strategy Report")
.SetAuthor("Strategy Team")
.Save("strategy-report.pdf");
Metadata Round-Trip: Write and Verify
A common workflow is to set metadata, save, then verify the output:
// Write
PdfEditor.Open("input.pdf")
.SetTitle("Invoice #10042")
.SetAuthor("Billing Department")
.SetMetadata("InvoiceNumber", "10042")
.SetMetadata("CustomerID", "CUST-7891")
.Save("invoice-final.pdf");
// Verify
using var doc = PdfiumDocument.Load(File.ReadAllBytes("invoice-final.pdf"));
var meta = doc.GetMetadata();
Debug.Assert(meta.Title == "Invoice #10042");
Debug.Assert(meta.Author == "Billing Department");
Next Steps
- Merge, Split & Overlay — combine documents, extract pages, layer PDFs
- Encryption — password-protect and restrict documents
- PDF/A Compliance — metadata requirements for archival PDFs