gopdf

package module
v0.0.0-...-adfb3bf Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2026 License: MIT Imports: 40 Imported by: 0

README

gopdf

Create, read, edit, fill, sign and redact PDFs — in pure Go, with nothing but the standard library.

test Go Reference

No cgo. No third-party dependencies. No native PDF library underneath — the document writer, the file parser, the font subsetter, the filters and the encryption are all implemented here.

📖 Documentation

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	doc := gopdf.New()
	page := doc.AddPage()
	page.SetFont(gopdf.Helvetica, 14)
	page.Text(72, 72, "Hello, PDF!")
	if err := doc.Save("hello.pdf"); err != nil {
		log.Fatal(err)
	}
}
go get github.com/SalvioniDigitalSolutions/gopdf

What it does

Writing

  • Multi-page documents, standard sizes (A3–A5, Letter, Legal) or custom, in either orientation, with Unicode metadata
  • The standard 14 fonts with accurate metrics, plus TrueType and OpenType embedding.ttf, .ttc and .otf are all subset to the glyphs you use, outlines, subroutines and glyph names alike, CID-keyed fonts included — a 55 MB CJK face embeds as a few hundred kilobytes — with pair kerning and ToUnicode maps for full Unicode text that stays searchable
  • Vector graphics: lines, rectangles, rounded rectangles, circles, ellipses, polygons, Bézier paths, dash patterns, caps and joins, fill/stroke opacity, clipping, and scoped transforms
  • Axial and radial gradients with any number of colour stops, painted into a rectangle, a circle or any path you clip to
  • Images: JPEG embedded byte-for-byte, PNG/GIF/image.Image with alpha preserved as soft masks, grayscale, and Adobe CMYK handling
  • Word wrapping, alignment, links, and a nestable bookmark tree
  • Encryption: AES-128 or AES-256 with per-field permissions
  • Page labels: the numbering a reader sees — roman front matter, prefixed appendices — read, written and resolved a page at a time
  • Layers: declare optional content, draw on it, and switch it on or off in a document that already has some
  • XMP metadata: read a packet in whichever shape its producer wrote, and write one generated from the information dictionary so the two cannot disagree
  • PDF/A: write to the archival profile — the metadata, the output intent and the identification it needs, and a refusal rather than a file that claims a conformance it does not meet — and check an existing document against it

Reading and manipulating

  • A native parser: classic xref tables, PDF 1.5+ cross-reference streams, object streams, hybrid files; Flate (with PNG/TIFF predictors), LZW, ASCII85, ASCIIHex and RunLength filters
  • Merge, split, rotate, stamp and watermark
  • Styled text fragments: PageTextFragments gives every show-text operation with its baseline, advance width, /BaseFont, effective size and render mode, in content-stream order, descending into forms — enough to anchor a frame over a word or to feed a detector that reports offsets
  • Justified text and ligatures: a line justified by drawing a space and then moving the pen reads with one space, not two, so a name typed the only way anyone types it still matches; and a face whose f exists only inside an fi can still be written back, because a ligature is inverted as a run rather than a rune at a time
  • Text extraction through ToUnicode CMaps and simple-font encodings, descending into nested form XObjects, with word breaks decided by measuring the gap rather than guessing — troff and TeX output reads as words, not as BA SH and Softw are
  • Type 3 fonts: glyph-space widths scaled through the font matrix, and text that extracts instead of coming out empty
  • In-place text editing that preserves the layout exactly
  • Paragraph reflow that re-wraps text across a paragraph's own lines
  • Flow engine: replace text of any length, keeping each part's styling, growing or shrinking the paragraph by whole lines and pushing what follows out of the way
  • Interactive forms: read fields, fill them (flattened or still editable), and author new ones from scratch
  • Images: list what a page draws, with placement, draw matrix and colour space, decode the pixels, and replace one in place
  • Restyling: change an existing run's typeface, size or colour, not just the characters it draws
  • Annotations: read, add and remove highlights, underlines, strike-outs, sticky notes, boxes and links — on new pages or in place
  • Embedded files: list what a document carries inside it, extract it, attach more — to a document being built or an existing one — and take them out again, including the paperclip annotations on a page
  • Page operations: delete, reorder and move pages, in place
  • Incremental update: edit text, draw, annotate and reorder, appended so the original file survives byte for byte — including everything the library does not model
  • Digital signatures: sign a document with an X.509 certificate, read the signatures already on one, and tell whether a file was changed after it was signed
  • Redaction that actually removes: glyphs come out of the content stream, pixels out of the image, annotations out of the page, and the result is a fresh file rather than an appended revision — then read back and checked, so a document that still shows the text is withheld
  • Pseudonymization: swap identifying text for tokens of any length, reflowing the paragraph and reaching the copies in metadata, annotations, bookmarks and form fields — then proving none of the original is left
  • Width-fitted substitutions: or keep the whole token and set it at the size that makes it exactly as wide as the text it replaced, so the line breaks stay where they were and nothing below them moves
  • OCR-driven redaction: plug in an engine (a tesseract adapter ships in the repo) and text rules also reach words inside a scan — pixels overwritten, a token drawn in their place, then read again to prove it
  • Fonts are told apart by identity, not by name: a form XObject carries its own /Font dictionary and producers reuse the same short names inside it, so /TT0 on the page and /TT0 in a form are routinely two different faces — drawn with each other's metrics, a heading comes out as one blot of ink
  • Page rendering: RenderPage draws a page to an image — paths, fills, strokes with caps, joins and dashes, clips, colour spaces, axial and radial shadings, mesh shadings, tiling and shading patterns, soft masks, all fifteen blend modes, raster images, annotation appearances, and text, set from the outlines the document's own fonts carry. Layers the document switches off are not painted. Each layer is a separate switch, so the artwork behind live text is one call and a full-page picture is another
  • Glyph outlines: TrueType glyf contours including composites, and CFF Type 2 charstrings run properly rather than approximated — the same shapes a viewer draws
  • Font substitution: a document may name Arial without embedding it. SubstituteFont lets you supply the shapes and SystemFonts builds that from the machine's own fonts; advances still come from the document, so the text lands exactly where the document says
  • The object graph: Resolve, Object, Catalog, PageDict and Walk read any object in the file, and AddObject/SetObject write them back — the escape hatch for anything the typed API does not model, going through the same decryption, filters and cross-reference machinery as everything else
  • Tagged PDF: read the structure tree, with the document's own element names mapped through its role map, the alternate text a screen reader depends on, and the heading outline a document has whether or not it also has bookmarks
  • Repairs damaged files: a wrong startxref, bytes before the header or a broken table are recovered by scanning for the objects
  • Reads encrypted files (RC4, AES-128, AES-256) with either password

Highlights

Edit text without destroying the layout
src, _ := gopdf.Open("invoice.pdf")
doc := gopdf.New()

page, _ := doc.EditPage(src, 0)          // keeps the original operators
page.ReplaceText("DRAFT", "FINAL")       // drawn in the page's own font
doc.Save("final.pdf")

The replacement is encoded with the font the original text used, so it renders identically, and the width difference is compensated so nothing else on the page moves. Editing a real-world PDF this way changes only the pixels of the edited lines — the rest of the page is byte-identical.

If the page's font is a subset without a glyph your replacement needs, the edit is refused with a clear message rather than rendering blank boxes.

Replace text of any length, keeping the styling

ReplaceText swaps text inside one line. When the replacement is a different length, a flow re-wraps the whole paragraph instead.

r, _ := gopdf.Open("contract.pdf")
u := gopdf.Update(r)
page, _ := u.Page(0)

// Rewrites every paragraph containing the phrase, and moves the ones
// below to make room.
page.ReplaceTextFlow("twelve months", "thirty-six calendar months from the effective date")

// Or work a paragraph at a time.
for _, f := range page.Flows() {
	f.Replace("EUR 1,200", "EUR 27,450.99")   // stays bold if it was bold
	fmt.Println(f.LineCount(), f.LineDelta()) // how it grew
}
u.Save("revised.pdf")

Two things it gets right. Styling survives: a paragraph is modelled as styled spans rather than lines, so a replacement inherits the styling of the text it replaces and everything around it keeps its own — swap a figure inside a bold phrase and it stays bold, while the sentence around it does not. Length is free: the paragraph is re-wrapped to its own column using each span's own font metrics, taking however many lines it needs, and everything below it on the page moves down or up to match.

A word split across two operations, or drawn one glyph at a time as justified documents often are, is matched all the same. Cap the growth with SetMaxExtraLines where a paragraph must not run past its box.

Fill a form, or build one
// Fill and flatten — the result cannot be changed by the recipient
doc.FillForm(src, map[string]string{"applicant": "Ada Lovelace"})

// Fill and keep it editable, with freshly generated appearances
doc.FillFormInteractive(src, map[string]string{"applicant": "Ada Lovelace"})

// Or author a form from scratch
page.AddTextField("name", 160, 100, 240, 20, gopdf.FieldOptions{MaxLen: 60})
page.AddCheckbox("newsletter", 160, 160, 16, gopdf.FieldOptions{Selected: true})
page.AddRadioButton("plan", "pro", 240, 190, 14, gopdf.FieldOptions{Selected: true})
page.AddChoiceField("country", 160, 130, 160, 20,
	[]string{"Italy", "France", "Spain"}, gopdf.FieldOptions{Value: "Italy"})
Update a file without rewriting it
r, _ := gopdf.Open("contract.pdf")
u := gopdf.Update(r)

page, _ := u.Page(0)
page.ReplaceText("2024", "2026")          // edit what is there
page.SetFont(gopdf.HelveticaBold, 48)     // draw on top, same pass
page.SetFillColor(gopdf.RGB(200, 30, 30))
page.Text(120, 400, "REVISED")
page.AddHighlight(60, 300, 200, 14, "check", gopdf.NoteOptions{Author: "AL"})
u.SetFormValues(map[string]string{"signatory": "A. Lovelace"})
u.MovePage(3, 0)                          // and reorder

u.Save("contract.pdf")   // safe to overwrite the source

An incremental update writes the original bytes out unchanged and appends only what differs, chained to the old cross-reference table. Structure trees, embedded files, optional content, scripts — anything gopdf does not model — survives untouched, because it is never rewritten. Rebuilding a document with EditPage or ImportPage keeps only what the library understands; Update keeps everything.

An updated page carries the full drawing API, so stamps, watermarks and signatures can be added without rewriting a single original object: the drawn content becomes an extra content stream and its resources are merged under a collision-proof prefix.

Images and restyling
for _, im := range r.PageImages(0) {
	fmt.Printf("%dx%d %s at (%.0f,%.0f)\n", im.Width, im.Height, im.ColorSpace, im.X, im.Y)
	pixels, err := im.Decode()          // an image.Image
}

u := gopdf.Update(r)
u.ReplaceImage(im, newLogo)             // scaled into the same box

page, _ := u.Page(0)
for _, run := range page.Runs() {
	if run.Text == "Heading" {
		blue := gopdf.RGB(20, 70, 190)
		run.Restyle(gopdf.TextStyle{Font: gopdf.HelveticaBold, Size: 15, Color: &blue})
	}
}
Gradients
page.FillGradientRect(30, 60, 200, 80, gopdf.GradientVertical,
	gopdf.Stop(0, gopdf.RGB(40, 90, 200)),
	gopdf.Stop(1, gopdf.RGB(230, 240, 255)))

page.FillGradientCircle(300, 100, 45,
	gopdf.Stop(0, gopdf.White), gopdf.Stop(1, gopdf.RGB(180, 30, 90)))

// Or into any shape you clip to
page.Push()
page.Circle(cx, cy, r, gopdf.ClipPath)
page.PaintLinearGradient(x0, y0, x1, y1, stops...)
page.Pop()
Draw a page
r, _ := gopdf.Open("report.pdf")

// The artwork only: paths, shadings, patterns and soft masks, with the
// text left out. Useful behind a live text layer.
art, _, _ := r.RenderPageDetail(0, gopdf.RenderOpts{
    DPI: 150, IncludeVector: true,
})

// Or the whole page. Fonts the document embeds are drawn from their own
// outlines; SystemFonts stands in for the ones it only names.
img, report, err := r.RenderPageDetail(0, gopdf.RenderOpts{
    DPI:                 150,
    IncludeVector:       true,
    IncludeText:         true,
    IncludeRasterImages: true,
    SubstituteFont:      gopdf.SystemFonts(),
})
if report.Missing > 0 {
    log.Printf("%d glyphs had no font to draw them with", report.Missing)
}

Text that clips is followed whether or not text is drawn, because a headline used as a clip decides where a gradient shows through — ignore it and the gradient covers the page.

MinTextSize draws only glyphs at or above a size, measured after the text matrix and the transform have scaled them — the size PageTextFragments reports. A watermark is set many times larger than the body it sits over, so a threshold between the two renders the watermark alone, from the document's own matrices and therefore in exactly the place the document puts it:

backdrop, _ := r.RenderPage(0, gopdf.RenderOpts{
    DPI: 150, IncludeText: true, MinTextSize: 72, Transparent: true,
})

The body is not drawn and painted over; it is never drawn. What comes back can be handed on without a stripped copy of the file ever existing.

Reach the object graph

Everything else in the package is an opinion about what a PDF is for. When a file does something those opinions have no word for, the graph itself is reachable:

r, _ := gopdf.Open("odd.pdf")

// Read: follow a reference, walk from the trailer, decode a stream.
lang := r.Resolve(r.Catalog()["Lang"])
if stm, ok := r.Resolve(r.PageDict(0)["Contents"]).(*gopdf.Stream); ok {
    data, _ := stm.Data() // decoded, and decrypted if the file is
    _ = data
}
r.Walk(func(ref gopdf.Ref, obj any) bool {
    _ = obj
    return true // false stops the walk
})

// Write: add objects and replace existing ones, appended incrementally.
u := gopdf.Update(r)
ref := u.AddObject(gopdf.NewStream(gopdf.Dict{}, []byte("q 1 0 0 RG 4 w 0 0 m 99 99 l S Q")))
u.SetCatalogEntry("Lang", gopdf.String("en-GB"))
_ = u.Save("odd-out.pdf")
_, _ = ref, lang

The reader hands back its own dictionaries: Clone one before changing it, and write the copy back with SetObject.

What a document says about itself

A file carries more than its pages: what a reader should call page 4, what can be switched off, what it claims to be, and what it has tucked inside.

doc := gopdf.New()

// The numbering a reader sees, which need not be the numbering underneath.
doc.SetPageLabels([]gopdf.PageLabelRange{
    {From: 0, Style: gopdf.LabelRomanLower},          // i, ii, iii, iv
    {From: 4, Style: gopdf.LabelDecimal, Start: 1},   // 1, 2, 3...
    {From: 40, Style: gopdf.LabelDecimal, Prefix: "A-"},
})

// Content that can be switched off.
draft, _ := doc.AddLayer("Draft stamp", false) // starts hidden
p := doc.AddPage()
p.BeginLayer(draft)
p.Text(100, 400, "DRAFT")
p.EndLayer()

// Metadata generated from the information dictionary, and the archival
// profile — which refuses rather than claim a conformance it does not meet.
doc.SetInfo(gopdf.Info{Title: "Report", Author: "Ada Lovelace"})
doc.SetXMP(true)
doc.SetPDFA(gopdf.PDFA2b)
_ = doc.Attach("figures.csv", csv)
_ = doc.Save("report.pdf")

And the same things, read back off a file someone else wrote:

r, _ := gopdf.Open("report.pdf")

