content

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 8, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package content reads PDF content streams: the token stream, and the graphics and text state machine that turns operators into positioned glyphs.

A content stream is a sequence of operands followed by an operator, in postfix order (ISO 32000-2 §7.8.2). The syntax is the same as the object syntax in §7.3 with two differences: there are no indirect references, and bare keywords are operators rather than errors.

This package is deliberately tolerant. Content streams are the part of a PDF most often malformed, because they are generated by drawing code rather than written by a serializer, and a stream that stops tokenizing at the first oddity loses the rest of the page.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type GraphicsState

type GraphicsState struct {
	CTM  geom.Matrix
	Text TextState

	// ClipDepth counts how many clipping paths are active. It is carried so a
	// consumer can tell that text may be clipped away, without this package
	// having to evaluate path geometry.
	ClipDepth int
}

GraphicsState is the part of the graphics state that affects text placement. The full state includes color, line width, and dash patterns, none of which change where a glyph lands, so none of them are tracked.

type Kind

type Kind uint8

Kind distinguishes the token classes a content stream can hold.

const (
	// KindEOF marks the end of the stream.
	KindEOF Kind = iota
	// KindObject is a complete direct object: number, string, name, boolean, null.
	KindObject
	// KindArrayOpen and the three that follow are structural punctuation. They are
	// tokens rather than assembled objects because the lexer does not recurse;
	// assembly is the scanner's job, which keeps nesting depth bounded by the
	// scanner's explicit limit instead of by the call stack.
	KindArrayOpen
	KindArrayClose
	KindDictOpen
	KindDictClose
	// KindOperator is a bare keyword: an operator such as Tj, or a stray token.
	KindOperator
)

type Lexer

type Lexer struct {
	// contains filtered or unexported fields
}

Lexer tokenizes a content stream. It holds no state beyond a position, so a caller may record Pos and resume.

func NewLexer

func NewLexer(data []byte) *Lexer

NewLexer returns a Lexer over data. The slice is not copied and must not be modified while lexing.

func (*Lexer) Next

func (l *Lexer) Next() Token

Next returns the next token, or a KindEOF token at the end of the stream.

func (*Lexer) Pos

func (l *Lexer) Pos() int

Pos returns the current byte offset.

type Machine

type Machine struct {
	// GS is the current graphics state.
	GS GraphicsState

	// Tm is the text matrix, Tlm the text line matrix. Both are only meaningful
	// between BT and ET, and both are reset by BT (§9.4.1).
	Tm  geom.Matrix
	Tlm geom.Matrix

	// InText reports whether the machine is inside a BT/ET pair. Text-showing
	// operators outside one are malformed but common, and are still processed
	// because the text is real.
	InText bool
	// contains filtered or unexported fields
}

Machine tracks graphics and text state across a content stream.

It is separate from Scanner because the same operator sequence drives both text extraction and image extraction, and because state tracking is where the subtle spec rules live. Keeping it apart from tokenizing means each can be tested against its own failure modes.

func NewMachine

func NewMachine(ctm geom.Matrix) *Machine

NewMachine returns a Machine initialized for a page whose media box maps to base with the given CTM.

func (*Machine) Advance

func (m *Machine) Advance(tx float64)

Advance moves the text matrix horizontally by tx unscaled text-space units, as showing a glyph does. Callers compute tx from font metrics.

func (*Machine) AdvanceVertical

func (m *Machine) AdvanceVertical(ty float64)

AdvanceVertical moves the text matrix by ty, for vertical writing mode.

func (*Machine) Apply

func (m *Machine) Apply(op Op) bool

Apply updates state for op and reports whether it was a state operator.

