Documentation
¶
Overview ¶
Package render turns an encoder.Matrix into a Canvas: a resolution independent description of a drawn code.
Rendering is deliberately separate from writing. A Canvas knows which modules are dark, what shape and colour they take, and where the quiet zone is, but nothing about pixels, files, or MIME types. That split is what lets one render feed a rasteriser, an SVG path writer, and a terminal writer without any of them knowing about the others.
Index ¶
- Constants
- Variables
- func ContrastRatio(fg, bg color.NRGBA) float64
- func EyeShapes() []string
- func FrameKinds() []string
- func HRIFonts() []string
- func HexColor(c color.NRGBA) string
- func Luminance(c color.NRGBA) float64
- func ModuleShapes() []string
- func ParseColor(s string) (color.NRGBA, error)
- func ParseLogo(dataURI string) (image.Image, error)
- func Register(r Renderer)
- func RegisterEyeShape(p EyePainter)
- func RegisterModuleShape(p ModulePainter)
- type Canvas
- func (c *Canvas) At(x, y int) bool
- func (c *Canvas) Caption() string
- func (c *Canvas) CaptionRect() (image.Rectangle, bool)
- func (c *Canvas) ColorAt(x, y int) color.NRGBA
- func (c *Canvas) Dark() int
- func (c *Canvas) FrameRect() (image.Rectangle, bool)
- func (c *Canvas) LogoRect() (image.Rectangle, bool)
- func (c *Canvas) Role(x, y int) EyeRole
- func (c *Canvas) SymbolRect() image.Rectangle
- type EyePainter
- type EyeRole
- type Frame
- type Grade
- type Gradient
- type GradientKind
- type Issue
- type Logo
- type ModulePainter
- type Neighbors
- type Renderer
- type Report
- type Severity
- type Stop
- type Style
Constants ¶
const ( // EyeCircle is a circular ring with a circular centre. EyeCircle = "circle" // EyeRounded is a square ring with softened corners. EyeRounded = "rounded" // EyeLeaf rounds two opposite corners to a quarter circle. EyeLeaf = "leaf" // EyeShield rounds three corners and leaves the bottom-left square. EyeShield = "shield" )
Registered eye shape names beyond the specified square.
An eye shape is riskier than a module shape. The three finder patterns are what a decoder locates first, by looking for the 1:1:3:1:1 dark-light run ratio along a scan line through the centre. Every shape here preserves that ratio through the horizontal and vertical centre lines — which is why they are all concentric rings of the same proportions and differ only at the corners, where no centre line passes.
const ( // FrameNone reserves no space at all. FrameNone = "none" // FrameBorder is a plain rectangular outline. FrameBorder = "border" // FrameRounded is a rectangular outline with rounded corners. FrameRounded = "rounded" // FrameBanner is a solid bar behind the caption with an outline around // the code. FrameBanner = "banner" // FrameBubble is a rounded outline with a speech-bubble tail below the // caption. FrameBubble = "bubble" )
Frame kinds.
const ( // DefaultFrameWidth is a frame two modules thick: visible at the sizes a // code is printed at without dominating it. DefaultFrameWidth = 2 // MaxFrameWidth caps the border so a frame cannot outgrow the symbol it // surrounds. MaxFrameWidth = 16 // CaptionBandModules is the height reserved for the caption, in modules. // Four modules leaves room for text about two modules tall with a module // of breathing space above and below, which is the proportion a printed // linear code uses for its human-readable line. CaptionBandModules = 4 // BubbleTailModules is the extra depth a speech-bubble tail needs under // the caption band. BubbleTailModules = 2 )
Frame sizing limits and defaults.
const ( // MinLogoScale is the smallest useful logo: below this it is a smudge. MinLogoScale = 0.05 // MaxLogoScale caps the width at just over a third of the symbol. Even at // level H the error-correction budget is 30% of the codewords, and the // codewords a logo destroys are contiguous, which is the worst case for // Reed-Solomon block interleaving. MaxLogoScale = 0.35 // DefaultLogoScale is a fifth of the width, comfortably inside level M. DefaultLogoScale = 0.2 // MaxLogoPadding stops a small logo from excavating half the symbol // through its clear space alone. MaxLogoPadding = 8 )
Logo sizing limits.
const ( DefaultHRISize = 2.0 MinHRISize = 1.0 MaxHRISize = 8.0 HRIFontMono = "mono" HRIFontSans = "sans" )
Human-readable text sizing and type families.
The families are generic names rather than font names because every output format has to be able to honour one: a name barqr cannot draw in raster would be an option accepted for SVG and quietly dropped for PNG. There is deliberately no serif family — it is free on the vector paths and impossible in a bitmap cell this small, so offering it would be a promise one writer could not keep.
const ( // ShapeDot is a circle inscribed in the module. ShapeDot = "dot" // ShapeRounded is a square rounded only on corners with no dark // neighbour, so a run of modules reads as one smooth blob. ShapeRounded = "rounded" // ShapeDiamond is a rhombus on the module's edge midpoints. ShapeDiamond = "diamond" // ShapeClassy is a square with two opposite corners rounded: the "leaf". ShapeClassy = "classy" // ShapeVertical merges modules into continuous vertical bars with rounded // caps. ShapeVertical = "vertical" // ShapeHorizontal is ShapeVertical turned through ninety degrees. ShapeHorizontal = "horizontal" )
Registered module shape names beyond the specified square.
Every one of these trades a little scannability for appearance, and the order below is roughly the order of that cost: a dot loses the module corners, a diamond loses half the module area, and the bar shapes change the apparent module boundary entirely. The square remains the default for a reason.
const ( // ShapeSquare is the specified appearance: a filled unit square. It is // the default and the most reliably scannable. ShapeSquare = "square" )
Registered module shape names.
const StandardRenderer = "standard"
StandardRenderer is the registry name of the default renderer.
Variables ¶
var ( // ErrUnknownRenderer means no renderer is registered under that name. ErrUnknownRenderer = errors.New("unknown renderer") // ErrUnknownShape means the requested module or eye shape is not // registered. ErrUnknownShape = errors.New("unknown shape") // ErrInvalidColor means a colour string could not be parsed. ErrInvalidColor = errors.New("invalid colour") // ErrInvalidStyle means the style is internally inconsistent, for example // a negative quiet zone or a logo larger than the code. ErrInvalidStyle = errors.New("invalid style") )
Sentinel errors. The HTTP layer maps these onto stable error codes.
var Transparent = color.NRGBA{}
Transparent is the fully transparent colour, and what the literal "transparent" parses to.
Functions ¶
func ContrastRatio ¶
ContrastRatio returns the WCAG contrast ratio between two colours, from 1 (identical) to 21 (black on white).
func FrameKinds ¶
func FrameKinds() []string
FrameKinds lists the frame styles this build draws, sorted.
It is exported so the HTTP layer can reject an unknown kind against the field the caller actually set, and so the documentation UI can build its control from the registry rather than a hand-written list.
func HRIFonts ¶ added in v0.2.0
func HRIFonts() []string
HRIFonts lists the type families, sorted, for validation and discovery.
func HexColor ¶
HexColor renders a colour as "#rrggbb", or "#rrggbbaa" when it is not fully opaque. It is the inverse of ParseColor for the hex forms and is what the SVG and JSON writers emit.
func Luminance ¶
Luminance returns the relative luminance of a colour on the 0..1 scale defined by WCAG 2, composited over white so that a translucent colour is judged as it will actually appear on a light background.
Contrast between foreground and background is the single strongest predictor of whether a scanner will read a code, so this backs the scannability report.
func ModuleShapes ¶
func ModuleShapes() []string
ModuleShapes lists every registered module shape, sorted.
func ParseColor ¶
ParseColor accepts "#rgb", "#rgba", "#rrggbb", "#rrggbbaa", the same forms without the leading '#', and a short list of colour names.
The alpha channel is optional everywhere and defaults to fully opaque.
func ParseLogo ¶
ParseLogo decodes a logo from a data URI.
Only data: URIs are accepted. Fetching a logo over the network turns every render into an outbound request on behalf of an untrusted caller — a server-side request forgery primitive and a denial-of-service amplifier — so remote logos are a separate, disabled-by-default feature and are not reachable from here.
func Register ¶
func Register(r Renderer)
Register adds a renderer under its own name. It panics on a duplicate.
func RegisterEyeShape ¶
func RegisterEyeShape(p EyePainter)
RegisterEyeShape adds an eye painter. It panics on a duplicate.
func RegisterModuleShape ¶
func RegisterModuleShape(p ModulePainter)
RegisterModuleShape adds a module painter. It panics on a duplicate.
Types ¶
type Canvas ¶
type Canvas struct {
// Cols and Rows are the drawable size in modules, quiet zone included.
Cols, Rows int
// QuietZone is the margin already included in Cols and Rows.
QuietZone int
// Style is the style as resolved, with every automatic value filled in.
Style Style
// Symbology and Kind describe what was rendered.
Symbology string
Kind encoder.Kind
// HRI is the human-readable text to draw beneath a linear code, or empty.
HRI string
// contains filtered or unexported fields
}
Canvas is a rendered code: the module grid with its quiet zone applied, plus everything a writer needs to draw it.
Coordinates are in modules. Cols and Rows include the quiet zone, so a writer can iterate the whole drawable area without adding margins itself.
func (*Canvas) At ¶
At reports whether the module at (x, y) is dark. Out-of-range coordinates read as light so writers can walk a padded area freely.
func (*Canvas) Caption ¶
Caption returns the caption text a writer should draw, or empty when there is none.
func (*Canvas) CaptionRect ¶
CaptionRect returns the band reserved for the caption in module coordinates, and false when there is no caption.
The band sits between the bottom of the quiet zone and the bottom of the frame, inset by the frame's thickness on the left and right. It never overlaps the quiet zone.
func (*Canvas) ColorAt ¶
ColorAt returns the colour a dark module at (x, y) should be painted with, honouring a distinct eye colour when one is set and a gradient fill over the data modules.
The gradient is sampled in symbol coordinates so that the ramp spans the code and not the quiet zone or the frame, which would compress it into the middle of the canvas and change with every margin the caller picks.
func (*Canvas) FrameRect ¶
FrameRect returns the frame's outer bounds in module coordinates, and false when the style has no frame.
The rectangle is the full canvas: the frame is a stroke of Frame.Width modules drawn inwards from these edges, so a writer strokes the outline and never has to work out where the code sits inside it.
func (*Canvas) LogoRect ¶
LogoRect returns the logo's area in module coordinates, and false when the style carries no logo.
The rectangle is centred on the symbol itself, not on the canvas: the quiet zone and any frame are margins, and a logo nudged off-centre by them would eat asymmetrically into the data.
func (*Canvas) SymbolRect ¶
SymbolRect is the code itself in module coordinates: quiet zone, frame and caption band all excluded.
Writers that need to place something relative to the data — a logo, an overlay, a gradient — must use this rather than the canvas bounds, which drift as soon as a frame is added.
type EyePainter ¶
type EyePainter interface {
// Name is the registry key.
Name() string
// SVGFrame returns the path for the ring of a finder pattern whose
// top-left module is at (x, y), in module units. The ring is 7x7 modules
// with a 5x5 hole.
SVGFrame(x, y int) string
// SVGBall returns the path for the solid 3x3 centre whose top-left module
// is at (x, y), in module units.
SVGBall(x, y int) string
// RasterFrame and RasterBall draw the same two parts as pixels. r is the
// full 7x7 (frame) or 3x3 (ball) pixel rectangle.
RasterFrame(dst *image.NRGBA, r image.Rectangle, c color.NRGBA)
RasterBall(dst *image.NRGBA, r image.Rectangle, c color.NRGBA)
}
EyePainter draws a finder pattern: the 7x7 frame and its 3x3 centre. Eyes are painted as whole units rather than module by module, because their shape is a property of the pattern, not of the individual modules.
Implementations must be stateless and safe for concurrent use.
func EyeShape ¶
func EyeShape(name string) (EyePainter, error)
EyeShape returns the painter for an eye shape.
type EyeRole ¶
type EyeRole uint8
EyeRole classifies a module's part in a finder pattern, so that writers can paint finder frames and their centres differently from data modules.
type Frame ¶
type Frame struct {
// Kind selects the outline: none, border, rounded, banner, or bubble.
Kind string
// Color is the frame's colour.
Color color.NRGBA
// Width is the frame's thickness in modules.
Width int
// Caption is the text for the caption band. It overrides Style.Caption
// when both are set.
Caption string
// CaptionColor is the caption's text colour.
CaptionColor color.NRGBA
}
Frame decorates a code with a border and an optional caption band.
Nothing here is drawn by the renderer. A frame is a stroke and a caption is text, and both belong to the writer that knows about pixels or about fonts; what the renderer owns is the geometry, so that a raster writer and a vector writer place them identically.
type Gradient ¶
type Gradient struct {
// Kind is linear or radial.
Kind GradientKind
// Angle is the sweep direction in degrees for a linear gradient, measured
// clockwise from left-to-right because the module grid's y axis points
// down. Ignored for a radial gradient.
Angle float64
// Stops are the colour stops, at least two, in non-decreasing offset
// order.
Stops []Stop
}
Gradient fills the data modules with a colour ramp instead of a flat colour.
It replaces Style.FG for data modules only. The finder patterns keep their own colour, because a gradient that runs light exactly where an eye sits costs the scanner the one landmark it cannot recover from losing.
func ParseGradient ¶
ParseGradient reads the compact gradient syntax used by the API.
Accepted forms:
linear(45deg,#000,#00f) linear(#000 0%,#888 50%,#fff 100%) radial(#000,#333)
The angle is optional and defaults to 90 degrees, a top-to-bottom sweep. Offsets are optional and are distributed evenly when omitted; a stop may carry a percentage or a 0..1 fraction.
func (*Gradient) ColorAt ¶
ColorAt samples the gradient at module (x, y) of a cols by rows area.
Coordinates are module-space and the sample is taken at the module's centre, so every pixel of a module gets one flat colour. Sampling per pixel instead would put a ramp across each module and give a scanner's binariser a gradient to threshold inside the very unit it is trying to classify.
func (*Gradient) Darkest ¶
Darkest returns the stop with the highest contrast against bg, and Lightest the lowest. Together they bound what the symbol will look like to a camera: if even the darkest stop is washed out the code is dead, and if the lightest stop disappears then part of the symbol does too.
func (*Gradient) SVGDef ¶
SVGDef renders the gradient as an SVG element for a <defs> block, referenced by a fill of "url(#id)".
The id is sanitised rather than trusted: it ends up unquoted inside an attribute, and a writer that derived it from a request field would otherwise hand the caller an XML injection.
type GradientKind ¶
type GradientKind string
GradientKind names the geometry of a gradient fill.
const ( // GradientLinear sweeps along a line at Gradient.Angle. GradientLinear GradientKind = "linear" // GradientRadial sweeps outwards from the centre of the symbol. GradientRadial GradientKind = "radial" )
Gradient geometries.
type Issue ¶
type Issue struct {
// Code is a stable identifier, so a client can suppress a known one.
Code string `json:"code"`
// Severity ranks it.
Severity Severity `json:"severity"`
// Message states the problem in the terms a designer thinks in.
Message string `json:"message"`
// Hint is the fix.
Hint string `json:"hint,omitempty"`
}
Issue is one scannability finding.
type Logo ¶
type Logo struct {
// Image is the decoded logo. Nil means the caller only wants the geometry
// reserved, which is what a vector writer that references an external
// asset needs.
Image image.Image
// Scale is the logo's width as a fraction of the symbol's width, from
// MinLogoScale to MaxLogoScale.
Scale float64
// Excavate clears the modules underneath the logo so it sits on the
// background rather than on top of live data.
Excavate bool
// Padding is the clear space around the logo, in modules.
Padding int
}
Logo is an image placed over the centre of a code.
type ModulePainter ¶
type ModulePainter interface {
// Name is the registry key.
Name() string
// SVGPath returns a path fragment for the module whose top-left corner is
// at (x, y) in module units. It must be a closed subpath and must not
// include the "d" attribute or any wrapper element.
SVGPath(x, y int, n Neighbors) string
// Raster fills the module into dst within r.
Raster(dst *image.NRGBA, r image.Rectangle, n Neighbors, c color.NRGBA)
}
ModulePainter draws a single module in two targets: as an SVG path fragment and as pixels.
Coordinates for SVGPath are in module units, so a writer scales the whole path once rather than per module. Raster receives the exact pixel rectangle the module occupies, already scaled.
Implementations must be stateless and safe for concurrent use.
func ModuleShape ¶
func ModuleShape(name string) (ModulePainter, error)
ModuleShape returns the painter for a module shape.
type Neighbors ¶
type Neighbors struct {
Up, Down, Left, Right bool
}
Neighbors records which orthogonally adjacent modules are dark. Connected shapes — rounded, classy, and the bar-joining linear shapes — use it to decide which corners to round and which edges to merge.
type Renderer ¶
type Renderer interface {
// Name is the registry key.
Name() string
// Render applies style to a matrix.
Render(m encoder.Matrix, s Style) (Canvas, error)
}
Renderer converts a Matrix into a Canvas.
Implementations must be safe for concurrent use and must not retain the Matrix they are given.
type Report ¶
type Report struct {
// Score is 0..100, worst finding dominating.
Score int `json:"score"`
// Grade is the human summary of Score.
Grade Grade `json:"grade"`
// ContrastRatio is the WCAG ratio between module and background colour.
ContrastRatio float64 `json:"contrast_ratio"`
// QuietZone is the margin actually applied, in modules.
QuietZone int `json:"quiet_zone"`
// Inverted reports light modules on a dark background.
Inverted bool `json:"inverted"`
// Issues lists every finding, worst first.
Issues []Issue `json:"issues,omitempty"`
}
Report is the scannability verdict for a canvas.
The checks encode the failure modes that actually happen in the field: a designer picks brand colours that look fine on a monitor and fail on a phone camera in a shop, or strips the quiet zone to fit a layout. None of these produce an invalid code — they produce a valid code that no scanner can read, which is a much more expensive kind of wrong.
func Scannability ¶
Scannability analyses a rendered canvas.
It never mutates the canvas and never fails: an unreadable design is a report, not an error. Whether a report becomes a rejection is BARQR_STRICT_SCANNABILITY's decision, made one layer up.
type Severity ¶
type Severity string
Severity ranks a scannability finding.
const ( // SeverityInfo is worth knowing but not worth changing. SeverityInfo Severity = "info" // SeverityWarn means the code will probably scan, but not on every // device, in every light, at every size. SeverityWarn Severity = "warn" // SeverityError means the code is unlikely to scan at all. Strict mode // rejects the request rather than rendering it. SeverityError Severity = "error" )
Finding severities.
type Stop ¶
type Stop struct {
// Offset is the stop's position along the gradient, 0 to 1.
Offset float64
// Color is the colour at that position.
Color color.NRGBA
}
Stop is one colour stop of a gradient.
type Style ¶
type Style struct {
// Module names the shape each dark module takes. Must be a registered
// module shape.
Module string
// Eye names the shape of the three QR finder-pattern frames, and EyeBall
// the solid centre inside them. Ignored by symbologies without finders.
Eye string
EyeBall string
// FG and BG are the module and background colours. A fully transparent
// BG is legal and is what "transparent" parses to.
FG color.NRGBA
BG color.NRGBA
// EyeFG, when non-nil, colours the finder patterns differently from the
// data modules.
EyeFG *color.NRGBA
// Gradient, when non-nil, replaces FG for data modules. Finder patterns
// keep FG or EyeFG: a ramp that runs light across an eye costs the
// scanner the landmark it locates the symbol with.
Gradient *Gradient
// Logo, when non-nil, reserves an area in the centre of the symbol and,
// if Logo.Excavate is set, clears the modules underneath it.
Logo *Logo
// Frame, when non-nil, reserves a border around the code. The renderer
// grows the canvas for it; the writer draws it.
Frame *Frame
// Caption is the text for the caption band beneath the code. Frame.Caption
// takes precedence when both are set.
Caption string
// ECC records the error-correction level the symbol was encoded at, so
// that style-level checks can judge how much data a logo may safely
// destroy. It is informational only: the renderer never changes the
// encoding, and an empty value means "unknown", which the scannability
// checks treat conservatively.
ECC string
// QuietZone is the margin in modules. Negative means "use whatever the
// symbology specifies".
QuietZone int
// BarHeight is the height of a linear code in modules. Ignored for 2D.
BarHeight int
// HRI controls whether human-readable text accompanies a linear code.
HRI bool
// HRISize is the height of that text in modules. Zero means
// DefaultHRISize, so a Style built in Go without it still renders.
HRISize float64
// HRIFont names the type family for it. Empty means HRIFontMono.
HRIFont string
}
Style is the appearance of a rendered code. The zero value is not usable; build one with DefaultStyle and override from there.
func DefaultStyle ¶
func DefaultStyle() Style
DefaultStyle is a plain black-on-white code with the symbology's own quiet zone: the most scannable thing barqr can produce, and the baseline every other style is a deviation from.