zc310/ofd (OFD Go Toolkit)
| Language | Go |
|---|---|
| License | Apache-2.0 |
| Repository | github.com/zc310/ofd |
| Documentation | pkg.go.dev: converter, pkg.go.dev: reader, Go API guide (Chinese) |
| Depends on | pure Go; optional external tools (LibreOffice, Chrome) only for Office, HTML, EML and SVG import |
| Read / write | yes / yes |
zc310/ofd parses, renders, converts, creates, validates, signs and encrypts OFD documents in pure Go. The library is the base of the project’s command-line tools (ofd-converter, ofd-creator, ofd-validator, ofd-invoice and others), a desktop and Android reader, and a WebAssembly reader that runs in the browser.
The main entry points:
pkg/converter: one-call conversion between OFD and PDF, images, text, HTML, Markdown and DOCX. Input can be a file path, bytes or anio.Reader.pkg/reader: page-by-page access for viewers and data extraction (text runs in millimeters, search, render, attachments, signatures).pkg/creator,pkg/sign,pkg/crypt: create OFD from JSON/YAML or code, sign with SM2/SM3 seals, GM/T 0099 encryption.
The project is new (first release in 2026) and at version 0.x; comments and docs are in Chinese.
Installation#
go get github.com/zc310/ofd@latest
Basic usage: convert OFD to PDF#
package main
import (
"context"
"log"
"os"
"github.com/zc310/ofd/pkg/converter"
)
func main() {
out, err := os.Create("invoice.pdf")
if err != nil {
log.Fatal(err)
}
err = converter.PDF(context.Background(), "invoice.ofd", out)
if cerr := out.Close(); err == nil {
err = cerr
}
if err != nil {
log.Fatal(err)
}
}
The first argument is a context.Context: pass one with a timeout to limit work on untrusted files. converter.Page(n) limits output to page n (1-based).
How to extract text from an OFD file#
converter.Text writes plain text, one text object per line, with a form feed between pages:
err := converter.Text(context.Background(), "invoice.ofd", os.Stdout)
For positions, use the reader package. Page indexes start at 0, and coordinates are in millimeters from the top-left corner:
package main
import (
"fmt"
"log"
"github.com/zc310/ofd/pkg/reader"
)
func main() {
r, err := reader.Open("invoice.ofd") // path, []byte or io.Reader
if err != nil {
log.Fatal(err)
}
defer r.Close()
fmt.Println(r.PageCount(), "pages")
runs, err := r.Text(0)
if err != nil {
log.Fatal(err)
}
for _, t := range runs {
fmt.Printf("%6.1f %6.1f %s\n", t.X, t.Y, t.Text)
}
}
How to convert OFD pages to PNG#
converter.Image calls your function to get a writer for each page:
err := converter.Image(context.Background(), "invoice.ofd",
converter.PNG(),
converter.DPI(150),
converter.BgColor(color.White),
converter.Writer(func(page int) (io.WriteCloser, error) {
return os.Create(fmt.Sprintf("page-%d.png", page))
}),
)
converter.JPG(), converter.SVG() and converter.WithFormat("tiff") select other formats. To render a single page into memory, use reader.RenderPage with reader.RenderOptions{DPI: 144, Format: reader.RenderPNG}.
How to read metadata, page size and attachments#
r, err := reader.Open("invoice.ofd")
if err != nil {
log.Fatal(err)
}
defer r.Close()
info, err := r.Info()
if err == nil {
fmt.Println(info.Title, info.Author, info.Creator, info.CreationDate)
}
if p, err := r.Page(0); err == nil {
fmt.Printf("%.0f x %.0f mm\n", p.Width, p.Height)
}
atts, _ := r.Attachments()
for _, a := range atts {
data, err := r.AttachmentData(a.Scope, a.ID, 0) // 0 = default size limit
if err == nil {
fmt.Println(a.Name, len(data), "bytes") // e-invoices attach their XML data here
}
}
How to convert PDF to OFD#
Importers are registered by a blank import; Convert then works between any registered formats:
package main
import (
"context"
"log"
"os"
"github.com/zc310/ofd/pkg/converter"
_ "github.com/zc310/ofd/pkg/converter/import/pdf"
)
func main() {
out, err := os.Create("out.ofd")
if err != nil {
log.Fatal(err)
}
err = converter.Convert(context.Background(), "pdf", "ofd", "in.pdf", out)
if cerr := out.Close(); err == nil {
err = cerr
}
if err != nil {
log.Fatal(err)
}
}
Other importers: import/image, import/markdown, import/csv, import/svg, import/xps, import/office (needs LibreOffice).
Tips#
- The command-line tools need no Go code:
ofd-converter input.ofd output.pdf,ofd-converter --page 1 --dpi 150 input.ofd page1.png,ofd-validator --format text input.ofd. ofd-invoiceextracts the fields of Chinese VAT e-invoices and railway e-tickets to JSON, CSV or XLSX.- Pages render with fallback fonts when the OFD doesn’t embed its fonts;
reader.RegisterFallbackFontadds your own.