Install the pdf library
masterTo use this library in your Go project, install it using go get:
go get -u github.com/ledongthuc/pdfrepository·master·Indexed 20 days ago
https://github.com/ledongthuc/pdfA Go library for reading and extracting content from PDF files. It supports plain text extraction, styled text extraction with font and positioning data, and spatial grouping of text by rows or columns. The library provides tools to access document outlines, page-specific content, and PDF metadata via a Value abstraction, with support for encrypted files and custom io.ReaderAt sources.
To use this library in your Go project, install it using go get:
go get -u github.com/ledongthuc/pdfA Value represents a single PDF data element (Integer, Real, String, Name, Dict, Array, Stream, etc.). Accessors on Value return a view of the data as the requested type.
Important Behavior: If an accessor is called on a Value of an incompatible Kind, it returns a zero result (e.g., Int64() returns 0 if the kind is not Integer). This allows for quick traversal without constant error checking, but mistakes may go unreported.
// Example of accessing different types
val := reader.Trailer()
if val.Kind() == pdf.Dict {
keys := val.Keys()
for _, k := range keys {
fmt.Println("Key:", k)
}
}
// Accessing a specific key
size := val.Key("Size").Int64()To process a PDF page-by-page and group text into rows, use the r.NumPage() and r.Page(index) methods. For each page, call p.GetTextByRow() to retrieve rows of text. Each row contains a Position and a Content slice of words.
package main
import (
"fmt"
"os"
"github.com/ledongthuc/pdf"
)
func main() {
content, err := readPdf(os.Args[1]) // Read local pdf file
if err != nil {
panic(err)
}
fmt.Println(content)
return
}
func readPdf(path string) (string, error) {
f, r, err := pdf.Open(path)
defer func() {
_ = f.Close()
}()
if err != nil {
return "", err
}
totalPage := r.NumPage()
for pageIndex := 1; pageIndex <= totalPage; pageIndex++ {
p := r.Page(pageIndex)
if p.V.IsNull() || p.V.Key("Contents").Kind() == pdf.Null {
continue
}
rows, _ := p.GetTextByRow()
for _, row := range rows {
println(">>>> row: ", row.Position)
for _, word := range row.Content {
fmt.Println(word.S)
}
}
}
return "", nil
}To extract text along with its metadata (font name, font size, and coordinates), use the r.GetStyledTexts() method. This returns a slice of objects containing the text string (S) and its styling properties.
package main
import (
"fmt"
"github.com/ledongthuc/pdf"
)
func main() {
f, r, err := pdf.Open("./pdf_test.pdf")
if err != nil {
panic(err)
}
defer f.Close()
sentences, err := r.GetStyledTexts()
if err != nil {
panic(err)
}
// Print all sentences
for _, sentence := range sentences {
fmt.Printf("Font: %s, Font-size: %f, x: %f, y: %f, content: %s \n",
sentence.Font,
sentence.FontSize,
sentence.X,
sentence.Y,
sentence.S)
}
}Use pdf.Open(path) to open a file and retrieve a reader. Call r.GetPlainText() to extract the raw text content without any formatting or font information. Note that GetPlainText() returns a bytes.Reader, so you may need to use a buffer to convert it to a string.
package main
import (
"bytes"
"fmt"
"github.com/ledongthuc/pdf"
)
func main() {
pdf.DebugOn = true
f, r, err := pdf.Open("./pdf_test.pdf")
if err != nil {
panic(err)
}
defer f.Close()
var buf bytes.Buffer
b, err := r.GetPlainText()
if err != nil {
panic(err)
}
buf.ReadFrom(b)
content := buf.String()
fmt.Println(content)
}You can extract all text from a PDF file as a single io.Reader using the GetPlainText() method on the Reader instance. This method iterates through all pages and aggregates the text.
Alternatively, you can extract plain text from a specific Page using GetPlainText(fonts), where fonts is a map of font names to *Font pointers (passing a map can improve performance by avoiding repeated parsing).
// Extract all text from the entire document
reader, err := r.GetPlainText()
if err != nil {
// handle error
}
// Use reader to get the string contentThe Outline() method on the Reader returns a tree structure representing the document's outline (bookmarks/Table of Contents). The returned Outline struct is the root of the tree; its Child slice contains the top-level entries.
outline := r.Outline()
// Recursively traverse outline.Child to read the TOCUse NumPage() on the Reader to get the total count of pages in the PDF document.
count := r.NumPage()
fmt.Printf("Total pages: %d\n", count)Use Open(file string) to open a PDF file from the filesystem. This function returns the underlying *os.File (which you are responsible for closing) and a *Reader instance for interacting with the PDF content.
file, reader, err := pdf.Open("example.pdf")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Use reader to access PDF contentIf a Value has the Stream kind, you can use its Reader() method to get an io.ReadCloser that provides access to the raw (or decrypted/decompressed) data within that stream.
// Assuming 'streamVal' is a Value of kind pdf.Stream
rc := streamVal.Reader()
if rc == nil {
// Handle error: stream not present
}
defer rc.Close()
data, err := io.ReadAll(rc)The Content() method on a Page returns a Content struct containing all text elements (Text) and all drawn rectangles (Rect) on that page. This is useful for low-level analysis of the page's visual elements.
content := p.Content()
for _, t := range content.Text {
fmt.Println(t.S)
}
for _, rect := range content.Rect {
fmt.Printf("Rect: Min(%f,%f) Max(%f,%f)\n", rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y)
}Use NewReader(f io.ReaderAt, size int64) to create a new *Reader from any source implementing io.ReaderAt. This is useful for reading PDFs from memory or other custom storage abstractions.
// Assuming 'data' is a []byte containing PDF content
reader, err := pdf.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
log.Fatal(err)
}