fmt.Println(r.PageLabel(3))  // "iv"
for _, l := range r.Layers() {
    fmt.Println(l.Name, l.On)
}
fmt.Println(r.XMP().Title)

// A tagged document says what its content is, not just where it sits.
if r.Tagged() {
    for _, h := range r.StructOutline() {
        fmt.Printf("%*s%s (page %d)\n", h.Level*2, "", h.Text, h.Page+1)
    }
    fmt.Println(r.StructText()) // in reading order, from the structure
}

for _, a := range r.Attachments() {
    data, _ := a.Data()
    fmt.Println(a.Name, len(data), a.Description)
}

for _, issue := range r.CheckPDFA(gopdf.PDFA2b) {
    fmt.Println(issue) // page 2: every font must be embedded (/F1 is Helvetica...)
}

An incremental update can change any of them without rewriting the file: SetPageLabels, SetLayerVisible, SetXMP, Attach and RemoveAttachments are all on Updater too.

Merge, watermark, encrypt
gopdf.Merge("combined.pdf", "a.pdf", "b.pdf")
gopdf.ExtractPages("first-two.pdf", "input.pdf", 0, 1)

doc := gopdf.New()
for i := 0; i < src.NumPages(); i++ {
	page, _ := doc.ImportPage(src, i)      // an imported page is a normal Page
	page.Push()
	page.SetAlpha(0.3, 0.3)
	page.RotateAt(45, page.Width()/2, page.Height()/2)
	page.SetFont(gopdf.HelveticaBold, 72)
	page.TextAligned(0, page.Height()/2, page.Width(), gopdf.AlignCenter, "DRAFT")
	page.Pop()
}
doc.Encrypt("", "owner-password", gopdf.AllowPrint, gopdf.AES256)
doc.Save("watermarked.pdf")
Redact, and mean it

Covering something with a black rectangle hides it from a reader and leaves it in the file. This removes it.

r, _ := gopdf.Open("case-file.pdf")
rd := gopdf.Redact(r)

rd.Text("Ada Lovelace")                                  // every occurrence
rd.Pattern(regexp.MustCompile(`\d{3}-\d{2}-\d{4}`))      // every match
rd.Area(2, 60, 200, 180, 40)                             // a rectangle on page 2
rd.Match(func(run *gopdf.TextRun) bool {                 // anything else
	return run.FontName == "F3"
})

marks, _ := rd.Marks()          // review before committing to it
for _, m := range marks {
	fmt.Printf("%s p%d %q\n", m.Kind, m.Page, m.Text)
}
rd.Save("redacted.pdf")

What gets removed, and how:

Content What happens
Text The glyphs are cut out of the content stream. A gap the same width is left behind, so nothing on the line moves.
Images The pixels in the area are overwritten and the image re-encoded. One that cannot be decoded is dropped whole rather than left.
Vector artwork A path lying entirely inside the area is deleted. One that straddles the edge is reported by PartialArtwork, not silently kept.
Annotations Removed, along with whatever text they hold.
Metadata The information dictionary and XMP stream go by default.
Second copies /Thumb, /Alternates and /PieceInfo are dropped: each can hold the page as it was before redaction.
Scans With an OCR engine set, words inside images are found, their pixels overwritten, and every image read again to prove it.
Annotations Their appearance streams are read as well as their strings, and a stale appearance is dropped when the strings change.

Matching is word-bounded (Rossi not inside Rossini), joins words hyphenated across a line break, reads lines a document set one fragment at a time, and matches non-breaking-space and soft-hyphen spellings — with the same definition used by the read-back, so the check and the matcher never disagree.

The output is read back before you get it. If a document draws text in a way redaction could not reach, WriteTo reports it and writes nothing, rather than handing back a file that looks redacted and is not. Turn the check off with SetVerify(false) if the second parse costs more than the assurance is worth.

Two properties it is built around. First, a word a content stream split in two — Administra then tion, or one glyph at a time, as justified documents often are — is still matched, because matching runs over a whole line rather than one operation at a time. Second, the output is a complete rewrite, not an incremental update: an update appends, and everything it replaced stays readable in the bytes underneath it.

Pseudonymize, when a marker beats a gap
res, _ := gopdf.PseudonymizeFile("case.pdf", "anonymous.pdf", []gopdf.Pseudonym{
	{From: "Ada Lovelace", To: "[[PII_NAME_1]]"},
	{From: "12 Dorset Street", To: "[REDACTED]"},
})

The token need not be the same length — the paragraph re-wraps around it and keeps its styling. Or it need not move the paragraph at all:

{From: "Ada Lovelace", To: "[[PII_NAME_1]]", FitWidth: true}

FitWidth keeps the whole token and makes it claim exactly the width the words it replaced took — shrinking a wider one, padding a narrower one with a kern — so nothing after it moves. Where every token on a page fits, the page is edited in place: the strings holding them are rewritten and nothing else is, kerns included, so a highlight or a rule or the dots of a dash leader still sit over the same text afterwards. Shrinking stops at 45% of the run's size, or wherever MinScale says: a key-reversible marker like [[PII_LOCATION_001]] is long by construction, and over a short word it needs a fifth of the size rather than a half — still searchable, still extractable, still exactly what the key file holds. A token that will not come down that far is set at the floor and the paragraph re-wraps as it otherwise would. Across 127 documents of a real corpus the page came back exactly as it was, tokens aside, on 108 of them — against 22 without the flag. Where the document's own subset font cannot set the token (no [ in it), the inserted text falls back to a standard font matched to the face, and only ever the inserted text. Mappings are also expanded into the spellings a document might have used, so a name typed with an ordinary space still matches one written with a non-breaking one. It reaches the copies of a name that nothing draws but everything reads: the metadata, the XMP packet, annotation notes, bookmark titles, form field values. Then it reads the result back and withholds it if any original is still findable.

Full guide: docs/REDACTION.md.

Sign a document, and check the ones already on it
r, _ := gopdf.Open("contract.pdf")
u := gopdf.Update(r)
u.Sign(gopdf.SignOptions{
	Certificate: cert,           // *x509.Certificate
	Key:         key,            // any crypto.Signer, including an HSM
	Name:        "Ada Lovelace",
	Reason:      "Approval",
})
u.Save("signed.pdf")

The signature is a detached PKCS#7 blob covering every byte of the file except itself, written as an incremental update — so signing a document twice leaves the first signature intact and still valid. Reading works on anything, not just files gopdf wrote:

for _, s := range r.Signatures() {
	fmt.Println(s.Signer, s.When, s.CoversWholeFile)
}

CoversWholeFile is the one that matters: a signature whose byte range stops short of the end of the file was signed before something else was appended.

Full guides, the complete API tour and the design notes are in the documentation.

Examples

Command What it shows
examples/demo Every drawing feature on three pages
examples/stamp Watermarking an existing PDF
examples/edit Listing and rewriting text in place
examples/redact Removing text permanently, with a dry run first
go run ./examples/edit -in report.pdf -list
go run ./examples/edit -in report.pdf -out final.pdf -replace "DRAFT=FINAL"
go run ./examples/redact -in case.pdf -list -text "Ada Lovelace"

Correctness

Coordinates are in points (1/72 inch) with the origin at the top-left of the page; Mm, Cm and Inch convert other units.

  • 554 tests at 86% statement coverage, covering the writer, the parser, the font subsetter, the filters, encryption, editing, reflow, flow, forms, signatures, redaction, rendering and attachments

  • Text extraction measured against pdftotext over 918 real documents: agreement rose from 0.773 to 0.849 when word breaks started being measured rather than guessed, improving 581 files and regressing 35. Two later corrections were measured the same way, over 127 documents: asking the font's own encoder how wide a space is rather than assuming code 32 — which in a CID font is whichever glyph happens to sit there — changed one document and moved it closer to Poppler; and reading the pen move that follows a drawn space as justification rather than as a second word break cut runs of two-or-more spaces from 2,172 to 1,621, changing 30 documents, 29 of them closer to Poppler and none farther

  • Swept against 4,635 real PDFs — macOS and application resources, Go module fixtures, and a 130,000-file legal corpus spanning Word, StarOffice, LibreOffice, iText, Aspose, Quartz, groff and TeX, PDF 1.1 through 2.0. Every one opens, extracts and round-trips. A further 3,613-file redaction sweep removed a word from each and confirmed it was gone from both the text and the raw bytes, and a 2,000-file flow sweep replaced a word with a much longer one and checked the paragraph reflowed intact. Between them the sweeps found four real bugs, all fixed and now regression-tested.

    A later 4,000-document redaction sweep removed a word from each and had pdftotext confirm it was gone: 3,986 succeeded, none silently, and none produced a damaged document. The 137 that were refused rather than written turned out to be the matcher and the verifier reading the same page differently — a gap inside one show-text operation, and a word hyphenated across a line — and fixing both left 14

  • Generated documents, as many as there is patience for. A corpus of real files covers what those files happen to do; a generator covers the combinations nobody thought of, and hands back a seed that reproduces a failure exactly. Each seed builds a document from a different mixture of page sizes, rotations, fonts, colours, transforms, transparency, compression and object streams, and the properties asserted are the ones that must hold for any document at all: it parses with a cross-reference table that leads everywhere, every word written comes back out, writing it again changes nothing, it renders, and what redaction says it removed is gone from the text and from the bytes. 250,000 documents have been through it

  • Fuzz targets for the PDF reader, the TrueType and CFF parsers, the content-stream lexer, the CMap parser, the stream filters and the JBIG2 decoder, with a checked-in regression corpus. Fuzzing has found and fixed real bugs: a denial-of-service in cmap parsing, and one in JBIG2 where the sizes a stream declares decided how many pixels to walk — forty-three bytes kept the decoder busy for thirteen seconds before reporting the stream malformed, and now take 245 microseconds

  • Rendering measured against pdftoppm: across 1,500 documents 99.9% of the glyphs a page asks for are drawn, and on the check that matters — ink where the reference has none — the median page scores zero and 99% are under two per cent

  • Validated against Poppler in both directions: files gopdf writes are read by an independent implementation, and files other tools wrote are read, edited and rewritten by gopdf with their text preserved. The JBIG2 decoder is checked against Poppler's own, which is an independent implementation of the same specification: a stream goes through both and every pixel has to match, for each generic-region template, for the typical-prediction shortcut, and for a symbol dictionary and text region — the comparison found two conformance mistakes in the test encoder that a round trip could not see. Attachments extracted by gopdf are byte-identical to pdfdetach, and every font a stream selects is checked to be declared where that stream resolves names — a thing gopdf's own forgiving reader could not have caught. Signatures are checked with pdfsig, which reports them valid and the document wholly covered, and the CMS blob parses under OpenSSL

  • Unencrypted output is byte-for-byte deterministic

  • Stream decoding is bounded against decompression bombs

Limitations

Stated plainly, because they matter when choosing a library:

  • The document's own words can only be re-set in the document's own fonts, and a subset carries only the glyphs its pages draw. Inserted text is not held to that: a token whose characters the subset lacks is set in a standard font matched to the face, and only ever the inserted text. A letter the document draws only inside a ligature — an f in a face that always joins it — can be written back, because the ligature is invertible as a run even though it is not invertible one rune at a time. What remains impossible is refused rather than mangled.
  • An incremental update only grows a file; superseded objects stay in it.
  • Object streams are opt-in and skipped for encrypted documents, whose strings are protected per object rather than by the enclosing stream.
  • Reflow (TextBlock) re-wraps within the lines a paragraph already occupies. Use a Flow when the length changes: it adds or removes lines and moves the text below. A flow moves text, not images or rules. Where the font has no space glyph — a document that sets every word separately and never draws one embeds a subset without it — the gap is written as a move, which is what that document already did. A paragraph may grow, and not past the bottom of its page: text written below the edge is in the file and on no page, so that is refused rather than written.
  • FillForm flattens; FillFormInteractive keeps fields editable.
  • Permission flags on encrypted documents are advisory, as the PDF specification defines them — they are not a security boundary.
  • Signatures are adbe.pkcs7.detached with SHA-256. Signing produces the blob and the byte range; obtaining a timestamp from a TSA, and deciding whether a certificate is one you trust, are left to the caller.
  • The archival profile is checked for the things that go wrong in practice — a font that is not embedded, an encrypted file, a script, a missing intent. It is not a certificate: a full validator also checks colour management and the internals of embedded font programs.
  • A JBIG2 image is decoded whether it is coded pixel by pixel as a generic region or shape by shape through a symbol dictionary and a text region, which is what a scanner produces for a page of prose. The Huffman-coded variants, refinement coding and halftone regions are reported rather than guessed at, and JPEG 2000 is not decoded at all; those are dropped whole by redaction rather than part-scrubbed.
  • Rendering draws a glyph from the outlines the document carries. A font the document names but does not embed has no outlines to draw, and a bare PostScript font is addressed by glyph name through the built-in encodings this package does not carry. Both are handled by supplying a substitute; without one their text is left undrawn and RenderPageDetail reports how much.
  • Redaction removes content. A string can also live somewhere structural — a font's /BaseFont name, for instance — and that is not content to remove. Attachments are removed by default, and a redaction that is told to keep one is refused if the words it removed are still readable inside it; a compressed attachment cannot be searched that way, so finding nothing there is not proof of the opposite. Vector artwork that straddles the edge of an area is covered but not deleted; PartialArtwork reports it so you can enlarge the area. A rewrite writes an encrypted source out unencrypted, since re-encrypting is a decision to take deliberately.

Roadmap

Nothing outstanding from the original plan. Candidates, in no order: PAdES timestamps, public-key (certificate) security handlers, JPEG 2000 decoding, Huffman-coded JBIG2, linearization, and reflow that carries a paragraph onto the next page.

License

MIT

Documentation

Overview

Package gopdf is a pure-Go PDF generation library with no dependencies outside the standard library.

A minimal document:

doc := gopdf.New()
page := doc.AddPage()
page.SetFont(gopdf.Helvetica, 14)
page.Text(72, 72, "Hello, PDF!")
if err := doc.Save("hello.pdf"); err != nil {
	log.Fatal(err)
}

All coordinates use points (1/72 inch) with the origin at the top-left corner of the page. The Mm, Cm and Inch constants convert other units to points, e.g. 25*gopdf.Mm.

Example

A minimal one-page document.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	doc := gopdf.New()
	page := doc.AddPage()
	page.SetFont(gopdf.Helvetica, 14)
	page.Text(72, 72, "Hello, PDF!")
	if err := doc.Save("hello.pdf"); err != nil {
		log.Fatal(err)
	}
}

Index

Examples

Constants

View Source
const (
	Pt   = 1.0
	Inch = 72.0
	Cm   = 72.0 / 2.54
	Mm   = 72.0 / 25.4
)

Unit conversion factors to points, e.g. 10*gopdf.Mm is ten millimeters.

Variables

View Source
var (
	A3     = PageSize{841.89, 1190.55}
	A4     = PageSize{595.28, 841.89}
	A5     = PageSize{419.53, 595.28}
	Letter = PageSize{612, 792}
	Legal  = PageSize{612, 1008}
)

Standard page sizes in portrait orientation.

View Source
var (
	Courier              = &Font{name: "Courier", defaultWidth: 600, winAnsi: true}
	CourierBold          = &Font{name: "Courier-Bold", defaultWidth: 600, winAnsi: true}
	CourierOblique       = &Font{name: "Courier-Oblique", defaultWidth: 600, winAnsi: true}
	CourierBoldOblique   = &Font{name: "Courier-BoldOblique", defaultWidth: 600, winAnsi: true}
	Helvetica            = &Font{name: "Helvetica", widths: &helveticaWidths, specials: helveticaSpecials, defaultWidth: 556, winAnsi: true}
	HelveticaBold        = &Font{name: "Helvetica-Bold", widths: &helveticaBoldWidths, specials: helveticaBoldSpecials, defaultWidth: 556, winAnsi: true}
	HelveticaOblique     = &Font{name: "Helvetica-Oblique", widths: &helveticaWidths, specials: helveticaSpecials, defaultWidth: 556, winAnsi: true}
	HelveticaBoldOblique = &Font{name: "Helvetica-BoldOblique", widths: &helveticaBoldWidths, specials: helveticaBoldSpecials, defaultWidth: 556, winAnsi: true}
	TimesRoman           = &Font{name: "Times-Roman", widths: &timesRomanWidths, specials: timesSpecials, defaultWidth: 500, winAnsi: true}
	TimesBold            = &Font{name: "Times-Bold", widths: &timesBoldWidths, specials: timesBoldSpecials, defaultWidth: 500, winAnsi: true}
	TimesItalic          = &Font{name: "Times-Italic", widths: &timesItalicWidths, specials: timesItalicSpecials, defaultWidth: 500, winAnsi: true}
	TimesBoldItalic      = &Font{name: "Times-BoldItalic", widths: &timesBoldItalicWidths, specials: timesBoldSpecials, defaultWidth: 500, winAnsi: true}
	Symbol               = &Font{name: "Symbol", defaultWidth: 600}
	ZapfDingbats         = &Font{name: "ZapfDingbats", defaultWidth: 700}
)

The standard 14 PDF fonts. Symbol and ZapfDingbats use their built-in symbolic encodings; the others use WinAnsi (CP-1252).

View Source
var (
	Black = Color{0, 0, 0}
	White = Color{255, 255, 255}
)
View Source
var ErrPasswordRequired = errors.New("gopdf: encrypted PDF requires a password")

ErrPasswordRequired is returned when a file is encrypted and the supplied password (the empty string, for Open and NewReader) does not open it. Retry with OpenPassword or NewReaderPassword.

Functions

func ExtractPages

func ExtractPages(dst, src string, pages ...int) error

ExtractPages writes the given pages (0-based indexes, in the given order) of the source PDF to a new file at dst.

func Merge

func Merge(dst string, sources ...string) error

Merge combines the pages of the source PDF files, in order, into a single new file at dst.

func Rewrite

func Rewrite(r *Reader, w io.Writer) (int64, error)

Rewrite writes a document as a fresh file containing only the objects it still reaches. Superseded objects left behind by earlier incremental updates are dropped, which both shrinks the file and removes content that was replaced but never deleted.

An encrypted source is written unencrypted: the objects are decrypted to be read, and re-encrypting them is a separate decision the caller should make deliberately.

func StrictLexPages

func StrictLexPages(data []byte) error

StrictLexPages reports the first place in a document where two tokens of a content stream have run together.

Writers here splice replacements into a stream that was written by somebody else, and a splice landing immediately after an operator whose trailing space it consumed leaves "Tc" and "1" as the single token "Tc1". Every reader in this package tolerates that, and so do the common ones, which is exactly why it goes unnoticed: the content is right and the file is not, and a strict parser rejects the page. Anyone who re-opens their own output to check it pays for the difference.

It is exported for that check. Nothing in this package needs it, and output from this package should always pass it.

func SystemFonts

func SystemFonts() func(FontRequest) []byte

SystemFonts returns a substitution function that looks for a matching face among the fonts installed on this machine.

It reads font files from disk, which is why it is not the default: a library should not go rummaging through the filesystem unasked, and a render that depends on what happens to be installed is a render that differs from machine to machine. Passing it is a decision, and the decision is the caller's.

Types

type Align

type Align int

Align selects horizontal text alignment for TextAligned.

const (
	AlignLeft Align = iota
	AlignCenter
	AlignRight
)

type AnnotType

type AnnotType string

AnnotType classifies an annotation.

const (
	AnnotText      AnnotType = "Text" // a sticky note
	AnnotLink      AnnotType = "Link"
	AnnotHighlight AnnotType = "Highlight"
	AnnotUnderline AnnotType = "Underline"
	AnnotStrikeOut AnnotType = "StrikeOut"
	AnnotSquare    AnnotType = "Square"
	AnnotCircle    AnnotType = "Circle"
	AnnotFreeText  AnnotType = "FreeText"
	AnnotWidget    AnnotType = "Widget" // a form field control
	AnnotPopup     AnnotType = "Popup"
	AnnotOther     AnnotType = "Other"
)

type Annotation

type Annotation struct {
	// Type is the annotation's subtype.
	Type AnnotType
	// Rect is its rectangle in points, from the top-left of the page.
	Rect [4]float64
	// Contents is the note text, where the annotation has any.
	Contents string
	// Author is the /T entry, the name shown as the note's author.
	Author string
	// Color is the annotation's colour, if it declares one.
	Color *Color
	// URL is a link annotation's destination.
	URL string
	// Page is the 0-based index of the page it belongs to.
	Page int
	// contains filtered or unexported fields
}

Annotation describes an annotation found on a page.

type Array

type Array []any

Array is a PDF array.

func (Array) Clone

func (a Array) Clone() Array

Clone copies an array one level deep. See Dict.Clone.

type Attachment

type Attachment struct {
	// Name is what the document calls the file.
	Name string
	// Description is the note attached to it, where there is one.
	Description string
	// MIMEType is the /Subtype the file specification declares.
	MIMEType string
	// Size is the declared length in bytes, which is not always the
	// actual length: Data is the authority.
	Size int
	// Created and Modified are the file's own dates, where given.
	Created, Modified time.Time
	// Page is the page a file attachment annotation sits on, or -1 for
	// one listed in the document's own collection.
	Page int
	// contains filtered or unexported fields
}

Attachment is a file carried inside a document.

func (Attachment) Data

func (a Attachment) Data() ([]byte, error)

Data returns the file's contents, decoded and decrypted.

type Color

type Color struct {
	R, G, B uint8
}

Color is an opaque RGB color.

func Gray

func Gray(v uint8) Color

Gray builds a neutral gray Color; 0 is black, 255 is white.

func RGB

func RGB(r, g, b uint8) Color

RGB builds a Color from 8-bit components.

type Dict

type Dict map[Name]any

Dict is a PDF dictionary.

func (Dict) Clone

func (d Dict) Clone() Dict

Clone copies a dictionary one level deep.

Everything the reader hands back is the reader's own. Change it in place and you change what the reader sees for the rest of its life, which is rarely what anyone means. Clone first, change the copy, and write the copy back with Updater.SetObject.

type Document

type Document struct {
	// Compress enables Flate compression of content streams, image data
	// and embedded fonts. It defaults to true; disable it to produce
	// human-readable output for debugging.
	Compress bool

	// CreationDate is stamped into the document metadata. It defaults to
	// the time New was called.
	CreationDate time.Time

	// CompressObjects packs the document's dictionaries into object
	// streams and writes a cross-reference stream, which makes files with
	// many small objects noticeably smaller. It requires PDF 1.5 readers
	// and is ignored for encrypted documents.
	CompressObjects bool
	// contains filtered or unexported fields
}

Document is an in-progress PDF document. Create one with New, add pages and content, then call Save or WriteTo.

A Document is not safe for concurrent use.

Example (AddOutline)

Bookmarks and an internal link.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	doc := gopdf.New()
	intro := doc.AddPage()
	details := doc.AddPage()

	intro.SetFont(gopdf.Helvetica, 12)
	intro.Text(72, 72, "See details")
	intro.LinkPage(72, 60, 80, 16, details, 0)

	chapter := doc.AddOutline(nil, "Introduction", intro, 0)
	doc.AddOutline(chapter, "Details", details, 0)
	if err := doc.Save("outline.pdf"); err != nil {
		log.Fatal(err)
	}
}
Example (EditPage)

Editing text in an existing document without moving anything else.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	src, err := gopdf.Open("invoice.pdf")
	if err != nil {
		log.Fatal(err)
	}
	doc := gopdf.New()
	for i := 0; i < src.NumPages(); i++ {
		page, err := doc.EditPage(src, i)
		if err != nil {
			log.Fatal(err)
		}
		// Replacements are drawn with the page's own font, and the width
		// difference is compensated so the rest of the line stays put.
		if _, err := page.ReplaceText("DRAFT", "FINAL"); err != nil {
			log.Fatal(err)
		}
	}
	if err := doc.Save("final.pdf"); err != nil {
		log.Fatal(err)
	}
}
Example (Encrypt)

Writing an encrypted document.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	doc := gopdf.New()
	doc.Encrypt("userpw", "ownerpw", gopdf.AllowPrint|gopdf.AllowCopy, gopdf.AES256)
	page := doc.AddPage()
	page.SetFont(gopdf.Helvetica, 12)
	page.Text(72, 72, "Confidential")
	if err := doc.Save("protected.pdf"); err != nil {
		log.Fatal(err)
	}
}
Example (ImportPage)

Merging files and stamping a watermark onto every page.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	src, err := gopdf.Open("report.pdf")
	if err != nil {
		log.Fatal(err)
	}
	doc := gopdf.New()
	for i := 0; i < src.NumPages(); i++ {
		page, err := doc.ImportPage(src, i)
		if err != nil {
			log.Fatal(err)
		}
		page.Push()
		page.SetAlpha(0.3, 0.3)
		page.RotateAt(45, page.Width()/2, page.Height()/2)
		page.SetFont(gopdf.HelveticaBold, 64)
		page.SetFillColor(gopdf.RGB(200, 30, 30))
		page.TextAligned(0, page.Height()/2, page.Width(), gopdf.AlignCenter, "DRAFT")
		page.Pop()
	}
	if err := doc.Save("watermarked.pdf"); err != nil {
		log.Fatal(err)
	}
}

func New

func New() *Document

New creates an empty document with A4 pages and compression enabled.

func (*Document) AddImage

func (d *Document) AddImage(m image.Image) (*Image, error)

AddImage registers a decoded image with the document. Grayscale images are stored as 8-bit gray samples, everything else as 8-bit RGB; if the image has any transparency, the alpha channel is preserved as a PDF soft mask. *image.Gray, *image.NRGBA and *image.RGBA use fast paths that read pixel data directly.

func (*Document) AddImageFile

func (d *Document) AddImageFile(path string) (*Image, error)

AddImageFile registers an image file (JPEG, PNG or GIF) with the document.

func (*Document) AddImageReader

func (d *Document) AddImageReader(r io.Reader) (*Image, error)

AddImageReader registers an image read from r. JPEG data is embedded directly without re-encoding; PNG and GIF images are decoded and stored as raw samples, preserving any alpha channel as a PDF soft mask.

func (*Document) AddLayer

func (d *Document) AddLayer(name string, on bool) (*Layer, error)

AddLayer declares a layer on a document being built.

Draw on it by bracketing content with BeginLayer and EndLayer; a layer nothing draws on still appears in a viewer's panel, which is what a caller wants for one they mean to fill in later.

func (*Document) AddOutline

func (d *Document) AddOutline(parent *Outline, title string, page *Page, y float64) *Outline

AddOutline adds a bookmark that jumps to y points from the top of page. Pass parent nil for a top-level entry, or a previously returned Outline to nest.

func (*Document) AddPage

func (d *Document) AddPage() *Page

AddPage appends a new page of the document's default size.

func (*Document) AddPageSize

func (d *Document) AddPageSize(s PageSize) *Page

AddPageSize appends a new page of the given size.

func (*Document) AppendPDF

func (d *Document) AppendPDF(r *Reader) error

AppendPDF imports every page of a parsed file into the document.

func (*Document) Attach

func (d *Document) Attach(name string, data []byte) error

Attach adds a file to a document being built. The name is what a reader will see and offer to save it as.

func (*Document) AttachWithDescription

func (d *Document) AttachWithDescription(name, description string, data []byte) error

AttachWithDescription adds a file to a document being built, along with the note that goes with it.

func (*Document) EditPage

func (d *Document) EditPage(r *Reader, index int) (*EditablePage, error)

EditPage imports a page from an existing document with its content stream left editable, preserving the page's own resources, media box and rotation exactly.

Unlike ImportPage, which places the source page as an opaque form XObject, EditPage keeps the original operators so their text can be rewritten in place.

func (*Document) Encrypt

func (d *Document) Encrypt(userPassword, ownerPassword string, perms Permissions, method EncryptionMethod)

Encrypt turns on encryption for the document. The user password is required to open the file (an empty user password means anyone can open it, but the permissions still apply); the owner password grants full access. Passing an empty owner password reuses the user password.

Encrypted output is not byte-deterministic: each save uses fresh random salts and initialization vectors.

Permissions are advisory — conforming viewers honor them, but they do not protect against a determined reader who can open the document.

func (*Document) FillForm

func (d *Document) FillForm(r *Reader, values map[string]string) (int, error)

FillForm imports every page of an interactive document, fills the named fields with the given values, and flattens the result: values are drawn into the page content, so they display and print identically everywhere and can no longer be changed.

Keys are fully qualified field names, as reported by Reader.FormFields. For a checkbox or radio group, pass the option's name to select it, or an empty string to clear it. Filling a name the document does not have is an error, so typos surface instead of silently doing nothing.

Example

Filling an interactive form and flattening the result.

package main

import (
	"fmt"
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	src, err := gopdf.Open("application.pdf")
	if err != nil {
		log.Fatal(err)
	}
	for _, f := range src.FormFields() {
		fmt.Printf("%s (%s) = %q\n", f.Name, f.Type, f.Value)
	}
	doc := gopdf.New()
	if _, err := doc.FillForm(src, map[string]string{
		"applicant": "Ada Lovelace",
		"country":   "France",
		"subscribe": "Yes",
	}); err != nil {
		log.Fatal(err)
	}
	if err := doc.Save("application-filled.pdf"); err != nil {
		log.Fatal(err)
	}
}

func (*Document) FillFormInteractive

func (d *Document) FillFormInteractive(r *Reader, values map[string]string) (int, error)

FillFormInteractive fills the named fields and keeps the form editable: the output still has interactive fields, with the new values in place and freshly generated appearance streams so they display correctly before a viewer touches them.

Use FillForm instead when the result should be final — flattening makes the values part of the page and stops the recipient changing them.

Example

Filling a form while leaving it editable.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	src, err := gopdf.Open("application.pdf")
	if err != nil {
		log.Fatal(err)
	}
	doc := gopdf.New()
	// The output keeps its interactive fields, with fresh appearance
	// streams so the values show before a viewer regenerates them.
	if _, err := doc.FillFormInteractive(src, map[string]string{
		"applicant": "Grace Hopper",
	}); err != nil {
		log.Fatal(err)
	}
	if err := doc.Save("application-draft.pdf"); err != nil {
		log.Fatal(err)
	}
}

func (*Document) ImportPage

func (d *Document) ImportPage(r *Reader, index int) (*Page, error)

ImportPage copies a page from a parsed file into the document and returns it as a regular Page. The imported content is placed as the page background — the page's rotation is normalized away — and the full drawing API works on top of it for stamping and watermarking. External (URI) link annotations are preserved; internal links and other annotations are dropped.

func (*Document) Save

func (d *Document) Save(path string) error

Save writes the document to a file.

func (*Document) SetInfo

func (d *Document) SetInfo(info Info)

SetInfo sets the document metadata, exactly as given: empty fields are left out of the file, so SetInfo(Info{}) — with CreationDate set to the zero time — authors a document with no provenance metadata at all. Documents that never call SetInfo are stamped with Producer "gopdf".

