qr

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: MIT Imports: 10 Imported by: 0

README

go-qr

A small QR code encoder for Go with no external dependencies.

It supports byte mode, error correction level M, and QR versions 1–20, for payloads up to 666 bytes.

Install

go get github.com/0xMillz/go-qr

Usage

import "github.com/0xMillz/go-qr" // package name is qr

code, err := qr.EncodeString("https://example.com/")
if err != nil {
    return err
}

svg := code.SVG("Example link") // template.HTML, ready to inline

Byte mode can encode arbitrary data. Alphanumeric and kanji modes can fit more data for certain character sets, but supporting byte mode alone keeps the implementation and test surface manageable.

Every supported version is tested at its maximum capacity.

Why this package?

Go already has mature QR encoders, including rsc.io/qr and skip2/go-qrcode. Those are good choices when you want a widely deployed, general-purpose package.

go-qr has a narrower focus:

  • It uses only the Go standard library.
  • Its SVG and PNG output is self-contained and works offline.
  • It supports a smaller portion of the QR specification, with tests covering every supported version at capacity.
  • The implementation is split into a few small files that follow the stages of QR encoding.

Having no transitive dependencies is useful when encoding sensitive values such as payment addresses. Self-contained output also means rendering a code does not make any external requests or disclose its contents to another service.

Output

Call Returns Notes
Encode / EncodeString *Code Encodes the data and returns the module grid. Use Code.Module(x, y) to inspect individual modules.
Code.SVG(label) template.HTML Returns a single inline SVG element.
Code.Image(scale) *image.Paletted Returns a 1-bit bitmap with the quiet zone included.
Code.DataURI(scale) string Returns a base64-encoded PNG data URI for use in an image source.
Render(data, label) template.HTML Returns an inline <img> containing the PNG. It returns an empty string if encoding fails.

SVG and Render do not include scripts, external references, or resources fetched at render time. Tests check this behavior.

Codes are always rendered black on white. This gives scanners a predictable, high-contrast image regardless of the surrounding page theme.

How it works

Encoding is divided into five stages:

  1. Bitstream (qr.go)
    Writes the mode indicator, character count, payload, terminator, and alternating 0xEC and 0x11 padding bytes.

  2. Error correction (galois.go)
    Generates Reed–Solomon parity bytes over GF(256).

  3. Interleaving (qr.go)
    Splits data into blocks, generates parity for each block, and interleaves their codewords.

  4. Layout (matrix.go)
    Adds the fixed QR patterns and writes the data using the required two-column zigzag.

  5. Masking (matrix.go)
    Tries all eight mask patterns and selects the one with the lowest penalty score.

Reed–Solomon error correction

QR error correction uses arithmetic in the finite field GF(256), whose elements correspond to the 256 possible byte values.

Addition is XOR. Multiplication is polynomial multiplication modulo the primitive polynomial:

x^8 + x^4 + x^3 + x^2 + 1     (0x11D)

Rather than performing polynomial multiplication for every operation, the package builds exponent and logarithm tables during initialization:

gfExp[i] = a^i // exponent to byte
gfLog[b] = i   // byte to exponent

Multiplication can then be implemented by adding logarithms:

gfExp[gfLog[a] + gfLog[b]]

The exponent table contains 512 entries, with values repeated after index 255. This handles the field’s period without requiring a modulo operation for each lookup.

Parity bytes are the remainder obtained by dividing the message polynomial by the generator polynomial:

g(x) = (x - a^0)(x - a^1) ... (x - a^(n-1))

rsDivisor constructs this polynomial one root at a time. rsRemainder performs synthetic division to calculate the parity block.

A valid block consisting of data followed by its parity is exactly divisible by g(x). The tests use this property to detect errors in encoding, interleaving, placement, or masking.

Mask selection

QR data is XORed with one of eight predefined mask patterns. Masking prevents payloads from producing large blank areas or patterns that could be confused with the symbol’s finder patterns.

The encoder scores all eight masks using the four penalty rules from the QR specification:

  • Long runs of the same color
  • Blocks of matching 2×2 modules
  • Finder-like 1:1:3:1:1 patterns
  • A large imbalance between dark and light modules

The mask with the lowest score is used.

Applying an XOR mask twice restores the original data, so each candidate can be tested and then removed without copying the entire grid.

Format information contains the error correction level and selected mask. It is encoded with BCH(15,5), XORed with 0x5412, and written in two places. Versions 7 and above also contain two copies of an 18-bit BCH-encoded version block.

Testing

Run the test suite with:

go test ./...

The tests cover:

  • Reading each generated symbol back from its module grid
  • Decoding the format bits and reversing the selected mask
  • Reversing the zigzag layout and de-interleaving the data blocks
  • Checking every Reed–Solomon block for a zero remainder
  • Encoding every supported version at maximum capacity
  • Verifying fixed patterns at their expected positions
  • Confirming that SVG and PNG output is self-contained

These checks catch disagreements between the different stages of the encoder. They cannot catch an interpretation error shared by both the implementation and its test helpers.

For an independent check, cmd/qrdump writes a QR code to a PNG that can be scanned with a phone or decoded by another library:

go run ./cmd/qrdump /tmp/qr "some payload"

License

MIT

Documentation

Overview

Package qr encodes data as QR codes and renders them as SVG or PNG.

It has no dependencies outside the standard library, and the whole encoder — bitstream, Reed-Solomon parity over GF(256), matrix layout and mask selection — is implemented here rather than bound to a C library.

Scope is deliberately narrow: byte mode, error correction level M, versions 1-20 (up to 666 bytes). Byte mode encodes any payload, so the alphanumeric and kanji modes only buy density for restricted character sets; leaving them out, along with versions 21-40, keeps the surface small enough to test exhaustively. Every version this package claims to support is verified at capacity by its tests.

Rendering is black on white and ignores any surrounding theme. A QR code is read by a camera, not a person, and light-on-dark defeats most scanners.

Index

Constants

View Source
const QuietZone = 4

QuietZone is the light margin the specification requires around a symbol. Without it many scanners will not lock on.

Variables

View Source
var ErrTooLong = errors.New("qr: data too long")

ErrTooLong means the data does not fit in a version-20 symbol at level M.

Functions

func Render

func Render(data, label string) template.HTML

Render encodes data and returns an <img> element carrying the QR code as an inline PNG, or an empty string if the data cannot be encoded. Callers that can proceed without a QR code use this to avoid failing a whole page over one element.

The image is embedded as a data URI rather than referenced by URL: nothing is fetched at render time, so the payload is never disclosed to a third party and the markup works offline. A test asserts the src is nothing but base64.

PNG rather than SVG for clients where SVG is unavailable or disabled. See Code.SVG, which is the better choice when it is.

Types

type Code

type Code struct {
	// Version is 1-20.
	Version int
	// Size is the side length in modules, 4*Version + 17.
	Size int
	// contains filtered or unexported fields
}

Code is an encoded QR symbol.

func Encode

func Encode(data []byte) (*Code, error)

Encode builds a QR code containing data, choosing the smallest version that fits.

func EncodeString

func EncodeString(s string) (*Code, error)

EncodeString is Encode for text.

func (*Code) DataURI

func (c *Code) DataURI(scale int) (string, error)

DataURI encodes the code as a base64 PNG data URI suitable for an <img> src.

func (*Code) Image

func (c *Code) Image(scale int) *image.Paletted

Image renders the code as a paletted bitmap at scale pixels per module, including the quiet zone the specification requires around the symbol.

func (*Code) Module

func (c *Code) Module(x, y int) bool

Module reports whether the module at (x, y) is dark. Out-of-range coordinates are light, which is what the surrounding quiet zone is.

func (*Code) SVG

func (c *Code) SVG(label string) template.HTML

SVG renders the code as a standalone <svg> element for inlining into a page.

The markup is self-contained: no <script>, no <use>, no external href or url() reference, so it renders identically with scripting and remote content disabled, and it cannot phone home. A test asserts this.

Resolution-independent and typically smaller than the equivalent PNG, so this is the better choice wherever SVG is available. Where it is not — some email clients, and browsers configured with svg.disabled — use Render, which emits a PNG.

Colors are hardcoded black on white rather than inheriting the page theme. A QR code is read by a camera, not a person, and light-on-dark defeats most scanners — so this is one element that must ignore dark mode.

Directories

Path Synopsis
cmd
qrdump command
Command qrdump writes QR codes as PNGs so the encoder's output can be checked against an independent decoder — a phone camera, or any third-party library.
Command qrdump writes QR codes as PNGs so the encoder's output can be checked against an independent decoder — a phone camera, or any third-party library.

Jump to

Keyboard shortcuts

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