Skip to content
How to implement text search and replace

How to implement text search and replace

Both go through extractor.Editor, built from a reader. Search returns the byte offsets and bounding boxes of each match; Replace rewrites the string operands of the content stream operators behind them.

editor := extractor.NewEditor(reader)

if err := editor.Replace("Australia", "America", []int{1, 2}); err != nil {
    return err
}

return editor.WriteToFile("output.pdf")

Patterns are Go regular expressions and page numbers are one-based. Nothing reaches disk until WriteToFile, so call Replace as many times as you need first.

Because it edits the existing operators rather than laying the text out again, the page keeps its original fonts and glyph positions. That also sets the limits. A replacement longer than the pattern is cut off at the pattern’s length, and a shorter one leaves the trailing glyphs of the match empty, so match the length where the wording allows it. Positions are not recalculated either: wider glyphs run into the text after them, narrower ones leave a gap.

The replacement is re-encoded with the font the matched text already used. A character that font cannot encode is dropped with a debug-level log message and no error, which is the usual reason a replacement comes back missing letters. A subset-embedded font carries only the glyphs the original document used, so replacing “cat” with “dog” can fail on the “g” alone.

Matching runs one page at a time, so a phrase spanning a page break is never found, and line breaks appear in the extracted text, so a pattern written as one spaced phrase can miss text that wraps. \s+ in place of a literal space is the usual fix. Text drawn as vector outlines, text inside an image, and text in annotations or form field appearances are all outside what the editor sees.

Replacing is not redacting. The matched string is overwritten, but nothing else in the file is examined, so a copy of the text elsewhere in the document survives. Use redaction when removal is the point.

Detail in search and replace. If the text is stored plainly enough that a string comparison would find it, the content stream approach in simple search and replace is an alternative that skips extraction entirely.

Last updated on