|
|
- // Package qr can be used to create QR barcodes.
- package qr
-
- import (
- "image"
-
- "github.com/boombuler/barcode"
- "github.com/boombuler/barcode/utils"
- )
-
- type encodeFn func(content string, eccLevel ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error)
-
- // Encoding mode for QR Codes.
- type Encoding byte
-
- const (
- // Auto will choose ths best matching encoding
- Auto Encoding = iota
- // Numeric encoding only encodes numbers [0-9]
- Numeric
- // AlphaNumeric encoding only encodes uppercase letters, numbers and [Space], $, %, *, +, -, ., /, :
- AlphaNumeric
- // Unicode encoding encodes the string as utf-8
- Unicode
- // only for testing purpose
- unknownEncoding
- )
-
- func (e Encoding) getEncoder() encodeFn {
- switch e {
- case Auto:
- return encodeAuto
- case Numeric:
- return encodeNumeric
- case AlphaNumeric:
- return encodeAlphaNumeric
- case Unicode:
- return encodeUnicode
- }
- return nil
- }
-
- func (e Encoding) String() string {
- switch e {
- case Auto:
- return "Auto"
- case Numeric:
- return "Numeric"
- case AlphaNumeric:
- return "AlphaNumeric"
- case Unicode:
- return "Unicode"
- }
- return ""
- }
-
- // Encode returns a QR barcode with the given content, error correction level and uses the given encoding
- func Encode(content string, level ErrorCorrectionLevel, mode Encoding) (barcode.Barcode, error) {
- bits, vi, err := mode.getEncoder()(content, level)
- if err != nil {
- return nil, err
- }
-
- blocks := splitToBlocks(bits.IterateBytes(), vi)
- data := blocks.interleave(vi)
- result := render(data, vi)
- result.content = content
- return result, nil
- }
-
- func render(data []byte, vi *versionInfo) *qrcode {
- dim := vi.modulWidth()
- results := make([]*qrcode, 8)
- for i := 0; i < 8; i++ {
- results[i] = newBarcode(dim)
- }
-
- occupied := newBarcode(dim)
-
- setAll := func(x int, y int, val bool) {
- occupied.Set(x, y, true)
- for i := 0; i < 8; i++ {
- results[i].Set(x, y, val)
- }
- }
-
- drawFinderPatterns(vi, setAll)
- drawAlignmentPatterns(occupied, vi, setAll)
-
- //Timing Pattern:
- var i int
- for i = 0; i < dim; i++ {
- if !occupied.Get(i, 6) {
- setAll(i, 6, i%2 == 0)
- }
- if !occupied.Get(6, i) {
- setAll(6, i, i%2 == 0)
- }
- }
- // Dark Module
- setAll(8, dim-8, true)
-
- drawVersionInfo(vi, setAll)
- drawFormatInfo(vi, -1, occupied.Set)
- for i := 0; i < 8; i++ {
- drawFormatInfo(vi, i, results[i].Set)
- }
-
- // Write the data
- var curBitNo int
-
- for pos := range iterateModules(occupied) {
- var curBit bool
- if curBitNo < len(data)*8 {
- curBit = ((data[curBitNo/8] >> uint(7-(curBitNo%8))) & 1) == 1
- } else {
- curBit = false
- }
-
- for i := 0; i < 8; i++ {
- setMasked(pos.X, pos.Y, curBit, i, results[i].Set)
- }
- curBitNo++
- }
-
- lowestPenalty := ^uint(0)
- lowestPenaltyIdx := -1
- for i := 0; i < 8; i++ {
- p := results[i].calcPenalty()
- if p < lowestPenalty {
- lowestPenalty = p
- lowestPenaltyIdx = i
- }
- }
- return results[lowestPenaltyIdx]
- }
-
- func setMasked(x, y int, val bool, mask int, set func(int, int, bool)) {
- switch mask {
- case 0:
- val = val != (((y + x) % 2) == 0)
- break
- case 1:
- val = val != ((y % 2) == 0)
- break
- case 2:
- val = val != ((x % 3) == 0)
- break
- case 3:
- val = val != (((y + x) % 3) == 0)
- break
- case 4:
- val = val != (((y/2 + x/3) % 2) == 0)
- break
- case 5:
- val = val != (((y*x)%2)+((y*x)%3) == 0)
- break
- case 6:
- val = val != ((((y*x)%2)+((y*x)%3))%2 == 0)
- break
- case 7:
- val = val != ((((y+x)%2)+((y*x)%3))%2 == 0)
- }
- set(x, y, val)
- }
-
- func iterateModules(occupied *qrcode) <-chan image.Point {
- result := make(chan image.Point)
- allPoints := make(chan image.Point)
- go func() {
- curX := occupied.dimension - 1
- curY := occupied.dimension - 1
- isUpward := true
-
- for true {
- if isUpward {
- allPoints <- image.Pt(curX, curY)
- allPoints <- image.Pt(curX-1, curY)
- curY--
- if curY < 0 {
- curY = 0
- curX -= 2
- if curX == 6 {
- curX--
- }
- if curX < 0 {
- break
- }
- isUpward = false
- }
- } else {
- allPoints <- image.Pt(curX, curY)
- allPoints <- image.Pt(curX-1, curY)
- curY++
- if curY >= occupied.dimension {
- curY = occupied.dimension - 1
- curX -= 2
- if curX == 6 {
- curX--
- }
- isUpward = true
- if curX < 0 {
- break
- }
- }
- }
- }
-
- close(allPoints)
- }()
- go func() {
- for pt := range allPoints {
- if !occupied.Get(pt.X, pt.Y) {
- result <- pt
- }
- }
- close(result)
- }()
- return result
- }
-
- func drawFinderPatterns(vi *versionInfo, set func(int, int, bool)) {
- dim := vi.modulWidth()
- drawPattern := func(xoff int, yoff int) {
- for x := -1; x < 8; x++ {
- for y := -1; y < 8; y++ {
- val := (x == 0 || x == 6 || y == 0 || y == 6 || (x > 1 && x < 5 && y > 1 && y < 5)) && (x <= 6 && y <= 6 && x >= 0 && y >= 0)
-
- if x+xoff >= 0 && x+xoff < dim && y+yoff >= 0 && y+yoff < dim {
- set(x+xoff, y+yoff, val)
- }
- }
- }
- }
- drawPattern(0, 0)
- drawPattern(0, dim-7)
- drawPattern(dim-7, 0)
- }
-
- func drawAlignmentPatterns(occupied *qrcode, vi *versionInfo, set func(int, int, bool)) {
- drawPattern := func(xoff int, yoff int) {
- for x := -2; x <= 2; x++ {
- for y := -2; y <= 2; y++ {
- val := x == -2 || x == 2 || y == -2 || y == 2 || (x == 0 && y == 0)
- set(x+xoff, y+yoff, val)
- }
- }
- }
- positions := vi.alignmentPatternPlacements()
-
- for _, x := range positions {
- for _, y := range positions {
- if occupied.Get(x, y) {
- continue
- }
- drawPattern(x, y)
- }
- }
- }
-
- var formatInfos = map[ErrorCorrectionLevel]map[int][]bool{
- L: {
- 0: []bool{true, true, true, false, true, true, true, true, true, false, false, false, true, false, false},
- 1: []bool{true, true, true, false, false, true, false, true, true, true, true, false, false, true, true},
- 2: []bool{true, true, true, true, true, false, true, true, false, true, false, true, false, true, false},
- 3: []bool{true, true, true, true, false, false, false, true, false, false, true, true, true, false, true},
- 4: []bool{true, true, false, false, true, true, false, false, false, true, false, true, true, true, true},
- 5: []bool{true, true, false, false, false, true, true, false, false, false, true, true, false, false, false},
- 6: []bool{true, true, false, true, true, false, false, false, true, false, false, false, false, false, true},
- 7: []bool{true, true, false, true, false, false, true, false, true, true, true, false, true, true, false},
- },
- M: {
- 0: []bool{true, false, true, false, true, false, false, false, false, false, true, false, false, true, false},
- 1: []bool{true, false, true, false, false, false, true, false, false, true, false, false, true, false, true},
- 2: []bool{true, false, true, true, true, true, false, false, true, true, true, true, true, false, false},
- 3: []bool{true, false, true, true, false, true, true, false, true, false, false, true, false, true, true},
- 4: []bool{true, false, false, false, true, false, true, true, true, true, true, true, false, false, true},
- 5: []bool{true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
- 6: []bool{true, false, false, true, true, true, true, true, false, false, true, false, true, true, true},
- 7: []bool{true, false, false, true, false, true, false, true, false, true, false, false, false, false, false},
- },
- Q: {
- 0: []bool{false, true, true, false, true, false, true, false, true, false, true, true, true, true, true},
- 1: []bool{false, true, true, false, false, false, false, false, true, true, false, true, false, false, false},
- 2: []bool{false, true, true, true, true, true, true, false, false, true, true, false, false, false, true},
- 3: []bool{false, true, true, true, false, true, false, false, false, false, false, false, true, true, false},
- 4: []bool{false, true, false, false, true, false, false, true, false, true, true, false, true, false, false},
- 5: []bool{false, true, false, false, false, false, true, true, false, false, false, false, false, true, true},
- 6: []bool{false, true, false, true, true, true, false, true, true, false, true, true, false, true, false},
- 7: []bool{false, true, false, true, false, true, true, true, true, true, false, true, true, false, true},
- },
- H: {
- 0: []bool{false, false, true, false, true, true, false, true, false, false, false, true, false, false, true},
- 1: []bool{false, false, true, false, false, true, true, true, false, true, true, true, true, true, false},
- 2: []bool{false, false, true, true, true, false, false, true, true, true, false, false, true, true, true},
- 3: []bool{false, false, true, true, false, false, true, true, true, false, true, false, false, false, false},
- 4: []bool{false, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
- 5: []bool{false, false, false, false, false, true, false, false, true, false, true, false, true, false, true},
- 6: []bool{false, false, false, true, true, false, true, false, false, false, false, true, true, false, false},
- 7: []bool{false, false, false, true, false, false, false, false, false, true, true, true, false, true, true},
- },
- }
-
- func drawFormatInfo(vi *versionInfo, usedMask int, set func(int, int, bool)) {
- var formatInfo []bool
-
- if usedMask == -1 {
- formatInfo = []bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true} // Set all to true cause -1 --> occupied mask.
- } else {
- formatInfo = formatInfos[vi.Level][usedMask]
- }
-
- if len(formatInfo) == 15 {
- dim := vi.modulWidth()
- set(0, 8, formatInfo[0])
- set(1, 8, formatInfo[1])
- set(2, 8, formatInfo[2])
- set(3, 8, formatInfo[3])
- set(4, 8, formatInfo[4])
- set(5, 8, formatInfo[5])
- set(7, 8, formatInfo[6])
- set(8, 8, formatInfo[7])
- set(8, 7, formatInfo[8])
- set(8, 5, formatInfo[9])
- set(8, 4, formatInfo[10])
- set(8, 3, formatInfo[11])
- set(8, 2, formatInfo[12])
- set(8, 1, formatInfo[13])
- set(8, 0, formatInfo[14])
-
- set(8, dim-1, formatInfo[0])
- set(8, dim-2, formatInfo[1])
- set(8, dim-3, formatInfo[2])
- set(8, dim-4, formatInfo[3])
- set(8, dim-5, formatInfo[4])
- set(8, dim-6, formatInfo[5])
- set(8, dim-7, formatInfo[6])
- set(dim-8, 8, formatInfo[7])
- set(dim-7, 8, formatInfo[8])
- set(dim-6, 8, formatInfo[9])
- set(dim-5, 8, formatInfo[10])
- set(dim-4, 8, formatInfo[11])
- set(dim-3, 8, formatInfo[12])
- set(dim-2, 8, formatInfo[13])
- set(dim-1, 8, formatInfo[14])
- }
- }
-
- var versionInfoBitsByVersion = map[byte][]bool{
- 7: []bool{false, false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false},
- 8: []bool{false, false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false},
- 9: []bool{false, false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true},
- 10: []bool{false, false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true},
- 11: []bool{false, false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false},
- 12: []bool{false, false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
- 13: []bool{false, false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true},
- 14: []bool{false, false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true},
- 15: []bool{false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false},
- 16: []bool{false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false},
- 17: []bool{false, true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true},
- 18: []bool{false, true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true},
- 19: []bool{false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false},
- 20: []bool{false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true, false},
- 21: []bool{false, true, false, true, false, true, false, true, true, false, true, false, false, false, false, false, true, true},
- 22: []bool{false, true, false, true, true, false, true, false, false, false, true, true, false, false, true, false, false, true},
- 23: []bool{false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false, false},
- 24: []bool{false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false, false},
- 25: []bool{false, true, true, false, false, true, false, false, false, true, true, true, true, false, false, false, false, true},
- 26: []bool{false, true, true, false, true, false, true, true, true, true, true, false, true, false, true, false, true, true},
- 27: []bool{false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true, false},
- 28: []bool{false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true, false},
- 29: []bool{false, true, true, true, false, true, false, false, true, true, false, false, true, true, true, true, true, true},
- 30: []bool{false, true, true, true, true, false, true, true, false, true, false, true, true, true, false, true, false, true},
- 31: []bool{false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false, false},
- 32: []bool{true, false, false, false, false, false, true, false, false, true, true, true, false, true, false, true, false, true},
- 33: []bool{true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false, false},
- 34: []bool{true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true, false},
- 35: []bool{true, false, false, false, true, true, false, true, true, true, true, false, false, true, true, true, true, true},
- 36: []bool{true, false, false, true, false, false, true, false, true, true, false, false, false, false, true, false, true, true},
- 37: []bool{true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true, false},
- 38: []bool{true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false, false},
- 39: []bool{true, false, false, true, true, true, false, true, false, true, false, true, false, false, false, false, false, true},
- 40: []bool{true, false, true, false, false, false, true, true, false, false, false, true, true, false, true, false, false, true},
- }
-
- func drawVersionInfo(vi *versionInfo, set func(int, int, bool)) {
- versionInfoBits, ok := versionInfoBitsByVersion[vi.Version]
-
- if ok && len(versionInfoBits) > 0 {
- for i := 0; i < len(versionInfoBits); i++ {
- x := (vi.modulWidth() - 11) + i%3
- y := i / 3
- set(x, y, versionInfoBits[len(versionInfoBits)-i-1])
- set(y, x, versionInfoBits[len(versionInfoBits)-i-1])
- }
- }
-
- }
-
- func addPaddingAndTerminator(bl *utils.BitList, vi *versionInfo) {
- for i := 0; i < 4 && bl.Len() < vi.totalDataBytes()*8; i++ {
- bl.AddBit(false)
- }
-
- for bl.Len()%8 != 0 {
- bl.AddBit(false)
- }
-
- for i := 0; bl.Len() < vi.totalDataBytes()*8; i++ {
- if i%2 == 0 {
- bl.AddByte(236)
- } else {
- bl.AddByte(17)
- }
- }
- }
|