// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package coding implements low-level QR coding details. package coding // import "rsc.io/qr/coding" import ( "fmt" "strconv" "strings" "rsc.io/qr/gf256" ) // Field is the field for QR error correction. var Field = gf256.NewField(0x11d, 2) // A Version represents a QR version. // The version specifies the size of the QR code: // a QR code with version v has 4v+17 pixels on a side. // Versions number from 1 to 40: the larger the version, // the more information the code can store. type Version int const MinVersion = 1 const MaxVersion = 40 func (v Version) String() string { return strconv.Itoa(int(v)) } func (v Version) sizeClass() int { if v <= 9 { return 0 } if v <= 26 { return 1 } return 2 } // DataBytes returns the number of data bytes that can be // stored in a QR code with the given version and level. func (v Version) DataBytes(l Level) int { vt := &vtab[v] lev := &vt.level[l] return vt.bytes - lev.nblock*lev.check } // Encoding implements a QR data encoding scheme. // The implementations--Numeric, Alphanumeric, and String--specify // the character set and the mapping from UTF-8 to code bits. // The more restrictive the mode, the fewer code bits are needed. type Encoding interface { Check() error Bits(v Version) int Encode(b *Bits, v Version) } type Bits struct { b []byte nbit int } func (b *Bits) Reset() { b.b = b.b[:0] b.nbit = 0 } func (b *Bits) Bits() int { return b.nbit } func (b *Bits) Bytes() []byte { if b.nbit%8 != 0 { panic("fractional byte") } return b.b } func (b *Bits) Append(p []byte) { if b.nbit%8 != 0 { panic("fractional byte") } b.b = append(b.b, p...) b.nbit += 8 * len(p) } func (b *Bits) Write(v uint, nbit int) { for nbit > 0 { n := nbit if n > 8 { n = 8 } if b.nbit%8 == 0 { b.b = append(b.b, 0) } else { m := -b.nbit & 7 if n > m { n = m } } b.nbit += n sh := uint(nbit - n) b.b[len(b.b)-1] |= uint8(v >> sh << uint(-b.nbit&7)) v -= v >> sh << sh nbit -= n } } // Num is the encoding for numeric data. // The only valid characters are the decimal digits 0 through 9. type Num string func (s Num) String() string { return fmt.Sprintf("Num(%#q)", string(s)) } func (s Num) Check() error { for _, c := range s { if c < '0' || '9' < c { return fmt.Errorf("non-numeric string %#q", string(s)) } } return nil } var numLen = [3]int{10, 12, 14} func (s Num) Bits(v Version) int { return 4 + numLen[v.sizeClass()] + (10*len(s)+2)/3 } func (s Num) Encode(b *Bits, v Version) { b.Write(1, 4) b.Write(uint(len(s)), numLen[v.sizeClass()]) var i int for i = 0; i+3 <= len(s); i += 3 { w := uint(s[i]-'0')*100 + uint(s[i+1]-'0')*10 + uint(s[i+2]-'0') b.Write(w, 10) } switch len(s) - i { case 1: w := uint(s[i] - '0') b.Write(w, 4) case 2: w := uint(s[i]-'0')*10 + uint(s[i+1]-'0') b.Write(w, 7) } } // Alpha is the encoding for alphanumeric data. // The valid characters are 0-9A-Z$%*+-./: and space. type Alpha string const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:" func (s Alpha) String() string { return fmt.Sprintf("Alpha(%#q)", string(s)) } func (s Alpha) Check() error { for _, c := range s { if strings.IndexRune(alphabet, c) < 0 { return fmt.Errorf("non-alphanumeric string %#q", string(s)) } } return nil } var alphaLen = [3]int{9, 11, 13} func (s Alpha) Bits(v Version) int { return 4 + alphaLen[v.sizeClass()] + (11*len(s)+1)/2 } func (s Alpha) Encode(b *Bits, v Version) { b.Write(2, 4) b.Write(uint(len(s)), alphaLen[v.sizeClass()]) var i int for i = 0; i+2 <= len(s); i += 2 { w := uint(strings.IndexRune(alphabet, rune(s[i])))*45 + uint(strings.IndexRune(alphabet, rune(s[i+1]))) b.Write(w, 11) } if i < len(s) { w := uint(strings.IndexRune(alphabet, rune(s[i]))) b.Write(w, 6) } } // String is the encoding for 8-bit data. All bytes are valid. type String string func (s String) String() string { return fmt.Sprintf("String(%#q)", string(s)) } func (s String) Check() error { return nil } var stringLen = [3]int{8, 16, 16} func (s String) Bits(v Version) int { return 4 + stringLen[v.sizeClass()] + 8*len(s) } func (s String) Encode(b *Bits, v Version) { b.Write(4, 4) b.Write(uint(len(s)), stringLen[v.sizeClass()]) for i := 0; i < len(s); i++ { b.Write(uint(s[i]), 8) } } // A Pixel describes a single pixel in a QR code. type Pixel uint32 const ( Black Pixel = 1 << iota Invert ) func (p Pixel) Offset() uint { return uint(p >> 6) } func OffsetPixel(o uint) Pixel { return Pixel(o << 6) } func (r PixelRole) Pixel() Pixel { return Pixel(r << 2) } func (p Pixel) Role() PixelRole { return PixelRole(p>>2) & 15 } func (p Pixel) String() string { s := p.Role().String() if p&Black != 0 { s += "+black" } if p&Invert != 0 { s += "+invert" } s += "+" + strconv.FormatUint(uint64(p.Offset()), 10) return s } // A PixelRole describes the role of a QR pixel. type PixelRole uint32 const ( _ PixelRole = iota Position // position squares (large) Alignment // alignment squares (small) Timing // timing strip between position squares Format // format metadata PVersion // version pattern Unused // unused pixel Data // data bit Check // error correction check bit Extra ) var roles = []string{ "", "position", "alignment", "timing", "format", "pversion", "unused", "data", "check", "extra", } func (r PixelRole) String() string { if Position <= r && r <= Check { return roles[r] } return strconv.Itoa(int(r)) } // A Level represents a QR error correction level. // From least to most tolerant of errors, they are L, M, Q, H. type Level int const ( L Level = iota M Q H ) func (l Level) String() string { if L <= l && l <= H { return "LMQH"[l : l+1] } return strconv.Itoa(int(l)) } // A Code is a square pixel grid. type Code struct { Bitmap []byte // 1 is black, 0 is white Size int // number of pixels on a side Stride int // number of bytes per row } func (c *Code) Black(x, y int) bool { return 0 <= x && x < c.Size && 0 <= y && y < c.Size && c.Bitmap[y*c.Stride+x/8]&(1<= pad { break } b.Write(0x11, 8) } } } func (b *Bits) AddCheckBytes(v Version, l Level) { nd := v.DataBytes(l) if b.nbit < nd*8 { b.Pad(nd*8 - b.nbit) } if b.nbit != nd*8 { panic("qr: too much data") } dat := b.Bytes() vt := &vtab[v] lev := &vt.level[l] db := nd / lev.nblock extra := nd % lev.nblock chk := make([]byte, lev.check) rs := gf256.NewRSEncoder(Field, lev.check) for i := 0; i < lev.nblock; i++ { if i == lev.nblock-extra { db++ } rs.ECC(dat[:db], chk) b.Append(chk) dat = dat[db:] } if len(b.Bytes()) != vt.bytes { panic("qr: internal error") } } func (p *Plan) Encode(text ...Encoding) (*Code, error) { var b Bits for _, t := range text { if err := t.Check(); err != nil { return nil, err } t.Encode(&b, p.Version) } if b.Bits() > p.DataBytes*8 { return nil, fmt.Errorf("cannot encode %d bits into %d-bit code", b.Bits(), p.DataBytes*8) } b.AddCheckBytes(p.Version, p.Level) bytes := b.Bytes() // Now we have the checksum bytes and the data bytes. // Construct the actual code. c := &Code{Size: len(p.Pixel), Stride: (len(p.Pixel) + 7) &^ 7} c.Bitmap = make([]byte, c.Stride*c.Size) crow := c.Bitmap for _, row := range p.Pixel { for x, pix := range row { switch pix.Role() { case Data, Check: o := pix.Offset() if bytes[o/8]&(1< 40 { return nil, fmt.Errorf("invalid QR version %d", int(v)) } siz := 17 + int(v)*4 m := grid(siz) p.Pixel = m // Timing markers (overwritten by boxes). const ti = 6 // timing is in row/column 6 (counting from 0) for i := range m { p := Timing.Pixel() if i&1 == 0 { p |= Black } m[i][ti] = p m[ti][i] = p } // Position boxes. posBox(m, 0, 0) posBox(m, siz-7, 0) posBox(m, 0, siz-7) // Alignment boxes. info := &vtab[v] for x := 4; x+5 < siz; { for y := 4; y+5 < siz; { // don't overwrite timing markers if (x < 7 && y < 7) || (x < 7 && y+5 >= siz-7) || (x+5 >= siz-7 && y < 7) { } else { alignBox(m, x, y) } if y == 4 { y = info.apos } else { y += info.astride } } if x == 4 { x = info.apos } else { x += info.astride } } // Version pattern. pat := vtab[v].pattern if pat != 0 { v := pat for x := 0; x < 6; x++ { for y := 0; y < 3; y++ { p := PVersion.Pixel() if v&1 != 0 { p |= Black } m[siz-11+y][x] = p m[x][siz-11+y] = p v >>= 1 } } } // One lonely black pixel m[siz-8][8] = Unused.Pixel() | Black return p, nil } // fplan adds the format pixels func fplan(l Level, m Mask, p *Plan) error { // Format pixels. fb := uint32(l^1) << 13 // level: L=01, M=00, Q=11, H=10 fb |= uint32(m) << 10 // mask const formatPoly = 0x537 rem := fb for i := 14; i >= 10; i-- { if rem&(1<>i)&1 == 1 { pix |= Black } if (invert>>i)&1 == 1 { pix ^= Invert | Black } // top left switch { case i < 6: p.Pixel[i][8] = pix case i < 8: p.Pixel[i+1][8] = pix case i < 9: p.Pixel[8][7] = pix default: p.Pixel[8][14-i] = pix } // bottom right switch { case i < 8: p.Pixel[8][siz-1-int(i)] = pix default: p.Pixel[siz-1-int(14-i)][8] = pix } } return nil } // lplan edits a version-only Plan to add information // about the error correction levels. func lplan(v Version, l Level, p *Plan) error { p.Level = l nblock := vtab[v].level[l].nblock ne := vtab[v].level[l].check nde := (vtab[v].bytes - ne*nblock) / nblock extra := (vtab[v].bytes - ne*nblock) % nblock dataBits := (nde*nblock + extra) * 8 checkBits := ne * nblock * 8 p.DataBytes = vtab[v].bytes - ne*nblock p.CheckBytes = ne * nblock p.Blocks = nblock // Make data + checksum pixels. data := make([]Pixel, dataBits) for i := range data { data[i] = Data.Pixel() | OffsetPixel(uint(i)) } check := make([]Pixel, checkBits) for i := range check { check[i] = Check.Pixel() | OffsetPixel(uint(i+dataBits)) } // Split into blocks. dataList := make([][]Pixel, nblock) checkList := make([][]Pixel, nblock) for i := 0; i < nblock; i++ { // The last few blocks have an extra data byte (8 pixels). nd := nde if i >= nblock-extra { nd++ } dataList[i], data = data[0:nd*8], data[nd*8:] checkList[i], check = check[0:ne*8], check[ne*8:] } if len(data) != 0 || len(check) != 0 { panic("data/check math") } // Build up bit sequence, taking first byte of each block, // then second byte, and so on. Then checksums. bits := make([]Pixel, dataBits+checkBits) dst := bits for i := 0; i < nde+1; i++ { for _, b := range dataList { if i*8 < len(b) { copy(dst, b[i*8:(i+1)*8]) dst = dst[8:] } } } for i := 0; i < ne; i++ { for _, b := range checkList { if i*8 < len(b) { copy(dst, b[i*8:(i+1)*8]) dst = dst[8:] } } } if len(dst) != 0 { panic("dst math") } // Sweep up pair of columns, // then down, assigning to right then left pixel. // Repeat. // See Figure 2 of http://www.pclviewer.com/rs2/qrtopology.htm siz := len(p.Pixel) rem := make([]Pixel, 7) for i := range rem { rem[i] = Extra.Pixel() } src := append(bits, rem...) for x := siz; x > 0; { for y := siz - 1; y >= 0; y-- { if p.Pixel[y][x-1].Role() == 0 { p.Pixel[y][x-1], src = src[0], src[1:] } if p.Pixel[y][x-2].Role() == 0 { p.Pixel[y][x-2], src = src[0], src[1:] } } x -= 2 if x == 7 { // vertical timing strip x-- } for y := 0; y < siz; y++ { if p.Pixel[y][x-1].Role() == 0 { p.Pixel[y][x-1], src = src[0], src[1:] } if p.Pixel[y][x-2].Role() == 0 { p.Pixel[y][x-2], src = src[0], src[1:] } } x -= 2 } return nil } // mplan edits a version+level-only Plan to add the mask. func mplan(m Mask, p *Plan) error { p.Mask = m for y, row := range p.Pixel { for x, pix := range row { if r := pix.Role(); (r == Data || r == Check || r == Extra) && p.Mask.Invert(y, x) { row[x] ^= Black | Invert } } } return nil } // posBox draws a position (large) box at upper left x, y. func posBox(m [][]Pixel, x, y int) { pos := Position.Pixel() // box for dy := 0; dy < 7; dy++ { for dx := 0; dx < 7; dx++ { p := pos if dx == 0 || dx == 6 || dy == 0 || dy == 6 || 2 <= dx && dx <= 4 && 2 <= dy && dy <= 4 { p |= Black } m[y+dy][x+dx] = p } } // white border for dy := -1; dy < 8; dy++ { if 0 <= y+dy && y+dy < len(m) { if x > 0 { m[y+dy][x-1] = pos } if x+7 < len(m) { m[y+dy][x+7] = pos } } } for dx := -1; dx < 8; dx++ { if 0 <= x+dx && x+dx < len(m) { if y > 0 { m[y-1][x+dx] = pos } if y+7 < len(m) { m[y+7][x+dx] = pos } } } } // alignBox draw an alignment (small) box at upper left x, y. func alignBox(m [][]Pixel, x, y int) { // box align := Alignment.Pixel() for dy := 0; dy < 5; dy++ { for dx := 0; dx < 5; dx++ { p := align if dx == 0 || dx == 4 || dy == 0 || dy == 4 || dx == 2 && dy == 2 { p |= Black } m[y+dy][x+dx] = p } } }