PDF Document
The starting point of all backend activity is to create a Document struct. Initialize with an io.Writer
func NewDocument(w io.Writer) *PDFDocumentwhich gets you a big structure:
type PDFDocument struct {
CreationDate time.Time
ColorProfile *ColorProfile
CurrentPage *Page
DefaultLanguage *lang.Lang
Languages map[string]*lang.Lang
PDFWriter *pdf.PDF
RoleMap map[string]string // maps custom roles to standard PDF roles
RootStructureElement *StructureElement
ViewerPreferences map[string]string
DefaultLanguageTag string // BCP 47 language tag for the document catalog (e.g. "de", "en-US")
Author string
Creator string
Filename string
Keywords string
Subject string
Title string
Attachments []Attachment
Faces []*pdf.Face
Pages []*Page
Spotcolors []*color.Color
Bleed bag.ScaledPoint
CompressLevel uint
DefaultPageHeight bag.ScaledPoint
DefaultPageWidth bag.ScaledPoint
Format Format // The PDF format (PDF/X-1, PDF/X-3, PDF/A, etc.)
DumpOutput bool
ShowCutmarks bool
ShowHyperlinks bool
SuppressInfo bool
}After you are done writing Pages and other material to the PDF, you must end your work with
func (d *PDFDocument) Finish() errorwhich does everything that is left over, except for closing the PDF file.
PDF metadata
To add metadata, just set the appropriate fields in the main PDFDocument struct:
pd := NewDocument(w)
pd.Author = "A. U. Thor"
pd.Title = "A short story"The backend fills in the Info dict and adds the required XML metadata to the PDF file.
/Title (A short story)
/Author (A. U. Thor.)in the Info dict and the XML metadata:
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title>A short story</dc:title>
<dc:creator>A. U. Thor.</dc:creator>
</rdf:Description>Creating pages
A new page is created with
func (d *PDFDocument) NewPage() *PageThis sets the current page of the document to a new page with the default page height and page width. It also adds this page to the slice of document pages, so that when writing the PDF, you don’t have to do anything special to get the page into the PDF.
type Page struct {
Userdata map[any]any
Background []Object
Objects []Object
StructureElements []*StructureElement
Annotations []pdf.Annotation
Spotcolors []*color.Color
Height bag.ScaledPoint
Width bag.ScaledPoint
ExtraOffset bag.ScaledPoint
Objectnumber pdf.Objectnumber
Finished bool
}If you are finished with a page, you call Shipout() to put the page into the PDF.
func (p *Page) Shipout()Shipout() creates crop marks (if requested), adds bleed amount to the page dimensions, writes all the Objects from the Background and Objects slice to the PDF as well as the Annotations, the Spotcolors and StructureElements.
func (p *Page) OutputAt(x bag.ScaledPoint, y bag.ScaledPoint, vlist *node.VList)type Object struct {
Vlist *node.VList
X bag.ScaledPoint
Y bag.ScaledPoint
}Page callback
You can register a pre-shipout callback (which is called before the bleed marks are created) with
func (d *PDFDocument) RegisterCallback(cb Callback, fn any)and register the PreShipoutCallback which must have the signature func(page *Page).
type CallbackShipout func(page *Page)preShipout := func(pg *document.Page) {
...
}
mydoc.RegisterCallback(document.CallbackPreShipout, preShipout)Images
func (d *PDFDocument) LoadImageFile(filename string) (*pdf.Imagefile, error)Attachments
You can attach documents to a PDF file:
a := document.Attachment{
Name: "test.pdf",
Description: "A very nice test document",
MimeType: "application/pdf",
Data: []byte("test"),
}
f.Doc.Attachments = append(f.Doc.Attachments, a)The possible attributes in the attachment structure are:
type Attachment struct {
// Creation date of the attachment
CreationDate time.Time
// Modified date of the attachment
ModDate time.Time
// Name of the attachment (the visible name in the PDF)
// This is the name of the file as it will be displayed in the PDF viewer
// and is not necessarily the same as the original file name.
Name string
// Description of the attachment
Description string
// MimeType of the attachment
MimeType string
// Data of the attachment
Data []byte
}XMP extension schemas
PDF/A documents only allow a fixed set of XMP metadata namespaces. To use custom namespaces (e.g. for ZUGFeRD/Factur-X e-invoicing), you must declare them as XMP extension schemas. The XMPExtension struct lets you do this without writing raw XML:
type XMPExtension struct {
Schema string // human-readable schema name
NamespaceURI string // namespace URI
Prefix string // XML prefix
Properties []XMPExtensionProperty // property declarations
Values map[string]string // property name → value
}Each property in the schema is described by:
type XMPExtensionProperty struct {
Name string
ValueType string
Category string
Description string
}Register extensions on the document with:
func (d *PDFDocument) AddXMPExtension(ext XMPExtension)The backend renders the extension into two XMP blocks automatically: a schema declaration (pdfaExtension:schemas) and a values block with the actual property values.
Example: ZUGFeRD / Factur-X
doc.AddXMPExtension(document.XMPExtension{
Schema: "ZUGFeRD PDFA Extension Schema",
NamespaceURI: "urn:ferd:pdfa:CrossIndustryDocument:invoice:1p0#",
Prefix: "zf",
Properties: []document.XMPExtensionProperty{
{Name: "DocumentFileName", ValueType: "Text", Category: "external",
Description: "name of the embedded XML invoice file"},
{Name: "DocumentType", ValueType: "Text", Category: "external",
Description: "INVOICE"},
{Name: "Version", ValueType: "Text", Category: "external",
Description: "The actual version of the ZUGFeRD data"},
{Name: "ConformanceLevel", ValueType: "Text", Category: "external",
Description: "The conformance level of the ZUGFeRD data"},
},
Values: map[string]string{
"ConformanceLevel": "EN 16931",
"DocumentFileName": "factur-x.xml",
"DocumentType": "INVOICE",
"Version": "1.0",
},
})For a higher-level API that handles ZUGFeRD automatically (format, attachment, and XMP in one call), see the bagme package.
Color profiles
The PDF/A and PDF/X formats require an ICC color profile (OutputIntent). The backend includes two embedded profiles:
func (d *PDFDocument) LoadDefaultColorprofile() (*ColorProfile, error)Loads the FOGRA39 CMYK profile (ISOcoated_v2_eci.icc). Use this for print workflows with CMYK colors.
func (d *PDFDocument) LoadSRGBColorprofile() (*ColorProfile, error)Loads the sRGB IEC61966-2.1 profile (sRGB2014.icc). Use this for documents that use RGB colors, for example when converting HTML/CSS to PDF/A-3b.
func (d *PDFDocument) LoadColorprofile(filename string) (*ColorProfile, error)Loads a custom ICC profile from a file path.
type ColorProfile struct {
Identifier string
Registry string
Info string
Condition string
Colors int
}Accessibility
type StructureElement struct {
Parent *StructureElement
Obj *pdf.Object
Role string
ActualText string
Alt string // alternative text (for Figure, Formula etc.)
Lang string // BCP 47 language tag for this element
Scope string // for TH: &#34;Row&#34;, &#34;Column&#34;, &#34;Both&#34;
BBox [4]float64 // bounding box [x, y, width, height] in PDF points; set during shipout
HasBBox bool
ID int
}func (se *StructureElement) AddChild(cld *StructureElement)Debugging
func (d *PDFDocument) SetVTrace(t VTrace)type VTrace intfunc (d *PDFDocument) ClearVTrace(t VTrace)func (d *PDFDocument) IsTrace(t VTrace) boolfunc (d *PDFDocument) OutputXMLDump(w io.Writer) error