pdf

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 23 Imported by: 0

README

fyne-pdf

CI Coverage Go Reference

A PDF viewer widget for Fyne, written in pure Go.

Pages are rendered with cera — no MuPDF, no cgo PDF library, no external binaries.

Features

  • Document widget: page view with thumbnail strip, zoom slider, zoom in/out, 100 % (physical size), fit page / fit width / fit height
  • Rendering in the background, the UI never waits for a page; the resolution of the main view follows the zoom level and the screen scale
  • Optional save button via SaveCallback, render errors via OnError
  • Source: goroutine-safe access to an opened PDF — page count, page bounds, rendering at arbitrary DPI, document metadata
  • NewImageFromMemory: render a single page to an image.Image without any UI
  • Render timeout per page (15 s); slow pages fall back to the partially drawn image instead of blocking the UI

Installation

go get github.com/timzifer/fyne-pdf

Requires Go 1.26+ and Fyne 2.6+ (uses fyne.Do). Fyne itself needs a C compiler and the platform graphics headers — see the Fyne prerequisites.

Usage

Viewer widget
package main

import (
	"os"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	pdf "github.com/timzifer/fyne-pdf"
)

func main() {
	a := app.New()
	w := a.NewWindow("PDF")

	contents, err := os.ReadFile("example.pdf")
	if err != nil {
		panic(err)
	}

	doc := pdf.NewDocument()
	defer doc.Close()
	if err := doc.LoadFromMemory(contents); err != nil {
		panic(err)
	}

	// doc.ShowPage(2) // jump to the third page

	w.SetContent(doc)
	w.Resize(fyne.NewSize(1024, 768))
	w.ShowAndRun()
}
Render a page to an image
img, err := pdf.NewImageFromMemory(contents, 0) // page index is 0-based
Low-level access
src, err := pdf.OpenSource(contents)
if err != nil {
	return err
}
defer src.Close()

fmt.Println(src.PageCount(), src.Metadata()["title"])
img, err := src.RenderPage(0, 300) // 300 dpi

Status

Usable, but young. The API may still change before v1.0.0. Not supported yet: text selection, search, links, annotations, scrolling through all pages continuously.

Contributing

Bug reports and pull requests are welcome — see CONTRIBUTING.md. For rendering bugs, please attach the PDF (or a minimal one reproducing the issue) if you can share it.

License

MIT

Documentation

Overview

Package pdf provides a PDF viewer widget for Fyne, written in pure Go.

Document is a ready-to-use viewer with a thumbnail strip and zoom controls. Source gives goroutine-safe low-level access to an opened PDF (page count, bounds, rendering, metadata), and NewImageFromMemory renders a single page without any UI.

Page indices are 0-based throughout the package.

Index

Examples

Constants

View Source
const ThumbnailMinimumDimension = float32(100)

ThumbnailMinimumDimension is the default width and height of a thumbnail.

Variables

This section is empty.

Functions

func NewImageFromMemory

func NewImageFromMemory(contents []byte, pageNumber int, documentCallbacks ...DocumentCallbackFn) (image.Image, error)

NewImageFromMemory renders page pageNumber (0-based) of the PDF in contents at 144 dpi. documentCallbacks are called with the opened Source before rendering.

Example
package main

import (
	"fmt"
	"os"

	pdf "github.com/timzifer/fyne-pdf"
)

func main() {
	contents, err := os.ReadFile("example.pdf")
	if err != nil {
		panic(err)
	}

	img, err := pdf.NewImageFromMemory(contents, 0)
	if err != nil {
		panic(err)
	}
	fmt.Println(img.Bounds().Size())
}

Types

type Document

type Document struct {
	widget.BaseWidget

	// BackgroundColor fills the area behind the page.
	BackgroundColor color.Color
	// SaveCallback, if set, shows a save button in the toolbar that calls
	// it. Call Refresh after changing it.
	SaveCallback func()
	// OnError is called on the main goroutine when a page cannot be
	// rendered. If nil, the error is logged.
	OnError func(page int, err error)
	// contains filtered or unexported fields
}

Document is a PDF viewer widget: a page view with a toolbar for zooming and a thumbnail strip for navigation. Create it with NewDocument.

Pages are rendered in the background; the resolution of the main view follows the zoom level. Methods must be called from the Fyne main goroutine (or before the app runs).

func NewDocument

func NewDocument() *Document

NewDocument creates an empty viewer. Load a PDF with Document.LoadFromMemory.

func (*Document) Close

func (d *Document) Close() error

Close releases the loaded PDF and stops pending renders. Pages already shown stay visible.

func (*Document) CreateRenderer

func (d *Document) CreateRenderer() fyne.WidgetRenderer

func (*Document) CurrentPage

func (d *Document) CurrentPage() int

CurrentPage returns the index (0-based) of the page shown in the main view.

func (*Document) LoadFromMemory

func (d *Document) LoadFromMemory(contents []byte) error

LoadFromMemory opens the PDF in contents and shows its first page. A previously loaded PDF is closed. Pages are rendered in the background.

func (*Document) PageCount

func (d *Document) PageCount() int

PageCount returns the number of pages of the loaded PDF.

func (*Document) Refresh

func (d *Document) Refresh()

func (*Document) Resize

func (d *Document) Resize(size fyne.Size)

Resize lays out the widget and applies a ZoomToFit that was requested before the widget had a size.

func (*Document) ShowPage

