Document Management

Document Management

Document is the entry point for every document-management operation covered here: opening and saving files, exporting to HTML, adding optional content layers, authoring Tagged PDF, and reading or writing AcroForm data. Page and Form are reached through Document.Page(n) and Document.Form().


Opening and Saving Documents

pdf.Open loads a PDF from a file path; pdf.OpenWithPassword does the same for password-protected files. Document.Save writes the result to a new file path.

doc, err := pdf.Open("input.pdf")
if err != nil {
	panic(err)
}

_, err = pdf.OpenWithPassword("locked.pdf", "userpassword")
if err != nil {
	panic(err)
}

err = doc.Save("output.pdf")

Exporting to HTML

Document.SaveHTML and Document.WriteHTML convert a document to HTML. HTMLSaveOptions.Mode selects the representation: HTMLModeText (visible styled text over a glyph-less raster background), HTMLModeNative (page graphics as one inline SVG layer), or HTMLModeFlow (reflowable HTML with headings and paragraphs in reading order). Leaving Mode unset produces the faithful default: a full page raster with a transparent selectable text layer.

doc, _ := pdf.Open("input.pdf")

// Faithful mode (default)
doc.SaveHTML("out.html")

// Visible-text mode
doc.SaveHTML("out.html", pdf.HTMLSaveOptions{Mode: pdf.HTMLModeText})

// Multi-file output: external resources plus one HTML file per page
doc.SaveHTML("site/doc.html", pdf.HTMLSaveOptions{
    Mode: pdf.HTMLModeNative, ResourceDir: "assets", SplitPages: true,
})

// Page subset, custom raster DPI and title
doc.SaveHTML("part.html", pdf.HTMLSaveOptions{Pages: []int{1, 3}, DPI: 96, Title: "Report"})

Managing Optional Content Layers

Document.AddLayer(name) creates a new Optional Content Group and returns a *Layer; Document.Layers() lists every layer already present. Page.BeginLayer / Page.EndLayer mark page content as belonging to a layer, and Layer.SetVisible(false) hides that content.

doc := pdf.NewDocumentFromFormat(pdf.PageFormatA4)
layer := doc.AddLayer("Watermark")

page, _ := doc.Page(1)
page.BeginLayer(layer)
page.AddText("DRAFT", pdf.TextStyle{Font: pdf.FontHelveticaBold, Size: 48},
    pdf.Rectangle{LLX: 150, LLY: 400, URX: 450, URY: 460})
page.EndLayer()

layer.SetVisible(false)
doc.Save("layered.pdf")

Building Tagged (Accessible) PDFs

Document.TaggedContent() returns the *TaggedContent facade that owns the logical structure tree of the document and sets the catalog metadata PDF/UA requires. Page.TagContent brackets a drawing call in marked content and returns a *StructElement for that node.

doc := pdf.NewDocumentFromFormat(pdf.PageFormatA4)
tc := doc.TaggedContent()
tc.SetTitle("Quarterly Report")
tc.SetLanguage("en-US")
page, _ := doc.Page(1)

page.TagContent(tc.Root(), pdf.StructH1, func() error {
    return page.AddText("Quarterly Report", pdf.TextStyle{Font: pdf.FontHelveticaBold, Size: 24},
        pdf.Rectangle{LLX: 50, LLY: 760, URX: 545, URY: 800})
})
fig, _ := page.TagContent(tc.Root(), pdf.StructFigure, func() error {
    return page.AddImage("chart.png", pdf.Rectangle{LLX: 50, LLY: 540, URX: 300, URY: 680})
})
fig.SetAlt("Bar chart of Q3 sales by region") // required for figures

doc.Save("accessible.pdf")

Grouping elements nest under the tree root as well:

doc := pdf.NewDocumentFromFormat(pdf.PageFormatA4)
tc := doc.TaggedContent()
page, _ := doc.Page(1)

tbl := tc.Root().AddChild(pdf.StructTable)
row := tbl.AddChild(pdf.StructTR)
cell := row.AddChild(pdf.StructTD)
page.TagContent(cell, pdf.StructP, func() error { return nil })

Exporting and Importing Form Data as JSON

