Help us shape the future of Go! Take the 2026 Go Developer Survey

goprint

package module
v0.6.0 Latest Latest
Warning

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

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

README

goprint

CI Fuzz Coverage Go Reference

Platform-independent printing for Go: one API for Linux/BSD, macOS and Windows, headless or through the native print dialog.

  • Pure Go – builds with CGO_ENABLED=0 for every GOOS/GOARCH from any host. Native APIs are reached through golang.org/x/sys/windows, purego and plain protocols (IPP, D-Bus).
  • PDF in, paper out – documents are PDF (or images, wrapped into a PDF). CUPS, PDFKit and Windows.Data.Pdf render them natively.
  • Native dialogs, fully preset – printer, copies, page ranges, media, orientation, duplex, color, collation – and the user's choice comes back as structured Settings. On Windows the modern dialog with live preview is supported.

Status: 0.x preview releases; the API may still change before v1.0.

Feature Linux/BSD macOS Windows
Printers, GetCapabilities ✓ (CUPS) ✓ (CUPS) ✓
Print (headless) ✓ all settings via IPP ✓ all settings via IPP ✓ amd64/386/arm64, all settings via DEVMODE/PrintTicket
IPP Everywhere printer by URI (Printer: "ipp://…") ✓ ✓ ✓
Dialog ✓ desktop dialog via xdg-desktop-portal (presets + result; no job tracking) ✓ print panel with PDFKit preview; bare panel (no preview) for PrintNow == false ✓ modern dialog with live preview and page selection; classic PrintDlgEx for settings-only use

Unimplemented parts return ErrUnsupported / ErrNoDialog.

Install

go get github.com/timzifer/goprint
go get github.com/timzifer/goprint/fyneprint   # Fyne apps only

Usage

doc := goprint.PDFFile("invoice.pdf") // or PDFBytes, or Document{PDF: …} / {Images: …}

// Headless
job, err := goprint.Print(ctx, doc, goprint.Settings{
	Copies: 2,
	Media:  goprint.MediaA4,
	Duplex: goprint.DuplexLongEdge,
})
if err != nil {
	return err
}
for _, w := range job.Warnings() {
	log.Println("not honored:", w)
}
err = job.Wait(ctx)

// Interactive
job, chosen, err := goprint.Dialog(ctx, doc, goprint.DialogOptions{
	Settings: goprint.Settings{Copies: 2},
	PrintNow: true,
})
if errors.Is(err, goprint.ErrCanceled) {
	// user canceled
}

// Settings only ("page setup"), print later without UI
_, chosen, err = goprint.Dialog(ctx, doc, goprint.DialogOptions{})
job, err = goprint.Print(ctx, doc, chosen)

Settings a printer cannot honor are reported by job.Warnings(); with Settings.Strict they fail the call with ErrUnsupported instead. Errors wrap sentinels (ErrCanceled, ErrPrinterNotFound, ErrNoDialog, …) for errors.Is. More examples are in the package documentation.

On Linux/BSD, Dialog shows the desktop's print dialog through xdg-desktop-portal (org.freedesktop.portal.Print), so it also works in Flatpak and Snap. Whether a preview is shown and whether presets such as the printer are honored is up to the portal implementation; with RequirePrinter, goprint refuses to print if the dialog returns another printer. Jobs printed through the portal cannot be tracked or canceled. Without a running portal, Dialog returns ErrNoDialog.

