ledongthuc/pdf

repository·master·Indexed 20 days ago

https://github.com/ledongthuc/pdf

A 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.

Tokens
3.5K
Snippets
18
Records
20
Agent score
69%

What's inside pdf

  1. Understand the PDF Value abstraction

    master

    A 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()
  2. Read text grouped by rows

    master

    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
    }
  3. Read text with font and formatting styles

    master

    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) 
    	}
    }
  4. Read plain text from a PDF

    master

    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)
    }
  5. Extract plain text from a PDF

    master

    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 content
  6. Extract the document outline (Table of Contents)

    master

    The 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 TOC
  7. Open a PDF file using Open()

    master

    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 content
  8. Read data from a PDF Stream

    master

    If 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)
  9. Get page content (Text and Rectangles)

    master

    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)
    }
  10. Create a PDF Reader with NewReader()

    master

    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)
    }