airscan

package module
v0.0.0-...-6d2d077 Latest Latest
Warning

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

Go to latest
Published: Apr 13, 2023 License: Apache-2.0 Imports: 15 Imported by: 2

README

GitHub Actions CI Go Report Card PkgGoDev

airscan 📄 🖨️ 🕸️

The airscan Go package can be used to scan paper documents 📄 from a scanner 🖨️ via the network 🕸️ using the Apple AirScan (eSCL) protocol.

Getting started: example program

First, install the example program coming with this package:

go install -v github.com/stapelberg/airscan/cmd/airscan1@latest

Then, query the local network for AirScan compatible devices:

% airscan1
2020/08/16 08:50:31 finding airscan-compatible devices for 1s
2020/08/16 08:50:31 device "Brother MFC-L2750DW series" discovered (use -host="BRW405BD8AxxDyz")

Now, I can scan the contents of the flatbed scanner:

% airscan1 -host=BRW405BD8AxxDyz
2020/08/16 08:52:44 finding device for 5s (use -timeout=0 for unlimited)
2020/08/16 08:52:45 device "Brother MFC-L2750DW series" found in 298.151935ms
2020/08/16 08:52:51 scan done in 6.738205326s

…or the page(s) inserted into the Automatic Document Feeder (ADF):

% airscan1 -host=BRW405BD8A10D7C -source=adf
2020/08/16 11:10:34 finding device for 5s (use -timeout=0 for unlimited)
2020/08/16 11:10:34 device "Brother MFC-L2750DW series" found in 112.127399ms
2020/08/16 11:10:45 wrote /tmp/page12.jpg (211305 bytes)
2020/08/16 11:10:47 wrote /tmp/page13.jpg (139335 bytes)
2020/08/16 11:10:47 scan done in 13.068799513s

…or the page(s) from ADF, colored, and as single PDF file output:

% airscan1 -host=HPFXXXXXXXXXXXX -source adf -color RGB24 -format "application/pdf"
2021/04/04 00:12:13 finding device for 5s (use -timeout=0 for unlimited)
2021/04/04 00:12:14 device "HP OfficeJet Pro 9010 series" found in 315.486148ms
2021/04/04 00:14:07 wrote /tmp/page5.pdf (123456 bytes)
2021/04/04 00:14:07 scan done in 1m53.772520178s

Getting started: using the package in your program

See the package airscan examples in godoc for how to use the package to scan.

See airscan1.go for a full example scan program, including network service discovery, timeouts, and writing scan data to files.

Project status

The package does what I needed: grayscale/color scan of A4 documents from the flat bed or the automatic document feeder (ADF).

If you have any improvements, I’d be happy to review a pull request. Please see the contribution guidelines.

Tested devices

If you successfully scanned documents from your device using the airscan1 example program as described above, please send a pull request to include your report in this table for the benefit of other interested users:

Device Name Working features Known issues
Brother MFC-L2710DN flat bed scan, automatic document feeder scan must be run with -duplex=false
Brother MFC-L2750DW flat bed scan, automatic document feeder scan
Canon G3560 flat bed scan, color: RGB24
Epson XP-7100 flat bed scan, automatic document feeder scan must be run with -duplex=false for flat bed scanning; also needs -skip_cert_verify
HP Laserjet M479fdw flat bed scan, automatic document feeder scan
HP OfficeJet Pro 9010 series flat bed scan, automatic document feeder scan, color: RGB24

Documentation

Overview

Package airscan can be used to scan paper documents from a scanner via the network, using the Apple AirScan (eSCL) protocol.

Index

Examples

Constants

View Source
const ServiceName = "_uscan._tcp.local."

ServiceName is the AirScan DNSSD service name.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	// HTTPClient is used for all requests made by this Client and can be
	// overridden for testing or to integrate custom behavior. The default
	// amounts to http.DefaultClient.
	HTTPClient interface {
		Do(*http.Request) (*http.Response, error)
	}
	// contains filtered or unexported fields
}

A Client allows scanning documents via AirScan, which is also known as eSCL.

func NewClient

func NewClient(host string) *Client

NewClient returns a ready-to-use Client. It is safe to update its struct fields before first using the returned Client.

When using DNSSD service discovery to locate the scanner (the most common choice), prefer NewClientForService instead.

func NewClientForService

func NewClientForService(service *dnssd.BrowseEntry) *Client

NewClientForService is like NewClient, but constructs a net.Dialer that attempts to connect to the specified DNSSD service using its host or IP address(es) directly, gracefully falling back between connection methods.

This maximizes the chance of a successful connection, even when local networks do not offer DHCP-based DNS, and when Avahi is not available.

func (*Client) Scan

func (c *Client) Scan(settings *ScanSettings) (*ScanState, error)

Scan starts a new scan job using the specified settings.

When scanning from an Automatic Document Feeder (ADF), the Scan method verifies a document is inserted before creating a scan job (which would otherwise fail with a less clear error message).

Example
package main

import (
	"io"

	"github.com/brutella/dnssd"
	"github.com/stapelberg/airscan"
	"github.com/stapelberg/airscan/preset"
)

