regular_symbol.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // go-qrcode
  2. // Copyright 2014 Tom Harwood
  3. package qrcode
  4. import (
  5. bitset "github.com/skip2/go-qrcode/bitset"
  6. )
  7. type regularSymbol struct {
  8. version qrCodeVersion
  9. mask int
  10. data *bitset.Bitset
  11. symbol *symbol
  12. size int
  13. }
  14. // Abbreviated true/false.
  15. const (
  16. b0 = false
  17. b1 = true
  18. )
  19. var (
  20. alignmentPatternCenter = [][]int{
  21. {}, // Version 0 doesn't exist.
  22. {}, // Version 1 doesn't use alignment patterns.
  23. {6, 18},
  24. {6, 22},
  25. {6, 26},
  26. {6, 30},
  27. {6, 34},
  28. {6, 22, 38},
  29. {6, 24, 42},
  30. {6, 26, 46},
  31. {6, 28, 50},
  32. {6, 30, 54},
  33. {6, 32, 58},
  34. {6, 34, 62},
  35. {6, 26, 46, 66},
  36. {6, 26, 48, 70},
  37. {6, 26, 50, 74},
  38. {6, 30, 54, 78},
  39. {6, 30, 56, 82},
  40. {6, 30, 58, 86},
  41. {6, 34, 62, 90},
  42. {6, 28, 50, 72, 94},
  43. {6, 26, 50, 74, 98},
  44. {6, 30, 54, 78, 102},
  45. {6, 28, 54, 80, 106},
  46. {6, 32, 58, 84, 110},
  47. {6, 30, 58, 86, 114},
  48. {6, 34, 62, 90, 118},
  49. {6, 26, 50, 74, 98, 122},
  50. {6, 30, 54, 78, 102, 126},
  51. {6, 26, 52, 78, 104, 130},
  52. {6, 30, 56, 82, 108, 134},
  53. {6, 34, 60, 86, 112, 138},
  54. {6, 30, 58, 86, 114, 142},
  55. {6, 34, 62, 90, 118, 146},
  56. {6, 30, 54, 78, 102, 126, 150},
  57. {6, 24, 50, 76, 102, 128, 154},
  58. {6, 28, 54, 80, 106, 132, 158},
  59. {6, 32, 58, 84, 110, 136, 162},
  60. {6, 26, 54, 82, 110, 138, 166},
  61. {6, 30, 58, 86, 114, 142, 170},
  62. }
  63. finderPattern = [][]bool{
  64. {b1, b1, b1, b1, b1, b1, b1},
  65. {b1, b0, b0, b0, b0, b0, b1},
  66. {b1, b0, b1, b1, b1, b0, b1},
  67. {b1, b0, b1, b1, b1, b0, b1},
  68. {b1, b0, b1, b1, b1, b0, b1},
  69. {b1, b0, b0, b0, b0, b0, b1},
  70. {b1, b1, b1, b1, b1, b1, b1},
  71. }
  72. finderPatternSize = 7
  73. finderPatternHorizontalBorder = [][]bool{
  74. {b0, b0, b0, b0, b0, b0, b0, b0},
  75. }
  76. finderPatternVerticalBorder = [][]bool{
  77. {b0},
  78. {b0},
  79. {b0},
  80. {b0},
  81. {b0},
  82. {b0},
  83. {b0},
  84. {b0},
  85. }
  86. alignmentPattern = [][]bool{
  87. {b1, b1, b1, b1, b1},
  88. {b1, b0, b0, b0, b1},
  89. {b1, b0, b1, b0, b1},
  90. {b1, b0, b0, b0, b1},
  91. {b1, b1, b1, b1, b1},
  92. }
  93. )
  94. func buildRegularSymbol(version qrCodeVersion, mask int,
  95. data *bitset.Bitset, includeQuietZone bool) (*symbol, error) {
  96. quietZoneSize := 0
  97. if includeQuietZone {
  98. quietZoneSize = version.quietZoneSize()
  99. }
  100. m := &regularSymbol{
  101. version: version,
  102. mask: mask,
  103. data: data,
  104. symbol: newSymbol(version.symbolSize(), quietZoneSize),
  105. size: version.symbolSize(),
  106. }
  107. m.addFinderPatterns()
  108. m.addAlignmentPatterns()
  109. m.addTimingPatterns()
  110. m.addFormatInfo()
  111. m.addVersionInfo()
  112. ok, err := m.addData()
  113. if !ok {
  114. return nil, err
  115. }
  116. return m.symbol, nil
  117. }
  118. func (m *regularSymbol) addFinderPatterns() {
  119. fpSize := finderPatternSize
  120. fp := finderPattern
  121. fpHBorder := finderPatternHorizontalBorder
  122. fpVBorder := finderPatternVerticalBorder
  123. // Top left Finder Pattern.
  124. m.symbol.set2dPattern(0, 0, fp)
  125. m.symbol.set2dPattern(0, fpSize, fpHBorder)
  126. m.symbol.set2dPattern(fpSize, 0, fpVBorder)
  127. // Top right Finder Pattern.
  128. m.symbol.set2dPattern(m.size-fpSize, 0, fp)
  129. m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder)
  130. m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder)
  131. // Bottom left Finder Pattern.
  132. m.symbol.set2dPattern(0, m.size-fpSize, fp)
  133. m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder)
  134. m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder)
  135. }
  136. func (m *regularSymbol) addAlignmentPatterns() {
  137. for _, x := range alignmentPatternCenter[m.version.version] {
  138. for _, y := range alignmentPatternCenter[m.version.version] {
  139. if !m.symbol.empty(x, y) {
  140. continue
  141. }
  142. m.symbol.set2dPattern(x-2, y-2, alignmentPattern)
  143. }
  144. }
  145. }
  146. func (m *regularSymbol) addTimingPatterns() {
  147. value := true
  148. for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ {
  149. m.symbol.set(i, finderPatternSize-1, value)
  150. m.symbol.set(finderPatternSize-1, i, value)
  151. value = !value
  152. }
  153. }
  154. func (m *regularSymbol) addFormatInfo() {
  155. fpSize := finderPatternSize
  156. l := formatInfoLengthBits - 1
  157. f := m.version.formatInfo(m.mask)
  158. // Bits 0-7, under the top right finder pattern.
  159. for i := 0; i <= 7; i++ {
  160. m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i))
  161. }
  162. // Bits 0-5, right of the top left finder pattern.
  163. for i := 0; i <= 5; i++ {
  164. m.symbol.set(fpSize+1, i, f.At(l-i))
  165. }
  166. // Bits 6-8 on the corner of the top left finder pattern.
  167. m.symbol.set(fpSize+1, fpSize, f.At(l-6))
  168. m.symbol.set(fpSize+1, fpSize+1, f.At(l-7))
  169. m.symbol.set(fpSize, fpSize+1, f.At(l-8))
  170. // Bits 9-14 on the underside of the top left finder pattern.
  171. for i := 9; i <= 14; i++ {
  172. m.symbol.set(14-i, fpSize+1, f.At(l-i))
  173. }
  174. // Bits 8-14 on the right side of the bottom left finder pattern.
  175. for i := 8; i <= 14; i++ {
  176. m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i))
  177. }
  178. // Always dark symbol.
  179. m.symbol.set(fpSize+1, m.size-fpSize-1, true)
  180. }
  181. func (m *regularSymbol) addVersionInfo() {
  182. fpSize := finderPatternSize
  183. v := m.version.versionInfo()
  184. l := versionInfoLengthBits - 1
  185. if v == nil {
  186. return
  187. }
  188. for i := 0; i < v.Len(); i++ {
  189. // Above the bottom left finder pattern.
  190. m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i))
  191. // Left of the top right finder pattern.
  192. m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i))
  193. }
  194. }
  195. type direction uint8
  196. const (
  197. up direction = iota
  198. down
  199. )
  200. func (m *regularSymbol) addData() (bool, error) {
  201. xOffset := 1
  202. dir := up
  203. x := m.size - 2
  204. y := m.size - 1
  205. for i := 0; i < m.data.Len(); i++ {
  206. var mask bool
  207. switch m.mask {
  208. case 0:
  209. mask = (y+x+xOffset)%2 == 0
  210. case 1:
  211. mask = y%2 == 0
  212. case 2:
  213. mask = (x+xOffset)%3 == 0
  214. case 3:
  215. mask = (y+x+xOffset)%3 == 0
  216. case 4:
  217. mask = (y/2+(x+xOffset)/3)%2 == 0
  218. case 5:
  219. mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0
  220. case 6:
  221. mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0
  222. case 7:
  223. mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0
  224. }
  225. // != is equivalent to XOR.
  226. m.symbol.set(x+xOffset, y, mask != m.data.At(i))
  227. if i == m.data.Len()-1 {
  228. break
  229. }
  230. // Find next free bit in the symbol.
  231. for {
  232. if xOffset == 1 {
  233. xOffset = 0
  234. } else {
  235. xOffset = 1
  236. if dir == up {
  237. if y > 0 {
  238. y--
  239. } else {
  240. dir = down
  241. x -= 2
  242. }
  243. } else {
  244. if y < m.size-1 {
  245. y++
  246. } else {
  247. dir = up
  248. x -= 2
  249. }
  250. }
  251. }
  252. // Skip over the vertical timing pattern entirely.
  253. if x == 5 {
  254. x--
  255. }
  256. if m.symbol.empty(x+xOffset, y) {
  257. break
  258. }
  259. }
  260. }
  261. return true, nil
  262. }