Form.ExportJSON serializes every field type and value to JSON ({"name": {"type": "text", "value": "Jane"}, ...}); Form.ImportJSON applies a JSON payload to a form and returns the number of fields it updated. JSONExportOptions.Indent pretty-prints the output.

doc, _ := pdf.Open("filled.pdf")
data, _ := doc.Form().ExportJSON(pdf.JSONExportOptions{Indent: true})

// Fill a template from a JSON payload; discard the applied-field count.
template, _ := pdf.Open("template.pdf")
_, _ = template.Form().ImportJSON(data)

Tips and Best Practices

  • Choose HTMLModeNative for vector-heavy pages, HTMLModeFlow for reflow/mobile reading, and the faithful default (no Mode set) when visual fidelity matters most.
  • Call Layer.SetVisible(false) before Save to hide watermark or annotation layers by default while keeping the content addressable through the Layer object.
  • Set TaggedContent.SetTitle and SetLanguage before calling Page.TagContent, since PDF/UA validation checks the catalog-level metadata those methods write.
  • Call StructElement.SetAlt on every StructFigure node — figures without alternate text fail PDF/UA validation.
  • Use JSONExportOptions.Indent for human-readable diffing of exported form state; omit it for compact machine-to-machine payloads.

Common Issues

IssueCauseFix
HTML output has no selectable text glyphsHTMLSaveOptions.Mode left at faithful default without a text layer requirementSet Mode: pdf.HTMLModeText for a visible, styled text layer
HTML export loses the source font appearanceNoFontEmbedding set to trueLeave NoFontEmbedding false (default) so embedded fonts are re-wrapped as WOFF @font-face data URLs
Page.TagContent returns an errorCalled before Document.TaggedContent() initialized the structure treeCall Document.TaggedContent() (and set title/language) before the first TagContent call
ValidatePDFUA reports non-conformant figuresA StructFigure element has no alt textCall StructElement.SetAlt on every figure node
Form.ImportJSON updates fewer fields than expectedJSON payload field names do not match the target form field names; import is lenient and skips unmatched entriesConfirm exported field names match the target form field names exactly

FAQ

Which HTML export mode should I use?

HTMLModeText for the smallest, fully selectable output; HTMLModeNative when vector fidelity (curves, native strokes) matters more than file size; HTMLModeFlow for a reflowable, mobile-friendly reading layout. Leave Mode unset for the faithful raster-plus-transparent-text default.

Can I export only specific pages to HTML?

Yes. Set HTMLSaveOptions.Pages to a slice of 1-based page numbers, e.g. pdf.HTMLSaveOptions{Pages: []int{1, 3}}.

How do I hide a layer by default but let a viewer toggle it back on?

Call Layer.SetVisible(false) before saving. The layer remains an addressable Optional Content Group; Layer.IsVisible() reports its current state.

Does the JSON form export include field types?

Yes. Each exported field is a JSON object with type and value keys, e.g. {"subscribe": {"type": "checkbox", "value": true}}.

Do I need to call TagContent for every element, or can I nest groups directly?

Both. Page.TagContent wraps a drawing call and adds a leaf structure element. For grouping elements (tables, lists), call StructElement.AddChild on the tree root or a parent element to build the nesting first, then tag the leaf content underneath.


API Reference Summary

Class/MethodDescription
Document.SaveHTML / WriteHTMLExport the document to HTML, controlled by HTMLSaveOptions
HTMLSaveOptionsHTML export configuration: mode, DPI, page subset, resource handling
Document.AddLayer / LayersCreate or list Optional Content Groups on the document
Page.BeginLayer / EndLayerMark page content as belonging to a layer
LayerAn Optional Content Group — name and visibility
Document.TaggedContentFacade for the logical structure tree of the document (Tagged PDF)
TaggedContentSets document title/language and exposes the structure tree root
Page.TagContent / TagArtifactBracket drawing calls in marked content and structure elements
StructElementA node in the logical structure tree — children, alt text, language
Form.ExportJSON / ImportJSONSerialize or apply AcroForm field data as JSON
JSONExportOptionsJSON export configuration: indentation, omit-empty

See Also