var discoveredService *dnssd.BrowseEntry

func main() {
	// For a full example using DNSSD service discovery, see:
	// https://github.com/stapelberg/airscan/blob/master/cmd/airscan1/airscan1.go
	cl := airscan.NewClientForService(discoveredService)

	// Set up scan job:
	grayscaleA4Platen := preset.GrayscaleA4ADF()
	grayscaleA4Platen.InputSource = "Platen"
	job, err := cl.Scan(grayscaleA4Platen)
	if err != nil {
		panic(err)
	}
	defer job.Close()

	// Scan one individual page at a time:
	for job.ScanPage() {
		// Read and discard scan data. This is where you would typically save
		// the data to a file, send it via the net, display or process it, etc.:
		if _, err := io.Copy(io.Discard, job.CurrentPage()); err != nil {
			panic(err)
		}
	}
	if err := job.Err(); err != nil {
		panic(err)
	}

	// Scan succeeded!
}

func (*Client) ScannerCapabilities

func (c *Client) ScannerCapabilities() (*scannerCapabilities, error)

func (*Client) ScannerStatus

func (c *Client) ScannerStatus() (*ScannerStatus, error)

ScannerStatus queries the device for its status. This can be used for example to find out whether a document has been inserted into the Automatic Document Feeder (ADF). The Scan method verifies this, too.

func (*Client) SetDebug

func (c *Client) SetDebug(debug bool)

type ScanRegion

type ScanRegion struct {
	XMLName            xml.Name `xml:"pwg:ScanRegion"`
	ContentRegionUnits string   `xml:"pwg:ContentRegionUnits"`
	Width              int      `xml:"pwg:Width"`
	Height             int      `xml:"pwg:Height"`
	XOffset            int      `xml:"pwg:XOffset"`
	YOffset            int      `xml:"pwg:YOffset"`
}

type ScanRegions

type ScanRegions struct {
	MustHonor bool `xml:"pwg:MustHonor,attr"`
	Regions   []*ScanRegion
}

type ScanSettings

type ScanSettings struct {
	XMLName        xml.Name    `xml:"scan:ScanSettings"`
	XmlnsScan      string      `xml:"xmlns:scan,attr"`
	XmlnsPWG       string      `xml:"xmlns:pwg,attr"`
	Version        string      `xml:"pwg:Version"`
	ScanRegions    ScanRegions `xml:"pwg:ScanRegions"`
	DocumentFormat string      `xml:"pwg:DocumentFormat"`
	InputSource    string      `xml:"pwg:InputSource"`
	ColorMode      string      `xml:"scan:ColorMode"`
	XResolution    int         `xml:"scan:XResolution"`
	YResolution    int         `xml:"scan:YResolution"`
	Duplex         bool        `xml:"scan:Duplex"`
}

ScanSettings instruct the device how to scan.

It is recommended to use the https://pkg.go.dev/github.com/stapelberg/airscan/preset package to start with a known-good configuration.

func (*ScanSettings) Marshal

func (s *ScanSettings) Marshal() (string, error)

type ScanState

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

ScanState represents an in-progress scan job.

func (*ScanState) Close

func (s *ScanState) Close() error

Close deletes the scan job on the device.

Some devices work just fine if you never call Close. To maximize compatibility, it is recommended to call Close. This mirrors what Apple’s scan program does, which might be required for certain scanners (speculation only).

func (*ScanState) CurrentPage

func (s *ScanState) CurrentPage() io.Reader

CurrentPage returns an io.Reader containing the scan data.

CurrentPage must only be called after ScanPage() returned true, and will return nil otherwise.

Note: package airscan never interprets scan data, the package only provides the data as-is. If you want to decode scan data, you will need to import e.g. image/jpeg (depending on scan settings) yourself.

func (*ScanState) Err

func (s *ScanState) Err() error

Err returns the first error that occurred. If it returns non-nil, the ScanState must no longer be used, except for the Close method.

func (*ScanState) ScanPage

func (s *ScanState) ScanPage() bool

ScanPage requests the next page of this scan job. It returns true if a new page is available, or false when all pages were exhausted or an error occurred. Errors are available via the Err() method.

Note that some scanners return 503 for NextDocument but will eventually return an accurate code when given more chances. See also https://github.com/alexpevzner/sane-airscan-ipp/blob/master/airscan-escl.c#L11.

type ScannerStatus

type ScannerStatus struct {
	Version  string `xml:"Version"`
	State    string `xml:"State"`
	ADFState string `xml:"AdfState"`
}

Directories

Path Synopsis
cmd
airscan1 command
Program airscan1 is a utility program that scans documents from one AirScan-compatible scanner at a time.
Program airscan1 is a utility program that scans documents from one AirScan-compatible scanner at a time.
Package preset contains settings that have been verified to match, character-by-character, the requests that Apple’s AirScanScanner library produces.
Package preset contains settings that have been verified to match, character-by-character, the requests that Apple’s AirScanScanner library produces.

Jump to

Keyboard shortcuts

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