Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libreofficekit

A pure-Go binding for LibreOfficeKit — the C API of LibreOffice — built on goexlib/cgo + purego to call liblibreofficekit.so dynamically. No cgo, no CGO_ENABLED.

Go Reference Go version License

Features

  • Pure Go: builds to a static binary; only requires a LibreOffice installation at runtime
  • Covers the main LOKit API surface: document loading, tiled rendering, PDF/PNG export, text/graphic selection, clipboard, commands (PostUnoCommand), multi-view, event callbacks, and more
  • Native event callback support (RegisterCallback) with a full CallbackType enum and String() descriptions
  • No cgo: cross-compiles anywhere, with runtime dynamic loading of the shared library

Requirements

  • Go >= 1.24
  • LibreOffice 7.x (developed and verified against Linux with LibreOffice 7.3), standard install path /usr/lib/libreoffice/program, overridable via NewOffice(installPath)

Installation

go get github.com/docgo/libreofficekit

Usage

Quick start

Load a document and render the first page to PNG:

package main

import (
	"fmt"
	"image"
	"image/png"
	"os"

	"github.com/docgo/libreofficekit"
)

func main() {
	office, err := libreofficekit.NewOffice("/usr/lib/libreoffice/program")
	if err != nil {
		panic(err)
	}
	defer office.Destroy()

	doc, err := office.DocumentLoad("file:///path/to/doc.docx")
	if err != nil {
		panic(err)
	}
	defer doc.Destroy()

	doc.InitializeForRendering("")
	w, h := doc.GetDocumentSize() // twips
	buf := make([]byte, w*h*4)
	doc.PaintTile(buf, int(w), int(h), 0, 0, int(w), int(h)) // 4 bytes per pixel
	if doc.GetTileMode() == libreofficekit.TILEMODE_BGRA {
		// PaintTile returns BGRA on most builds; convert to RGBA.
		for i := 0; i < len(buf); i += 4 {
			buf[i], buf[i+2] = buf[i+2], buf[i]
		}
	}

	img := image.NewRGBA(image.Rect(0, 0, int(w), int(h)))
	copy(img.Pix, buf)
	f, _ := os.Create("page.png")
	defer f.Close()
	png.Encode(f, img)
	fmt.Println("rendered", w, "x", h)
}

Run it (important — see Concurrency model below):

GODEBUG=asyncpreemptoff=1 go run main.go

A complete runnable example lives in test/main.go (loads test/test.pptx and renders the first page).

Receiving events

Register a callback to be notified about document events (selection changes, tile invalidations, state changes, ...):

doc.RegisterCallback(func(typ libreofficekit.CallbackType, payload string, viewID int) {
	switch typ {
	case libreofficekit.CALLBACK_INVALIDATE_TILES:
		// re-render the affected area
	case libreofficekit.CALLBACK_TEXT_SELECTION:
		// payload is the selection rectangle list in JSON
	}
})

Never call any LOKit API from inside a callback — see Concurrency model.

Documentation

The full API is documented on pkg.go.dev. In short:

Office

Method Description
NewOffice(installPath) Initialize LOKit (once per process)
DocumentLoad(url) / DocumentLoadWithOptions(url, options) Load a document (returns Document, see GetError on failure)
GetVersionInfo() / GetFilterTypes() Version and filter information
RegisterCallback(cb) Register global callbacks (status indicator, password prompt, ...)
SetOptionalFeatures / SetOption / SetDocumentPassword Feature flags and options
RunMacro / SignDocument / RunLoop(poll, wake) Macros, signing, event-loop mode
GetError() Retrieve the last underlying error

Document

  • Parts/rendering: GetParts, SetPart/GetPart, GetPartName, GetPartPageRectangles, GetDocumentSize, SetClientZoom, PaintTile, PaintPartTile, PaintWindow(DPI), RenderFont, RenderShapeSelection
  • Export: SaveAs(url, format, filterOptions) (e.g. "pdf")
  • Selection/text: SetTextSelection, GetTextSelection(mimeType), ResetSelection, SetGraphicSelection, GetSelectionType, Paste(mimeType, data)
  • Clipboard: GetClipboard(requested []string) / SetClipboard(data) (ClipboardData{MimeType, Data})
  • Input/commands: PostKeyEvent, PostMouseEvent, PostUnoCommand(command, args, notify), GetCommandValues
  • Views: CreateView(WithOptions), SetView, DestroyView, GetViewsCount, SetViewLanguage, SelectPart, MoveSelectedParts
  • Signatures/certificates: GetSignatureState, InsertCertificate, AddCertificate
  • Callbacks: RegisterCallback (per document)

Callback payloads are JSON/text produced by LibreOffice, one per CallbackType (see enums.go).

Concurrency model (measured)

The LibreOfficeKit C API is not thread-safe, and every call is serialized by LibreOffice's global SolarMutex. Measured on LO 7.3:

  • GODEBUG=asyncpreemptoff=1 is mandatory: LibreOffice installs its own signal handlers without SA_ONSTACK; Go 1.24's preemption signal (SIGURG) then aborts with non-Go code set up signal handler without SA_ONSTACK. This applies to every usage, with or without callbacks.
  • Concurrent calls from multiple goroutines in one process (concurrent DocumentLoad, operating on different Document objects) are safe (without callbacks), but give no speedup: SolarMutex serializes everything (measured slightly slower than sequential).
  • Callbacks fire on LibreOffice-internal threads (foreign threads, not Go goroutines): keep handlers to enqueueing/locked state updates only, and never call LOKit APIs from them (the callback thread may hold SolarMutex — re-entrancy deadlocks).
  • True parallelism requires multiple processes: one Office per process (use capi.LokInit2(installPath, userProfileURL) with a distinct user profile per process to avoid profile-lock contention), kept warm in a process pool to amortize the ~400 ms Office cold start.
  • Recommended single-process pattern: one goroutine driving the whole Office serially (see the verify/main.go harness).

Testing

Integration verification

verify/ contains an end-to-end integration check: 24 samples (docx/xlsx/pptx) compared against an independent oracle covering text selection, rendering, export and clipboard round-trips:

go build -o out/verify ./verify
GODEBUG=asyncpreemptoff=1 ./out/verify
python3 verify/compare.py

See verify/README.md for details. Known LO 7.3 limitations (not binding bugs): comment callbacks are never emitted, Impress has no text selection, Calc multi-sheet text selection does not switch sheets, headless clipboard read-back returns nothing.

Compatibility notes

  • Requires github.com/goexlib/cgo >= v0.1.0 (in v0.0.9 CString returned Go heap memory and crashed on Free; fixed in v0.1.0)
  • LO 7.3 crashes when a second view is created on a second document (LO bug); prefer one view per document

Contributing

Contributions are welcome. Please:

  1. Run gofmt -l . and go build ./... before submitting.
  2. Keep the API style consistent with the existing binding (see document.go/office.go for the doc-comment convention).

License

MIT

About

Pure golang libreofficekit binding. nocgo!

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages