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

MethodPDF KeyDescription
SetTitle(string)/TitleThe document's title, shown in viewer title bars and search results.
SetAuthor(string)/AuthorThe person or organisation that created the content.
SetSubject(string)/SubjectA brief description of the document's topic.
SetKeywords(string)/KeywordsComma-separated keywords for search and categorisation.
SetCreator(string)/CreatorThe application that authored the original document (e.g. "Microsoft Word").
SetProducer(string)/ProducerThe 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

PropertyTypeDescription
Titlestring?Document title, or null when not set.
Authorstring?Document author.
Subjectstring?Document subject.
Keywordsstring?Keywords as a single string (comma-separated by convention).
Creatorstring?Originating application name.
Producerstring?PDF generation application name.
CreationDatestring?Raw PDF date string (e.g. D:20260407120000+02'00').
ModDatestring?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

ParameterTypeDescription
titlestringThe text displayed in the viewer's bookmark panel.
pageIndexintZero-based page number the bookmark jumps to. Pass -1 for no destination.
insertIndexintPosition 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

PropertyTypeDescription
TitlestringDisplay title shown in the viewer's navigation panel.
PageIndexintZero-based target page, or -1 if the destination could not be resolved.
ChildrenIReadOnlyList<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