func (*Document) SetPDFA

func (d *Document) SetPDFA(level PDFAConformance)

SetPDFA asks for a document to be written to the archival profile.

It adds the metadata packet, the colour space declaration and the identification the profile requires, and refuses what it forbids: encryption is the one this package can otherwise do and PDF/A cannot have. Save reports an error rather than writing a file that claims a conformance it does not meet.

func (*Document) SetPageLabels

func (d *Document) SetPageLabels(ranges []PageLabelRange)

SetPageLabels gives a document being built its page numbering.

Ranges need not be sorted and need not cover page 0; pages before the first range are numbered plainly, as a viewer numbers a document with no labels at all.

func (*Document) SetPageSize

func (d *Document) SetPageSize(s PageSize)

SetPageSize sets the default size used by AddPage.

func (*Document) SetXMP

func (d *Document) SetXMP(on bool)

SetXMP writes a metadata packet describing the document.

Only the fields the information dictionary also carries are written, and they are taken from it: a packet that contradicts the dictionary is worse than none, since the two are read by different tools and disagreement is how a document ends up with two authors.

func (*Document) WriteTo

func (d *Document) WriteTo(w io.Writer) (int64, error)

WriteTo serializes the document as a complete PDF file.

type DrawMode

type DrawMode int

DrawMode selects how a shape or path is painted.

const (
	// Stroke outlines the shape with the stroke color.
	Stroke DrawMode = iota
	// Fill fills the shape with the fill color.
	Fill
	// FillStroke fills the shape, then outlines it.
	FillStroke
	// ClipPath restricts subsequent drawing to the shape instead of
	// painting it. Use it between Push and Pop so the clip is undone.
	ClipPath
)

type EditablePage

type EditablePage struct {
	*Page
	// contains filtered or unexported fields
}

EditablePage is a page imported from an existing document with its content left editable. It embeds *Page, so the whole drawing API is available for adding content on top; the text-editing methods rewrite the content the source file already had.

Changes are materialized when the document is written.

func (*EditablePage) Blocks

func (e *EditablePage) Blocks() []*TextBlock

Blocks groups the page's runs into paragraphs. A block needs at least one line; lines built from several runs (a bold word inside a sentence, say) are not reflowable and become single-line blocks of their own.

func (*EditablePage) ExtractText

func (e *EditablePage) ExtractText() string

ExtractText returns the page's current text, with line breaks inferred from the runs' positions. It reflects any replacements made so far.

This is deliberately not called Text: the embedded Page's Text method draws new text on top of the page.

func (*EditablePage) Flows

func (e *EditablePage) Flows() []*Flow

Flows groups the page's text into paragraphs that can be replaced at any length. Unlike Blocks, a line built from several runs — a bold word inside a sentence — stays part of its paragraph and keeps its styling.

The same paragraphs are returned every time, so edits accumulate instead of two calls fighting over the same operators.

func (*EditablePage) ReplaceFunc

func (e *EditablePage) ReplaceFunc(fn func(*TextRun) (string, bool)) (int, error)

ReplaceFunc rewrites runs for which fn returns true, replacing the run's text with the returned string. It returns the number of runs rewritten; on error nothing is changed.

func (*EditablePage) ReplaceText

func (e *EditablePage) ReplaceText(old, new string) (int, error)

ReplaceText replaces every occurrence of old with new across the page, rewriting the original content stream in place. It returns the number of occurrences replaced.

Matching happens within a single show-text operation: text that a generator split across separate operations is matched per run. Use Runs to inspect exactly how the page is laid out.

The replacement is encoded with the run's own font, so it renders identically to the surrounding text. If that font has no glyph for one of the replacement's characters, ReplaceText reports an error and changes nothing.

func (*EditablePage) ReplaceTextFlow

func (e *EditablePage) ReplaceTextFlow(old, new string) (int, error)

ReplaceTextFlow replaces occurrences of old across the page's paragraphs, re-wrapping each one it changes. Unlike ReplaceTextReflow it handles paragraphs of mixed styling, gives the replacement the styling of the text it replaces, and lets a paragraph grow or shrink by whole lines. It returns the number of paragraphs rewritten.

func (*EditablePage) ReplaceTextReflow

func (e *EditablePage) ReplaceTextReflow(old, new string) (int, error)

ReplaceTextReflow replaces occurrences of old with new across the page's paragraphs, re-wrapping each affected paragraph so the change flows across its lines instead of stretching one of them. It returns the number of paragraphs rewritten.

Use it when the replacement changes a sentence's length appreciably; ReplaceText is the better fit for short, in-place substitutions such as a date or an amount.

Example

Re-wrapping a paragraph after changing its wording.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	src, err := gopdf.Open("terms.pdf")
	if err != nil {
		log.Fatal(err)
	}
	doc := gopdf.New()
	page, err := doc.EditPage(src, 0)
	if err != nil {
		log.Fatal(err)
	}
	// The paragraph re-wraps across the lines it already occupies; if the
	// new text needs more, the edit is refused rather than overrunning
	// what follows.
	if _, err := page.ReplaceTextReflow("internal use only", "any lawful purpose"); err != nil {
		log.Fatal(err)
	}
	if err := doc.Save("terms-revised.pdf"); err != nil {
		log.Fatal(err)
	}
}

func (*EditablePage) Runs

func (e *EditablePage) Runs() []*TextRun

Runs returns every text run on the page, in content order, including runs inside form XObjects the page draws.

Example

Inspecting a page's text runs before editing them.

package main

import (
	"fmt"
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	src, err := gopdf.Open("statement.pdf")
	if err != nil {
		log.Fatal(err)
	}
	page, err := gopdf.New().EditPage(src, 0)
	if err != nil {
		log.Fatal(err)
	}
	for _, run := range page.Runs() {
		fmt.Printf("%.1f,%.1f %.1fpt %q\n", run.X, run.Y, run.FontSize, run.Text)
	}
}

func (*EditablePage) SetFitMode

func (e *EditablePage) SetFitMode(m FitMode)

SetFitMode selects how replacements of a different width are fitted.

func (*EditablePage) SetMaxExtraLines

func (e *EditablePage) SetMaxExtraLines(n int)

SetMaxExtraLines allows a reflowed paragraph to grow by up to n lines beyond the ones it already occupies. The default is zero: a replacement that does not fit is refused rather than allowed to overrun whatever follows it on the page, which reflow cannot move.

type EncryptionMethod

type EncryptionMethod int

EncryptionMethod selects the algorithm used to protect a document.

const (
	// AES128 is AES-128 with revision 4, readable by essentially every
	// PDF viewer in use.
	AES128 EncryptionMethod = iota
	// AES256 is AES-256 with revision 6 (PDF 2.0), the strongest option;
	// it requires a reasonably modern viewer.
	AES256
)

type FieldOptions

type FieldOptions struct {
	// Value is the field's initial value. For a checkbox or radio button
	// use the Selected flag instead.
	Value string
	// Font and FontSize set the text appearance. A zero FontSize means
	// the text is sized to fit the field.
	Font     *Font
	FontSize float64
	// Color is the text colour.
	Color Color
	// Align sets the horizontal alignment of the value.
	Align Align
	// MaxLen limits a text field to a number of characters; 0 is
	// unlimited.
	MaxLen int
	// Multiline makes a text field accept line breaks.
	Multiline bool
	// ReadOnly and Required set the corresponding field flags.
	ReadOnly, Required bool
	// Border is the colour of the field's outline. Set NoBorder to omit
	// it entirely.
	Border   Color
	NoBorder bool
	// Background fills the field when non-nil.
	Background *Color
	// Tooltip is the text a viewer shows on hover.
	Tooltip string
	// Selected marks a checkbox or radio button as initially chosen.
	Selected bool
}

FieldOptions configures a form field being added to a page. The zero value is valid: a left-aligned, black, auto-sized Helvetica field with a thin grey border and no initial value.

type FieldType

type FieldType string

FieldType classifies an interactive form field.

const (
	FieldText      FieldType = "text"
	FieldCheckbox  FieldType = "checkbox"
	FieldRadio     FieldType = "radio"
	FieldChoice    FieldType = "choice" // list box or combo box
	FieldButton    FieldType = "button" // pushbutton: has no value
	FieldSignature FieldType = "signature"
	FieldUnknown   FieldType = "unknown"
)

type FitMode

type FitMode int

FitMode controls how a text replacement of a different width is fitted back into the original layout.

const (
	// FitAdvance keeps everything that follows the replaced text exactly
	// where it was, by compensating for the width difference. The
	// replacement itself may be shorter or longer than the text it
	// replaced. This is the default and the safest choice.
	FitAdvance FitMode = iota
	// FitScale additionally scales the replacement horizontally so it
	// occupies precisely the original width. Use it when the replacement
	// would otherwise overlap adjacent content.
	FitScale
	// FitNone writes the replacement at its natural width and lets the
	// rest of the line shift, as a word processor would.
	FitNone
)

type Flow

type Flow struct {
	// X and Y position the first line's baseline, from the top-left of
	// the page.
	X, Y float64
	// Width is the column width in points.
	Width float64
	// LineHeight is the baseline-to-baseline distance in points.
	LineHeight float64
	// contains filtered or unexported fields
}

Flow is a paragraph whose text can be replaced at any length. Each part keeps the styling it had, and the paragraph is re-wrapped to its own column width, growing or shrinking by whole lines as it needs to.

func (*Flow) LineCount

func (f *Flow) LineCount() int

LineCount returns how many lines the paragraph occupies.

func (*Flow) LineDelta

func (f *Flow) LineDelta() int

LineDelta returns how many lines the last rewrite added, or removed if negative. It is zero until the paragraph is rewritten.

func (*Flow) OverflowsPage

func (f *Flow) OverflowsPage(pageHeight float64) bool

OverflowsPage reports whether the paragraph, as it now stands, extends below the bottom of the page it sits on.

A paragraph that grew pushes the ones under it down, and at the foot of a page that pushes them off it. Nothing here clips or refuses on its own — a caller may well be happy for a footer to move — but the question is worth being able to ask before writing.

func (*Flow) Replace

func (f *Flow) Replace(old, new string) (int, error)

Replace substitutes every occurrence of old within the paragraph and re-wraps it. The replacement takes the styling of the text it replaces, so swapping a word inside a bold phrase leaves it bold, and the rest of the paragraph is untouched. It reports how many occurrences changed.

func (*Flow) SetFitWidth

func (f *Flow) SetFitWidth(on bool)

SetFitWidth makes a replacement occupy the width of the text it replaces, by setting it smaller rather than by re-wrapping.

It applies to the replacements this package inserts, never to the document's own words, and it only ever shrinks: a replacement narrower than what it replaces keeps its size and the line simply gains slack. Where even the floor leaves it wider, it is set at the floor and the paragraph re-wraps as it otherwise would — being able to read the token matters more than where the line ends.

func (*Flow) SetFitWidthFloor

func (f *Flow) SetFitWidthFloor(min float64)

SetFitWidthFloor sets how far a fitted replacement in this paragraph may be shrunk, as a fraction of the run's size. Zero restores the default of 0.45.

func (*Flow) SetMaxExtraLines

func (f *Flow) SetMaxExtraLines(n int)

SetMaxExtraLines caps how many lines this paragraph may grow by. The default for a flow is no cap: pass a value to keep a paragraph from running into whatever follows it.

func (*Flow) SetShrinkToFit

func (f *Flow) SetShrinkToFit(on bool, minSize float64)

SetShrinkToFit lets an inserted token be set smaller when it will not fit the width the paragraph has, down to a floor of minSize points.

It applies only to inserted text: the document's own words keep the size they were set in. Off by default, because a token in a size nobody chose is a surprise; on, it is the difference between a table that still reads and one whose cells collide.

func (*Flow) SetSpans

func (f *Flow) SetSpans(spans []FlowSpan) error

SetSpans replaces the paragraph with the given styled spans. A span created by Spans carries its original styling; one built from scratch must borrow a style from an existing span, which is what Replace and SetText do.

func (*Flow) SetText

func (f *Flow) SetText(s string) error

SetText replaces the whole paragraph with plain text in the styling of its first span. Use SetSpans, or Replace, to keep a mixed paragraph mixed.

func (*Flow) Spans

func (f *Flow) Spans() []FlowSpan

Spans returns the paragraph's styled pieces, in reading order.

func (*Flow) Text

func (f *Flow) Text() string

Text returns the paragraph's text, its lines joined with single spaces.

type FlowSpan

type FlowSpan struct {
	// Text is the span's text.
	Text string
	// FontName is the font resource name in the source file, and
	// FontSize the effective size in points.
	FontName string
	FontSize float64
	// contains filtered or unexported fields
}

FlowSpan is a piece of a paragraph that shares one style.

type Font

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

Font is a typeface usable with Page.SetFont.

The package provides the standard 14 PDF fonts, which every viewer renders without the font being embedded in the file; they are limited to the WinAnsi (CP-1252) character set. For full Unicode text, load a TrueType font with LoadFont or ParseFont: it is embedded in the document, subset to the glyphs actually used.

func LoadFont

func LoadFont(path string) (*Font, error)

LoadFont loads a font for embedding: TrueType (.ttf), the first font of a collection (.ttc), or CFF-based OpenType (.otf). The font may be used with any number of documents.

TrueType fonts are subset to the glyphs actually used. OpenType fonts with PostScript outlines are embedded whole, so they produce larger files.

Example

Embedding a TrueType font for full Unicode text.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	font, err := gopdf.LoadFont("NotoSans-Regular.ttf")
	if err != nil {
		log.Fatal(err)
	}
	doc := gopdf.New()
	page := doc.AddPage()
	page.SetFont(font, 12)
	page.Text(72, 72, "Καλημέρα κόσμε — Привет, мир")
	if err := doc.Save("unicode.pdf"); err != nil {
		log.Fatal(err)
	}
}

func ParseFont

func ParseFont(data []byte) (*Font, error)

ParseFont parses TrueType or OpenType font data for embedding.

func (*Font) Name

func (f *Font) Name() string

Name returns the PostScript name of the font.

func (*Font) TextWidth

func (f *Font) TextWidth(s string, size float64) float64

TextWidth returns the rendered width of s in points at the given font size. For embedded TrueType fonts the widths come from the font's metric tables, including kerning, and are exact for every glyph. For the standard fonts they are exact for the WinAnsi letters and most symbols (and for all characters in the Courier family); the few remaining characters use an approximate per-font default.

type FontRequest

type FontRequest struct {
	// BaseFont is the name the document gives, subset prefix and all,
	// such as "Arial-BoldMT" or "BCDEEE+Cambria".
	BaseFont string
	// Bold, Italic, Serif and Fixed are what the font descriptor and the
	// name between them say about the face, so a substitute can be
	// chosen to match rather than merely to exist.
	Bold, Italic, Serif, Fixed bool
}

FontRequest describes a font the document names but does not embed.

type FormField

type FormField struct {
	// Name is the field's fully qualified name, the key used to fill it.
	Name string
	// Type is what kind of control the field is.
	Type FieldType
	// Value is the field's current value. Checkboxes and radio groups
	// report the name of the selected state, or "" when unselected.
	Value string
	// Options lists the permitted values of a choice field or the export
	// values of a radio group.
	Options []string
	// ReadOnly and Required mirror the field's flags.
	ReadOnly, Required bool
	// MaxLen is a text field's character limit, or 0 when unlimited.
	MaxLen int
	// Page is the 0-based index of the page the field appears on, or -1
	// if it has no widget on any page.
	Page int
	// Rect is the widget's rectangle in points, from the top-left of its
	// page.
	Rect [4]float64
}

