QR Code Generator — Go source
Turn any text or URL into a downloadable QR code. Full ISO encoder with all error-correction levels, versions 1-40, SVG and PNG export, colors and quiet-zone control — runs entirely in your browser.
This is the Go implementation — the same logic the interactive tool runs, in a shareable, citable form.
// Package qrcodegen is the Go twin of CosmoDev's src/lib/qr.ts + src/lib/qr-tables.ts
// (dual source: the web lib is TypeScript, the CLI lib is Go — kept in lock-step).
// Pure QR encoder — ISO/IEC 18004, versions 1-40, EC levels L/M/Q/H, numeric +
// alphanumeric + byte modes, all 8 masks with penalty auto-select. Deterministic;
// invalid input returns nil (null in the TS lib).
//
// The table-driven tests in qr-code-generator_test.go share vectors with
// src/lib/qr.test.ts — including the Thonky 1-Q "HELLO WORLD" golden matrix —
// so the two implementations are held to one contract.
//
// Pipeline mirrors the TS lib exactly: mode auto-selection → smallest fitting
// version → bit buffer (mode + count + payload) → terminator/alignment/padding →
// block split → Reed-Solomon EC over GF(256) → interleave → matrix (finders,
// timing, alignment, dark module, format/version reservations) → zigzag data
// placement → mask (forced or penalty-selected from a pristine base) → format
// and version information.
package qrcodegen
import (
"fmt"
"math"
"strconv"
"strings"
)
// EcLevel is the error-correction level of the symbol.
type EcLevel int
const (
// EcM is the zero value so Options{} defaults to M, matching the TS
// default (ecLevel: 'M').
EcM EcLevel = iota
EcL
EcQ
EcH
)
// String returns the one-letter spec name ("L", "M", "Q", "H").
func (l EcLevel) String() string {
switch l {
case EcL:
return "L"
case EcM:
return "M"
case EcQ:
return "Q"
case EcH:
return "H"
}
return "?"
}
// Data mode names, identical to the TS union 'numeric' | 'alphanumeric' | 'byte'.
const (
modeNumeric = "numeric"
modeAlphanumeric = "alphanumeric"
modeByte = "byte"
)
// Options configures Encode. The zero value (Options{}) matches the TS default
// (encode(text)): level M, versions 1..40, automatic mask selection.
//
// Mask is a *int so the zero value means "automatic penalty selection" while a
// non-nil 0..7 forces that mask — exactly like the TS lib's distinction between
// an omitted and an explicit mask.
type Options struct {
EcLevel EcLevel // default EcM (zero value)
MinVersion int // 0 → 1
MaxVersion int // 0 → 40; values above 40 clamp to 40
Mask *int // nil → automatic; 0-7 forced, anything else → nil result
}
// Result is the encoded symbol. Modules is row-major; Modules[y][x]; true = dark.
type Result struct {
Version int
EcLevel EcLevel
Size int
Mask int
Mode string
Modules [][]bool
}
// SvgOptions configures ToSVG. The zero value matches the TS default
// (scale 4, margin 4, black on white).
type SvgOptions struct {
Scale int // 0 → 4
Margin int // 0 → 4
Fg string // "" → "#000000"
Bg string // "" → "#ffffff"
}
// ---------------------------------------------------------------------------
// Modes and character sets
// ---------------------------------------------------------------------------
// alnumCharset mirrors ALNUM_CHARSET in src/lib/qr.ts (ISO 18004 Table 5 JIS8
// subset + the four symbols), value = index.
const alnumCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
func pickMode(text string) string {
if len(text) > 0 && allIn(text, '0', '9') {
return modeNumeric
}
if len(text) > 0 && isAlphanumeric(text) {
return modeAlphanumeric
}
return modeByte
}
func allIn(s string, lo, hi rune) bool {
for _, r := range s {
if r < lo || r > hi {
return false
}
}
return true
}
func isAlphanumeric(s string) bool {
for _, r := range s {
if !strings.ContainsRune(alnumCharset, r) {
return false
}
}
return true
}
func countBitsFor(mode string, version int) int {
if mode == modeByte {
if version <= 9 {
return 8
}
return 16
}
if version <= 9 {
return 9
} else if version <= 26 {
return 11
}
return 13
}
func modeIndicator(mode string) int {
switch mode {
case modeNumeric:
return 0b0001
case modeAlphanumeric:
return 0b0010
default:
return 0b0100
}
}
func alnumIndex(c byte) int { return strings.IndexByte(alnumCharset, c) }
// ---------------------------------------------------------------------------
// BitBuffer — MSB-first packing
// ---------------------------------------------------------------------------
type bitBuffer struct{ bits []bool }
func (b *bitBuffer) length() int { return len(b.bits) }
func (b *bitBuffer) push(value, count int) {
for i := count - 1; i >= 0; i-- {
b.bits = append(b.bits, (value>>uint(i))&1 == 1)
}
}
func (b *bitBuffer) toBytes() []int {
// The caller pads to a byte boundary before calling, so every group is 8.
out := make([]int, 0, len(b.bits)/8)
for i := 0; i < len(b.bits); i += 8 {
v := 0
for j := 0; j < 8; j++ {
v = v<<1 | b2i(b.bits[i+j])
}
out = append(out, v)
}
return out
}
func b2i(b bool) int {
if b {
return 1
}
return 0
}
// ---------------------------------------------------------------------------
// Reed-Solomon over GF(256), primitive polynomial 0x11D
// ---------------------------------------------------------------------------
var gfExp = func() [256]int {
var exp [256]int
x := 1
for i := 0; i < 255; i++ {
exp[i] = x
x <<= 1
if x&0x100 != 0 {
x ^= 0x11d
}
}
return exp
}()
var gfLog = func() [256]int {
var log [256]int
x := 1
for i := 0; i < 255; i++ {
log[x] = i
x <<= 1
if x&0x100 != 0 {
x ^= 0x11d
}
}
return log
}()
// GFMul multiplies in GF(256) with primitive 0x11D. Exported to mirror the
// TS lib's gfMul.
func GFMul(a, b int) int {
if a == 0 || b == 0 {
return 0
}
return gfExp[(gfLog[a]+gfLog[b])%255]
}
// rsGenerator returns the generator polynomial of the given degree,
// coefficients highest-first, monic.
func rsGenerator(degree int) []int {
poly := []int{1}
for i := 0; i < degree; i++ {
next := make([]int, len(poly)+1)
for j := 0; j < len(poly); j++ {
next[j] ^= poly[j]
next[j+1] ^= GFMul(poly[j], gfExp[i])
}
poly = next
}
return poly
}
// rsEncode returns the error-correction codewords for data (synthetic
// division by the generator).
func rsEncode(data []int, ecLen int) []int {
gen := rsGenerator(ecLen)
rem := make([]int, ecLen)
for _, b := range data {
factor := b ^ rem[0]
copy(rem, rem[1:])
rem[ecLen-1] = 0
if factor != 0 {
for i := 0; i < ecLen; i++ {
rem[i] ^= GFMul(gen[i+1], factor)
}
}
}
return rem
}
// ---------------------------------------------------------------------------
// BCH format / version information (computed, not transcribed)
// ---------------------------------------------------------------------------
func ecFormatBits(level EcLevel) (int, bool) {
switch level {
case EcL:
return 0b01, true
case EcM:
return 0b00, true
case EcQ:
return 0b11, true
case EcH:
return 0b10, true
}
return 0, false
}
// FormatInfoString returns the 15-bit format info string (MSB first) for
// (level, mask), including the 0x5412 XOR mask.
func FormatInfoString(level EcLevel, mask int) string {
bits, ok := ecFormatBits(level)
if !ok {
return ""
}
data := bits<<3 | mask
v := data << 10
const G = 0x537
for i := 14; i >= 10; i-- {
if v&(1<<uint(i)) != 0 {
v ^= G << uint(i-10)
}
}
rem := v & 0x3ff
full := ((data << 10) | rem) ^ 0x5412
return fmt.Sprintf("%015b", full)
}
// VersionInfoString returns the 18-bit version info string (MSB first) for
// versions >= 7.
func VersionInfoString(version int) string {
v := version << 12
const G = 0x1f25
for i := 17; i >= 12; i-- {
if v&(1<<uint(i)) != 0 {
v ^= G << uint(i-12)
}
}
full := (version << 12) | (v & 0xfff)
return fmt.Sprintf("%018b", full)
}
// ---------------------------------------------------------------------------
// Matrix construction
// ---------------------------------------------------------------------------
type matrix struct {
size int
modules [][]bool
reserved [][]bool
}
func newMatrix(size int) *matrix {
m := &matrix{size: size}
m.modules = make([][]bool, size)
m.reserved = make([][]bool, size)
for i := range m.modules {
m.modules[i] = make([]bool, size)
m.reserved[i] = make([]bool, size)
}
return m
}
func (m *matrix) set(x, y int, dark bool) {
m.modules[y][x] = dark
m.reserved[y][x] = true
}
// write writes a DATA module — marks nothing reserved, so masking still applies.
func (m *matrix) write(x, y int, dark bool) {
m.modules[y][x] = dark
}
// reserve marks a format/version cell; written after masking.
func (m *matrix) reserve(x, y int) {
m.reserved[y][x] = true
}
func (m *matrix) drawFinder(x, y int) {
for dy := -1; dy <= 7; dy++ {
for dx := -1; dx <= 7; dx++ {
X, Y := x+dx, y+dy
if X < 0 || X >= m.size || Y < 0 || Y >= m.size {
continue
}
dark := (dx >= 0 && dx <= 6 && (dy == 0 || dy == 6)) ||
(dy >= 0 && dy <= 6 && (dx == 0 || dx == 6)) ||
(dx >= 2 && dx <= 4 && dy >= 2 && dy <= 4)
m.set(X, Y, dark)
}
}
}
func (m *matrix) drawAlignment(cx, cy int) {
for dy := -2; dy <= 2; dy++ {
for dx := -2; dx <= 2; dx++ {
dark := max(abs(dx), abs(dy)) != 1
m.set(cx+dx, cy+dy, dark)
}
}
}
func abs(v int) int {
if v < 0 {
return -v
}
return v
}
func (m *matrix) drawFunctionPatterns(version int) {
size := m.size
m.drawFinder(0, 0)
m.drawFinder(size-7, 0)
m.drawFinder(0, size-7)
for i := 8; i < size-8; i++ {
dark := i%2 == 0
m.set(i, 6, dark)
m.set(6, i, dark)
}
m.set(8, size-8, true) // dark module: (col 8, row 4*version+9) = size-8
// Alignment patterns — skip the three that would overlap finders.
// (Version 1 has no centers, so `last` is only read when non-empty.)
if centers := alignmentCenters[version-1]; len(centers) > 0 {
last := centers[len(centers)-1]
for _, cy := range centers {
for _, cx := range centers {
if (cx == 6 && cy == 6) || (cx == 6 && cy == last) || (cx == last && cy == 6) {
continue
}
m.drawAlignment(cx, cy)
}
}
}
m.reserveFormatAreas(version)
}
// reserveFormatAreas reserves both format copies + the version info areas (v >= 7).
func (m *matrix) reserveFormatAreas(version int) {
size := m.size
for i := 0; i <= 8; i++ {
m.reserve(i, 8) // row 8, cols 0-8 (skip col 6 — already timing)
if i != 6 {
m.reserve(8, i) // col 8, rows 0-8
}
}
for i := 0; i < 8; i++ {
m.reserve(size-1-i, 8) // col 8, bottom 8 rows
}
for i := 0; i < 8; i++ {
m.reserve(8, size-1-i) // row 8, right 8 cols
}
if version >= 7 {
// 3x6 + 6x3 blocks at bottom-right of the top-right finder and
// right of the bottom-left finder. Both copies mirror each other.
for i := 0; i < 18; i++ {
a := size - 11 + (i % 3)
b := i / 3
m.reserve(a, b)
m.reserve(b, a)
}
}
}
func (m *matrix) drawFormatAndVersion(ecLevel EcLevel, mask, version int) {
size := m.size
format := FormatInfoString(ecLevel, mask)
bit := func(i int) bool { return format[i] == '1' }
// Copy 1 (top-left): row 8 cols 0-5,7,8 then col 8 rows 7,5..0 — MSB first.
copy1 := [15][2]int{
{0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {7, 8}, {8, 8},
{8, 7}, {8, 5}, {8, 4}, {8, 3}, {8, 2}, {8, 1}, {8, 0},
}
for i, p := range copy1 {
m.set(p[0], p[1], bit(i))
}
// Copy 2: col 8 bottom 7 rows, then row 8 right 8 cols — MSB first.
for i := 0; i < 7; i++ {
m.set(8, size-1-i, bit(i))
}
for i := 7; i < 15; i++ {
m.set(size-15+i, 8, bit(i))
}
if version >= 7 {
vinfo := VersionInfoString(version)
for i := 0; i < 18; i++ {
dark := vinfo[i] == '1'
a := size - 11 + (i % 3)
b := i / 3
m.set(a, b, dark)
m.set(b, a, dark)
}
}
}
// ---------------------------------------------------------------------------
// Masking and penalty
// ---------------------------------------------------------------------------
func maskBit(mask, x, y int) bool {
switch mask {
case 0:
return (x+y)%2 == 0
case 1:
return y%2 == 0
case 2:
return x%3 == 0
case 3:
return (x+y)%3 == 0
case 4:
return (y/2 + x/3) % 2 == 0
case 5:
return (x*y)%2+(x*y)%3 == 0
case 6:
return ((x*y)%2+(x*y)%3)%2 == 0
default:
return ((x+y)%2+(x*y)%3)%2 == 0
}
}
func penalty(m *matrix) int {
size := m.size
mod := m.modules
score := 0
// N1: runs of 5+ same color, rows and columns.
runScore := func(get func(i, j int) bool) int {
s := 0
for i := 0; i < size; i++ {
run := 1
color := get(i, 0)
for j := 1; j < size; j++ {
c := get(i, j)
if c == color {
run++
if run == 5 {
s += 3
} else if run > 5 {
s++
}
} else {
run = 1
color = c
}
}
}
return s
}
score += runScore(func(y, x int) bool { return mod[y][x] })
score += runScore(func(x, y int) bool { return mod[y][x] })
// N2: 2x2 blocks of one color.
for y := 0; y < size-1; y++ {
for x := 0; x < size-1; x++ {
c := mod[y][x]
if c == mod[y][x+1] && c == mod[y+1][x] && c == mod[y+1][x+1] {
score += 3
}
}
}
// N3: finder-like 1011101 with 4 light modules on one side.
finderPenalty := func(get func(i, j int) bool) int {
s := 0
for i := 0; i < size; i++ {
for j := 0; j <= size-11; j++ {
fwd, rev := true, true
for k := 0; k < 11; k++ {
dark := get(i, j+k)
if dark != seqPattern[k] {
fwd = false
}
if dark != seqPatternRev[k] {
rev = false
}
}
if fwd || rev {
s += 40
}
}
}
return s
}
score += finderPenalty(func(y, x int) bool { return mod[y][x] })
score += finderPenalty(func(x, y int) bool { return mod[y][x] })
// N4: dark-module ratio deviation from 50%.
dark := 0
for _, row := range mod {
for _, cell := range row {
if cell {
dark++
}
}
}
total := size * size
k := int(math.Floor(math.Abs(float64(dark)*100/float64(total)-50) / 5))
score += k * 10
return score
}
var seqPattern = [11]bool{true, false, true, true, true, false, true, false, false, false, false}
var seqPatternRev = [11]bool{false, false, false, false, true, false, true, true, true, false, true}
// ---------------------------------------------------------------------------
// Encoder pipeline
// ---------------------------------------------------------------------------
func dataCapacityCodewords(version int, ecLevel EcLevel) int {
spec := ecTable(ecLevel)[version-1]
sum := 0
for _, b := range spec.blocks {
sum += b.count * b.dataCodewords
}
return sum
}
func payloadBits(mode string, text string, bytes []int, version int, buf *bitBuffer) {
count := len(text)
if mode == modeByte {
count = len(bytes)
}
buf.push(modeIndicator(mode), 4)
buf.push(count, countBitsFor(mode, version))
if mode == modeNumeric {
for i := 0; i < len(text); i += 3 {
end := i + 3
if end > len(text) {
end = len(text)
}
chunk := text[i:end]
value, _ := strconv.Atoi(chunk)
switch len(chunk) {
case 3:
buf.push(value, 10)
case 2:
buf.push(value, 7)
default:
buf.push(value, 4)
}
}
} else if mode == modeAlphanumeric {
for i := 0; i < len(text); i += 2 {
if i+1 < len(text) {
buf.push(alnumIndex(text[i])*45+alnumIndex(text[i+1]), 11)
} else {
buf.push(alnumIndex(text[i]), 6)
}
}
} else {
for _, b := range bytes {
buf.push(b, 8)
}
}
}
// estimatePayloadSize returns the payload bit count for the mode, independent
// of the count-indicator width.
func estimatePayloadSize(mode string, text string, bytes []int) int {
switch mode {
case modeNumeric:
pairs := len(text) / 3
rest := len(text) - pairs*3
bits := pairs * 10
if rest == 2 {
bits += 7
} else if rest == 1 {
bits += 4
}
return bits
case modeAlphanumeric:
pairs := len(text) / 2
rest := len(text) - pairs*2
if rest == 1 {
return pairs*11 + 6
}
return pairs * 11
default:
return len(bytes) * 8
}
}
// Encode produces the QR symbol for text. It returns nil when the options are
// inconsistent (bad mask range, min > max, invalid level) or no version in
// [MinVersion..MaxVersion] can hold the text — nil is the TS null.
func Encode(text string, opts Options) *Result {
ecLevel := opts.EcLevel
if ecTable(ecLevel) == nil {
return nil
}
minVersion := opts.MinVersion
if minVersion == 0 {
minVersion = 1
}
maxVersion := opts.MaxVersion
if maxVersion == 0 {
maxVersion = 40
}
if maxVersion > 40 {
maxVersion = 40
}
if minVersion < 1 || minVersion > maxVersion {
return nil
}
mode := pickMode(text)
var bytes []int
if mode == modeByte {
for _, b := range []byte(text) {
bytes = append(bytes, int(b))
}
}
// Smallest version whose data capacity fits the payload.
version := 0
for v := minVersion; v <= maxVersion; v++ {
needed := 4 + countBitsFor(mode, v) + estimatePayloadSize(mode, text, bytes)
if needed <= dataCapacityCodewords(v, ecLevel)*8 {
version = v
break
}
}
if version == 0 {
return nil
}
capacityBits := dataCapacityCodewords(version, ecLevel) * 8
buf := &bitBuffer{}
payloadBits(mode, text, bytes, version, buf)
// Terminator (up to 4 bits), byte alignment, pad codewords.
maxTerminator := 4
if capacityBits-buf.length() < maxTerminator {
maxTerminator = capacityBits - buf.length()
}
if maxTerminator > 0 {
buf.push(0, maxTerminator)
}
for buf.length()%8 != 0 {
buf.push(0, 1)
}
padBytes := [2]int{0xec, 0x11}
padIndex := 0
for buf.length() < capacityBits {
buf.push(padBytes[padIndex%2], 8)
padIndex++
}
dataCodewords := buf.toBytes()
// Split into blocks, compute EC, interleave.
spec := ecTable(ecLevel)[version-1]
var dataBlocks [][]int
for _, group := range spec.blocks {
for b := 0; b < group.count; b++ {
offset := 0
for _, blk := range dataBlocks {
offset += len(blk)
}
dataBlocks = append(dataBlocks, dataCodewords[offset:offset+group.dataCodewords])
}
}
ecBlocks := make([][]int, len(dataBlocks))
for i, blk := range dataBlocks {
ecBlocks[i] = rsEncode(blk, spec.ecPerBlock)
}
var finalCodewords []int
maxData := 0
for _, blk := range dataBlocks {
if len(blk) > maxData {
maxData = len(blk)
}
}
for i := 0; i < maxData; i++ {
for _, blk := range dataBlocks {
if i < len(blk) {
finalCodewords = append(finalCodewords, blk[i])
}
}
}
for i := 0; i < spec.ecPerBlock; i++ {
for _, blk := range ecBlocks {
finalCodewords = append(finalCodewords, blk[i])
}
}
// Matrix: function patterns, zigzag data, mask, format/version info.
size := version*4 + 17
m := newMatrix(size)
m.drawFunctionPatterns(version)
var bitStream []bool
for _, cw := range finalCodewords {
for i := 7; i >= 0; i-- {
bitStream = append(bitStream, (cw>>uint(i))&1 == 1)
}
}
bitIndex := 0
upward := true
for right := size - 1; right >= 1; right -= 2 {
if right == 6 {
right--
}
for vert := 0; vert < size; vert++ {
y := vert
if upward {
y = size - 1 - vert
}
for _, x := range [2]int{right, right - 1} {
if m.reserved[y][x] {
continue
}
dark := false
if bitIndex < len(bitStream) {
dark = bitStream[bitIndex]
}
m.write(x, y, dark)
bitIndex++
}
}
upward = !upward
}
// Mask from a pristine post-placement base — candidates never accumulate.
base := make([][]bool, size)
for y := range base {
base[y] = append([]bool(nil), m.modules[y]...)
}
applyToModules := func(mask int) {
for y := 0; y < size; y++ {
for x := 0; x < size; x++ {
m.modules[y][x] = base[y][x]
if !m.reserved[y][x] && maskBit(mask, x, y) {
m.modules[y][x] = !m.modules[y][x]
}
}
}
}
chosenMask := 0
if opts.Mask != nil {
if *opts.Mask < 0 || *opts.Mask > 7 {
return nil
}
chosenMask = *opts.Mask
applyToModules(chosenMask)
} else {
best := math.MaxInt
for mask := 0; mask < 8; mask++ {
applyToModules(mask)
if p := penalty(m); p < best {
best = p
chosenMask = mask
}
}
applyToModules(chosenMask)
}
m.drawFormatAndVersion(ecLevel, chosenMask, version)
return &Result{Version: version, EcLevel: ecLevel, Size: size, Mask: chosenMask, Mode: mode, Modules: m.modules}
}
// ---------------------------------------------------------------------------
// SVG rendering
// ---------------------------------------------------------------------------
// ToSVG renders the symbol as a standalone SVG string (mirrors toSvg in the
// TS lib): one path subcommand per dark module, crispEdges, aria-label.
func ToSVG(res *Result, o SvgOptions) string {
scale := o.Scale
if scale == 0 {
scale = 4
}
margin := o.Margin
if margin == 0 {
margin = 4
}
fg := o.Fg
if fg == "" {
fg = "#000000"
}
bg := o.Bg
if bg == "" {
bg = "#ffffff"
}
dim := (res.Size + margin*2) * scale
var path strings.Builder
for y := 0; y < res.Size; y++ {
for x := 0; x < res.Size; x++ {
if !res.Modules[y][x] {
continue
}
px := (x + margin) * scale
py := (y + margin) * scale
fmt.Fprintf(&path, "M%d %dh%dv%dh-%dz", px, py, scale, scale, scale)
}
}
return fmt.Sprintf(
`<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d" viewBox="0 0 %d %d" `+
`shape-rendering="crispEdges" role="img" aria-label="QR code">`+
`<rect width="%d" height="%d" fill="%s"/>`+
`<path d="%s" fill="%s"/>`+
`</svg>`,
dim, dim, dim, dim, dim, dim, bg, path.String(), fg)
}
// ---------------------------------------------------------------------------
// Spec tables — transcribed verbatim from src/lib/qr-tables.ts
//
// qr-tables.ts documents its three independent sources (nayuki QR-Code-generator,
// thonky.com, davidshimjs/qrcodejs) and the codeword invariant every entry
// satisfies; qr-code-generator_test.go re-proves that invariant here.
// ---------------------------------------------------------------------------
// ecBlockGroup is one block group: count blocks, each carrying dataCodewords
// data codewords.
type ecBlockGroup struct {
count int
dataCodewords int
}
// ecSpec is the error-correction layout of one (version, level) pair.
type ecSpec struct {
totalCodewords int
ecPerBlock int
blocks []ecBlockGroup
}
// The four level tables, indexed [version-1]. L/M/Q/H order as in the TS file.
var (
ecBlocksL = [40]ecSpec{
/* 1-L */ {26, 7, []ecBlockGroup{{1, 19}}},
/* 2-L */ {44, 10, []ecBlockGroup{{1, 34}}},
/* 3-L */ {70, 15, []ecBlockGroup{{1, 55}}},
/* 4-L */ {100, 20, []ecBlockGroup{{1, 80}}},
/* 5-L */ {134, 26, []ecBlockGroup{{1, 108}}},
/* 6-L */ {172, 18, []ecBlockGroup{{2, 68}}},
/* 7-L */ {196, 20, []ecBlockGroup{{2, 78}}},
/* 8-L */ {242, 24, []ecBlockGroup{{2, 97}}},
/* 9-L */ {292, 30, []ecBlockGroup{{2, 116}}},
/* 10-L */ {346, 18, []ecBlockGroup{{2, 68}, {2, 69}}},
/* 11-L */ {404, 20, []ecBlockGroup{{4, 81}}},
/* 12-L */ {466, 24, []ecBlockGroup{{2, 92}, {2, 93}}},
/* 13-L */ {532, 26, []ecBlockGroup{{4, 107}}},
/* 14-L */ {581, 30, []ecBlockGroup{{3, 115}, {1, 116}}},
/* 15-L */ {655, 22, []ecBlockGroup{{5, 87}, {1, 88}}},
/* 16-L */ {733, 24, []ecBlockGroup{{5, 98}, {1, 99}}},
/* 17-L */ {815, 28, []ecBlockGroup{{1, 107}, {5, 108}}},
/* 18-L */ {901, 30, []ecBlockGroup{{5, 120}, {1, 121}}},
/* 19-L */ {991, 28, []ecBlockGroup{{3, 113}, {4, 114}}},
/* 20-L */ {1085, 28, []ecBlockGroup{{3, 107}, {5, 108}}},
/* 21-L */ {1156, 28, []ecBlockGroup{{4, 116}, {4, 117}}},
/* 22-L */ {1258, 28, []ecBlockGroup{{2, 111}, {7, 112}}},
/* 23-L */ {1364, 30, []ecBlockGroup{{4, 121}, {5, 122}}},
/* 24-L */ {1474, 30, []ecBlockGroup{{6, 117}, {4, 118}}},
/* 25-L */ {1588, 26, []ecBlockGroup{{8, 106}, {4, 107}}},
/* 26-L */ {1706, 28, []ecBlockGroup{{10, 114}, {2, 115}}},
/* 27-L */ {1828, 30, []ecBlockGroup{{8, 122}, {4, 123}}},
/* 28-L */ {1921, 30, []ecBlockGroup{{3, 117}, {10, 118}}},
/* 29-L */ {2051, 30, []ecBlockGroup{{7, 116}, {7, 117}}},
/* 30-L */ {2185, 30, []ecBlockGroup{{5, 115}, {10, 116}}},
/* 31-L */ {2323, 30, []ecBlockGroup{{13, 115}, {3, 116}}},
/* 32-L */ {2465, 30, []ecBlockGroup{{17, 115}}},
/* 33-L */ {2611, 30, []ecBlockGroup{{17, 115}, {1, 116}}},
/* 34-L */ {2761, 30, []ecBlockGroup{{13, 115}, {6, 116}}},
/* 35-L */ {2876, 30, []ecBlockGroup{{12, 121}, {7, 122}}},
/* 36-L */ {3034, 30, []ecBlockGroup{{6, 121}, {14, 122}}},
/* 37-L */ {3196, 30, []ecBlockGroup{{17, 122}, {4, 123}}},
/* 38-L */ {3362, 30, []ecBlockGroup{{4, 122}, {18, 123}}},
/* 39-L */ {3532, 30, []ecBlockGroup{{20, 117}, {4, 118}}},
/* 40-L */ {3706, 30, []ecBlockGroup{{19, 118}, {6, 119}}},
}
ecBlocksM = [40]ecSpec{
/* 1-M */ {26, 10, []ecBlockGroup{{1, 16}}},
/* 2-M */ {44, 16, []ecBlockGroup{{1, 28}}},
/* 3-M */ {70, 26, []ecBlockGroup{{1, 44}}},
/* 4-M */ {100, 18, []ecBlockGroup{{2, 32}}},
/* 5-M */ {134, 24, []ecBlockGroup{{2, 43}}},
/* 6-M */ {172, 16, []ecBlockGroup{{4, 27}}},
/* 7-M */ {196, 18, []ecBlockGroup{{4, 31}}},
/* 8-M */ {242, 22, []ecBlockGroup{{2, 38}, {2, 39}}},
/* 9-M */ {292, 22, []ecBlockGroup{{3, 36}, {2, 37}}},
/* 10-M */ {346, 26, []ecBlockGroup{{4, 43}, {1, 44}}},
/* 11-M */ {404, 30, []ecBlockGroup{{1, 50}, {4, 51}}},
/* 12-M */ {466, 22, []ecBlockGroup{{6, 36}, {2, 37}}},
/* 13-M */ {532, 22, []ecBlockGroup{{8, 37}, {1, 38}}},
/* 14-M */ {581, 24, []ecBlockGroup{{4, 40}, {5, 41}}},
/* 15-M */ {655, 24, []ecBlockGroup{{5, 41}, {5, 42}}},
/* 16-M */ {733, 28, []ecBlockGroup{{7, 45}, {3, 46}}},
/* 17-M */ {815, 28, []ecBlockGroup{{10, 46}, {1, 47}}},
/* 18-M */ {901, 26, []ecBlockGroup{{9, 43}, {4, 44}}},
/* 19-M */ {991, 26, []ecBlockGroup{{3, 44}, {11, 45}}},
/* 20-M */ {1085, 26, []ecBlockGroup{{3, 41}, {13, 42}}},
/* 21-M */ {1156, 26, []ecBlockGroup{{17, 42}}},
/* 22-M */ {1258, 28, []ecBlockGroup{{17, 46}}},
/* 23-M */ {1364, 28, []ecBlockGroup{{4, 47}, {14, 48}}},
/* 24-M */ {1474, 28, []ecBlockGroup{{6, 45}, {14, 46}}},
/* 25-M */ {1588, 28, []ecBlockGroup{{8, 47}, {13, 48}}},
/* 26-M */ {1706, 28, []ecBlockGroup{{19, 46}, {4, 47}}},
/* 27-M */ {1828, 28, []ecBlockGroup{{22, 45}, {3, 46}}},
/* 28-M */ {1921, 28, []ecBlockGroup{{3, 45}, {23, 46}}},
/* 29-M */ {2051, 28, []ecBlockGroup{{21, 45}, {7, 46}}},
/* 30-M */ {2185, 28, []ecBlockGroup{{19, 47}, {10, 48}}},
/* 31-M */ {2323, 28, []ecBlockGroup{{2, 46}, {29, 47}}},
/* 32-M */ {2465, 28, []ecBlockGroup{{10, 46}, {23, 47}}},
/* 33-M */ {2611, 28, []ecBlockGroup{{14, 46}, {21, 47}}},
/* 34-M */ {2761, 28, []ecBlockGroup{{14, 46}, {23, 47}}},
/* 35-M */ {2876, 28, []ecBlockGroup{{12, 47}, {26, 48}}},
/* 36-M */ {3034, 28, []ecBlockGroup{{6, 47}, {34, 48}}},
/* 37-M */ {3196, 28, []ecBlockGroup{{29, 46}, {14, 47}}},
/* 38-M */ {3362, 28, []ecBlockGroup{{13, 46}, {32, 47}}},
/* 39-M */ {3532, 28, []ecBlockGroup{{40, 47}, {7, 48}}},
/* 40-M */ {3706, 28, []ecBlockGroup{{18, 47}, {31, 48}}},
}
ecBlocksQ = [40]ecSpec{
/* 1-Q */ {26, 13, []ecBlockGroup{{1, 13}}},
/* 2-Q */ {44, 22, []ecBlockGroup{{1, 22}}},
/* 3-Q */ {70, 18, []ecBlockGroup{{2, 17}}},
/* 4-Q */ {100, 26, []ecBlockGroup{{2, 24}}},
/* 5-Q */ {134, 18, []ecBlockGroup{{2, 15}, {2, 16}}},
/* 6-Q */ {172, 24, []ecBlockGroup{{4, 19}}},
/* 7-Q */ {196, 18, []ecBlockGroup{{2, 14}, {4, 15}}},
/* 8-Q */ {242, 22, []ecBlockGroup{{4, 18}, {2, 19}}},
/* 9-Q */ {292, 20, []ecBlockGroup{{4, 16}, {4, 17}}},
/* 10-Q */ {346, 24, []ecBlockGroup{{6, 19}, {2, 20}}},
/* 11-Q */ {404, 28, []ecBlockGroup{{4, 22}, {4, 23}}},
/* 12-Q */ {466, 26, []ecBlockGroup{{4, 20}, {6, 21}}},
/* 13-Q */ {532, 24, []ecBlockGroup{{8, 20}, {4, 21}}},
/* 14-Q */ {581, 20, []ecBlockGroup{{11, 16}, {5, 17}}},
/* 15-Q */ {655, 30, []ecBlockGroup{{5, 24}, {7, 25}}},
/* 16-Q */ {733, 24, []ecBlockGroup{{15, 19}, {2, 20}}},
/* 17-Q */ {815, 28, []ecBlockGroup{{1, 22}, {15, 23}}},
/* 18-Q */ {901, 28, []ecBlockGroup{{17, 22}, {1, 23}}},
/* 19-Q */ {991, 26, []ecBlockGroup{{17, 21}, {4, 22}}},
/* 20-Q */ {1085, 30, []ecBlockGroup{{15, 24}, {5, 25}}},
/* 21-Q */ {1156, 28, []ecBlockGroup{{17, 22}, {6, 23}}},
/* 22-Q */ {1258, 30, []ecBlockGroup{{7, 24}, {16, 25}}},
/* 23-Q */ {1364, 30, []ecBlockGroup{{11, 24}, {14, 25}}},
/* 24-Q */ {1474, 30, []ecBlockGroup{{11, 24}, {16, 25}}},
/* 25-Q */ {1588, 30, []ecBlockGroup{{7, 24}, {22, 25}}},
/* 26-Q */ {1706, 28, []ecBlockGroup{{28, 22}, {6, 23}}},
/* 27-Q */ {1828, 30, []ecBlockGroup{{8, 23}, {26, 24}}},
/* 28-Q */ {1921, 30, []ecBlockGroup{{4, 24}, {31, 25}}},
/* 29-Q */ {2051, 30, []ecBlockGroup{{1, 23}, {37, 24}}},
/* 30-Q */ {2185, 30, []ecBlockGroup{{15, 24}, {25, 25}}},
/* 31-Q */ {2323, 30, []ecBlockGroup{{42, 24}, {1, 25}}},
/* 32-Q */ {2465, 30, []ecBlockGroup{{10, 24}, {35, 25}}},
/* 33-Q */ {2611, 30, []ecBlockGroup{{29, 24}, {19, 25}}},
/* 34-Q */ {2761, 30, []ecBlockGroup{{44, 24}, {7, 25}}},
/* 35-Q */ {2876, 30, []ecBlockGroup{{39, 24}, {14, 25}}},
/* 36-Q */ {3034, 30, []ecBlockGroup{{46, 24}, {10, 25}}},
/* 37-Q */ {3196, 30, []ecBlockGroup{{49, 24}, {10, 25}}},
/* 38-Q */ {3362, 30, []ecBlockGroup{{48, 24}, {14, 25}}},
/* 39-Q */ {3532, 30, []ecBlockGroup{{43, 24}, {22, 25}}},
/* 40-Q */ {3706, 30, []ecBlockGroup{{34, 24}, {34, 25}}},
}
ecBlocksH = [40]ecSpec{
/* 1-H */ {26, 17, []ecBlockGroup{{1, 9}}},
/* 2-H */ {44, 28, []ecBlockGroup{{1, 16}}},
/* 3-H */ {70, 22, []ecBlockGroup{{2, 13}}},
/* 4-H */ {100, 16, []ecBlockGroup{{4, 9}}},
/* 5-H */ {134, 22, []ecBlockGroup{{2, 11}, {2, 12}}},
/* 6-H */ {172, 28, []ecBlockGroup{{4, 15}}},
/* 7-H */ {196, 26, []ecBlockGroup{{4, 13}, {1, 14}}},
/* 8-H */ {242, 26, []ecBlockGroup{{4, 14}, {2, 15}}},
/* 9-H */ {292, 24, []ecBlockGroup{{4, 12}, {4, 13}}},
/* 10-H */ {346, 28, []ecBlockGroup{{6, 15}, {2, 16}}},
/* 11-H */ {404, 24, []ecBlockGroup{{3, 12}, {8, 13}}},
/* 12-H */ {466, 28, []ecBlockGroup{{7, 14}, {4, 15}}},
/* 13-H */ {532, 22, []ecBlockGroup{{12, 11}, {4, 12}}},
/* 14-H */ {581, 24, []ecBlockGroup{{11, 12}, {5, 13}}},
/* 15-H */ {655, 24, []ecBlockGroup{{11, 12}, {7, 13}}},
/* 16-H */ {733, 30, []ecBlockGroup{{3, 15}, {13, 16}}},
/* 17-H */ {815, 28, []ecBlockGroup{{2, 14}, {17, 15}}},
/* 18-H */ {901, 28, []ecBlockGroup{{2, 14}, {19, 15}}},
/* 19-H */ {991, 26, []ecBlockGroup{{9, 13}, {16, 14}}},
/* 20-H */ {1085, 28, []ecBlockGroup{{15, 15}, {10, 16}}},
/* 21-H */ {1156, 30, []ecBlockGroup{{19, 16}, {6, 17}}},
/* 22-H */ {1258, 24, []ecBlockGroup{{34, 13}}},
/* 23-H */ {1364, 30, []ecBlockGroup{{16, 15}, {14, 16}}},
/* 24-H */ {1474, 30, []ecBlockGroup{{30, 16}, {2, 17}}},
/* 25-H */ {1588, 30, []ecBlockGroup{{22, 15}, {13, 16}}},
/* 26-H */ {1706, 30, []ecBlockGroup{{33, 16}, {4, 17}}},
/* 27-H */ {1828, 30, []ecBlockGroup{{12, 15}, {28, 16}}},
/* 28-H */ {1921, 30, []ecBlockGroup{{11, 15}, {31, 16}}},
/* 29-H */ {2051, 30, []ecBlockGroup{{19, 15}, {26, 16}}},
/* 30-H */ {2185, 30, []ecBlockGroup{{23, 15}, {25, 16}}},
/* 31-H */ {2323, 30, []ecBlockGroup{{23, 15}, {28, 16}}},
/* 32-H */ {2465, 30, []ecBlockGroup{{19, 15}, {35, 16}}},
/* 33-H */ {2611, 30, []ecBlockGroup{{11, 15}, {46, 16}}},
/* 34-H */ {2761, 30, []ecBlockGroup{{59, 16}, {1, 17}}},
/* 35-H */ {2876, 30, []ecBlockGroup{{22, 15}, {41, 16}}},
/* 36-H */ {3034, 30, []ecBlockGroup{{2, 15}, {64, 16}}},
/* 37-H */ {3196, 30, []ecBlockGroup{{24, 15}, {46, 16}}},
/* 38-H */ {3362, 30, []ecBlockGroup{{42, 15}, {32, 16}}},
/* 39-H */ {3532, 30, []ecBlockGroup{{10, 15}, {67, 16}}},
/* 40-H */ {3706, 30, []ecBlockGroup{{20, 15}, {61, 16}}},
}
)
// ecTable returns the level's block table, or nil for an unknown level.
func ecTable(level EcLevel) *[40]ecSpec {
switch level {
case EcL:
return &ecBlocksL
case EcM:
return &ecBlocksM
case EcQ:
return &ecBlocksQ
case EcH:
return &ecBlocksH
}
return nil
}
// alignmentCenters holds the alignment-pattern center coordinates (row =
// column), by version. Indexed [version-1]; version 1 has none; the last
// center of every version is (17 + 4*version) - 7.
var alignmentCenters = [40][]int{
/* 1 */ {},
/* 2 */ {6, 18},
/* 3 */ {6, 22},
/* 4 */ {6, 26},
/* 5 */ {6, 30},
/* 6 */ {6, 34},
/* 7 */ {6, 22, 38},
/* 8 */ {6, 24, 42},
/* 9 */ {6, 26, 46},
/* 10 */ {6, 28, 50},
/* 11 */ {6, 30, 54},
/* 12 */ {6, 32, 58},
/* 13 */ {6, 34, 62},
/* 14 */ {6, 26, 46, 66},
/* 15 */ {6, 26, 48, 70},
/* 16 */ {6, 26, 50, 74},
/* 17 */ {6, 30, 54, 78},
/* 18 */ {6, 30, 56, 82},
/* 19 */ {6, 30, 58, 86},
/* 20 */ {6, 34, 62, 90},
/* 21 */ {6, 28, 50, 72, 94},
/* 22 */ {6, 26, 50, 74, 98},
/* 23 */ {6, 30, 54, 78, 102},
/* 24 */ {6, 28, 54, 80, 106},
/* 25 */ {6, 32, 58, 84, 110},
/* 26 */ {6, 30, 58, 86, 114},
/* 27 */ {6, 34, 62, 90, 118},
/* 28 */ {6, 26, 50, 74, 98, 122},
/* 29 */ {6, 30, 54, 78, 102, 126},
/* 30 */ {6, 26, 52, 78, 104, 130},
/* 31 */ {6, 30, 56, 82, 108, 134},
/* 32 */ {6, 34, 60, 86, 112, 138},
/* 33 */ {6, 30, 58, 86, 114, 142},
/* 34 */ {6, 34, 62, 90, 118, 146},
/* 35 */ {6, 30, 54, 78, 102, 126, 150},
/* 36 */ {6, 24, 50, 76, 102, 128, 154},
/* 37 */ {6, 28, 54, 80, 106, 132, 158},
/* 38 */ {6, 32, 58, 84, 110, 136, 162},
/* 39 */ {6, 26, 54, 82, 110, 138, 166},
/* 40 */ {6, 30, 58, 86, 114, 142, 170},
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →