Documentation
¶
Overview ¶
Package pdfgen programmatically builds PDF documents -- both structurally valid skeletons and deliberately corrupted, "crazy, broken" variants -- for fuzzing and stress-testing the parser, verifier, and converter.
It emits PDFs entirely in memory as []byte and never touches disk, so no external document files are needed. Given a fixed seed the output is fully deterministic and reproducible.
The package depends only on the standard library (notably compress/zlib for stream compression) and intentionally does NOT import internal/pdf, so it can be imported by fuzz targets in any package without risking an import cycle.
Index ¶
- func BreakStartxref(in []byte) []byte
- func BreakXrefOffset(in []byte, objNum int, newOffset int64) []byte
- func Generate(seed int64) []byte
- func GenerateGrammar(seed int64) []byte
- func GenerateN(seed int64, n int) [][]byte
- func PlainThreeIssue() []byte
- func Seeds() [][]byte
- type Builder
- func (b *Builder) Bytes() []byte
- func (b *Builder) FinishClassic(trailerBody string) []byte
- func (b *Builder) FinishStartxref(startxrefOffset int64) []byte
- func (b *Builder) Len() int64
- func (b *Builder) Obj(num int, body string)
- func (b *Builder) OffsetOf(num int) int64
- func (b *Builder) StreamObj(num int, dictHead string, raw []byte)
- func (b *Builder) Write(p []byte)
- func (b *Builder) WriteString(s string)
- type Corruptor
- type Generator
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BreakStartxref ¶
BreakStartxref returns a copy of a document with its startxref offset replaced by an unusable value, so the cross-reference section can no longer be located and must be rebuilt by a full-file object scan. It targets the digits after the last "startxref" keyword. Deterministic, for whole-table xref-recovery tests.
func BreakXrefOffset ¶
BreakXrefOffset returns a copy of a classic-xref document with object objNum's 10-digit cross-reference offset replaced by newOffset, leaving every other byte untouched. The document must use a single classic table whose first entry is the standard "0000000000 65535 f" free entry (as Builder.FinishClassic emits). Deterministic, for offset-recovery tests.
func GenerateGrammar ¶
GenerateGrammar builds a fresh random object graph from the PDF grammar (rather than by corrupting a fixed seed), deterministically from seed. It is the generative counterpart to Generate.
func PlainThreeIssue ¶
func PlainThreeIssue() []byte
PlainThreeIssue builds the offset-recovery repro shape: a structurally valid PDF whose PDF/A-1b verification yields three unrelated document-level findings (no trailer /ID, an RGB fill without a matching OutputIntent, no catalog Metadata). The content stream is object 4, so offset-recovery oracles can break its xref entry and assert the other findings survive.
func Seeds ¶
func Seeds() [][]byte
Seeds returns a fresh set of structurally-valid PDF documents covering the distinct read paths the parser supports: classic cross-reference tables, cross-reference streams, object streams, and a metadata-carrying document. They serve both as valid baselines and as the bases the corruptors mutate into broken inputs. Each call returns freshly-allocated byte slices so callers may mutate them in place.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder assembles a PDF byte-for-byte, tracking the byte offset of each indirect object as it is written so a classic cross-reference table or a /W-encoded cross-reference stream can be computed exactly like a real writer would. It is a reusable promotion of the previously test-only pdfBuilder (internal/pdf/xrefstream_test.go) and writeMinimalPDF/buildClassicXRefBody (internal/pdf/document_test.go) helpers.
func NewBuilder ¶
NewBuilder starts a document with the given header (e.g. "%PDF-1.7\n"). A binary-marker comment line is conventionally included by callers after the version comment.
func (*Builder) FinishClassic ¶
FinishClassic appends a correct classic cross-reference table covering the objects written so far (which must be numbered 1..N contiguously, in order), the given trailer dictionary body, and "startxref/%%EOF". It returns the full document bytes.
func (*Builder) FinishStartxref ¶
FinishStartxref appends "startxref\n<offset>\n%%EOF" and returns the full document bytes. Used when the cross-reference section is a stream object the caller has already written.
func (*Builder) Len ¶
Len returns the current length of the document in bytes (e.g. the offset a cross-reference stream about to be written will have).
func (*Builder) Obj ¶
Obj writes a non-stream indirect object with framing that satisfies ISO 32000 6.1.8 (single LF after "obj" and around "endobj").
func (*Builder) StreamObj ¶
StreamObj writes an indirect stream object. dictHead is the dictionary without its closing ">>" (e.g. "<< /Type /ObjStm /N 3 /First 18"); /Length and the closing ">>" are appended automatically with a correct length.
func (*Builder) Write ¶
Write appends raw bytes to the document (for hand-assembled trailers or cross-reference sections).
func (*Builder) WriteString ¶
WriteString appends s to the document.
type Corruptor ¶
Corruptor is a named, deterministic transformation that breaks a valid PDF in one structurally-meaningful way. Given the same input and rng state it produces the same output. It must always return a non-nil slice and must not mutate in; callers may reuse in.
func Corruptors ¶
func Corruptors() []Corruptor
Corruptors returns the full table of structural corruptors. Targeted structural breakage reaches deep parser paths (xref recovery, stream framing, object resolution) far more reliably than blind bit-flipping, which mostly produces trivially-rejected garbage.
Every corruptor falls back to a generic byte mutation when its target token is absent, so no corruptor is ever a silent no-op on an unexpected input.
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator produces deterministic, deliberately-broken PDF documents by taking a structurally-valid seed and applying a random chain of corruptors. It holds no mutable state, so it is safe for concurrent use.
func New ¶
func New() *Generator
New returns a Generator over the default seed corpus and corruptor table.