Skip to content

Search

extractor.Editor searches the text of chosen pages and reports where each match sits on the page. Use it when you need coordinates as well as the text: drawing a highlight or a rectangle over a phrase, or checking whether a document mentions something before deciding what to do with it. Plain text extraction is enough if all you want is the string.

Doing it

reader, _, err := model.NewPdfReaderFromFile("input.pdf", nil)
if err != nil {
    return err
}

editor := extractor.NewEditor(reader)

matchesPerPage, err := editor.Search("copyright law", []int{1, 2})
if err != nil {
    return err
}

for page, match := range matchesPerPage {
    for i, box := range match.Locations {
        fmt.Printf("page %d %v %v\n", page, match.Indexes[i], box.BBox)
    }
}

Search takes the page numbers to look at, one-based, and returns map[int]extractor.Match keyed by page number. A page with no match still gets an entry, with empty Indexes and Locations, so check the length rather than the presence of the key.

Match carries the pattern that was searched for, Indexes as [][]int pairs of start and end byte offsets, and Locations as one Box per match. Box holds a single field, BBox model.PdfRectangle, so the coordinates are box.BBox.Llx, Lly, Urx and Ury in PDF units with the origin at the bottom left of the page.

The pattern is compiled with Go’s regexp package, not matched literally. That gives you \d, alternation and character classes for free, and it means characters like ., ( and $ need escaping when you want them literally. Search returns an error if the pattern does not compile.

Limitations

Offsets in Indexes are byte offsets into the page’s mark text, which is the paragraph text produced by the default layout extraction mode. They are not offsets into the string you would get from ExtractText, and they are not rune indexes. Their only reliable use is as input to TextMarkArray.RangeOffset.

Matching runs one page at a time, so a phrase broken across a page boundary is never found. Within a page, the text follows the reading order layout mode worked out, and line breaks appear in it, so a pattern written as a single spaced phrase can miss text that wraps. Using \s+ in place of a literal space is the usual fix.

The Editor builds its own extractor internally and always uses the default extraction mode, so there is no option to search in plain or grid mode. That is deliberate: search depends on text marks, and only the layout modes produce marks whose byte offsets line up with the extracted text. See text extraction for what each mode does.

Run the example

search_text.go searches one pattern across a comma-separated page list and prints the offsets and boxes. printSearchResults is the formatting helper; the search itself is three calls in main.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/search-and-replace
go run search_text.go copyright 1,2 ./test-data/file1.pdf

If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.

Quote the pattern if it contains spaces or shell metacharacters:

go run search_text.go "copyright law" 1,2 ./test-data/file1.pdf
View the full source

Sample output

Page 1:
indexes: [292:305]
locations: {307.08 469.42 362.53 479.42}

Page 2:
indexes: [2459:2472], [2785:2798]
locations: {128.93 231.18 184.54 241.18}, {103.11 183.18 158.88 193.18}
Last updated on