linearize

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 19, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package linearize implements ISO 32000-1:2008 Annex F linearization: the object partition, the two cross-reference sections, the linearization parameter dictionary and the primary hint stream.

It knows nothing about parsing PDF. Everything it needs arrives as a neutral representation -- object numbers, outgoing references, and already-serialized object bodies -- built by internal/pdfdoc, which is the only package allowed to import pdfcpu (arch_test.go). The split is not cosmetic: the hint tables are bit-packed, column-major, MSB-first structures where a wrong bit width produces a file every viewer accepts and qpdf rejects with "overflow reading bit stream". In front of the pdfcpu wall they can be tested against hand-decoded byte vectors from real linearized files; behind it they could only be tested by writing a PDF and reading it back.

This package must import nothing outside the standard library. TestLinearizePackageDependsOnlyOnTheStandardLibrary (linearize_arch_test.go in the root package) enforces that, so an implementer cannot resolve a compile error by reaching for pdfcpu.

Tracked as byb-1y7.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Write

func Write(w io.Writer, p Plan, bodies map[int][]byte, m Meta) error

Write assembles the linearized file.

bodies is keyed by NEW object number and holds each object's serialized bytes WITHOUT the "N 0 obj" / "endobj" wrapper, with every reference already rewritten to new numbers. A stream's body is its dictionary, then "\nstream\n", then the raw payload, then "\nendstream" -- the shape pdfcpu's own writer produces, so an object that round-trips through this package is byte-identical to one that did not.

Types

type Graph

type Graph struct {
	Refs     map[int][]int
	Pages    []int // page object numbers, in page order
	PageTree []int // every /Pages node, root first
	Catalog  int
	Info     int // 0 when the document has none

	// OpenDoc holds the direct targets of the catalog keys a reader consults
	// before it can display anything: /ViewerPreferences, /PageMode, /Threads,
	// /OpenAction and /AcroForm. They belong to part 4.
	OpenDoc []int

	// Outlines is the catalog's /Outlines target, and UseOutlines records
	// whether /PageMode is /UseOutlines. An outline tree that will be shown
	// immediately belongs to the first-page section; one that will not belongs
	// to part 9 (F.3.8).
	Outlines    int
	UseOutlines bool

	// Other holds the direct targets of every remaining catalog key, plus the
	// info dictionary. An object reachable from one of these is document-level
	// material that no single page owns, so it may not be filed under a page
	// even when exactly one page also refers to it.
	Other []int
}

Graph is a document's object graph, with no PDF parser attached. Every object number in it is the ORIGINAL number from the input document.

Refs holds the outgoing references of every object that must survive, so its key set is also the set of objects to be written. A page dictionary's /Parent and /Thumb are deliberately absent from its entry: /Parent points back at the page tree, which Annex F.3.10 puts in part 9, and a thumbnail is not needed to display the page. Both would otherwise drag part-9 material into the first-page section. They stay in the key set because they are still reachable and still get written -- they just do not steer the partition.

type Hint

type Hint struct {
	Payload []byte
	S       int
	O       int
}

Hint is an encoded primary hint stream.

S and O are the /S and /O entries of the hint stream dictionary: the offsets, within Payload, of the shared object table and of the outline table. O is 0 when there is no outline table, and /O must then be omitted.

func EncodeHints

func EncodeHints(h Hints) (Hint, error)

EncodeHints builds the primary hint stream payload.

type Hints

type Hints struct {
	Pages             []PageHint
	FirstPageOffset   int
	Shared            []SharedHint
	NSharedFirstPage  int
	FirstSharedObj    int
	FirstSharedOffset int
	Outline           *OutlineHint
}

Hints is everything the primary hint stream is computed from.

FirstPageOffset, like every offset stored in a hint table, is measured as if the primary hint stream were not present (Annex F.4) -- so it is the offset the first page's first object had before the hint stream was spliced in, not its offset in the finished file.

type Meta

type Meta struct {
	Version string    // e.g. "1.5"; the %PDF- header. "1.4" when empty.
	ID      [2][]byte // the trailer /ID; omitted when the first half is nil
}

Meta is what the assembled file needs that is not structural.

type OutlineHint

type OutlineHint struct {
	FirstObject       int
	FirstObjectOffset int
	NObjects          int
	GroupLength       int
}

OutlineHint is the generic hint table Annex F.4.6 defines for the outline tree. It is emitted only when the document has one; the hint stream dictionary's /O names its offset.

type PageHint

type PageHint struct {
	NObjects  int
	Length    int
	SharedIDs []int
}

PageHint is one page's row of the page offset hint table.

Length is the number of bytes the page's objects occupy, i.e. the sum over the page's NObjects consecutive object numbers of each object's serialized extent. SharedIDs are indices into Hints.Shared, and must be empty for page 0: Table F.4 item 3 defines the first page's shared objects implicitly, and qpdf warns "page 0 has shared identifier entries" for a file that lists them.

type Plan

type Plan struct {
	// Renumber maps original object number to new, over exactly Graph.Refs's
	// key set.
	Renumber map[int]int

	Part4 []int   // catalog first
	Part6 []int   // Part6[0] is the first page's dictionary, i.e. /O
	Part7 [][]int // one group per page 2..N; each group's page dictionary first
	Part8 []int
	Part9 []int

	// PageShared[i] holds, for page i, the indices into the shared-object hint
	// table (Part6 followed by Part8) of the shared objects that page uses.
	// PageShared[0] is always empty: Table F.4 defines the first page's shared
	// objects implicitly.
	PageShared [][]int

	// Outlines holds the outline tree's objects in placement order, the
	// /Outlines root first. They sit at the end of part 6 or the end of part 9
	// -- and they are also members of whichever of those slices they went into,
	// so this is an index, not a fourth part. It is empty when the document has
	// no outline tree, and non-empty means the primary hint stream must carry
	// an outline hint table: the reference implementation computes one whether
	// the outlines went to part 6 or part 9, and warns "incorrect object count
	// in outline hint table" when the file does not have one.
	Outlines []int

	LinDict int // the linearization parameter dictionary's object number
	Hint    int // the primary hint stream's object number
	Catalog int
	Info    int // 0 when the document has none
	Size    int // trailer /Size for the whole file: every object plus object 0
	NPages  int
}

Plan is the layout decision. Every object number in it is the NEW number.

func PlanLayout

func PlanLayout(g Graph) (Plan, error)

PlanLayout partitions g and assigns new object numbers.

type SharedHint

type SharedHint struct {
	GroupLength int
}

SharedHint is one group of the shared object hint table. Every group here is a single object, so the table's "number of objects in the group" column is zero-width.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL