Documentation
¶
Overview ¶
Package decoder reads codes back out of an image: bytes in, data out.
It is the second half of barqr's round-trip invariant. Everything else in the service runs one way — build a payload, encode it, render it, write it — and this package is the only thing that can prove the result of that chain is a code a scanner will actually read:
Build -> Encode -> Render -> Write(png) -> Decode -> Parse == payload
Decoding is also the only place where barqr parses attacker-controlled binary data, so image.go is written to a different standard from the rest of the codebase: every limit is checked before the allocation it bounds, and no third-party parser is trusted not to panic.
The scan itself is gozxing, a pure-Go port of ZXing. Only the formats with a reader in that port are decodable, and Symbologies is honest about which those are — it is a strict subset of what the encoder side can draw.
Index ¶
- Constants
- Variables
- func BarcodeFormat(name string) (gozxing.BarcodeFormat, bool)
- func DataFromURI(s string) ([]byte, string, error)
- func LoadImage(ctx context.Context, data []byte, o Options) (image.Image, error)
- func PossibleFormats(names []string) ([]gozxing.BarcodeFormat, error)
- func Symbologies() []string
- func SymbologyName(f gozxing.BarcodeFormat) (string, bool)
- type Options
- type Point
- type Result
Constants ¶
const ( // DefaultMaxPixels caps width*height of the decoded image. DefaultMaxPixels int64 = 8_000_000 // DefaultMaxBytes caps the encoded input. Eight mebibytes holds any // realistic photograph and is far more than a generated code needs. DefaultMaxBytes int64 = 8 << 20 )
Default limits, applied when Options leaves them at zero.
Both are chosen from the memory a single decode costs rather than from what looks generous. A decoded image is roughly four bytes per pixel, and the scanner allocates one luminance byte plus one bit of binarised matrix on top of that, so eight megapixels is already about forty megabytes in flight for one request. That is a phone photograph at full resolution, which is the largest thing anyone legitimately points at a barcode scanner.
Variables ¶
var ( // ErrNoCodeFound means the image was read successfully but contains no // code any enabled reader recognised. It is the expected outcome of // pointing the service at a photograph, not a failure of the service. ErrNoCodeFound = errors.New("no code found in the image") // ErrUnsupportedImage means the bytes are not an image barqr can read: // an unknown container, a truncated file, or a non-image data: uri. ErrUnsupportedImage = errors.New("unsupported or malformed image") // ErrImageTooLarge means the input, or the image it declares, exceeds the // configured limits. It is a limit, not a bug: it is what stops a // hundred-byte decompression bomb from claiming ten gigabytes. ErrImageTooLarge = errors.New("image exceeds the maximum decodable size") // ErrDecodeFailed means a code was located but could not be read, for // example a checksum that does not verify or a symbol damaged past // recovery. ErrDecodeFailed = errors.New("code found but could not be decoded") )
Sentinel errors. The HTTP layer maps these onto stable error codes, so a caller can switch on the code rather than on message text.
Functions ¶
func BarcodeFormat ¶
func BarcodeFormat(name string) (gozxing.BarcodeFormat, bool)
BarcodeFormat maps a barqr registry name onto the gozxing BarcodeFormat for the same symbology. The second result is false for an unknown name.
func DataFromURI ¶
DataFromURI decodes a data: URI into its bytes and its media type.
Both RFC 2397 payload encodings are accepted: base64 and percent-encoding. Anything that is not an image/* media type is rejected here rather than left for the decoder, so that a text/html or application/octet-stream URI fails with a message that names the real problem.
func LoadImage ¶
LoadImage decodes untrusted image bytes under the limits in o.
This is the most dangerous surface in the service: the bytes come off the network and drive a lot of code barqr did not write. The order of the checks below is the whole defence and must not be rearranged — size first, then the header, then and only then actual pixels.
func PossibleFormats ¶
func PossibleFormats(names []string) ([]gozxing.BarcodeFormat, error)
PossibleFormats turns the caller's symbology filter into the value of the gozxing POSSIBLE_FORMATS hint. An empty filter returns nil, which means "every format this build can read" rather than "none".
An unrecognised name is an error naming the offending value: silently ignoring it would let a typo widen the scan instead of narrowing it, which is the opposite of what the caller asked for.
func Symbologies ¶
func Symbologies() []string
Symbologies lists every symbology this build can decode, sorted.
It is deliberately narrower than the encode side: a symbology barqr can draw is not automatically one it can read back, and a caller comparing the two lists should see the honest difference rather than a promise that fails at decode time.
func SymbologyName ¶
func SymbologyName(f gozxing.BarcodeFormat) (string, bool)
SymbologyName maps a gozxing BarcodeFormat onto the barqr registry name for the same symbology. The second result is false for a format barqr has no name for, such as MaxiCode or the RSS family.
Types ¶
type Options ¶
type Options struct {
// TryHarder asks for a slower, more thorough scan. It is worth setting
// for a photograph — rotated, skewed or poorly lit — and wasted on an
// image barqr generated itself.
TryHarder bool
// Multi finds every code in the image rather than stopping at the first.
Multi bool
// Symbologies restricts the scan to these barqr symbology names. Empty
// means every decodable symbology. Narrowing it is both faster and safer:
// the loosest linear formats cannot then invent a code out of noise.
Symbologies []string
// MaxPixels caps width*height of the decoded image. Zero or negative
// means DefaultMaxPixels.
MaxPixels int64
// MaxBytes caps the encoded input size. Zero or negative means
// DefaultMaxBytes.
MaxBytes int64
}
Options controls a decode. The zero value is usable and applies the default limits; DefaultOptions spells them out for a caller that wants to adjust one.
func DefaultOptions ¶
func DefaultOptions() Options
DefaultOptions returns the options a decode with no overrides gets.
type Result ¶
type Result struct {
// Symbology is the barqr registry name, e.g. "qr" or "ean13".
Symbology string `json:"symbology"`
// Data is the decoded payload, exactly as the symbol carried it.
Data string `json:"data"`
// Points are the positions the reader locked onto: finder-pattern centres
// for a matrix code, the ends of the scanned line for a linear one. They
// are what a caller needs to draw a box around what it found.
Points []Point `json:"points,omitempty"`
}
Result is one decoded code.
func Decode ¶
Decode finds codes in an encoded image.
data is either raw image bytes or a data: uri; the two are told apart by the "data:" prefix, which no image format can begin with. Results are returned in the order they were found, which is scan order: matrix symbologies first, then the linear ones.
func DecodeImage ¶
DecodeImage decodes an already-parsed image.
It is the entry point for a caller that produced the image itself and has therefore already bounded it — the round-trip tests, and anything that has been through LoadImage. o.MaxPixels is still enforced: an image.Image from an untrusted source is no safer for having been decoded elsewhere.