FormField describes one field of an interactive (AcroForm) document.

type GradientDirection

type GradientDirection int

GradientDirection selects the axis of a rectangular gradient.

const (
	// GradientVertical runs from the top edge to the bottom edge.
	GradientVertical GradientDirection = iota
	// GradientHorizontal runs from the left edge to the right edge.
	GradientHorizontal
	// GradientDiagonal runs from the top-left corner to the bottom-right.
	GradientDiagonal
)

type GradientStop

type GradientStop struct {
	Offset float64
	Color  Color
}

GradientStop is one colour in a gradient ramp, at a position from 0 at the start of the gradient axis to 1 at its end.

func Stop

func Stop(offset float64, c Color) GradientStop

Stop is shorthand for building a GradientStop.

type Image

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

Image is an image registered with a Document, ready to be placed on any of its pages with Page.DrawImage.

func (*Image) Height

func (img *Image) Height() int

Height returns the intrinsic height of the image in pixels.

func (*Image) Width

func (img *Image) Width() int

Width returns the intrinsic width of the image in pixels.

type ImageRef

type ImageRef struct {
	// Name is the image's resource name in the page or form that draws it.
	Name Name
	// Width and Height are the image's pixel dimensions.
	Width, Height int
	// BitsPerComponent is the precision of each colour component.
	BitsPerComponent int
	// ColorSpace names the image's colour space, as the file declares it.
	ColorSpace string
	// Page is the 0-based index of the page it appears on.
	Page int
	// X, Y, W and H give where the image is drawn, in points from the
	// top-left of the page. They are zero if the image is in the page's
	// resources but never painted.
	//
	// They bound the image. For anything but an upright placement the box
	// is larger than the picture — a rotated image fills its bounding box
	// no better than a tilted photograph fills the envelope it came in —
	// so use Matrix where the placement matters.
	X, Y, W, H float64
	// Filter is the codec the image is stored in — "DCTDecode" for a
	// JPEG, "JBIG2Decode" for a scan, "FlateDecode" for anything this
	// package wrote — or empty for an image stored raw. It is the last
	// filter in the chain, which is the one that decides what the bytes
	// are a picture of.
	Filter string
	// Matrix is the transform in force where the image was drawn, mapping
	// the unit square onto the page. It is the placement exactly:
	// rotation, skew and flip included, in PDF's bottom-up coordinates.
	// It is the zero matrix for an image the page never paints.
	Matrix [6]float64
	// contains filtered or unexported fields
}

ImageRef describes an image drawn on a page.

func (ImageRef) Decode

func (im ImageRef) Decode() (image.Image, error)

Decode returns the image's pixels.

JPEG data is handed to the standard decoder, CCITT Group 3/4 fax data to the package's own; other images are decoded from their samples. Where the image has a soft mask, it is applied as the alpha channel. JBIG2 and JPEG 2000 images report an error: their codecs are not implemented.

func (ImageRef) JPEG

func (im ImageRef) JPEG() ([]byte, bool)

JPEG returns the image's own JPEG stream when it is stored as one (DCTDecode), unwrapping any outer compression, so callers that keep photographs in their original encoding can embed the bytes untouched. The bool reports whether such a stream exists; images stored any other way return false — decode those with Decode.

func (ImageRef) ObjectNumber

func (im ImageRef) ObjectNumber() int

ObjectNumber identifies the image XObject behind this reference, or 0 when the image is not an indirect object. Two references with the same non-zero number share one underlying image — a picture drawn several times, or on several pages — so callers processing each image once can key on it.

func (ImageRef) Rotation

func (im ImageRef) Rotation() float64

Rotation returns the angle the image is drawn at, in degrees clockwise from upright as seen on the page, in the range [0, 360).

It is the angle of the placement's horizontal axis. A sheared placement has no single angle; this reports the one its baseline runs at.

func (ImageRef) Upright

func (im ImageRef) Upright() bool

Upright reports whether the image is drawn square to the page, which is when its bounding box is the picture and not merely around it.

type Info

type Info struct {
	Title    string
	Author   string
	Subject  string
	Keywords string
	Creator  string
	Producer string
}

Info holds document metadata written to the PDF information dictionary.

type Key

type Key struct {
	// Mappings are the substitutions as they were applied.
	Mappings []Pseudonym
	// PixelsDestroyed counts the words removed from images, which no key
	// restores.
	PixelsDestroyed int
}

Key is the record needed to undo a substitution: the mappings, and a note of what cannot be undone.

func (Key) Reverse

func (k Key) Reverse() []Pseudonym

Reverse returns the mappings that undo this key's substitutions.

func (Key) Reversible

func (k Key) Reversible() bool

Reversible reports whether everything this key describes can be undone. It is false once any pixels were overwritten.

type Layer

type Layer struct {
	// Name is what a viewer shows in its layers panel.
	Name string
	// On is whether the layer starts visible.
	On bool
	// contains filtered or unexported fields
}

Layer is a named piece of optional content: a watermark, a set of annotations for one audience, a language of labels.

type LineCap

type LineCap int

LineCap selects how stroke ends are drawn.

const (
	CapButt LineCap = iota
	CapRound
	CapSquare
)

type LineJoin

type LineJoin int

LineJoin selects how stroke corners are drawn.

const (
	JoinMiter LineJoin = iota
	JoinRound
	JoinBevel
)

type Name

type Name string

Name is a PDF name object, without the leading slash.

type NoteOptions

type NoteOptions struct {
	// Author is shown as the note's author in a viewer's comment list.
	Author string
	// Color tints the annotation; the zero value picks a sensible default
	// for the annotation's kind.
	Color Color
	// Opacity is the annotation's constant alpha, from 0 to 1. Zero means
	// the default for the kind.
	Opacity float64
	// Open shows a sticky note's popup immediately.
	Open bool
}

NoteOptions configures an annotation being added to a page.

type OCREngine

type OCREngine interface {
	Recognize(img image.Image) ([]OCRWord, error)
}

OCREngine reads the text in an image.

Implementations live outside this package: see ocr/tesseract for one that drives the tesseract command. An engine is called once per image per page, and may be called again on the redacted image to confirm the words are gone, so it should be safe to call more than once.

type OCRWord

type OCRWord struct {
	// Text is the word as recognised.
	Text string
	// X, Y, W and H bound the word in pixels.
	X, Y, W, H int
	// Confidence runs from 0 to 1. An engine that does not report one
	// should leave it at 1.
	Confidence float64
}

OCRWord is one word an engine recognised, positioned in the image's own pixel coordinates with the origin at the top-left.

type Outline

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

Outline is a bookmark in the document's outline tree, shown in the viewer's sidebar.

type PDFAConformance

type PDFAConformance string

PDFAConformance names a level of the archival profile.

const (
	// PDFA2b is the level that asks the file to be self-contained and to
	// look the same everywhere. It is what most archives require.
	PDFA2b PDFAConformance = "2B"
	// PDFA2u adds that every glyph must map to a character, so the text
	// can be extracted and searched.
	PDFA2u PDFAConformance = "2U"
)

type PDFAIssue

type PDFAIssue struct {
	// Rule names the requirement, briefly.
	Rule string
	// Detail says what in this document breaks it.
	Detail string
	// Page is where the trouble is, or -1 for the document as a whole.
	Page int
}

PDFAIssue is one reason a document does not meet the profile.

func (PDFAIssue) String

func (i PDFAIssue) String() string

type Page

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

Page is a single page of a Document. All coordinates are in points with the origin at the top-left corner; y grows downward.

Example (SetAlpha)

Vector graphics with scoped transparency.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	doc := gopdf.New()
	page := doc.AddPage()
	page.Push()
	page.SetAlpha(0.5, 1)
	page.SetFillColor(gopdf.RGB(200, 40, 40))
	page.Circle(150, 150, 60, gopdf.Fill)
	page.SetFillColor(gopdf.RGB(40, 40, 200))
	page.Circle(200, 150, 60, gopdf.Fill)
	page.Pop()
	if err := doc.Save("circles.pdf"); err != nil {
		log.Fatal(err)
	}
}
Example (TextWrapped)

Word-wrapping a paragraph inside a column.

package main

import (
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	doc := gopdf.New()
	page := doc.AddPage()
	page.SetFont(gopdf.TimesRoman, 11)
	next := page.TextWrapped(72, 72, 300, 15,
		"TextWrapped lays out a paragraph inside a fixed width and "+
			"returns the baseline for whatever comes after it.")
	page.Text(72, next, "…like this line.")
	if err := doc.Save("paragraph.pdf"); err != nil {
		log.Fatal(err)
	}
}

func (*Page) AddCheckbox

func (p *Page) AddCheckbox(name string, x, y, size float64, opts FieldOptions) error

AddCheckbox adds a square checkbox with its top-left corner at (x, y).

func (*Page) AddChoiceField

func (p *Page) AddChoiceField(name string, x, y, w, h float64, options []string, opts FieldOptions) error

AddChoiceField adds a drop-down list of the given options.

func (*Page) AddCircleAnnotation

func (p *Page) AddCircleAnnotation(x, y, w, h float64, contents string, opts NoteOptions)

AddCircleAnnotation outlines an ellipse as an annotation.

func (*Page) AddHighlight

func (p *Page) AddHighlight(x, y, w, h float64, contents string, opts NoteOptions)

AddHighlight marks a rectangle as highlighted. Position it over the text you want to mark — Reader.PageText and the editing API report where text sits.

func (*Page) AddNote

func (p *Page) AddNote(x, y float64, contents string, opts NoteOptions)

AddNote attaches a sticky note at a point. Viewers draw their own icon and show the text when it is opened.

func (*Page) AddRadioButton

func (p *Page) AddRadioButton(group, value string, x, y, size float64, opts FieldOptions) error

AddRadioButton adds one button of a radio group. Call it once per choice, with the same group name and a distinct value; only the button whose value matches the group's selection is filled in.

func (*Page) AddSquareAnnotation

func (p *Page) AddSquareAnnotation(x, y, w, h float64, contents string, opts NoteOptions)

AddSquareAnnotation outlines a rectangle as an annotation, which stays selectable and removable rather than becoming part of the page.

func (*Page) AddStrikeOut

func (p *Page) AddStrikeOut(x, y, w, h float64, contents string, opts NoteOptions)

AddStrikeOut draws a strike-through annotation across a rectangle.

func (*Page) AddTextField

func (p *Page) AddTextField(name string, x, y, w, h float64, opts FieldOptions) error

AddTextField adds an interactive text field with its top-left corner at (x, y). The document becomes an interactive form.

func (*Page) AddUnderline

func (p *Page) AddUnderline(x, y, w, h float64, contents string, opts NoteOptions)

AddUnderline draws an underline annotation across a rectangle.

func (*Page) BeginLayer

func (p *Page) BeginLayer(l *Layer)

BeginLayer starts a run of content belonging to a layer. Every BeginLayer needs an EndLayer, and they may not overlap: a viewer reading a stream that closes them out of order draws the wrong things.

func (*Page) Circle

func (p *Page) Circle(cx, cy, r float64, mode DrawMode)

Circle draws a circle centered at (cx, cy).

func (*Page) Clip

func (p *Page) Clip(evenOdd bool)

Clip restricts subsequent drawing to the path just built with MoveTo, LineTo and CurveTo. Use it between Push and Pop, and follow it with a gradient or other painting operator.

evenOdd selects the even-odd rule instead of the default nonzero winding rule.

func (*Page) ClipRect

func (p *Page) ClipRect(x, y, w, h float64)

ClipRect restricts subsequent drawing to the given rectangle. Use between Push and Pop to restore the previous clipping region.

func (*Page) ClosePath

func (p *Page) ClosePath()

ClosePath closes the current subpath back to its starting point.

func (*Page) CurveTo

func (p *Page) CurveTo(cx1, cy1, cx2, cy2, x, y float64)

CurveTo appends a cubic Bézier segment from the current point to (x, y) using control points (cx1, cy1) and (cx2, cy2).

func (*Page) DrawImage

func (p *Page) DrawImage(img *Image, x, y, w, h float64)

DrawImage places img with its top-left corner at (x, y), scaled to w by h points. Use img.Width and img.Height to preserve the aspect ratio.

func (*Page) DrawPath

func (p *Page) DrawPath(mode DrawMode)

DrawPath paints the path built by preceding MoveTo/LineTo/CurveTo calls.

func (*Page) Ellipse

func (p *Page) Ellipse(cx, cy, rx, ry float64, mode DrawMode)

Ellipse draws an axis-aligned ellipse centered at (cx, cy) with the given horizontal and vertical radii.

func (*Page) EndLayer

func (p *Page) EndLayer()

EndLayer closes the most recent BeginLayer.

func (*Page) FillGradientCircle

func (p *Page) FillGradientCircle(cx, cy, r float64, stops ...GradientStop) error

FillGradientCircle fills a circle with a radial gradient spreading from its centre to its edge.

func (*Page) FillGradientRect

func (p *Page) FillGradientRect(x, y, w, h float64, dir GradientDirection, stops ...GradientStop) error

FillGradientRect fills a rectangle with a linear gradient.

func (*Page) Height

func (p *Page) Height() float64

Height returns the page height in points.

func (*Page) Line

func (p *Page) Line(x1, y1, x2, y2 float64)

Line draws a straight line from (x1, y1) to (x2, y2) with the current stroke color and width.

func (*Page) LineTo

func (p *Page) LineTo(x, y float64)

LineTo appends a straight segment from the current point to (x, y).

func (*Page) LinkPage

func (p *Page) LinkPage(x, y, w, h float64, target *Page, targetY float64)

LinkPage makes the rectangle with top-left corner (x, y) a link that jumps to targetY points from the top of the target page.

func (*Page) LinkURL

func (p *Page) LinkURL(x, y, w, h float64, url string)

LinkURL makes the rectangle with top-left corner (x, y) a clickable link to the given URL.

func (*Page) MoveTo

func (p *Page) MoveTo(x, y float64)

MoveTo begins a new subpath at (x, y). Build paths with MoveTo, LineTo, CurveTo and ClosePath, then paint them with DrawPath.

func (*Page) PaintLinearGradient

func (p *Page) PaintLinearGradient(x0, y0, x1, y1 float64, stops ...GradientStop) error

PaintLinearGradient fills the current clipping region with a gradient running from (x0, y0) to (x1, y1). Clip first — with ClipRect, or a path followed by Clip — or the gradient covers the whole page.

func (*Page) PaintRadialGradient

func (p *Page) PaintRadialGradient(cx, cy, rInner, rOuter float64, stops ...GradientStop) error

PaintRadialGradient fills the current clipping region with a gradient spreading from a circle of radius rInner to one of radius rOuter, both centred at (cx, cy).

func (*Page) Polygon

func (p *Page) Polygon(mode DrawMode, xy ...float64)

Polygon draws a closed polygon through the given points, supplied as alternating x, y pairs. Polygon panics if given fewer than three points or an odd number of coordinates.

func (*Page) Pop

func (p *Page) Pop()

Pop restores the most recently pushed graphics state.

func (*Page) Push

func (p *Page) Push()

Push saves the graphics state (colors, line settings, transform). Every Push must be paired with a Pop.

func (*Page) Rect

func (p *Page) Rect(x, y, w, h float64, mode DrawMode)

Rect draws a rectangle with its top-left corner at (x, y).

func (*Page) RotateAt

func (p *Page) RotateAt(deg, x, y float64)

