pdf

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package pdf renders a chart to PDF using nothing but the standard library.

It is the second built-in emitter, and it exists for the same reason as backend/svg: PDF is a text format with a simple imaging model, and writing it directly costs a few hundred lines and no dependency at all. A report generator that wants a vector chart in a document links the same stdlib-only core it would have linked for SVG.

p := figure.New(figure.Title("Signal"))
p.Add(geom.Line(src, geom.X("t"), geom.Y("y")))
err := p.Render(pdf.File("signal.pdf"))

Coordinates

PDF puts the origin at the bottom left with Y running up; figure's IR puts it at the top left with Y running down. The page's content stream opens with a flip, so every coordinate figure emits is written unchanged and the two backends' geometry agrees exactly. Text is placed with its own matrix, which undoes the flip for the glyphs alone so they read the right way up.

Text

By default the output uses the base-14 Helvetica, which every PDF reader has and no document has to embed. That is also the metric set internal/fontmetrics carries, so this backend measures with exactly the font it draws with — margins, tick spacing and collision decisions are not approximations here.

Text is then encoded as WinAnsi, which covers Latin-1 plus the usual typographic punctuation. A rune outside it is written as "?" rather than silently dropped, because a missing label is harder to notice than a wrong one — and that is a code page rather than a policy: Greek, Cyrillic, Hebrew, Thai and every CJK script are outside it.

WithFont is the answer to that. Given a TrueType or OpenType face the document carries its own copy, text is written as glyph ids through an Identity-H encoding, and the repertoire is the font's rather than a code page's. A TrueType font is subset to the glyphs the document actually draws; a CFF-flavoured OpenType font is embedded whole, because cutting charstrings up is a second outline interpreter this package does not have. A ToUnicode map is always written, so an embedded document's text can still be selected, copied, searched and read aloud — a picture of a label is what [ADR 0024](../../docs/adr/0024-accessibility.md) exists to avoid, and an embedded font without one would be exactly that.

An embedded face measures with its own tables, so the promise above holds either way round: this backend still measures with the font it draws with.

Not here

One page per document, no tagging or accessibility structure, no transparency groups. Alpha is expressed as a graphics-state constant, which is what a faded area fill needs and is not the same thing as a full transparency model. Font embedding does not fall back per glyph: a document draws every label in the face it was given, and a rune that face has no glyph for is .notdef rather than a character borrowed from somewhere else.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func File

func File(path string, opts ...Option) ir.Target

File returns a Target that writes a PDF document to the named file. The file is created on Open and closed on Close.

func Writer

func Writer(w io.Writer, opts ...Option) ir.Target

Writer returns a Target that writes a PDF document to w.

Types

type Option

type Option func(*options)

Option configures a PDF target.

func Author

func Author(s string) Option

Author sets the document author.

func Subject

func Subject(s string) Option

Subject sets the document subject.

func Title

func Title(s string) Option

Title sets the document title shown in a reader's properties panel. It is independent of the chart title.

func Uncompressed

func Uncompressed() Option

Uncompressed writes the content stream as plain text rather than deflating it. The file is several times larger and can be read in a text editor, which is what it is for: reading a diff of what the backend emitted.

func WithFont

func WithFont(regular, bold, italic []byte) Option

WithFont embeds the given TrueType or OpenType faces in the document and draws every label with them, instead of naming the base-14 Helvetica that every reader already has.

It is what a chart labelled in anything but Latin-1 needs. Without it the output carries no font at all — which is the right default, because it makes a chart of Latin text a few kilobytes and universally readable — and a rune outside WinAnsi becomes "?": no Greek, no Cyrillic, no Hebrew, no Thai, no CJK. With it, the text is written as glyph ids through an Identity-H encoding and the font travels with the document, so the label reads the same on a machine that has never heard of the typeface.

ttf, err := os.ReadFile("NotoSansJP-Regular.ttf")
// …
p.Render(pdf.File("chart.pdf", pdf.WithFont(ttf, nil, nil)))

bold and italic may be nil, and a face that is absent falls back to the regular one — a bold label drawn in the regular weight is a better answer than one drawn in a font the document does not carry.

What is embedded

A TrueType font is **subset**: only the glyphs the document actually draws are written out, so a chart with twenty Japanese labels carries twenty glyphs rather than a twenty-megabyte font. A CFF-flavoured OpenType font — usually an `.otf` — is embedded **whole**, because cutting up charstrings is a second outline interpreter this package does not have; the document is correct and larger, and a `.ttf` build of the same family avoids it.

A `ToUnicode` map is always written, so the text in the document can still be selected, copied, searched and read aloud. A picture of a label is what the accessibility work exists to avoid, and it would be exactly what an embedded font without one produced.

A parse failure surfaces when the target is opened rather than here, because an Option cannot return an error.

Jump to

Keyboard shortcuts

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