On Windows, the modern print dialog cannot preselect a printer. Windows 11 also shows the classic dialog (PrintDlgEx) as its modern dialog and then ignores the preselection, so DialogOptions.RequirePrinter returns ErrUnsupported there. StyleAuto uses the modern dialog (with preview) for printing and the classic one for PrintNow == false: only the classic dialog reports the chosen printer, and it creates no job, so print-to-file printers never ask for a file name. It has no preview (see #18).

DialogOptions.NoFileOutput keeps dialogs from writing files: printers that write files ("Microsoft Print to PDF", CUPS-PDF, GTK's "Print to File"; Printer.ToFile) and "Save as PDF" or "Open in Preview" on macOS. None of the platform dialogs can hide them all, so choosing one returns goprint.ErrFileOutput before anything is printed or written. On Windows this uses the classic dialog (StyleModern returns ErrUnsupported); on macOS the panel then shows no preview.

On Windows, Settings.Vendor[goprint.VendorOutputFile] writes the printer output to a file instead of the device, e.g. to get a PDF from "Microsoft Print to PDF" without its save dialog.

On Windows, goprint.PrinterProperties opens the printer driver's own settings dialog ("Printing preferences") for what Settings has no field for: finishing, stapling, secure print and so on. It returns the settings with the user's choices; the driver's part travels as Settings.Vendor[goprint.VendorDevMode] (a base64 DEVMODE) to Print, which applies the other settings on top. The classic dialog returns it too. Apps can store it to reuse the choice later, but only for the same printer.

Providers

The package-level functions print through goprint.Default, a Client with only the System() provider (CUPS, the Windows spooler, IPP URIs). A Client can combine further providers that implement goprint.Provider, such as the simulated printers of goprint/virtualprinter. Printer.Provider tells where a printer comes from; Settings.Provider selects the provider that prints (empty: the system).

c := goprint.NewClient(goprint.System(), myProvider) // myProvider.Name() == "virtual"
printers, err := c.Printers(ctx) // all providers; partial results if some fail
job, err := c.Print(ctx, doc, goprint.Settings{Provider: "virtual", Printer: "Label-62"})

Programs may also replace goprint.Default before they print. Providers create their jobs with goprint.NewJob; implementing DialogProvider or PropertiesProvider adds a dialog or a driver dialog.

goprint.IPPEverywhere(goprint.IPPEverywhereOptions{}) finds the IPP printers on the local network through DNS-SD (multicast DNS) and prints to them directly: no driver and no print server, on every platform, also on Linux without CUPS. Printers are named by their DNS-SD instance name. PDF goes to printers that accept it as it is. Many printers only take raster formats (PWG Raster, Apple Raster/URF); for them the separate module goprint/raster renders the PDF with cera, choosing format, resolution and color space from the printer's attributes. Without it, such printers return ErrUnsupported.

ipp := goprint.IPPEverywhere(goprint.IPPEverywhereOptions{Rasterizer: raster.New()})
goprint.Default = goprint.NewClient(goprint.System(), ipp)
// Printers lists them with Provider "ipp"

Two-sided raster jobs need printers that take back sides as they come (pwg-raster-document-sheet-back "normal", URF "DM1"); others print one-sided with a warning for now.

goprint/virtualprinter simulates printers without hardware, for tests, demos and development: jobs are kept in memory as PDF, settings a printer cannot honor become warnings, and failures are scripted (offline printers, failing or held jobs, latency). Presets: Office, Label, Photo, Receipt, PDFWriter.

vp := virtualprinter.New("virtual", virtualprinter.Office("Office"), virtualprinter.Label("Label-62"))
goprint.Default = goprint.NewClient(goprint.System(), vp)
// ... print with Settings{Provider: "virtual", Printer: "Label-62"}
pdf := vp.Jobs()[0].PDF

vp.Listen(":8631") serves the same printers over IPP (PDF only), so other processes and devices can print to them: goprint by printer URI (srv.PrinterURI("Office")), or CUPS with lpadmin -p virtual -E -v ipp://host:8631/printers/Office -m everywhere. srv.Advertise() also announces them with DNS-SD as IPP Everywhere printers, so goprint.IPPEverywhere, CUPS and other devices find them on the network.

Document.Attributes carries values for whoever receives the job, e.g. a virtual printer that files documents in a DMS:

doc := goprint.PDFFile("invoice.pdf")
doc.Attributes = map[string]string{"customer": "4711", "type": "invoice"}
// in-process or over IPP: vp.Jobs()[0].Attributes["customer"] == "4711"

They reach providers in the same process and IPP printers that list the job attribute goprint-attributes as supported (a virtualprinter server does). Spoolers, native dialogs and other printers do not pass them on; that is reported as an Attributes warning (an error with Strict).

macOS: main thread

AppKit runs only on the main thread. Lock the main goroutine to it in an init of package main and run your program through goprint.RunMain; Dialog may then be called from any goroutine inside it (or directly from the main goroutine). Called from another thread without RunMain, Dialog returns ErrWrongThread. Programs with their own Cocoa event loop call Dialog on that thread. RunMain just calls f on other platforms.

func init() { runtime.LockOSThread() }

func main() {
	goprint.RunMain(func() {
		job, chosen, err := goprint.Dialog(ctx, doc, goprint.DialogOptions{PrintNow: true})
		// ...
	})
}

The panel is app-modal (DialogOptions.Owner is not used for a sheet yet) and takes a single page range. A job printed from the panel is found in CUPS by name; if the user saves a PDF or opens Preview instead, the returned Job cannot be tracked and reports completed.

Platforms

Platform Headless Dialog
Linux/BSD IPP to CUPS (no libcups) xdg-desktop-portal (org.freedesktop.portal.Print)
macOS IPP to CUPS NSPrintOperation via purego
Windows PDF → Direct2D → XPS → spooler Modern (PrintManager, preview) or classic (PrintDlgEx)
any IPP Everywhere printers by URI, or found via DNS-SD (IPPEverywhere) –
iOS to a printer URL picked in the dialog (printToPrinter:) UIKit print sheet (AirPrint), or the printer picker without PrintNow
Android to printer URLs over IPP (ipp://…) PrintManager print dialog (needs cgo and SetAndroidRunner; fyneprint sets it)

Packages

Package Purpose
goprint Public API, types, errors
goprint/ipp IPP codec and client (RFC 8010/8011), usable standalone
goprint/ipp/ipptest IPP mock server for tests
goprint/raster PDF → PWG Raster / Apple Raster for IPPEverywhere (separate module, uses cera)
goprint/virtualprinter Simulated printers as a Provider: jobs kept in memory as PDF, scripted failures
goprint/fyneprint Fyne integration (separate module)

Fyne

Fyne's event loop owns the main thread, so a Fyne app uses fyneprint instead of goprint.RunMain: importing it lets goprint reach the main thread through fyne.DoAndWait, and ShowDialog runs the dialog owned by a Fyne window without blocking the UI:

fyneprint.ShowDialog(w, doc, goprint.DialogOptions{PrintNow: true},
	func(job *goprint.Job, s goprint.Settings, err error) { /* on the UI goroutine */ })

Other toolkits that own the main thread install their own runner with goprint.SetMainThreadRunner.

Example

examples/printdemo lists printers, shows capabilities, opens the print dialog and prints headless; its README has a manual test checklist for macOS.

Development

go test -race ./...
./.github/scripts/fuzz.sh 30s          # all fuzz targets
GOOS=windows go vet ./...             # vet another platform

CI runs tests on Linux, macOS and Windows, go vet, staticcheck, govulncheck, a cross-compile matrix with CGO_ENABLED=0 and nightly fuzzing.

Changes to main go through pull requests; the ci-ok check must be green.

License

MIT

Documentation

Overview

Package goprint prints documents on Linux/BSD, macOS and Windows through one API, either headless or through the platform's native print dialog.

The package is pure Go: it builds with CGO_ENABLED=0 for every supported GOOS/GOARCH from any host. Documents are handled as PDF internally; raster sources ([]image.Image) are wrapped into an image-only PDF so that there is a single print path.

Overview

The API has three verbs:

A Document is a PDF (PDFFile, PDFBytes or any reader) or a list of images. Settings flows both ways: into Dialog as presets, out of it as the user's choice, which can be passed to Print later.

job, err := goprint.Print(ctx, goprint.PDFFile("invoice.pdf"), goprint.Settings{
	Copies: 2,
	Media:  goprint.MediaA4,
	Duplex: goprint.DuplexLongEdge,
})
if err != nil {
	return err
}
return job.Wait(ctx)

Settings and warnings

Zero values in Settings mean "printer default". Settings a printer or platform cannot honor do not fail the job; they are reported by Job.Warnings. With Settings.Strict they are errors wrapping ErrUnsupported instead, and nothing is printed. Settings.Vendor passes platform-specific values through (IPP attributes on Linux/BSD and macOS, see also VendorOutputFile, VendorGTKPrefix and VendorDevMode).

On Windows, PrinterProperties opens the driver's own settings dialog for what Settings has no field for (finishing, stapling, secure print, ...); the result travels to Print in Settings.Vendor[VendorDevMode].

Providers

The package-level functions print through Default, a Client with only the System provider. A Client can combine further providers, such as the simulated printers of package virtualprinter; Printer.Provider tells where a printer comes from and Settings.Provider selects the provider that prints. IPPEverywhere is a provider for the IPP printers on the local network, found through DNS-SD; a Rasterizer (module goprint/raster) renders for those that accept no PDF. A Provider returns its jobs through NewJob; DialogProvider and PropertiesProvider add a dialog and a driver dialog.

Document.Attributes carry values for whoever receives a job (e.g. a virtual printer filing documents); they reach providers in the same process and IPP printers that take the IPPAttributes job attribute.

Errors

Errors wrap the sentinel errors (ErrCanceled, ErrPrinterNotFound, ErrUnsupported, ...) and are tested with errors.Is. A canceled dialog is ErrCanceled, not a failure.

Platforms

Linux/BSD and macOS print through CUPS over IPP (no libcups); printers given by URI ("ipp://…", "ipps://…") are reached directly on every platform, including Windows. Windows renders the PDF with Windows.Data.Pdf and Direct2D into an XPS job for the spooler.

Dialogs: the desktop's dialog through xdg-desktop-portal on Linux/BSD, NSPrintOperation with a PDFKit preview on macOS, and on Windows the modern dialog with live preview or the classic PrintDlgEx (see DialogStyle and DialogOptions.PrintNow).

On iOS the dialog is UIKit's print sheet (AirPrint); without PrintNow it only picks a printer. iOS lists no printers to apps: Print reaches a printer by the URL the dialog returned. Dialog and Print wait for UIKit's main thread and must be called from another goroutine. On Android the dialog is the system's print dialog (PrintManager); it needs cgo and a runner that lends goprint the Java VM and the Activity (SetAndroidRunner; fyneprint installs one). Android lists no printers to apps; Print reaches only printer URLs, over IPP.

Main thread (macOS)

AppKit, and so the print panel, runs only on the process's main thread. Command-line style programs run through RunMain; programs whose main thread belongs to a GUI toolkit install a runner with SetMainThreadRunner (package github.com/timzifer/goprint/fyneprint does that for Fyne). Other platforms need neither.

Index

Examples

Constants

View Source
const (
	// FormatPWGRaster is PWG Raster (PWG 5102.4), required by IPP
	// Everywhere.
	FormatPWGRaster = "image/pwg-raster"
	// FormatURF is Apple Raster, required by AirPrint.
	FormatURF = "image/urf"
)

Raster document formats of IPP printers.

View Source
const (
	// VendorOutputFile (Windows): the printer output is written to this file
	// instead of the device (print to file). With "Microsoft Print to PDF"
	// this yields a PDF without the save dialog.
	VendorOutputFile = "windows:output-file"

	// VendorGTKPrefix (Linux/BSD dialog) marks keys passed to the print
	// portal as GTK print settings, e.g. Vendor["gtk:output-bin"] = "top".
	// On the IPP path, keys without a namespace are sent as IPP attributes.
	VendorGTKPrefix = "gtk:"

	// VendorDevMode (Windows) is the printer's DEVMODE in standard base64,
	// as returned by [PrinterProperties] and the classic dialog. It carries
	// what the driver's own dialog offers (finishing, stapling, secure
	// print, ...). [Print] starts from it instead of the printer's defaults
	// and applies the other settings on top. It only applies to the printer
	// it was made for; on another printer it is ignored with a warning.
	VendorDevMode = "windows:devmode"
)

Settings.Vendor keys with platform-specific meaning. They are defined on every platform so that portable code compiles everywhere; backends that do not know a key report it as a warning.

View Source
const IPPAttributes = "goprint-attributes"

IPPAttributes is the IPP job attribute that carries Document.Attributes: a 1setOf text with "key=value" values. goprint sends it only to printers whose job-creation-attributes-supported lists it.

Variables

View Source
var (
	// ErrCanceled: the user canceled the dialog, or the job was canceled.
	ErrCanceled = errdefs.ErrCanceled
	// ErrNoPrinter: no printer is installed, or there is no default printer.
	ErrNoPrinter = errdefs.ErrNoPrinter
	// ErrPrinterNotFound: the named printer does not exist.
	ErrPrinterNotFound = errdefs.ErrPrinterNotFound
	// ErrUnsupported: the platform, printer or dialog cannot do what was
	// asked, e.g. a setting under Settings.Strict.
	ErrUnsupported = errdefs.ErrUnsupported
	// ErrNoDialog: no print dialog is available, e.g. no xdg-desktop-portal
	// or no session bus on Linux.
	ErrNoDialog = errdefs.ErrNoDialog
	// ErrWrongThread: Dialog on macOS was called off the main thread without
	// RunMain or a runner from SetMainThreadRunner.
	ErrWrongThread = errdefs.ErrWrongThread
	// ErrBusy: the printer or server does not accept jobs right now; a later
	// retry may succeed.
	ErrBusy = errdefs.ErrBusy
	// ErrInvalid: invalid arguments, e.g. a Document with neither PDF nor
	// Images, or negative Copies.
	ErrInvalid = errdefs.ErrInvalid
	// ErrFileOutput: with DialogOptions.NoFileOutput, the user chose a
	// printer that writes files, or "Save as PDF" on macOS. Nothing was
	// printed or written.
	ErrFileOutput = errdefs.ErrFileOutput
)

Sentinel errors. Detailed errors wrap them and are reachable via errors.Is.

View Source
var (
	MediaA3     = Media{"iso_a3_297x420mm", 297000, 420000}
	MediaA4     = Media{"iso_a4_210x297mm", 210000, 297000}
	MediaA5     = Media{"iso_a5_148x210mm", 148000, 210000}
	MediaLetter = Media{"na_letter_8.5x11in", 215900, 279400}
	MediaLegal  = Media{"na_legal_8.5x14in", 215900, 355600}
)

Common media.

View Source
var Default = NewClient(System())

Default is the Client behind the package-level functions. It has only the System provider. Programs that add providers replace it before they print, e.g. goprint.Default = goprint.NewClient(goprint.System(), p).

Functions

func Dialog

func Dialog(ctx context.Context, doc Document, opts DialogOptions) (*Job, Settings, error)

Dialog shows the native print dialog, preset with opts.Settings, and returns the job (if printed) together with the settings the user chose. It returns ErrCanceled if the user cancels and ErrNoDialog if the platform has no dialog available.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	ctx := context.Background()
	job, chosen, err := goprint.Dialog(ctx, goprint.PDFFile("report.pdf"), goprint.DialogOptions{
		Settings: goprint.Settings{Copies: 2, Media: goprint.MediaA4},
		PrintNow: true,
	})
	switch {
	case errors.Is(err, goprint.ErrCanceled):
		return
	case errors.Is(err, goprint.ErrNoDialog):
		log.Fatal("no print dialog on this system")
	case err != nil:
		log.Fatal(err)
	}
	fmt.Println("printing on", chosen.Printer)
	if err := job.Wait(ctx); err != nil {
		log.Fatal(err)
	}
}
Example (SettingsOnly)
package main

import (
	"context"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	// Ask once, print later (e.g. a "Page setup" command): without
	// PrintNow the dialog returns the chosen settings and no job.
	ctx := context.Background()
	doc := goprint.PDFFile("report.pdf")
	_, chosen, err := goprint.Dialog(ctx, doc, goprint.DialogOptions{})
	if err != nil {
		return
	}
	// ... later, without UI:
	if _, err := goprint.Print(ctx, doc, chosen); err != nil {
		log.Fatal(err)
	}
}

func RunMain

func RunMain(f func())

RunMain runs f and, while f runs, lets Dialog use the calling thread for the native UI. It returns when f returns.

On macOS, AppKit (and so the print panel) works only on the process's main thread. A program that shows dialogs locks its main goroutine to the main thread in an init function of package main and runs its work through RunMain:

func init() { runtime.LockOSThread() }

func main() {
	goprint.RunMain(func() {
		// Dialog may be called from any goroutine in here.
	})
}

Alternatively Dialog can be called directly from the (locked) main goroutine. Called from another thread without RunMain, Dialog returns ErrWrongThread. Programs that already run a Cocoa event loop on the main thread (e.g. a GUI toolkit) call Dialog from that thread instead.

On other platforms RunMain just calls f.

Example
package main

import (
	"context"
	"errors"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	// In package main: keep the main goroutine on the main thread, which
	// macOS needs for the print panel.
	//
	//	func init() { runtime.LockOSThread() }

	goprint.RunMain(func() {
		// The program's work; Dialog may be called from any goroutine.
		_, _, err := goprint.Dialog(context.Background(), goprint.PDFFile("report.pdf"), goprint.DialogOptions{PrintNow: true})
		if err != nil && !errors.Is(err, goprint.ErrCanceled) {
			log.Fatal(err)
		}
	})
}

func SetAndroidRunner added in v0.6.0

func SetAndroidRunner(run AndroidRunner)

SetAndroidRunner tells goprint how to reach Java on Android, where the print dialog is Android's PrintManager. The package github.com/timzifer/goprint/fyneprint installs one for Fyne; without a runner, Dialog returns ErrNoDialog. Other platforms ignore it.

func SetMainThreadRunner added in v0.2.0

func SetMainThreadRunner(run func(f func()))

SetMainThreadRunner tells goprint how to run code on the process's main thread in programs whose main thread is owned by a GUI toolkit, so that RunMain cannot be used. run must execute its argument on the main thread and return after it returned, e.g. fyne.DoAndWait. nil removes the runner. The package github.com/timzifer/goprint/fyneprint installs one for Fyne.

Only macOS needs the main thread (for the print panel); elsewhere run is never called. If run does not reach the main thread, Dialog returns ErrWrongThread. A RunMain loop, when active, takes precedence.

Types

type AndroidRunner added in v0.6.0

type AndroidRunner func(f func(vm, env, activity uintptr) error) error

AndroidRunner runs f with the app's Java VM (JavaVM*), a JNIEnv* attached to the calling thread and a global reference to the app's Activity (android.content.Context), and returns f's error.

type Capabilities

type Capabilities struct {
	Media       []Media
	Duplex      bool
	Color       bool
	Resolutions []Resolution
	// Formats lists accepted document MIME types, e.g. "application/pdf".
	Formats []string
	// DialogPreview reports whether the native dialog shows a print preview.
	DialogPreview bool
	// Trays lists the paper sources, as values for [Settings.Tray]: IPP
	// keywords such as "auto", "manual" or "tray-1" on Linux/BSD and macOS,
	// the driver's bin names on Windows. Empty if unknown.
	Trays []string
	// Qualities lists the supported [Settings.Quality] values, without
	// QualityDefault. Empty if unknown.
	Qualities []Quality
	// DriverDialog reports whether [PrinterProperties] can show the
	// driver's own settings dialog for the printer.
	DriverDialog bool
}

Capabilities lists what a printer (and the platform's dialog) supports.

func GetCapabilities

func GetCapabilities(ctx context.Context, printer string) (Capabilities, error)

GetCapabilities reports the capabilities of the named printer of the System provider. An empty name refers to the default printer. Other providers' printers are reached through Client.Capabilities.

type Client added in v0.6.0

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

Client prints through a set of providers. Settings.Provider selects the provider for a call. The package-level functions use Default.

func NewClient added in v0.6.0

func NewClient(providers ...Provider) *Client

NewClient returns a Client for providers, in the order given; Client.Printers lists their printers in that order. It panics if two providers have the same name.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	ctx := context.Background()
	// A Client combines providers; System is the platform's print system.
	// Further providers (e.g. simulated printers) are passed after it.
	c := goprint.NewClient(goprint.System())
	printers, err := c.Printers(ctx)
	if err != nil {
		log.Fatal(err)
	}
	if len(printers) == 0 {
		return
	}
	// Provider and Name together address a printer.
	p := printers[0]
	job, err := c.Print(ctx, goprint.PDFFile("label.pdf"), goprint.Settings{Provider: p.Provider, Printer: p.Name})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(job.ID())
}

func (*Client) Capabilities added in v0.6.0

func (c *Client) Capabilities(ctx context.Context, provider, printer string) (Capabilities, error)

Capabilities reports the capabilities of the named printer of the named provider. An empty printer name refers to the provider's default printer.

func (*Client) Dialog added in v0.6.0

func (c *Client) Dialog(ctx context.Context, doc Document, opts DialogOptions) (*Job, Settings, error)

Dialog shows the print dialog of the provider opts.Settings.Provider, preset with opts.Settings, and returns the job (if printed) together with the settings the user chose. It returns ErrCanceled if the user cancels and ErrNoDialog if the provider has no dialog available.

func (*Client) Print added in v0.6.0

func (c *Client) Print(ctx context.Context, doc Document, s Settings) (*Job, error)

Print prints doc with s through the provider s.Provider, without any UI.

func (*Client) PrinterProperties added in v0.6.0

func (c *Client) PrinterProperties(ctx context.Context, s Settings, owner uintptr) (Settings, error)

PrinterProperties shows the driver settings dialog of s.Printer at the provider s.Provider, as described at the package-level PrinterProperties. Providers without one return ErrUnsupported.

func (*Client) Printers added in v0.6.0

func (c *Client) Printers(ctx context.Context) ([]Printer, error)

Printers lists the printers of all providers. If some providers fail, it returns the printers of the others together with an error that names the failed providers.

func (*Client) Providers added in v0.6.0

func (c *Client) Providers() []Provider

Providers returns the client's providers in order.

type ColorMode

type ColorMode int

ColorMode selects color or monochrome output.

const (
	ColorAuto ColorMode = iota
	Color
	Monochrome
)

func (ColorMode) String

func (c ColorMode) String() string

type Credentials

type Credentials struct {
	Username string
	Password string
}

Credentials for authenticated print queues.

type DialogOptions

type DialogOptions struct {
	// Settings are the presets shown in the dialog.
	Settings Settings
	// Owner is the parent window: HWND, NSWindow* or X11 window / portal
	// parent. 0 means a private helper window.
	Owner uintptr
	// Style selects the dialog on Windows.
	Style DialogStyle
	// RequirePrinter forces a dialog that preselects Settings.Printer.
	RequirePrinter bool
	// NoFileOutput keeps the dialog from writing files: printers that
	// write files ("Microsoft Print to PDF", CUPS-PDF, GTK's "Print to
	// File") and "Save as PDF" / "Open in Preview" on macOS. Where the
	// dialog cannot hide them, choosing one returns [ErrFileOutput] before
	// anything is printed or written. On Windows this needs the classic
	// dialog: StyleAuto uses it, StyleModern returns [ErrUnsupported].
	NoFileOutput bool
	// PrintNow prints after confirmation. If false, Dialog only returns the
	// chosen settings and a nil Job.
	//
	// On Windows, StyleAuto without PrintNow shows the classic dialog: it
	// reports the chosen printer and full settings but has no preview, also
	// where Windows 11 draws it in the modern look. StyleModern previews,
	// but cannot report the printer and makes print-to-file printers ask
	// for a file name.
	PrintNow bool
}

DialogOptions configures Dialog.

type DialogProvider added in v0.6.0

type DialogProvider interface {
	Provider
	// Dialog shows the print dialog as described at [Client.Dialog].
	Dialog(ctx context.Context, doc Document, opts DialogOptions) (*Job, Settings, error)
}

DialogProvider is a Provider with its own print dialog.

type DialogStyle

type DialogStyle int

DialogStyle selects the native dialog on platforms that offer several.

const (
	// StyleAuto picks the best dialog for the platform and settings.
	StyleAuto DialogStyle = iota
	// StyleModern is the dialog with print preview (Windows 10+).
	StyleModern
	// StyleClassic is the dialog with full presets, including the printer.
	StyleClassic
)

func (DialogStyle) String

func (s DialogStyle) String() string

type Document

type Document struct {
	// Title is the job name in the spooler and the dialog title.
	Title string

	// PDF opens the PDF content. It is called once per consumer (preview
	// and print run separately), so it must return a fresh reader each time.
	PDF func() (io.ReadSeekCloser, error)

	// Images is a raster source, one image per page.
	Images []image.Image
	// DPI is the resolution of Images. Required when Images is set.
	DPI int

	// Attributes are values of the job for whoever receives it, e.g. to
	// file the document in a DMS (customer number, document type). They
	// travel where the path allows: to providers in the same process
	// (virtualprinter keeps them in Job.Attributes) and to IPP printers
	// that list the job attribute [IPPAttributes] as supported, such as a
	// virtualprinter server. Elsewhere (spoolers, native dialogs, other
	// printers) they are reported as an "Attributes" warning. Keys must
	// not be empty or contain "=".
	Attributes map[string]string
}

Document is the content to print. Exactly one of PDF or Images is set.

func PDFBytes added in v0.3.0

func PDFBytes(title string, data []byte) Document

PDFBytes returns a Document for a PDF held in memory. data must not be modified while the document is in use.

func PDFFile added in v0.3.0

func PDFFile(path string) Document

PDFFile returns a Document that reads the PDF file at path. The title is the file name; set Title on the result to change it. The file is opened when the document is printed, not here.

type Duplex

type Duplex int

Duplex mode.

const (
	DuplexDefault Duplex = iota
	DuplexNone
	DuplexLongEdge
	DuplexShortEdge
)

func (Duplex) String

func (d Duplex) String() string

type IPPEverywhereOptions added in v0.6.0

type IPPEverywhereOptions struct {
	// Name is the provider name; empty means "ipp".
	Name string
	// BrowseTimeout bounds the network search of Printers; 0 means two
	// seconds. A shorter deadline of the caller's context wins.
	BrowseTimeout time.Duration
	// Rasterizer renders PDF for printers that accept no PDF but PWG
	// Raster or Apple Raster, as many network printers do. Nil leaves
	// them out: Print returns ErrUnsupported. The module
	// github.com/timzifer/goprint/raster provides one.
	Rasterizer Rasterizer
}

IPPEverywhereOptions configures IPPEverywhere.

type Job

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

Job is a submitted print job.

func NewJob added in v0.6.0

func NewJob(h JobHandle, warnings []Warning) *Job

NewJob returns the Job for a provider's job h, with the settings that could not be honored.

func Print

func Print(ctx context.Context, doc Document, s Settings) (*Job, error)

Print prints doc with s, without any UI, through Default.

Example
package main

import (
	"context"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	ctx := context.Background()
	job, err := goprint.Print(ctx, goprint.PDFFile("invoice.pdf"), goprint.Settings{
		Printer: "Office",
		Copies:  2,
		Media:   goprint.MediaA4,
		Duplex:  goprint.DuplexLongEdge,
	})
	if err != nil {
		log.Fatal(err)
	}
	for _, w := range job.Warnings() {
		log.Println("not honored:", w)
	}
	if err := job.Wait(ctx); err != nil {
		log.Fatal(err)
	}
}
Example (Images)
package main

import (
	"context"
	"image"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	// Raster pages, e.g. rendered by the application at 300 dpi.
	var pages []image.Image // one image per page
	doc := goprint.Document{Title: "Scan", Images: pages, DPI: 300}
	if _, err := goprint.Print(context.Background(), doc, goprint.Settings{Scaling: goprint.ScalingFit}); err != nil {
		log.Fatal(err)
	}
}
Example (Strict)
package main

import (
	"context"
	"errors"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	// With Strict, a setting the printer cannot honor fails the call
	// before anything is printed.
	_, err := goprint.Print(context.Background(), goprint.PDFFile("label.pdf"), goprint.Settings{
		Printer: "Label printer",
		Duplex:  goprint.DuplexLongEdge,
		Strict:  true,
	})
	if errors.Is(err, goprint.ErrUnsupported) {
		log.Println("printer cannot print two-sided:", err)
	}
}

func (*Job) Cancel

func (j *Job) Cancel(ctx context.Context) error

Cancel cancels the job.

func (*Job) ID

func (j *Job) ID() string

ID returns the platform job id (IPP job-id or spooler job id).

func (*Job) State

func (j *Job) State(ctx context.Context) (JobState, error)

State queries the current job state.

func (*Job) Wait

func (j *Job) Wait(ctx context.Context) error

Wait blocks until the job reaches a final state or ctx is done.

func (*Job) Warnings

func (j *Job) Warnings() []Warning

Warnings lists settings that could not be honored.

type JobHandle added in v0.6.0

type JobHandle interface {
	// ID returns the provider's job id.
	ID() string
	// State queries the current job state.
	State(ctx context.Context) (JobState, error)
	// Wait blocks until the job reaches a final state or ctx is done.
	Wait(ctx context.Context) error
	// Cancel cancels the job.
	Cancel(ctx context.Context) error
}

JobHandle is a print job of a Provider, wrapped into a Job by NewJob.

type JobState

type JobState int

JobState is the state of a print job.

const (
	JobPending JobState = iota
	JobProcessing
	JobCompleted
	JobCanceled
	JobAborted
)

func (JobState) Done

func (s JobState) Done() bool

Done reports whether the state is final.

func (JobState) String

func (s JobState) String() string

type Media

type Media struct {
	Name          string
	Width, Height int
}

Media is a paper size, identified by its PWG 5101.1 self-describing name (e.g. "iso_a4_210x297mm"). Width and Height are in micrometers.

func ParseMedia

func ParseMedia(name string) (Media, error)

ParseMedia parses a PWG 5101.1 self-describing media name of the form "class_name_WxHunit" where unit is "mm" or "in".

Example
package main

import (
	"fmt"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	m, err := goprint.ParseMedia("iso_a4_210x297mm")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(m.Width, m.Height)
}
Output:
210000 297000

func (Media) String

func (m Media) String() string

type Orientation

type Orientation int

Orientation of the content on the page.

const (
	OrientationDefault Orientation = iota
	Portrait
	Landscape
	ReversePortrait
	ReverseLandscape
)

func (Orientation) String

func (o Orientation) String() string

type PageRange

type PageRange struct {
	From, To int
}

PageRange is an inclusive, 1-based page range. To == 0 means "to the end".

type Printer

type Printer struct {
	// Provider names the [Provider] the printer belongs to; "" is the
	// [System] provider. Use it as [Settings.Provider].
	Provider    string
	Name        string
	Description string
	Location    string
	Default     bool
	// ToFile reports a printer that writes files instead of paper, such as
	// "Microsoft Print to PDF", "Microsoft XPS Document Writer" or
	// CUPS-PDF. It is detected from the name, and on Windows from the port.
	ToFile bool
	Caps   Capabilities
}

Printer describes a print queue.

func Printers

func Printers(ctx context.Context) ([]Printer, error)

Printers lists the available printers of Default's providers, as Client.Printers.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/timzifer/goprint"
)

func main() {
	ctx := context.Background()
	printers, err := goprint.Printers(ctx)
	if err != nil {
		log.Fatal(err)
	}
	for _, p := range printers {
		fmt.Printf("%s (default %v, duplex %v, color %v)\n", p.Name, p.Default, p.Caps.Duplex, p.Caps.Color)
	}
}

type PropertiesProvider added in v0.6.0

type PropertiesProvider interface {
	Provider
	// Properties shows the driver dialog as described at
	// [Client.PrinterProperties].
	Properties(ctx context.Context, s Settings, owner uintptr) (Settings, error)
}

PropertiesProvider is a Provider whose printers have a driver settings dialog.

type Provider added in v0.6.0

type Provider interface {
	// Name identifies the provider in [Printer.Provider] and
	// [Settings.Provider]. It is unique within a Client; "" is reserved
	// for [System].
	Name() string
	// Printers lists the provider's printers.
	Printers(ctx context.Context) ([]Printer, error)
	// Capabilities reports the capabilities of the named printer. An empty
	// name refers to the provider's default printer.
	Capabilities(ctx context.Context, printer string) (Capabilities, error)
	// Print prints doc with s, without any UI. s.Printer names one of the
	// provider's printers; empty means its default printer.
	Print(ctx context.Context, doc Document, s Settings) (*Job, error)
}

Provider is a source of printers: the platform's print system (System), or anything else that can list printers and print, such as a simulated printer. A Client combines several providers.

The Client validates documents and settings before it calls a provider, and sets Printer.Provider and Settings.Provider to the provider's name; providers need not do either.

func IPPEverywhere added in v0.6.0

func IPPEverywhere(opts IPPEverywhereOptions) Provider

IPPEverywhere returns a provider for the IPP printers on the local network (IPP Everywhere, AirPrint, Mopria), found through DNS-SD (multicast DNS) without a driver or print server. It works on every platform, also on Linux without CUPS.

Printers are named by their DNS-SD instance name; there is no default printer. Print also takes a printer URI ("ipp://…") as name. PDF is sent as it is to printers that accept it; for the others it is rendered by IPPEverywhereOptions.Rasterizer.

func System added in v0.6.0

func System() Provider

System returns the provider for the platform's print system: CUPS on Linux/BSD and macOS, the spooler on Windows, and IPP printers given by URI. Its name is "". It has the native dialog and, on Windows, the driver dialog.

type Quality

type Quality int

Quality is the print quality.

const (
	QualityDefault Quality = iota
	QualityDraft
	QualityNormal
	QualityHigh
)

func (Quality) String

func (q Quality) String() string

type RasterFormat added in v0.6.0

type RasterFormat struct {
	// Type is [FormatPWGRaster] or [FormatURF].
	Type string
	// Resolution is the device resolution; X == Y.
	Resolution Resolution
	// Color selects 8-bit sRGB; otherwise 8-bit gray.
	Color bool
	// Media is the sheet size; zero means each page's own size.
	Media       Media
	Orientation Orientation
	Scaling     Scaling
	// Duplex is recorded in the page headers. Back sides are not
	// transformed: it is only set for printers that print them as they
	// come.
	Duplex     Duplex
	Quality    Quality
	PageRanges []PageRange
}

RasterFormat is what a Rasterizer produces. It is chosen from the printer's attributes and the settings; the rasterizer applies page selection, orientation and scaling itself, since a printer does not for raster data.

type Rasterizer added in v0.6.0

type Rasterizer interface {
	// Rasterize writes the pages of pdf to w in the format f describes.
	Rasterize(ctx context.Context, w io.Writer, pdf []byte, f RasterFormat) error
}

Rasterizer renders PDF into a raster format, for printers that accept no PDF. The module github.com/timzifer/goprint/raster provides one; it is passed to IPPEverywhereOptions.Rasterizer.

type Resolution

type Resolution struct {
	X, Y int
}

Resolution is a printer resolution in dots per inch.

type Scaling

type Scaling int

Scaling controls how pages are fitted to the media.

const (
	ScalingDefault Scaling = iota
	ScalingFit
	ScalingFill
	ScalingNone
)

func (Scaling) String

func (s Scaling) String() string

type Settings

type Settings struct {
	// Provider selects the [Provider] that prints, by name; empty means the
	// [System] provider.
	Provider string
	// Printer is the queue or printer name; empty means the provider's
	// default printer.
	Printer string
	// Copies is the number of copies; 0 means 1.
	Copies int
	// Collate requests collated copies; nil means printer default.
	Collate *bool
	// PageRanges selects pages (1-based, inclusive); empty means all pages.
	PageRanges  []PageRange
	Media       Media
	Orientation Orientation
	Duplex      Duplex
	Color       ColorMode
	Quality     Quality
	Scaling     Scaling
	// Tray is the media source, e.g. "tray-1" or "manual".
	Tray string
	// Vendor carries platform-specific values verbatim: IPP attributes on
	// Unix and macOS, PrintTicket features on Windows.
	Vendor map[string]string

	// Strict turns warnings about unsupported settings into errors.
	Strict bool
	// Credentials authenticate against queues that require it (CUPS).
	Credentials *Credentials
}

Settings are print settings. Zero values mean "printer default".

func PrinterProperties added in v0.4.0

func PrinterProperties(ctx context.Context, s Settings, owner uintptr) (Settings, error)

PrinterProperties shows the printer driver's own settings dialog ("Printing preferences" on Windows) for s.Printer (empty: the default printer), preset with s, and returns s with the user's choices. The standard settings are read back into their fields; everything else the driver offers travels in s.Vendor[VendorDevMode] to Print. owner is the parent window as in DialogOptions.Owner.

It blocks until the dialog closes and returns ErrCanceled if the user cancels. Only Windows printers have such a dialog (Capabilities.DriverDialog); elsewhere it returns ErrUnsupported.

type Warning

type Warning struct {
	// Setting names the affected setting, e.g. "Duplex".
	Setting string
	Message string
}

Warning reports a setting that could not be honored.

func (Warning) String

func (w Warning) String() string

Directories

Path Synopsis
examples
printdemo command
Command printdemo exercises goprint by hand: list printers, show capabilities, open the native print dialog, or print headless.
Command printdemo exercises goprint by hand: list printers, show capabilities, open the native print dialog, or print headless.
fyneprint module
internal
androidprint
Package androidprint holds the Java part of goprint's Android backend: PDFAdapter (java/), compiled to classes.dex by gen.sh.
Package androidprint holds the Java part of goprint's Android backend: PDFAdapter (java/), compiled to classes.dex by gen.sh.
com
Package com calls COM and WinRT interfaces through hand-written vtables and implements COM objects in Go, without cgo.
Package com calls COM and WinRT interfaces through hand-written vtables and implements COM objects in Go, without cgo.
core
Package core holds the platform-independent core: settings mapping, media tables and the PDF source.
Package core holds the platform-independent core: settings mapping, media tables and the PDF source.
dnssd
Package dnssd browses DNS-SD services over multicast DNS (RFC 6762, RFC 6763), just enough to find IPP printers on the local network.
Package dnssd browses DNS-SD services over multicast DNS (RFC 6762, RFC 6763), just enough to find IPP printers on the local network.
errdefs
Package errdefs defines the sentinel errors shared by goprint and its internal backends.
Package errdefs defines the sentinel errors shared by goprint and its internal backends.
macprint
Package macprint shows the macOS print panel and runs PDFKit print operations.
Package macprint shows the macOS print panel and runs PDFKit print operations.
portal
Package portal talks to the print portal of xdg-desktop-portal (org.freedesktop.portal.Print) over D-Bus.
Package portal talks to the print portal of xdg-desktop-portal (org.freedesktop.portal.Print) over D-Bus.
portal/portaltest
Package portaltest runs a private D-Bus session with a fake org.freedesktop.portal.Print implementation for tests.
Package portaltest runs a private D-Bus session with a fake org.freedesktop.portal.Print implementation for tests.
testpdf
Package testpdf generates small, valid PDF documents for tests.
Package testpdf generates small, valid PDF documents for tests.
winprint
Package winprint implements printing on Windows: PDF pages are rendered by Windows.Data.Pdf onto Direct2D command lists, which ID2D1PrintControl turns into an XPS package for the spooler.
Package winprint implements printing on Windows: PDF pages are rendered by Windows.Data.Pdf onto Direct2D command lists, which ID2D1PrintControl turns into an XPS package for the spooler.
ipp
Package ipp implements the Internet Printing Protocol (RFC 8010/8011): an attribute codec and a client for CUPS and IPP Everywhere printers.
Package ipp implements the Internet Printing Protocol (RFC 8010/8011): an attribute codec and a client for CUPS and IPP Everywhere printers.
ipptest
Package ipptest provides an in-process IPP mock server for tests.
Package ipptest provides an in-process IPP mock server for tests.
raster module
Package virtualprinter simulates printers for goprint.
Package virtualprinter simulates printers for goprint.

Jump to

Keyboard shortcuts

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