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