Text-showing operators (Tj, TJ, ', ") are not handled here: they need font metrics to advance the text matrix, which this package does not own. A caller handles them and calls Advance with the displacement it computed.

func (*Machine) InArtifact

func (m *Machine) InArtifact() bool

InArtifact reports whether any enclosing marked-content region is an Artifact. Checking the whole stack, not just the innermost, because an Artifact region can contain nested marked content.

func (*Machine) MCID

func (m *Machine) MCID() int

MCID returns the innermost marked-content identifier, or -1 when none is active. Innermost rather than outermost: nested BDCs occur, and the innermost is the one the structure tree references.

func (*Machine) MarkedTag

func (m *Machine) MarkedTag() Name

MarkedTag returns the innermost marked-content tag, or "" when none is active. Artifact regions are tagged this way, and are what must be dropped to keep running headers and page numbers out of extracted prose.

func (*Machine) NextLine

func (m *Machine) NextLine()

NextLine moves to the next line using the current leading, as T* does.

func (*Machine) RenderMatrix

func (m *Machine) RenderMatrix() geom.Matrix

RenderMatrix returns the composed matrix mapping text space to device space, per ISO 32000-2 §9.4.4.

The parameter matrix folds in font size, horizontal scaling, and rise, and is composed with the text matrix and then the CTM. Getting this composition wrong is the usual cause of extracted text with plausible characters in an implausible order, because every position and every width derives from it.

func (*Machine) SetMCID

func (m *Machine) SetMCID(id int)

SetMCID overrides the innermost region's MCID. A BDC whose properties are a named resource rather than an inline dictionary needs this, since resolving the name requires the page resources that this package does not hold.

func (*Machine) Visible

func (m *Machine) Visible() bool

Visible reports whether text in the current state paints pixels. Mode 3 is invisible and mode 7 is clip-only; both are used for the hidden text layer under a scanned page, which is exactly the text an extractor wants, so this is informational rather than a filter.

type Name

type Name string

Name is a resource name, kept as a distinct type so a font resource key is not confused with a font's PostScript name.

type Op

type Op struct {
	Name     string
	Operands []objects.Object
}

Op is one operator with its operands, in the order they appeared.

Operands are only valid until the next call to Scanner.Next, which reuses the backing array. An operator that needs to retain them copies. This saves one slice allocation per operator, which is worth having across the millions an entire document runs, but it is not the whole cost: each operand value is still boxed into an objects.Object, and strings and names each own a buffer. See the note on Scanner.operands.

func (Op) Arr

func (o Op) Arr(i int) objects.Array

Arr returns operand i as an array, or nil if absent or not an array.

func (Op) Dict

func (o Op) Dict(i int) objects.Dict

Dict returns operand i as a dictionary, or nil if absent or not a dictionary.

func (Op) Int

func (o Op) Int(i int) int

Int returns operand i as an int, or 0 if absent or not numeric.

func (Op) NameAt

func (o Op) NameAt(i int) objects.Name

Name returns operand i as a name, or "" if absent or not a name.

func (Op) Num

func (o Op) Num(i int) float64

Num returns operand i as a float, or 0 if absent or not numeric.

func (Op) Str

func (o Op) Str(i int) objects.String

Str returns operand i as a string, or nil if absent or not a string.

type Scanner

type Scanner struct {
	// contains filtered or unexported fields
}

Scanner groups a content stream's tokens into operators with their operands.

It assembles nested arrays and dictionaries iteratively with an explicit stack, so a stream nesting a million arrays hits maxNestDepth instead of the call stack. Content streams are untrusted input.

func NewScanner

func NewScanner(data []byte) *Scanner

NewScanner returns a Scanner over a decoded content stream.

func (*Scanner) InlineData

func (s *Scanner) InlineData() []byte

InlineData returns the image data of the last BI/ID/EI operator scanned. It is valid until the next call to Next.

func (*Scanner) Next

func (s *Scanner) Next() (Op, bool)

Next returns the next operator, or false at the end of the stream.

Operands that appear without a following operator are discarded at EOF, which is what a truncated stream looks like.

type TextState

type TextState struct {
	// Font is the resource name from Tf, and Size its operand. Size may be
	// negative, which mirrors the glyph, and may be zero, which makes text
	// invisible but still advancing.
	Font Name
	Size float64

	// CharSpace is Tc, added to every glyph's advance in unscaled text units.
	CharSpace float64

	// WordSpace is Tw, added to the advance of single-byte code 32 only. The
	// single-byte restriction is why a CID font with a two-byte code 32 is
	// unaffected, a rule that silently breaks naive extractors.
	WordSpace float64

	// Scale is Th from Tz, as a percentage. It scales horizontal advances and
	// glyph widths but not vertical movement.
	Scale float64

	// Leading is TL, the baseline-to-baseline distance used by T*, ', and ".
	// Positive leading moves down the page.
	Leading float64

	// Rise is Ts, the baseline offset for superscripts and subscripts.
	Rise float64

	// Render is Tr, the rendering mode. Mode 3 is invisible, and mode 7 adds to
	// a clipping path without painting. Both still advance the text position,
	// and both are how OCR layers hide text under a scanned image.
	Render int
}

TextState holds the text-related parameters of the graphics state (ISO 32000-2 §9.3). These persist across BT/ET blocks: a Tf outside any text object still applies to the next one.

type Token

type Token struct {
	Kind Kind
	Val  objects.Object
	Op   string
	Pos  int
}

Token is one lexical unit. Val is set when Kind is KindObject, Op when Kind is KindOperator.

Jump to

Keyboard shortcuts

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