func (d *Document) ShowPage(page int)

ShowPage shows page (0-based) in the main view and zooms it to fit.

func (*Document) Zoom

func (d *Document) Zoom(scale float64)

Zoom sets the zoom factor; 1 shows the page at roughly its physical size. The factor is clamped to 0.2 to 4.

func (*Document) ZoomToFit

func (d *Document) ZoomToFit()

ZoomToFit scales the page so it fits completely into the view.

func (*Document) ZoomToFitHorizontal

func (d *Document) ZoomToFitHorizontal()

ZoomToFitHorizontal scales the page to the width of the view.

func (*Document) ZoomToFitVertical

func (d *Document) ZoomToFitVertical()

ZoomToFitVertical scales the page to the height of the view.

type DocumentCallbackFn

type DocumentCallbackFn func(src *Source)

DocumentCallbackFn is called with the opened Source before rendering, see NewImageFromMemory.

type Page

type Page struct {
	widget.BaseWidget

	Scale           float64
	BackgroundColor color.Color
	// contains filtered or unexported fields
}

Page displays a single rendered page image. Its minimum size is Page.PageSize multiplied by Scale; the image is scaled to fit, so it can be rendered at any resolution. The aspect ratio is kept.

func NewPage

func NewPage() *Page

NewPage creates an empty Page.

func NewPageWithImage

func NewPageWithImage(img image.Image) *Page

NewPageWithImage creates a Page showing img.

func (*Page) CreateRenderer

func (p *Page) CreateRenderer() fyne.WidgetRenderer

func (*Page) PageSize

func (p *Page) PageSize() fyne.Size

PageSize returns the size of the page at Scale 1. Unless set with Page.SetPageSize, this is the size of the page image in pixels.

func (*Page) SetImage

func (p *Page) SetImage(pageImage image.Image)

SetImage replaces the displayed page image.

func (*Page) SetPageSize

func (p *Page) SetPageSize(size fyne.Size)

SetPageSize sets the size of the page at Scale 1, independent of the resolution of the image.

type Source

type Source struct {
	// contains filtered or unexported fields
}

Source is an opened PDF. All methods are safe for concurrent use: cera only allows interpreting pages of a document from one goroutine at a time, hence the mutex. A single page is still rasterized by cera on all cores.

func OpenSource

func OpenSource(contents []byte) (*Source, error)

OpenSource parses the PDF in contents.

Example
package main

import (
	"fmt"
	"os"

	pdf "github.com/timzifer/fyne-pdf"
)

func main() {
	contents, err := os.ReadFile("example.pdf")
	if err != nil {
		panic(err)
	}

	src, err := pdf.OpenSource(contents)
	if err != nil {
		panic(err)
	}
	defer func() { _ = src.Close() }()

	fmt.Println("pages:", src.PageCount())
	fmt.Println("title:", src.Metadata()["title"])

	img, err := src.RenderPage(0, 300)
	if err != nil {
		panic(err)
	}
	fmt.Println("size:", img.Bounds().Size())
}

func (*Source) Bound

func (s *Source) Bound(page int) (image.Rectangle, error)

Bound returns the visible area of page (0-based) in points (72 dpi), with /Rotate and /UserUnit already applied.

func (*Source) Close

func (s *Source) Close() error

Close releases the document. Afterwards all methods return an error or zero pages. Closing more than once is allowed.

func (*Source) Metadata

func (s *Source) Metadata() map[string]string

Metadata returns the entries of the document info dictionary with lowercase keys ("title", "author", ...).

func (*Source) PageCount

func (s *Source) PageCount() int

PageCount returns the number of pages, or 0 after Close.

func (*Source) RenderPage

func (s *Source) RenderPage(page int, dpi float64) (image.Image, error)

RenderPage renders page (0-based) at the given resolution onto a white background. If rendering takes longer than 15 seconds, the partially drawn image is returned without an error.

func (*Source) RenderPageContext

func (s *Source) RenderPageContext(ctx context.Context, page int, dpi float64) (image.Image, error)

RenderPageContext is like Source.RenderPage, but stops early and returns ctx.Err() when ctx is cancelled.

type Thumbnail

type Thumbnail struct {
	widget.BaseWidget

	OnTapped func()
	// contains filtered or unexported fields
}

Thumbnail is a tappable page preview with a title below it.

func NewThumbnail

func NewThumbnail() *Thumbnail

NewThumbnail creates an empty Thumbnail.

func (*Thumbnail) CreateRenderer

func (t *Thumbnail) CreateRenderer() fyne.WidgetRenderer

func (*Thumbnail) Selected

func (t *Thumbnail) Selected() bool

Selected reports whether the thumbnail is highlighted.

func (*Thumbnail) SetImage

func (t *Thumbnail) SetImage(theImage image.Image)

SetImage sets the preview image.

func (*Thumbnail) SetMinimumSize

func (t *Thumbnail) SetMinimumSize(size fyne.Size)

SetMinimumSize sets the size of the preview image area.

func (*Thumbnail) SetSelected

func (t *Thumbnail) SetSelected(selected bool)

SetSelected highlights the thumbnail, e.g. for the page currently shown.

func (*Thumbnail) SetTitle

func (t *Thumbnail) SetTitle(title string)

SetTitle sets the label shown below the preview.

func (*Thumbnail) Tapped

func (t *Thumbnail) Tapped(_ *fyne.PointEvent)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL