encoder.go raw

   1  // Package qr can be used to create QR barcodes.
   2  package qr
   3  
   4  import (
   5  	"image"
   6  
   7  	"github.com/boombuler/barcode"
   8  	"github.com/boombuler/barcode/utils"
   9  )
  10  
  11  type encodeFn func(content string, eccLevel ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error)
  12  
  13  // Encoding mode for QR Codes.
  14  type Encoding byte
  15  
  16  const (
  17  	// Auto will choose ths best matching encoding
  18  	Auto Encoding = iota
  19  	// Numeric encoding only encodes numbers [0-9]
  20  	Numeric
  21  	// AlphaNumeric encoding only encodes uppercase letters, numbers and  [Space], $, %, *, +, -, ., /, :
  22  	AlphaNumeric
  23  	// Unicode encoding encodes the string as utf-8
  24  	Unicode
  25  	// only for testing purpose
  26  	unknownEncoding
  27  )
  28  
  29  func (e Encoding) getEncoder() encodeFn {
  30  	switch e {
  31  	case Auto:
  32  		return encodeAuto
  33  	case Numeric:
  34  		return encodeNumeric
  35  	case AlphaNumeric:
  36  		return encodeAlphaNumeric
  37  	case Unicode:
  38  		return encodeUnicode
  39  	}
  40  	return nil
  41  }
  42  
  43  func (e Encoding) String() string {
  44  	switch e {
  45  	case Auto:
  46  		return "Auto"
  47  	case Numeric:
  48  		return "Numeric"
  49  	case AlphaNumeric:
  50  		return "AlphaNumeric"
  51  	case Unicode:
  52  		return "Unicode"
  53  	}
  54  	return ""
  55  }
  56  
  57  // Encode returns a QR barcode with the given content, error correction level and uses the given encoding
  58  func Encode(content string, level ErrorCorrectionLevel, mode Encoding) (barcode.Barcode, error) {
  59  	bits, vi, err := mode.getEncoder()(content, level)
  60  	if err != nil {
  61  		return nil, err
  62  	}
  63  
  64  	blocks := splitToBlocks(bits.IterateBytes(), vi)
  65  	data := blocks.interleave(vi)
  66  	result := render(data, vi)
  67  	result.content = content
  68  	return result, nil
  69  }
  70  
  71  func render(data []byte, vi *versionInfo) *qrcode {
  72  	dim := vi.modulWidth()
  73  	results := make([]*qrcode, 8)
  74  	for i := 0; i < 8; i++ {
  75  		results[i] = newBarcode(dim)
  76  	}
  77  
  78  	occupied := newBarcode(dim)
  79  
  80  	setAll := func(x int, y int, val bool) {
  81  		occupied.Set(x, y, true)
  82  		for i := 0; i < 8; i++ {
  83  			results[i].Set(x, y, val)
  84  		}
  85  	}
  86  
  87  	drawFinderPatterns(vi, setAll)
  88  	drawAlignmentPatterns(occupied, vi, setAll)
  89  
  90  	//Timing Pattern:
  91  	var i int
  92  	for i = 0; i < dim; i++ {
  93  		if !occupied.Get(i, 6) {
  94  			setAll(i, 6, i%2 == 0)
  95  		}
  96  		if !occupied.Get(6, i) {
  97  			setAll(6, i, i%2 == 0)
  98  		}
  99  	}
 100  	// Dark Module
 101  	setAll(8, dim-8, true)
 102  
 103  	drawVersionInfo(vi, setAll)
 104  	drawFormatInfo(vi, -1, occupied.Set)
 105  	for i := 0; i < 8; i++ {
 106  		drawFormatInfo(vi, i, results[i].Set)
 107  	}
 108  
 109  	// Write the data
 110  	var curBitNo int
 111  
 112  	for pos := range iterateModules(occupied) {
 113  		var curBit bool
 114  		if curBitNo < len(data)*8 {
 115  			curBit = ((data[curBitNo/8] >> uint(7-(curBitNo%8))) & 1) == 1
 116  		} else {
 117  			curBit = false
 118  		}
 119  
 120  		for i := 0; i < 8; i++ {
 121  			setMasked(pos.X, pos.Y, curBit, i, results[i].Set)
 122  		}
 123  		curBitNo++
 124  	}
 125  
 126  	lowestPenalty := ^uint(0)
 127  	lowestPenaltyIdx := -1
 128  	for i := 0; i < 8; i++ {
 129  		p := results[i].calcPenalty()
 130  		if p < lowestPenalty {
 131  			lowestPenalty = p
 132  			lowestPenaltyIdx = i
 133  		}
 134  	}
 135  	return results[lowestPenaltyIdx]
 136  }
 137  
 138  func setMasked(x, y int, val bool, mask int, set func(int, int, bool)) {
 139  	switch mask {
 140  	case 0:
 141  		val = val != (((y + x) % 2) == 0)
 142  		break
 143  	case 1:
 144  		val = val != ((y % 2) == 0)
 145  		break
 146  	case 2:
 147  		val = val != ((x % 3) == 0)
 148  		break
 149  	case 3:
 150  		val = val != (((y + x) % 3) == 0)
 151  		break
 152  	case 4:
 153  		val = val != (((y/2 + x/3) % 2) == 0)
 154  		break
 155  	case 5:
 156  		val = val != (((y*x)%2)+((y*x)%3) == 0)
 157  		break
 158  	case 6:
 159  		val = val != ((((y*x)%2)+((y*x)%3))%2 == 0)
 160  		break
 161  	case 7:
 162  		val = val != ((((y+x)%2)+((y*x)%3))%2 == 0)
 163  	}
 164  	set(x, y, val)
 165  }
 166  
 167  func iterateModules(occupied *qrcode) <-chan image.Point {
 168  	result := make(chan image.Point)
 169  	allPoints := make(chan image.Point)
 170  	go func() {
 171  		curX := occupied.dimension - 1
 172  		curY := occupied.dimension - 1
 173  		isUpward := true
 174  
 175  		for true {
 176  			if isUpward {
 177  				allPoints <- image.Pt(curX, curY)
 178  				allPoints <- image.Pt(curX-1, curY)
 179  				curY--
 180  				if curY < 0 {
 181  					curY = 0
 182  					curX -= 2
 183  					if curX == 6 {
 184  						curX--
 185  					}
 186  					if curX < 0 {
 187  						break
 188  					}
 189  					isUpward = false
 190  				}
 191  			} else {
 192  				allPoints <- image.Pt(curX, curY)
 193  				allPoints <- image.Pt(curX-1, curY)
 194  				curY++
 195  				if curY >= occupied.dimension {
 196  					curY = occupied.dimension - 1
 197  					curX -= 2
 198  					if curX == 6 {
 199  						curX--
 200  					}
 201  					isUpward = true
 202  					if curX < 0 {
 203  						break
 204  					}
 205  				}
 206  			}
 207  		}
 208  
 209  		close(allPoints)
 210  	}()
 211  	go func() {
 212  		for pt := range allPoints {
 213  			if !occupied.Get(pt.X, pt.Y) {
 214  				result <- pt
 215  			}
 216  		}
 217  		close(result)
 218  	}()
 219  	return result
 220  }
 221  
 222  func drawFinderPatterns(vi *versionInfo, set func(int, int, bool)) {
 223  	dim := vi.modulWidth()
 224  	drawPattern := func(xoff int, yoff int) {
 225  		for x := -1; x < 8; x++ {
 226  			for y := -1; y < 8; y++ {
 227  				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)
 228  
 229  				if x+xoff >= 0 && x+xoff < dim && y+yoff >= 0 && y+yoff < dim {
 230  					set(x+xoff, y+yoff, val)
 231  				}
 232  			}
 233  		}
 234  	}
 235  	drawPattern(0, 0)
 236  	drawPattern(0, dim-7)
 237  	drawPattern(dim-7, 0)
 238  }
 239  
 240  func drawAlignmentPatterns(occupied *qrcode, vi *versionInfo, set func(int, int, bool)) {
 241  	drawPattern := func(xoff int, yoff int) {
 242  		for x := -2; x <= 2; x++ {
 243  			for y := -2; y <= 2; y++ {
 244  				val := x == -2 || x == 2 || y == -2 || y == 2 || (x == 0 && y == 0)
 245  				set(x+xoff, y+yoff, val)
 246  			}
 247  		}
 248  	}
 249  	positions := vi.alignmentPatternPlacements()
 250  
 251  	for _, x := range positions {
 252  		for _, y := range positions {
 253  			if occupied.Get(x, y) {
 254  				continue
 255  			}
 256  			drawPattern(x, y)
 257  		}
 258  	}
 259  }
 260  
 261  var formatInfos = map[ErrorCorrectionLevel]map[int][]bool{
 262  	L: {
 263  		0: []bool{true, true, true, false, true, true, true, true, true, false, false, false, true, false, false},
 264  		1: []bool{true, true, true, false, false, true, false, true, true, true, true, false, false, true, true},
 265  		2: []bool{true, true, true, true, true, false, true, true, false, true, false, true, false, true, false},
 266  		3: []bool{true, true, true, true, false, false, false, true, false, false, true, true, true, false, true},
 267  		4: []bool{true, true, false, false, true, true, false, false, false, true, false, true, true, true, true},
 268  		5: []bool{true, true, false, false, false, true, true, false, false, false, true, true, false, false, false},
 269  		6: []bool{true, true, false, true, true, false, false, false, true, false, false, false, false, false, true},
 270  		7: []bool{true, true, false, true, false, false, true, false, true, true, true, false, true, true, false},
 271  	},
 272  	M: {
 273  		0: []bool{true, false, true, false, true, false, false, false, false, false, true, false, false, true, false},
 274  		1: []bool{true, false, true, false, false, false, true, false, false, true, false, false, true, false, true},
 275  		2: []bool{true, false, true, true, true, true, false, false, true, true, true, true, true, false, false},
 276  		3: []bool{true, false, true, true, false, true, true, false, true, false, false, true, false, true, true},
 277  		4: []bool{true, false, false, false, true, false, true, true, true, true, true, true, false, false, true},
 278  		5: []bool{true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
 279  		6: []bool{true, false, false, true, true, true, true, true, false, false, true, false, true, true, true},
 280  		7: []bool{true, false, false, true, false, true, false, true, false, true, false, false, false, false, false},
 281  	},
 282  	Q: {
 283  		0: []bool{false, true, true, false, true, false, true, false, true, false, true, true, true, true, true},
 284  		1: []bool{false, true, true, false, false, false, false, false, true, true, false, true, false, false, false},
 285  		2: []bool{false, true, true, true, true, true, true, false, false, true, true, false, false, false, true},
 286  		3: []bool{false, true, true, true, false, true, false, false, false, false, false, false, true, true, false},
 287  		4: []bool{false, true, false, false, true, false, false, true, false, true, true, false, true, false, false},
 288  		5: []bool{false, true, false, false, false, false, true, true, false, false, false, false, false, true, true},
 289  		6: []bool{false, true, false, true, true, true, false, true, true, false, true, true, false, true, false},
 290  		7: []bool{false, true, false, true, false, true, true, true, true, true, false, true, true, false, true},
 291  	},
 292  	H: {
 293  		0: []bool{false, false, true, false, true, true, false, true, false, false, false, true, false, false, true},
 294  		1: []bool{false, false, true, false, false, true, true, true, false, true, true, true, true, true, false},
 295  		2: []bool{false, false, true, true, true, false, false, true, true, true, false, false, true, true, true},
 296  		3: []bool{false, false, true, true, false, false, true, true, true, false, true, false, false, false, false},
 297  		4: []bool{false, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
 298  		5: []bool{false, false, false, false, false, true, false, false, true, false, true, false, true, false, true},
 299  		6: []bool{false, false, false, true, true, false, true, false, false, false, false, true, true, false, false},
 300  		7: []bool{false, false, false, true, false, false, false, false, false, true, true, true, false, true, true},
 301  	},
 302  }
 303  
 304  func drawFormatInfo(vi *versionInfo, usedMask int, set func(int, int, bool)) {
 305  	var formatInfo []bool
 306  
 307  	if usedMask == -1 {
 308  		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.
 309  	} else {
 310  		formatInfo = formatInfos[vi.Level][usedMask]
 311  	}
 312  
 313  	if len(formatInfo) == 15 {
 314  		dim := vi.modulWidth()
 315  		set(0, 8, formatInfo[0])
 316  		set(1, 8, formatInfo[1])
 317  		set(2, 8, formatInfo[2])
 318  		set(3, 8, formatInfo[3])
 319  		set(4, 8, formatInfo[4])
 320  		set(5, 8, formatInfo[5])
 321  		set(7, 8, formatInfo[6])
 322  		set(8, 8, formatInfo[7])
 323  		set(8, 7, formatInfo[8])
 324  		set(8, 5, formatInfo[9])
 325  		set(8, 4, formatInfo[10])
 326  		set(8, 3, formatInfo[11])
 327  		set(8, 2, formatInfo[12])
 328  		set(8, 1, formatInfo[13])
 329  		set(8, 0, formatInfo[14])
 330  
 331  		set(8, dim-1, formatInfo[0])
 332  		set(8, dim-2, formatInfo[1])
 333  		set(8, dim-3, formatInfo[2])
 334  		set(8, dim-4, formatInfo[3])
 335  		set(8, dim-5, formatInfo[4])
 336  		set(8, dim-6, formatInfo[5])
 337  		set(8, dim-7, formatInfo[6])
 338  		set(dim-8, 8, formatInfo[7])
 339  		set(dim-7, 8, formatInfo[8])
 340  		set(dim-6, 8, formatInfo[9])
 341  		set(dim-5, 8, formatInfo[10])
 342  		set(dim-4, 8, formatInfo[11])
 343  		set(dim-3, 8, formatInfo[12])
 344  		set(dim-2, 8, formatInfo[13])
 345  		set(dim-1, 8, formatInfo[14])
 346  	}
 347  }
 348  
 349  var versionInfoBitsByVersion = map[byte][]bool{
 350  	7:  []bool{false, false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false},
 351  	8:  []bool{false, false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false},
 352  	9:  []bool{false, false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true},
 353  	10: []bool{false, false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true},
 354  	11: []bool{false, false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false},
 355  	12: []bool{false, false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
 356  	13: []bool{false, false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true},
 357  	14: []bool{false, false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true},
 358  	15: []bool{false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false},
 359  	16: []bool{false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false},
 360  	17: []bool{false, true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true},
 361  	18: []bool{false, true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true},
 362  	19: []bool{false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false},
 363  	20: []bool{false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true, false},
 364  	21: []bool{false, true, false, true, false, true, false, true, true, false, true, false, false, false, false, false, true, true},
 365  	22: []bool{false, true, false, true, true, false, true, false, false, false, true, true, false, false, true, false, false, true},
 366  	23: []bool{false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false, false},
 367  	24: []bool{false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false, false},
 368  	25: []bool{false, true, true, false, false, true, false, false, false, true, true, true, true, false, false, false, false, true},
 369  	26: []bool{false, true, true, false, true, false, true, true, true, true, true, false, true, false, true, false, true, true},
 370  	27: []bool{false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true, false},
 371  	28: []bool{false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true, false},
 372  	29: []bool{false, true, true, true, false, true, false, false, true, true, false, false, true, true, true, true, true, true},
 373  	30: []bool{false, true, true, true, true, false, true, true, false, true, false, true, true, true, false, true, false, true},
 374  	31: []bool{false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false, false},
 375  	32: []bool{true, false, false, false, false, false, true, false, false, true, true, true, false, true, false, true, false, true},
 376  	33: []bool{true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false, false},
 377  	34: []bool{true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true, false},
 378  	35: []bool{true, false, false, false, true, true, false, true, true, true, true, false, false, true, true, true, true, true},
 379  	36: []bool{true, false, false, true, false, false, true, false, true, true, false, false, false, false, true, false, true, true},
 380  	37: []bool{true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true, false},
 381  	38: []bool{true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false, false},
 382  	39: []bool{true, false, false, true, true, true, false, true, false, true, false, true, false, false, false, false, false, true},
 383  	40: []bool{true, false, true, false, false, false, true, true, false, false, false, true, true, false, true, false, false, true},
 384  }
 385  
 386  func drawVersionInfo(vi *versionInfo, set func(int, int, bool)) {
 387  	versionInfoBits, ok := versionInfoBitsByVersion[vi.Version]
 388  
 389  	if ok && len(versionInfoBits) > 0 {
 390  		for i := 0; i < len(versionInfoBits); i++ {
 391  			x := (vi.modulWidth() - 11) + i%3
 392  			y := i / 3
 393  			set(x, y, versionInfoBits[len(versionInfoBits)-i-1])
 394  			set(y, x, versionInfoBits[len(versionInfoBits)-i-1])
 395  		}
 396  	}
 397  
 398  }
 399  
 400  func addPaddingAndTerminator(bl *utils.BitList, vi *versionInfo) {
 401  	for i := 0; i < 4 && bl.Len() < vi.totalDataBytes()*8; i++ {
 402  		bl.AddBit(false)
 403  	}
 404  
 405  	for bl.Len()%8 != 0 {
 406  		bl.AddBit(false)
 407  	}
 408  
 409  	for i := 0; bl.Len() < vi.totalDataBytes()*8; i++ {
 410  		if i%2 == 0 {
 411  			bl.AddByte(236)
 412  		} else {
 413  			bl.AddByte(17)
 414  		}
 415  	}
 416  }
 417