RotateAt rotates the coordinate system by deg degrees counterclockwise about the point (x, y). Coordinates passed to subsequent drawing calls are interpreted in the rotated system. Use between Push and Pop.

func (*Page) RoundedRect

func (p *Page) RoundedRect(x, y, w, h, r float64, mode DrawMode)

RoundedRect draws a rectangle with corners rounded to radius r.

func (*Page) Scale

func (p *Page) Scale(sx, sy, x, y float64)

Scale scales the coordinate system about the point (x, y).

func (*Page) SetAlpha

func (p *Page) SetAlpha(fill, stroke float64)

SetAlpha sets the constant opacity for filling and stroking: 0 is fully transparent, 1 fully opaque. Use between Push and Pop to keep the effect scoped.

func (*Page) SetDash

func (p *Page) SetDash(pattern ...float64)

SetDash sets the stroke dash pattern as alternating dash and gap lengths in points. Call with no arguments to return to solid lines.

func (*Page) SetFillColor

func (p *Page) SetFillColor(c Color)

SetFillColor sets the color used to fill shapes and draw text.

func (*Page) SetFont

func (p *Page) SetFont(f *Font, size float64)

SetFont selects the font and size (in points) for subsequent text calls.

func (*Page) SetLineCap

func (p *Page) SetLineCap(c LineCap)

SetLineCap sets the stroke cap style.

func (*Page) SetLineJoin

func (p *Page) SetLineJoin(j LineJoin)

SetLineJoin sets the stroke join style.

func (*Page) SetLineWidth

func (p *Page) SetLineWidth(w float64)

SetLineWidth sets the stroke width in points.

func (*Page) SetRotate

func (p *Page) SetRotate(deg int)

SetRotate sets the page's display rotation in degrees clockwise; only multiples of 90 are meaningful.

func (*Page) SetStrokeColor

func (p *Page) SetStrokeColor(c Color)

SetStrokeColor sets the color used to outline shapes and draw lines.

func (*Page) Text

func (p *Page) Text(x, y float64, s string)

Text draws s with its baseline starting at (x, y) using the current font. Text is filled with the current fill color.

Embedded TrueType fonts render any character the font provides, with pair kerning applied when the font has a kern table. The standard 14 fonts are limited to WinAnsi (CP-1252); characters outside that repertoire are replaced with '?'.

func (*Page) TextAligned

func (p *Page) TextAligned(x, y, width float64, align Align, s string)

TextAligned draws s aligned within the horizontal span from x to x+width, with the baseline at y.

func (*Page) TextWidth

func (p *Page) TextWidth(s string) float64

TextWidth returns the rendered width of s in points for the current font and size.

func (*Page) TextWrapped

func (p *Page) TextWrapped(x, y, width, lineHeight float64, s string) float64

TextWrapped draws s word-wrapped to the given width, starting with the first baseline at (x, y) and advancing by lineHeight per line. Newlines in s force line breaks. It returns the y coordinate of the baseline that would follow the last drawn line.

func (*Page) Translate

func (p *Page) Translate(dx, dy float64)

Translate shifts the coordinate system by (dx, dy). Use between Push and Pop to keep the effect scoped.

func (*Page) Width

func (p *Page) Width() float64

Width returns the page width in points.

type PageLabelRange

type PageLabelRange struct {
	// From is the zero-based index of the first page in the range.
	From int
	// Style is how the pages are numbered.
	Style PageLabelStyle
	// Prefix is put before the number, where there is one: "A-" gives
	// A-1, A-2.
	Prefix string
	// Start is the number the range counts from, which is 1 unless the
	// document says otherwise.
	Start int
}

PageLabelRange is one run of pages sharing a numbering scheme.

type PageLabelStyle

type PageLabelStyle string

PageLabelStyle is how a range of pages is numbered.

const (
	// LabelDecimal numbers pages 1, 2, 3.
	LabelDecimal PageLabelStyle = "D"
	// LabelRomanUpper numbers them I, II, III.
	LabelRomanUpper PageLabelStyle = "R"
	// LabelRomanLower numbers them i, ii, iii.
	LabelRomanLower PageLabelStyle = "r"
	// LabelLettersUpper numbers them A, B, C, and after Z carries on
	// AA, BB, CC — which is what the specification asks for, however odd
	// it looks.
	LabelLettersUpper PageLabelStyle = "A"
	// LabelLettersLower numbers them a, b, c.
	LabelLettersLower PageLabelStyle = "a"
	// LabelNone gives the pages of a range no number at all, leaving
	// only the prefix.
	LabelNone PageLabelStyle = ""
)

type PageSize

type PageSize struct {
	W, H float64
}

PageSize is a page size in points.

func (PageSize) Landscape

func (s PageSize) Landscape() PageSize

Landscape returns the size with the longer edge horizontal.

func (PageSize) Portrait

func (s PageSize) Portrait() PageSize

Portrait returns the size with the longer edge vertical.

type Permissions

type Permissions uint32

Permissions is a set of operations an encrypted document allows. The PDF specification treats these as advisory: conforming viewers honor them, but they are not a security boundary — anyone able to open the document can extract its contents.

const (
	AllowPrint        Permissions = 1 << 2  // print at reduced resolution
	AllowModify       Permissions = 1 << 3  // change the document
	AllowCopy         Permissions = 1 << 4  // copy text and graphics
	AllowAnnotate     Permissions = 1 << 5  // add or modify annotations
	AllowFillForms    Permissions = 1 << 8  // fill in form fields
	AllowAccessible   Permissions = 1 << 9  // extract for accessibility
	AllowAssemble     Permissions = 1 << 10 // insert, rotate, delete pages
	AllowHighResPrint Permissions = 1 << 11 // print at full resolution

	// AllowAll grants every permission.
	AllowAll = AllowPrint | AllowModify | AllowCopy | AllowAnnotate |
		AllowFillForms | AllowAccessible | AllowAssemble | AllowHighResPrint
	// AllowNone grants nothing beyond opening the document.
	AllowNone Permissions = 0
)

Permission bits, numbered as in the PDF specification's /P entry.

type Pseudonym

type Pseudonym struct {
	From, To string
	// FitWidth keeps the whole token and makes it fit, instead of
	// re-wrapping the paragraph around it.
	//
	// When To would set wider than From did — measured with the replaced
	// occurrence's own font and size — To is set at a smaller size, so
	// that it takes exactly the width From took and every line break in
	// the paragraph stays where it was. It never enlarges: a token
	// narrower than what it replaces keeps its size, and the line gains
	// slack. It never goes below 45% of the run's size either; where
	// even that leaves the token wider, it is set at the floor and the
	// paragraph re-wraps as it otherwise would, because a token nobody
	// can read is the worse of the two failures.
	//
	// Reverse drops it: restoring the original text has no width to fit.
	FitWidth bool
	// MinScale is how far FitWidth may shrink this token, as a fraction
	// of the size the run is set in. Zero means the default of 0.45.
	//
	// The default suits a marker meant to be read. It does not suit one
	// meant to be matched: a key-reversible stand-in is long by
	// construction, and dropped over a short word — "[[PII_LOCATION_001]]"
	// where "Milo" was — it needs a fifth of the size, not a half.
	// Refusing to go that small means the paragraph re-wraps, and a
	// caller who would rather have a small marker than a moved page can
	// say so here. The marker is still text: searchable, extractable,
	// and exactly what the key file holds.
	MinScale float64
}

Pseudonym is one substitution: every occurrence of From becomes To.

func Reverse

func Reverse(subs []Pseudonym) []Pseudonym

Reverse returns the mappings that undo a substitution, for a caller holding the key.

Feed them back through Pseudonymize to restore the original text:

key := []gopdf.Pseudonym{{From: "Ada Lovelace", To: "[[PII_NAME_1]]"}}
gopdf.Pseudonymize(r, w, key)                 // anonymize
gopdf.Pseudonymize(r2, w2, gopdf.Reverse(key)) // and back

Only text is restored. A word an OCR engine found in a picture was removed by overwriting the pixels, and no key brings those back: the token drawn over the hole is all there is. Keep the key somewhere the pseudonymized document is not, or there was no point.

type PseudonymizeResult

type PseudonymizeResult struct {
	// Replaced counts the paragraphs rewritten, per original string.
	Replaced map[string]int
	// Pages is how many pages were touched.
	Pages int
}

PseudonymizeResult reports what a substitution pass did.

func Pseudonymize

func Pseudonymize(r *Reader, w io.Writer, subs []Pseudonym) (PseudonymizeResult, error)

Pseudonymize replaces identifying text with tokens throughout a document and writes the result.

Each paragraph it changes is re-wrapped, so a token need not be the same length as the name it replaces, and each part of the paragraph keeps the styling it had. The output is a complete file rather than an incremental update, so the original text cannot be recovered from an earlier revision, and it is read back and checked before being handed over: if any original is still readable, Pseudonymize reports that and writes nothing.

It reports an error, and writes nothing, if a document's fonts cannot represent a token — writing one that renders as blank boxes would be worse than declining.

func PseudonymizeFile

func PseudonymizeFile(src, dst string, subs []Pseudonym) (PseudonymizeResult, error)

PseudonymizeFile is Pseudonymize between two paths.

func (PseudonymizeResult) Total

func (r PseudonymizeResult) Total() int

Total returns the number of paragraphs rewritten across every mapping.

type Reader

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

Reader provides read access to an existing PDF file: page inspection, text extraction, and importing pages into a Document.

A Reader is not safe for concurrent use.

func NewReader

func NewReader(data []byte) (*Reader, error)

NewReader parses a PDF file held in memory. The Reader keeps a reference to data; it must not be modified afterwards.

func NewReaderPassword

func NewReaderPassword(data []byte, password string) (*Reader, error)

NewReaderPassword parses an encrypted PDF file held in memory. Either the user or the owner password is accepted.

func Open

func Open(path string) (*Reader, error)

Open reads a PDF file from disk. Encrypted files open when they have an empty user password; otherwise Open returns ErrPasswordRequired and the file must be opened with OpenPassword.

func OpenPassword

func OpenPassword(path, password string) (*Reader, error)

OpenPassword reads an encrypted PDF file from disk. Either the user or the owner password is accepted.

Example

Reading a password-protected file.

package main

import (
	"errors"
	"fmt"
	"log"

	"github.com/SalvioniDigitalSolutions/gopdf"
)

func main() {
	r, err := gopdf.OpenPassword("protected.pdf", "secret")
	if errors.Is(err, gopdf.ErrPasswordRequired) {
		log.Fatal("wrong password")
	} else if err != nil {
		log.Fatal(err)
	}
	text, err := r.PageText(0)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(text)
}

func (*Reader) Annotations

func (r *Reader) Annotations(page int) ([]Annotation, error)

Annotations lists the annotations on a page, in the order the file stores them.

func (*Reader) Attachments

func (r *Reader) Attachments() []Attachment

Attachments lists every file the document carries.

The document's own collection comes first, in the order the name tree gives, followed by the ones attached to pages.

func (*Reader) Catalog

func (r *Reader) Catalog() Dict

Catalog returns the document catalog, the root of the object graph.

func (*Reader) CheckPDFA

func (r *Reader) CheckPDFA(level PDFAConformance) []PDFAIssue

CheckPDFA reports what stops a document meeting the archival profile.

An empty result means nothing this package can see is wrong, which is not the same as a certificate: the profile has requirements about colour management and about the internals of embedded font programs that a full validator checks and this does not. What it does catch is the things that actually go wrong — a font that is not embedded, an encrypted file, a reference to something outside the document.

func (*Reader) FormFields

func (r *Reader) FormFields() []FormField

FormFields lists the document's interactive form fields, in a stable order by name.

func (*Reader) HasForm

func (r *Reader) HasForm() bool

HasForm reports whether the document has an interactive form.

func (*Reader) HasSignatures

func (r *Reader) HasSignatures() bool

HasSignatures reports whether the document carries any signature.

func (*Reader) Info

func (r *Reader) Info() Info

Info returns the document metadata.

func (*Reader) InheritedPageValue

func (r *Reader) InheritedPageValue(index int, key Name) any

InheritedPageValue looks a key up on a page and then on its ancestors in the page tree, which is where /Resources, /MediaBox, /CropBox and /Rotate are allowed to live.

func (*Reader) IsEncrypted

func (r *Reader) IsEncrypted() bool

IsEncrypted reports whether the source file was encrypted.

func (*Reader) Layers

func (r *Reader) Layers() []Layer

Layers lists the optional content an existing document defines, in the order its default configuration puts them.

func (*Reader) MarkedTagged

func (r *Reader) MarkedTagged() bool

MarkedTagged reports whether the document claims to be a tagged PDF, which is a stronger statement than merely carrying a tree: it says the tree covers the content in reading order.

func (*Reader) NumPages

func (r *Reader) NumPages() int

NumPages returns the number of pages in the file.

func (*Reader) Object

func (r *Reader) Object(ref Ref) any

Object returns the object a reference names, or nil if the file has no such object. Resolve is usually what you want; Object is for walking the file by number.

func (*Reader) Objects

func (r *Reader) Objects() []Ref

Objects lists every object the file defines, in numeric order. The list includes objects nothing points at, which is how you find what a document is carrying that its page tree never mentions.

func (*Reader) PageDict

func (r *Reader) PageDict(index int) Dict

PageDict returns a page's dictionary, or nil if there is no such page. Inherited attributes are not merged in: /Resources, /MediaBox and /Rotate may live on an ancestor node, which is what InheritedPageValue is for.

func (*Reader) PageImages

func (r *Reader) PageImages(page int) ([]ImageRef, error)

PageImages lists the images a page draws, including those inside form XObjects it invokes.

func (*Reader) PageLabel

func (r *Reader) PageLabel(index int) string

PageLabel returns the label a reader sees for a page: "iv", "A-3", "12". A document with no labels numbers its pages from one, which is what a viewer shows, so that is what comes back.

func (*Reader) PageLabels

func (r *Reader) PageLabels() []PageLabelRange

PageLabels returns the label ranges the document defines, in page order, or nil if it defines none.

func (*Reader) PageRef

func (r *Reader) PageRef(index int) (Ref, bool)

PageRef returns the reference naming a page's dictionary. The second result is false for the rare file that writes a page inline rather than as an indirect object.

func (*Reader) PageSize

func (r *Reader) PageSize(index int) (PageSize, error)

PageSize returns the display size of a page in points, accounting for the page's rotation.

func (*Reader) PageText

func (r *Reader) PageText(index int) (string, error)

PageText extracts the text of a page (0-based index) in content order, including text inside nested form XObjects (so pages imported from other documents extract too). Line breaks are inferred from vertical movement of the text cursor.

Text in fonts with a ToUnicode CMap (including everything this library generates) extracts exactly; other fonts fall back to their declared encoding, approximated as WinAnsi when the encoding is nonstandard.

func (*Reader) PageTextFragments

func (r *Reader) PageTextFragments(page int) (frags []TextFragment, err error)

PageTextFragments returns the page's text one show-text operation at a time, in the order the content stream draws them, descending into form XObjects with their matrices composed.

A malformed content stream is reported as an error for that page, as is one too large to lex whole. It is never a panic, and never a silent prefix: a caller matching on offsets would take a missing tail for absent text.

Measured against PageText over 24,254 pages of real documents, the two agree on all but ten. Where they differ the fragments hold more, save on eight pages of one corpus whose text is mis-decoded either way; that case is understood to exist and not yet understood in detail.

func (*Reader) RenderPage

func (r *Reader) RenderPage(page int, opts RenderOpts) (image.Image, error)

RenderPage draws a page and returns the picture.

A page is measured in points and the result in pixels, so the size follows from the DPI: an A4 page at 150 comes out 1240 by 1754.

What is drawn is chosen by the options, and nothing is drawn that they do not ask for. A malformed content stream is reported for that page rather than panicking.

func (*Reader) RenderPageDetail

func (r *Reader) RenderPageDetail(page int, opts RenderOpts) (img image.Image, rep RenderReport, err error)

RenderPageDetail draws a page and says what it managed, which matters when text is switched on: a font this package cannot read leaves holes, and silence about them would be the wrong answer.

func (*Reader) Repaired

func (r *Reader) Repaired() bool

Repaired reports whether the file's cross-reference table was missing or wrong, and the objects had to be found by scanning the file. Such a document reads normally, but it was damaged, and anything the scan could not reach is gone.

func (*Reader) Resolve

func (r *Reader) Resolve(v any) any

Resolve follows an indirect reference to the object it names, and returns anything else unchanged. Use it on every value read out of a dictionary: a PDF may write any value indirectly, so a key that holds a number in one file holds a reference to a number in the next.

A stream comes back as *Stream. Everything else comes back as one of Name, String, Array, Dict, int64, float64, bool, or nil.

func (*Reader) Signatures

func (r *Reader) Signatures() []Signature

Signatures lists the document's signatures.

func (*Reader) StructOutline

func (r *Reader) StructOutline() []StructHeading

StructOutline returns the headings a tagged document declares, in order, with their level: H1 is 1, H2 is 2. It is the table of contents a document has whether or not it also has bookmarks.

func (*Reader) StructText

func (r *Reader) StructText() string

StructText walks the tree and returns what the document says, in the order the structure puts it rather than the order the operators drew it.

Where an element declares its actual text, that is used in place of whatever the glyphs spell; where a figure has alternate text, that stands in for the picture. An untagged document has no structure to walk and gives back nothing, which is the honest answer — PageText is what to use there.

func (*Reader) Structure

func (r *Reader) Structure() []*StructNode

Structure returns the document's structure tree, or nil if it has none.

func (*Reader) Tagged

func (r *Reader) Tagged() bool

Tagged reports whether the document carries a structure tree.

func (*Reader) Trailer

func (r *Reader) Trailer() Dict

Trailer returns the document's trailer dictionary, merged across the cross-reference chain so that /Root, /Info and /Encrypt are present wherever the file put them.

func (*Reader) Walk

func (r *Reader) Walk(fn func(ref Ref, obj any) bool)

Walk visits every object reachable from the trailer, depth first, calling fn with the reference that named it and the object itself. Each object is visited once however many times it is referenced, so a shared font or image arrives one time only. Returning false from fn stops the walk.

Values found inline rather than behind a reference are visited with a zero Ref, since they have no number of their own.

func (*Reader) XMP

func (r *Reader) XMP() XMP

XMP returns the document's metadata packet.

A document with no packet gives back a zero XMP, which is not an error: most documents have none.

type RedactionKind

type RedactionKind string

RedactionKind says what a mark will remove.

const (
	// RedactText marks glyphs in a content stream.
	RedactText RedactionKind = "text"
	// RedactImage marks all or part of an image's pixels.
	RedactImage RedactionKind = "image"
	// RedactAnnotation marks an annotation, with whatever text it holds.
	RedactAnnotation RedactionKind = "annotation"
	// RedactPath marks vector artwork.
	RedactPath RedactionKind = "path"
	// RedactImageText marks words an OCR engine read inside an image.
	RedactImageText RedactionKind = "image-text"
	// RedactCopy marks a second copy of the page or an image — a
	// thumbnail, an alternate, a producer's private cache — dropped
	// because it would still show what was removed.
	RedactCopy RedactionKind = "copy"
)

type RedactionMark

type RedactionMark struct {
	// Kind is what sort of content this is.
	Kind RedactionKind
	// Page is the 0-based page index.
	Page int
	// X, Y, W and H bound the affected area, in points from the
	// top-left of the page.
	X, Y, W, H float64
	// Text is the text that will be removed, where the content has any.
	Text string
	// Partial reports that only part of the object is affected: some
	// characters of a run, or a region of an image.
	Partial bool
}

RedactionMark describes one piece of content that will be removed. Marks are what a caller should show for review before writing, since afterwards the content is gone.

type Redactor

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

Redactor collects what to remove from a document and writes the redacted result. Create one with Redact, mark content with Area, Text, Pattern, Match or Image, then call Save or WriteTo.

A Redactor is not safe for concurrent use.

func Redact

func Redact(r *Reader) *Redactor

Redact opens a document for redaction.

func (*Redactor) Area

func (rd *Redactor) Area(page int, x, y, w, h float64)

Area marks a rectangle on a page. Every piece of content that falls inside it is removed: text, images, vector artwork and annotations. Coordinates are in points from the top-left of the page.

func (*Redactor) Attachments

func (rd *Redactor) Attachments() []Attachment

Attachments lists the files the document carries inside it.

They are worth looking at before redacting. An attachment is not content on a page and no rule here reaches into one, so a spreadsheet attached to a report still holds whatever the report said — the whole point of redacting having been to remove it. RemoveAttachments takes them out.

func (*Redactor) Image

func (rd *Redactor) Image(img ImageRef)

Image marks an entire image for removal, wherever it is drawn.

func (*Redactor) KeepAnnotations

func (rd *Redactor) KeepAnnotations(on bool)

KeepAnnotations leaves annotations in place even where they fall inside a redacted area. Off by default, since an annotation's text is content like any other.

func (*Redactor) KeepAttachments

func (rd *Redactor) KeepAttachments(on bool)

KeepAttachments leaves the files a document carries inside it.

Off by default, so a redaction removes them. No rule here reaches into an attachment, and a spreadsheet attached to a report holds whatever the report said — which makes leaving one in place the likeliest way for a redacted document to give up what it was redacted for. Keeping them is a decision worth stating.

func (*Redactor) Marks

func (rd *Redactor) Marks() ([]RedactionMark, error)

Marks lists what will be removed, without removing it. Call it to show a reviewer what is about to happen.

func (*Redactor) Match

func (rd *Redactor) Match(fn func(*TextRun) bool)

Match marks whole runs chosen by a callback, for decisions the other methods cannot express — a particular font, a position, a size.

func (*Redactor) MatchSubstrings

func (rd *Redactor) MatchSubstrings(on bool)

MatchSubstrings finds a literal wherever it appears, including inside a longer word. It is off by default.

func (*Redactor) PartialArtwork

func (rd *Redactor) PartialArtwork() (int, error)

PartialArtwork reports how many pieces of vector artwork or shading straddle the edge of a redacted area on the last plan. Such a piece is covered by the overlay box but not deleted, because trimming a path to a rectangle needs a clipper this package does not have. When the count is not zero and the artwork itself is sensitive, redact a larger area so it falls wholly inside.

func (*Redactor) Pattern

func (rd *Redactor) Pattern(re *regexp.Regexp)

Pattern marks every match of a regular expression. Use it for the shapes personal data comes in — account numbers, dates of birth, email addresses.

func (*Redactor) Save

func (rd *Redactor) Save(path string) error

Save writes the redacted document to a file.

func (*Redactor) SetFill

func (rd *Redactor) SetFill(c Color)

SetFill sets the colour of the box painted over a redacted area. The default is black.

func (*Redactor) SetLabel

func (rd *Redactor) SetLabel(token string)

SetLabel sets the token written into every bar this redaction paints. It is what turns a blank rectangle into a marked one:

rd.SetLabel("[REDACTED]")

A token is set in Helvetica, shrunk to fit the space the removed content occupied, so a long token in a small box comes out small. Pass an empty string to go back to a plain bar.

For text in a content stream this is the cruder of two tools: a Pseudonym passed to Pseudonymize re-wraps the paragraph around the token and keeps the surrounding styling. Labels exist for the case that cannot do — words found by an OCR engine inside a picture, where there is no text to re-wrap and no font to match.

func (*Redactor) SetLabelColor

func (rd *Redactor) SetLabelColor(c Color)

SetLabelColor sets the colour a token is written in. The default is white, which shows against the black bar.

func (*Redactor) SetOCR

func (rd *Redactor) SetOCR(e OCREngine)

SetOCR supplies an engine that reads text in images, so that Text and Pattern also match words inside a scan. A word that matches has its pixels scrubbed, exactly as an Area covering it would.

Recognition is not exhaustive. An engine misses words, especially on poor scans, and a word it misses stays in the document. Review Marks before relying on the result, and prefer Area where the region to remove is known.

func (*Redactor) SetOCRConfidence

func (rd *Redactor) SetOCRConfidence(min float64)

SetOCRConfidence ignores recognised words the engine is less sure of than min, which runs from 0 to 1. The default is 0: for redaction a doubtful match is still worth removing, since removing too much is the lesser mistake.

func (*Redactor) SetOverlay

func (rd *Redactor) SetOverlay(on bool)

SetOverlay controls whether a box is painted over each redacted area. It is on by default, so a redaction is visible as one. Turning it off still removes the content; it just leaves no mark.

func (*Redactor) SetVerify

func (rd *Redactor) SetVerify(on bool)

SetVerify controls whether the written document is read back and checked. It is on by default: a redaction that quietly leaves one occurrence behind is the worst way for this to fail, so the result is proved rather than assumed. Turn it off only where the cost of parsing the output again matters more than that.

func (*Redactor) StripMetadata

func (rd *Redactor) StripMetadata(on bool)

StripMetadata controls whether the document information dictionary and XMP metadata stream are discarded. They are, by default: metadata routinely carries author names, file paths and earlier titles that the visible content no longer does.

func (*Redactor) Substitute

func (rd *Redactor) Substitute(from, to string)

Substitute marks text and gives the token to write where it was. It is the pseudonymizing form of Text: the content is removed exactly as Text removes it, and the token is set into the bar left behind.

For words an OCR engine finds inside a picture this is the only way to substitute, there being no text to rewrite and no font to match.

func (*Redactor) Text

func (rd *Redactor) Text(s string)

Text marks every occurrence of a literal string, on every page. The match is made against the text as extracted, so it sees the document's reading order rather than its visual layout. Text marks every occurrence of a literal string, on every page.

An occurrence counts where the literal stands on its own: "Rossi" is found in "Sig. Rossi," and not inside "Rossini". For personal data removing too much is the lesser mistake, but removing the wrong thing is still a mistake, and a surname inside a longer surname is the wrong thing. MatchSubstrings turns that off.

The literal is also sought in the spellings a document might have used instead — a non-breaking space for a space, a soft hyphen for a hyphen — so a caller need not know which the producer chose.

func (*Redactor) WriteTo

func (rd *Redactor) WriteTo(w io.Writer) (int64, error)

WriteTo writes the redacted document. The output is a complete file, not an incremental update, so the removed content is not left behind in an earlier revision.

type Ref

type Ref struct {
	Num, Gen int
}

Ref is an indirect object reference in a parsed file.

type RenderOpts

type RenderOpts struct {
	// DPI is the resolution. Zero means 150, which is where a rule stops
	// looking soft.
	DPI float64
	// IncludeText draws glyphs, using the outlines the document's own
	// fonts carry. Fonts whose glyphs are addressed by name through the
	// built-in encodings are the exception and are left undrawn;
	// RenderPageDetail reports how many glyphs that came to.
	//
	// Text that clips is followed whether or not this is set, because
	// what a text clip removes is part of the artwork.
	IncludeText bool
	// IncludeRasterImages draws photographs and scans. Off by default:
	// they can be pulled out and placed separately, and leaving them out
	// keeps the layer small.
	IncludeRasterImages bool
	// IncludeVector draws paths — fills, strokes and shadings. This is
	// the point; with everything off the result is blank.
	IncludeVector bool
	// IncludeAnnotations draws the appearance streams of the page's
	// annotations: a filled form field, a signature block, a stamp, a
	// highlight, a sticky note. Much of what a reader sees is not in the
	// content stream at all, so a render of a form without this is a
	// render of an empty form.
	IncludeAnnotations bool
	// MinTextSize draws only glyphs whose effective size is at least this
	// many points — the /Tf operand after the text matrix and the current
	// transformation have scaled it, which is the size
	// PageTextFragments reports for the same glyph. Zero draws them all.
	//
	// A page's watermark is set many times larger than its body, so a
	// threshold between the two draws the watermark and nothing else,
	// from the document's own matrices and so in exactly the place the
	// document puts it. The body glyphs are never drawn at all rather
	// than drawn and painted over, which is the difference between a
	// backdrop that can be handed on and one that has the text in it.
	//
	// A glyph below the threshold neither paints nor clips, and is
	// counted neither drawn nor missing: it was not attempted. It still
	// advances the pen, so the glyphs that do draw land where they
	// would have.
	MinTextSize float64
	// Transparent leaves untouched pixels clear instead of white.
	Transparent bool
	// SubstituteFont supplies a font program for a font the document
	// names but does not embed, and for the Type 1 programs this package
	// does not read. It is asked for TrueType or OpenType bytes and may
	// return nil, in which case that font's glyphs are left undrawn.
	//
	// A substitute provides shapes only: every advance still comes from
	// the widths in the document, so the text lands where the document
	// says. SystemFonts returns one built on the machine's own fonts.
	SubstituteFont func(FontRequest) []byte
}

RenderOpts says what to draw and how large.

type RenderReport

type RenderReport struct {
	Glyphs  int
	Missing int
}

RenderReport says what a render managed.

Glyphs is how many were drawn and Missing how many were asked for and could not be: a font whose program is not embedded, or one whose glyphs are addressed by name through the built-in encodings this package does not carry. A page that reports missing glyphs is a page with holes in it, and the count is the only way to tell that apart from a page that simply had little text.

type SignOptions

type SignOptions struct {
	// Certificate is the signing certificate, and Key the private key
	// matching it. Both are required.
	Certificate *x509.Certificate
	Key         crypto.Signer
	// Chain holds any intermediate certificates to embed alongside.
	Chain []*x509.Certificate
	// Name, Reason, Location and ContactInfo are recorded in the
	// signature dictionary for a reader to display.
	Name, Reason, Location, ContactInfo string
	// When is the claimed signing time; the zero value means now.
	When time.Time
	// FieldName is the form field to create. It defaults to "Signature1".
	FieldName string
	// ReservedBytes is how much room to leave for the signature blob.
	// The default suits an ordinary certificate chain.
	ReservedBytes int
}

SignOptions describes a signature to apply.

type Signature

type Signature struct {
	// Field is the form field the signature occupies.
	Field string
	// Name, Reason, Location and ContactInfo are what the signer
	// declared, where they declared anything.
	Name, Reason, Location, ContactInfo string
	// When is the claimed signing time.
	When time.Time
	// Signer is the common name on the signing certificate.
	Signer string
	// Certificate is the signing certificate, when it could be read out
	// of the signature blob.
	Certificate *x509.Certificate
	// ByteRange is the span of the file the signature covers, as pairs of
	// offset and length.
	ByteRange []int
	// CoversWholeFile reports whether the byte range reaches the end of
	// the file. When it does not, the document was changed after signing.
	CoversWholeFile bool
	// Certified marks a signature that also restricts what later changes
	// are permitted.
	Certified bool
	// Permissions is a certifying signature's /DocMDP level: 1 forbids
	// any change, 2 allows form filling, 3 also allows annotations.
	Permissions int
}

Signature describes a signature found in a document.

type Stream

type Stream struct {
	// Dict is the stream's dictionary, including /Filter and
	// /DecodeParms. It is the reader's own dictionary: copy it with
	// Clone before changing anything.
	Dict Dict
	// contains filtered or unexported fields
}

Stream is a stream object: a dictionary and the bytes that follow it.

The bytes are held as the file stores them, still encoded. Data decodes them; Raw hands them back untouched, which is what you want when copying a stream from one file to another without paying to decompress and recompress it.

func NewStream

func NewStream(d Dict, encoded []byte) *Stream

NewStream builds a stream from a dictionary and its already-encoded bytes, for handing to Updater.AddObject. The dictionary should name whatever /Filter the bytes are in, and should not carry a /Length: the writer sets it from the data it actually writes.

func (*Stream) Data

func (s *Stream) Data() ([]byte, error)

Data returns the stream's decoded contents.

func (*Stream) Raw

func (s *Stream) Raw() []byte

Raw returns the stream's bytes exactly as the file stores them, still encoded by whatever /Filter the dictionary names.

type String

type String []byte

String is a PDF string object's raw bytes.

type StructHeading

type StructHeading struct {
	Level int
	Text  string
	Page  int
}

StructHeading is one heading of a tagged document.

type StructNode

type StructNode struct {
	// Type is the element's role: "P", "H1", "Table", "Figure". A
	// document may define its own and map them to the standard ones,
	// which Role resolves.
	Type string
	// Role is Type mapped through the document's role map to a standard
	// type, or Type itself where there is no mapping.
	Role string
	// Title is the element's own title, where it has one.
	Title string
	// Alt is the alternate text: what an image means, for a reader that
	// cannot see it.
	Alt string
	// ActualText is what the element really says, where the glyphs do
	// not spell it — a ligature drawn as one glyph, a word broken by a
	// decorative rule.
	ActualText string
	// Lang is the element's language, where it differs from the
	// document's.
	Lang string
	// Page is the page the element's content sits on, or -1 when the
	// element has no content of its own.
	Page int
	// Children are the elements below this one.
	Children []*StructNode
}

StructNode is one element of the structure tree.

type TextBlock

type TextBlock struct {
	// Text is the paragraph's text, its lines joined with single spaces.
	Text string
	// X and Y position the first line's baseline, from the top-left of
	// the page.
	X, Y float64
	// Width is the column width in points: the widest line in the block.
	Width float64
	// LineHeight is the vertical distance between baselines, in points.
	LineHeight float64
	// FontSize is the effective size in points.
	FontSize float64
	// FontName is the font resource name in the source file.
	FontName string
	// contains filtered or unexported fields
}

TextBlock is a paragraph: a group of consecutive single-run lines that share a font, a left edge and a constant leading. Editing a block re-wraps its text across the lines the paragraph already occupies, so a sentence can grow or shrink without the paragraph losing its shape.

func (*TextBlock) Lines

func (b *TextBlock) Lines() []*TextRun

Lines returns the runs making up the block, one per line.

func (*TextBlock) SetText

func (b *TextBlock) SetText(s string) error

SetText replaces the paragraph's text, re-wrapping it to the block's column width. Lines the new text no longer needs are cleared; if it needs more lines than the block has (plus any allowance from SetMaxExtraLines), SetText reports how many are missing and changes nothing.

type TextFragment

type TextFragment struct {
	// Text is the decoded text, through the font's ToUnicode CMap where
	// it has one and its encoding and /Differences where it does not.
	//
	// A code the font gives no mapping for becomes U+FFFD rather than
	// disappearing: an unreadable glyph is still a glyph, and dropping it
	// silently moves every offset after it.
	Text string
	// X and Y are the baseline's starting point in points, measured from
	// the top-left of the page — the same convention as ImageRef.
	X, Y float64
	// W is the advance width in points, with character, word and
	// horizontal spacing applied. It is zero when the font declares no
	// widths for what it drew.
	W float64
	// FontName is the /BaseFont, subset prefix and all, as in
	// "ABCDEF+OpenSans-Bold". It names the face; the resource name the
	// page happens to use for it does not.
	FontName string
	// FontSize is the effective size in points: the Tf operand after the
	// text and current transformation matrices have scaled it.
	FontSize float64
	// RenderMode is the Tr in force. Mode 3 draws nothing, which is how a
	// scanned page carries the OCR layer under its picture, and is what a
	// caller skips to avoid reading the same words twice.
	RenderMode int
}

TextFragment is one show-text operation: the text it draws, where the baseline starts, and how it is set.

func (TextFragment) Invisible

func (f TextFragment) Invisible() bool

Invisible reports whether the fragment is drawn in a mode that paints nothing — mode 3, or mode 7 which only adds to the clip.

type TextRun

type TextRun struct {
	// Text is the run's decoded text.
	Text string
	// X and Y are the position of the run's baseline origin in points,
	// measured from the top-left of the page like the rest of this
	// package's coordinates.
	X, Y float64
	// FontSize is the effective size in points, after transforms.
	FontSize float64
	// FontName is the run's font resource name in the source file.
	FontName string
	// Width is the run's advance width in points.
	Width float64
	// contains filtered or unexported fields
}

TextRun is one run of text in an existing page, as the page's content stream draws it: a single show-text operation with its position, font and size. Runs are the unit of editing.

func (*TextRun) Restyle

func (run *TextRun) Restyle(s TextStyle) error

Restyle changes how the run is drawn. The run's text is unaffected unless the new font cannot represent it, in which case Restyle reports the offending character and changes nothing.

func (*TextRun) SetText

func (run *TextRun) SetText(s string, mode FitMode) error

SetText rewrites a single run's text. It reports an error, and changes nothing, if the run's font cannot represent the new text.

type TextStyle

type TextStyle struct {
	// Font replaces the run's typeface. The replacement text is
	// re-encoded for it, and it is registered with the page.
	Font *Font
	// Size sets the type size in points.
	Size float64
	// Color sets the fill colour the text is painted with.
	Color *Color
}

TextStyle describes a change to how a run of existing text is drawn. A zero field leaves that aspect of the run alone.

type UpdatablePage

type UpdatablePage struct {
	// Page carries the drawing API. Anything drawn is appended to the
	// page as an extra content stream, leaving the original untouched.
	*Page
	// contains filtered or unexported fields
}

UpdatablePage is one page of a document being updated incrementally.

func (*UpdatablePage) Blocks

func (p *UpdatablePage) Blocks() []*TextBlock

Blocks groups the page's runs into paragraphs for reflowing.

func (*UpdatablePage) Flows

func (p *UpdatablePage) Flows() []*Flow

Flows groups the page's text into paragraphs that can be replaced at any length, keeping each part's styling.

func (*UpdatablePage) ReplaceFunc

func (p *UpdatablePage) ReplaceFunc(fn func(*TextRun) (string, bool)) (int, error)

ReplaceFunc rewrites the runs for which fn returns true. On error nothing is changed.

func (*UpdatablePage) ReplaceText

func (p *UpdatablePage) ReplaceText(old, new string) (int, error)

ReplaceText rewrites every occurrence of old on this page, using the page's own font so the result renders identically.

func (*UpdatablePage) ReplaceTextFlow

func (p *UpdatablePage) ReplaceTextFlow(old, new string) (int, error)

ReplaceTextFlow replaces occurrences of old across the page's paragraphs, re-wrapping each one it changes and keeping its styling.

func (*UpdatablePage) ReplaceTextReflow

func (p *UpdatablePage) ReplaceTextReflow(old, new string) (int, error)

ReplaceTextReflow rewrites paragraphs containing old, re-wrapping each one across the lines it already occupies.

func (*UpdatablePage) Runs

func (p *UpdatablePage) Runs() []*TextRun

Runs returns the page's text runs, in content order.

type Updater

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

Updater modifies an existing PDF by appending an incremental update: the original bytes are written out unchanged and the modifications follow, referenced by a new cross-reference section.

This is the highest-fidelity way to change a document. Everything the original contains — structure trees, embedded files, optional content, annotations, scripts, anything this library does not model — survives byte for byte, because it is never rewritten. Rebuilding a document with Document.EditPage or ImportPage, by contrast, keeps only what the library understands.

An Updater is not safe for concurrent use.

func Update

func Update(r *Reader) *Updater

Update opens a parsed document for incremental modification.

func (*Updater) AddImage

func (u *Updater) AddImage(m image.Image) (*Image, error)

AddImage registers an image for drawing onto updated pages, mirroring Document.AddImage.

func (*Updater) AddImageFile

func (u *Updater) AddImageFile(path string) (*Image, error)

AddImageFile registers an image file (JPEG, PNG or GIF) for drawing onto updated pages.

func (*Updater) AddImageReader

func (u *Updater) AddImageReader(r io.Reader) (*Image, error)

AddImageReader registers an image read from r for drawing onto updated pages.

func (*Updater) AddObject

func (u *Updater) AddObject(v any) Ref

AddObject writes a brand-new indirect object into the update and returns the reference naming it. The value may be any of Name, String, Array, Dict, Ref, a number, a bool, or a *Stream.

func (*Updater) Attach

func (u *Updater) Attach(name string, data []byte) error

Attach adds a file to an existing document, appended incrementally.

func (*Updater) AttachWithDescription

func (u *Updater) AttachWithDescription(name, description string, data []byte) error

AttachWithDescription adds a file and its note to an existing document.

func (*Updater) MovePage

func (u *Updater) MovePage(from, to int) error

MovePage moves a page to a new position, counted in the document's current order.

func (*Updater) Page

func (u *Updater) Page(index int) (*UpdatablePage, error)

Page prepares a page for text editing. The page's content stream and any form XObjects it draws become editable; every other object in the file is left alone.

func (*Updater) Reader

func (u *Updater) Reader() *Reader

Reader returns the document the update is built on.

func (*Updater) RemoveAnnotations

func (u *Updater) RemoveAnnotations(pageIndex int, drop func(Annotation) bool) (int, error)

RemoveAnnotations drops the annotations on a page for which drop returns true, and reports how many were removed. The annotation objects stay in the file; the page simply stops referencing them.

func (*Updater) RemoveAttachments

func (u *Updater) RemoveAttachments(drop func(Attachment) bool) (int, error)

RemoveAttachments takes files out of a document.

It returns how many it removed. The file specifications are emptied rather than merely unlinked from the collection, because an object a document no longer points at is still an object in the file: an incremental update appends, and what it stops referring to is still there to be found.

func (*Updater) RemovePage

func (u *Updater) RemovePage(index int) error

RemovePage drops a page from the document.

func (*Updater) ReplaceImage

func (u *Updater) ReplaceImage(img ImageRef, m image.Image) error

ReplaceImage swaps an image's pixels for those of m, keeping the placement the page already has: the new image is scaled into exactly the same area, whatever its pixel dimensions.

The image object is shared, so every page drawing it shows the replacement. Alpha in m is preserved as a soft mask.

func (*Updater) Save

func (u *Updater) Save(path string) error

Save writes the updated document to a file. Pass the path the document was read from to update it in place.

func (*Updater) SetCatalogEntry

func (u *Updater) SetCatalogEntry(key Name, v any) error

SetCatalogEntry sets one key on the document catalog, adding the catalog to the update. It saves the read-clone-write dance for the common case of switching something on at the top of a document.

func (*Updater) SetCompress

func (u *Updater) SetCompress(on bool)

SetCompress controls whether content and resource streams added by the update are compressed. It defaults to true; turn it off to inspect the appended objects.

func (*Updater) SetFitMode

func (u *Updater) SetFitMode(m FitMode)

SetFitMode selects how text replacements of a different width are fitted back into the layout.

func (*Updater) SetFormValues

func (u *Updater) SetFormValues(values map[string]string) error

SetFormValues fills interactive form fields, keeping the form editable. Field names are the fully qualified names Reader.FormFields reports.

func (*Updater) SetInfo

func (u *Updater) SetInfo(info Info)

SetInfo replaces the document information dictionary.

func (*Updater) SetLayerVisible

func (u *Updater) SetLayerVisible(name string, on bool) error

SetLayerVisible turns a layer of an existing document on or off, appended incrementally. The layer is named as Layers reports it.

func (*Updater) SetMaxExtraLines

func (u *Updater) SetMaxExtraLines(n int)

SetMaxExtraLines allows reflowed paragraphs to grow by up to n lines.

func (*Updater) SetObject

func (u *Updater) SetObject(ref Ref, v any) error

SetObject replaces an existing object. The reference must name an object the file already defines, or one AddObject returned; anything else is an error rather than a silent hole in the document.

func (*Updater) SetPageEntry

func (u *Updater) SetPageEntry(index int, key Name, v any) error

SetPageEntry sets one key on a page's dictionary.

func (*Updater) SetPageLabels

func (u *Updater) SetPageLabels(ranges []PageLabelRange) error

SetPageLabels gives an existing document its page numbering, appended incrementally.

func (*Updater) SetPageOrder

func (u *Updater) SetPageOrder(order []int) error

SetPageOrder rewrites the document's page order. The argument lists the 0-based indexes of the pages to keep, in the order they should appear; pages left out are removed from the tree.

The page objects themselves stay in the file, so this is cheap and reversible by another update.

func (*Updater) SetPageRotation

func (u *Updater) SetPageRotation(index, deg int) error

SetPageRotation sets a page's display rotation in degrees clockwise.

func (*Updater) SetXMP

func (u *Updater) SetXMP(info Info) error

SetXMP writes a metadata packet into an existing document, describing it as its information dictionary does.

func (*Updater) Sign

func (u *Updater) Sign(opts SignOptions) error

Sign adds a digital signature to the document. The signature covers every byte of the resulting file except its own blob, and is written as an incremental update, so any signature already on the document keeps covering the bytes it signed.

The signature is computed when the document is written, once the file's layout is known.

func (*Updater) WriteTo

func (u *Updater) WriteTo(w io.Writer) (int64, error)

WriteTo writes the original file followed by the appended changes.

type XMP

type XMP struct {
	// Title, Author, Subject, Keywords, Creator and Producer are the
	// fields the information dictionary also carries.
	Title, Author, Subject, Keywords string
	Creator, Producer                string
	// Created and Modified are the packet's own timestamps.
	Created, Modified time.Time
	// Raw is the packet as it stands, for anything this does not model.
	// It is empty for a document that has no packet.
	Raw []byte
}

XMP is a document's metadata packet.

Directories

Path Synopsis
examples
demo command
Command demo generates demo.pdf, a one-stop showcase of gopdf features: text in the standard fonts, vector graphics, transforms, and images.
Command demo generates demo.pdf, a one-stop showcase of gopdf features: text in the standard fonts, vector graphics, transforms, and images.
edit command
Command edit rewrites text inside an existing PDF without disturbing its layout.
Command edit rewrites text inside an existing PDF without disturbing its layout.
redact command
Command redact removes text from a PDF and writes a fresh file with the content gone, rather than covered.
Command redact removes text from a PDF and writes a fresh file with the content gone, rather than covered.
stamp command
Command stamp demonstrates PDF manipulation: it reads an existing PDF, overlays a diagonal watermark on every page, and writes the result.
Command stamp demonstrates PDF manipulation: it reads an existing PDF, overlays a diagonal watermark on every page, and writes the result.
internal
ccitt
Package ccitt implements a CCITT (fax) image decoder.
Package ccitt implements a CCITT (fax) image decoder.
ocr
tesseract
Package tesseract reads the text in an image by running the tesseract command, for use with gopdf's redaction.
Package tesseract reads the text in an image by running the tesseract command, for use with gopdf's redaction.

Jump to

Keyboard shortcuts

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