Skip to content

PDF objects#

General#

You can create a new PDF object with

func (pw *PDF) NewObject() *Object

or

func (pw *PDF) NewObjectWithNumber(objnum Objectnumber) *Object

which create a new Object:

type Object struct {
    ObjectNumber Objectnumber
    Data         *bytes.Buffer
    Dictionary   Dict
    Array        []any
    Raw          bool // Data holds everything between object number and endobj
    ForceStream  bool
}

The object is not written to the PDF until saving it

func (obj *Object) Save() error

Example:

myObj := PDFWriter.NewObject()
myObj.Dictionary = pdf.Dict{"Type": "/whatever", "Foo": pdf.Array([]any{1, 2, 3})}
myObj.Save()

creates this object in the PDF file:

7 0 obj
<<
  /Type /whatever
  /Foo [ 1 2 3 ]
>>
endobj

The Object struct has two fields: Dictionary and Array. If the Dictionary is present and has entries (there will be always a /Length entry if the object contains data), this will be written before the contents of the data. Otherwise the Array is used. If you set Raw to true, the header will be skipped and the data will be written to the PDF between the x 0 obj and endobj marker.

Object numbers#

When you create an object, it will get the next object number starting from 1. In PDF it is often necessary to have back and forth references. For example a page needs to know the object number of the pages object and the pages object needs to know the page objects. This can be solved with indirect objects where the /Kids entry in a pages object is an indirect object to be filled when every Page and the Pages objects are written. But it is often easier to reserve an object number for a specific purpose or to create an object and save it later.

For example in this situation:

% The pages object
8 0 obj
<<
  /Type /Pages
  /MediaBox [0 0 595.28 841.89]
  /Kids [ 2 0 R ]
  /Count 2
>>
endobj

% The first page
2 0 obj
<<
  /Type /Page
  /Contents 3 0 R
  /Parent 8 0 R
  % ...
>>
endobj

The code could be something like this:

page1 := PDFWriter.NewObject()

pages := PDFWriter.NewObject()
pages.Dictionary = pdf.Dict{
    "Type": "Pages",
    "Kids": pdf.Array([]any{page1.ObjectNumber}),
}
page1.Dictionary = pdf.Dict {
    "Type": "Page",
    "Parent": pages.ObjectNumber,
}

// todo: error handling
pages.Save()
page1.Save()

Reserve an object number:#

If you just want a unique and free object number, you can call

func (pw *PDF) NextObject() Objectnumber

and use this object number for a new object:

func (pw *PDF) NewObjectWithNumber(objnum Objectnumber) *Object

Names#

Names are PDF objects that start with a slash and are used for keys in dictionaries (among other places). When you assign a string value to a Name object, it should not contain a leading slash. Names are used in keys for the Dict object.

Strings#

PDF strings are enclosed in balancing parenthesis ( ... ) or encoded as hexadecimal values in angle brackets. The PDF backend uses either encoding, depending on the input (but no guarantee is made which encoding is used).

Arrays#

Arrays are implemented as []any. When using the String() method, the array is correctly encoded. Arrays may contain the following types: string, Array, int, float64, Dict, String. Every other value calls the String() method.

Dict#

Dictionaries are implemented as map[Name]any.

Object numbers as values#

The object numbers (type Objectnumber) has the base type int. The String() method returns a reference to the object such as 12 0 R.

Direct PDF writing#

If you want to write text to the PDF without using Objects/Save(), you can use the three Print methods:

func (pw *PDF) Printf(format string, a ...any) error
func (pw *PDF) Println(s string) error
func (pw *PDF) Print(s string) error

Using these methods ensure that the XRef table knows the PDF offsets.