git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/qr/versioninfo.go (about) 1 package qr 2 3 import "math" 4 5 // ErrorCorrectionLevel indicates the amount of "backup data" stored in the QR code 6 type ErrorCorrectionLevel byte 7 8 const ( 9 // L recovers 7% of data 10 L ErrorCorrectionLevel = iota 11 // M recovers 15% of data 12 M 13 // Q recovers 25% of data 14 Q 15 // H recovers 30% of data 16 H 17 ) 18 19 func (ecl ErrorCorrectionLevel) String() string { 20 switch ecl { 21 case L: 22 return "L" 23 case M: 24 return "M" 25 case Q: 26 return "Q" 27 case H: 28 return "H" 29 } 30 return "unknown" 31 } 32 33 type encodingMode byte 34 35 const ( 36 numericMode encodingMode = 1 37 alphaNumericMode encodingMode = 2 38 byteMode encodingMode = 4 39 kanjiMode encodingMode = 8 40 ) 41 42 type versionInfo struct { 43 Version byte 44 Level ErrorCorrectionLevel 45 ErrorCorrectionCodewordsPerBlock byte 46 NumberOfBlocksInGroup1 byte 47 DataCodeWordsPerBlockInGroup1 byte 48 NumberOfBlocksInGroup2 byte 49 DataCodeWordsPerBlockInGroup2 byte 50 } 51 52 var versionInfos = []*versionInfo{ 53 &versionInfo{1, L, 7, 1, 19, 0, 0}, 54 &versionInfo{1, M, 10, 1, 16, 0, 0}, 55 &versionInfo{1, Q, 13, 1, 13, 0, 0}, 56 &versionInfo{1, H, 17, 1, 9, 0, 0}, 57 &versionInfo{2, L, 10, 1, 34, 0, 0}, 58 &versionInfo{2, M, 16, 1, 28, 0, 0}, 59 &versionInfo{2, Q, 22, 1, 22, 0, 0}, 60 &versionInfo{2, H, 28, 1, 16, 0, 0}, 61 &versionInfo{3, L, 15, 1, 55, 0, 0}, 62 &versionInfo{3, M, 26, 1, 44, 0, 0}, 63 &versionInfo{3, Q, 18, 2, 17, 0, 0}, 64 &versionInfo{3, H, 22, 2, 13, 0, 0}, 65 &versionInfo{4, L, 20, 1, 80, 0, 0}, 66 &versionInfo{4, M, 18, 2, 32, 0, 0}, 67 &versionInfo{4, Q, 26, 2, 24, 0, 0}, 68 &versionInfo{4, H, 16, 4, 9, 0, 0}, 69 &versionInfo{5, L, 26, 1, 108, 0, 0}, 70 &versionInfo{5, M, 24, 2, 43, 0, 0}, 71 &versionInfo{5, Q, 18, 2, 15, 2, 16}, 72 &versionInfo{5, H, 22, 2, 11, 2, 12}, 73 &versionInfo{6, L, 18, 2, 68, 0, 0}, 74 &versionInfo{6, M, 16, 4, 27, 0, 0}, 75 &versionInfo{6, Q, 24, 4, 19, 0, 0}, 76 &versionInfo{6, H, 28, 4, 15, 0, 0}, 77 &versionInfo{7, L, 20, 2, 78, 0, 0}, 78 &versionInfo{7, M, 18, 4, 31, 0, 0}, 79 &versionInfo{7, Q, 18, 2, 14, 4, 15}, 80 &versionInfo{7, H, 26, 4, 13, 1, 14}, 81 &versionInfo{8, L, 24, 2, 97, 0, 0}, 82 &versionInfo{8, M, 22, 2, 38, 2, 39}, 83 &versionInfo{8, Q, 22, 4, 18, 2, 19}, 84 &versionInfo{8, H, 26, 4, 14, 2, 15}, 85 &versionInfo{9, L, 30, 2, 116, 0, 0}, 86 &versionInfo{9, M, 22, 3, 36, 2, 37}, 87 &versionInfo{9, Q, 20, 4, 16, 4, 17}, 88 &versionInfo{9, H, 24, 4, 12, 4, 13}, 89 &versionInfo{10, L, 18, 2, 68, 2, 69}, 90 &versionInfo{10, M, 26, 4, 43, 1, 44}, 91 &versionInfo{10, Q, 24, 6, 19, 2, 20}, 92 &versionInfo{10, H, 28, 6, 15, 2, 16}, 93 &versionInfo{11, L, 20, 4, 81, 0, 0}, 94 &versionInfo{11, M, 30, 1, 50, 4, 51}, 95 &versionInfo{11, Q, 28, 4, 22, 4, 23}, 96 &versionInfo{11, H, 24, 3, 12, 8, 13}, 97 &versionInfo{12, L, 24, 2, 92, 2, 93}, 98 &versionInfo{12, M, 22, 6, 36, 2, 37}, 99 &versionInfo{12, Q, 26, 4, 20, 6, 21}, 100 &versionInfo{12, H, 28, 7, 14, 4, 15}, 101 &versionInfo{13, L, 26, 4, 107, 0, 0}, 102 &versionInfo{13, M, 22, 8, 37, 1, 38}, 103 &versionInfo{13, Q, 24, 8, 20, 4, 21}, 104 &versionInfo{13, H, 22, 12, 11, 4, 12}, 105 &versionInfo{14, L, 30, 3, 115, 1, 116}, 106 &versionInfo{14, M, 24, 4, 40, 5, 41}, 107 &versionInfo{14, Q, 20, 11, 16, 5, 17}, 108 &versionInfo{14, H, 24, 11, 12, 5, 13}, 109 &versionInfo{15, L, 22, 5, 87, 1, 88}, 110 &versionInfo{15, M, 24, 5, 41, 5, 42}, 111 &versionInfo{15, Q, 30, 5, 24, 7, 25}, 112 &versionInfo{15, H, 24, 11, 12, 7, 13}, 113 &versionInfo{16, L, 24, 5, 98, 1, 99}, 114 &versionInfo{16, M, 28, 7, 45, 3, 46}, 115 &versionInfo{16, Q, 24, 15, 19, 2, 20}, 116 &versionInfo{16, H, 30, 3, 15, 13, 16}, 117 &versionInfo{17, L, 28, 1, 107, 5, 108}, 118 &versionInfo{17, M, 28, 10, 46, 1, 47}, 119 &versionInfo{17, Q, 28, 1, 22, 15, 23}, 120 &versionInfo{17, H, 28, 2, 14, 17, 15}, 121 &versionInfo{18, L, 30, 5, 120, 1, 121}, 122 &versionInfo{18, M, 26, 9, 43, 4, 44}, 123 &versionInfo{18, Q, 28, 17, 22, 1, 23}, 124 &versionInfo{18, H, 28, 2, 14, 19, 15}, 125 &versionInfo{19, L, 28, 3, 113, 4, 114}, 126 &versionInfo{19, M, 26, 3, 44, 11, 45}, 127 &versionInfo{19, Q, 26, 17, 21, 4, 22}, 128 &versionInfo{19, H, 26, 9, 13, 16, 14}, 129 &versionInfo{20, L, 28, 3, 107, 5, 108}, 130 &versionInfo{20, M, 26, 3, 41, 13, 42}, 131 &versionInfo{20, Q, 30, 15, 24, 5, 25}, 132 &versionInfo{20, H, 28, 15, 15, 10, 16}, 133 &versionInfo{21, L, 28, 4, 116, 4, 117}, 134 &versionInfo{21, M, 26, 17, 42, 0, 0}, 135 &versionInfo{21, Q, 28, 17, 22, 6, 23}, 136 &versionInfo{21, H, 30, 19, 16, 6, 17}, 137 &versionInfo{22, L, 28, 2, 111, 7, 112}, 138 &versionInfo{22, M, 28, 17, 46, 0, 0}, 139 &versionInfo{22, Q, 30, 7, 24, 16, 25}, 140 &versionInfo{22, H, 24, 34, 13, 0, 0}, 141 &versionInfo{23, L, 30, 4, 121, 5, 122}, 142 &versionInfo{23, M, 28, 4, 47, 14, 48}, 143 &versionInfo{23, Q, 30, 11, 24, 14, 25}, 144 &versionInfo{23, H, 30, 16, 15, 14, 16}, 145 &versionInfo{24, L, 30, 6, 117, 4, 118}, 146 &versionInfo{24, M, 28, 6, 45, 14, 46}, 147 &versionInfo{24, Q, 30, 11, 24, 16, 25}, 148 &versionInfo{24, H, 30, 30, 16, 2, 17}, 149 &versionInfo{25, L, 26, 8, 106, 4, 107}, 150 &versionInfo{25, M, 28, 8, 47, 13, 48}, 151 &versionInfo{25, Q, 30, 7, 24, 22, 25}, 152 &versionInfo{25, H, 30, 22, 15, 13, 16}, 153 &versionInfo{26, L, 28, 10, 114, 2, 115}, 154 &versionInfo{26, M, 28, 19, 46, 4, 47}, 155 &versionInfo{26, Q, 28, 28, 22, 6, 23}, 156 &versionInfo{26, H, 30, 33, 16, 4, 17}, 157 &versionInfo{27, L, 30, 8, 122, 4, 123}, 158 &versionInfo{27, M, 28, 22, 45, 3, 46}, 159 &versionInfo{27, Q, 30, 8, 23, 26, 24}, 160 &versionInfo{27, H, 30, 12, 15, 28, 16}, 161 &versionInfo{28, L, 30, 3, 117, 10, 118}, 162 &versionInfo{28, M, 28, 3, 45, 23, 46}, 163 &versionInfo{28, Q, 30, 4, 24, 31, 25}, 164 &versionInfo{28, H, 30, 11, 15, 31, 16}, 165 &versionInfo{29, L, 30, 7, 116, 7, 117}, 166 &versionInfo{29, M, 28, 21, 45, 7, 46}, 167 &versionInfo{29, Q, 30, 1, 23, 37, 24}, 168 &versionInfo{29, H, 30, 19, 15, 26, 16}, 169 &versionInfo{30, L, 30, 5, 115, 10, 116}, 170 &versionInfo{30, M, 28, 19, 47, 10, 48}, 171 &versionInfo{30, Q, 30, 15, 24, 25, 25}, 172 &versionInfo{30, H, 30, 23, 15, 25, 16}, 173 &versionInfo{31, L, 30, 13, 115, 3, 116}, 174 &versionInfo{31, M, 28, 2, 46, 29, 47}, 175 &versionInfo{31, Q, 30, 42, 24, 1, 25}, 176 &versionInfo{31, H, 30, 23, 15, 28, 16}, 177 &versionInfo{32, L, 30, 17, 115, 0, 0}, 178 &versionInfo{32, M, 28, 10, 46, 23, 47}, 179 &versionInfo{32, Q, 30, 10, 24, 35, 25}, 180 &versionInfo{32, H, 30, 19, 15, 35, 16}, 181 &versionInfo{33, L, 30, 17, 115, 1, 116}, 182 &versionInfo{33, M, 28, 14, 46, 21, 47}, 183 &versionInfo{33, Q, 30, 29, 24, 19, 25}, 184 &versionInfo{33, H, 30, 11, 15, 46, 16}, 185 &versionInfo{34, L, 30, 13, 115, 6, 116}, 186 &versionInfo{34, M, 28, 14, 46, 23, 47}, 187 &versionInfo{34, Q, 30, 44, 24, 7, 25}, 188 &versionInfo{34, H, 30, 59, 16, 1, 17}, 189 &versionInfo{35, L, 30, 12, 121, 7, 122}, 190 &versionInfo{35, M, 28, 12, 47, 26, 48}, 191 &versionInfo{35, Q, 30, 39, 24, 14, 25}, 192 &versionInfo{35, H, 30, 22, 15, 41, 16}, 193 &versionInfo{36, L, 30, 6, 121, 14, 122}, 194 &versionInfo{36, M, 28, 6, 47, 34, 48}, 195 &versionInfo{36, Q, 30, 46, 24, 10, 25}, 196 &versionInfo{36, H, 30, 2, 15, 64, 16}, 197 &versionInfo{37, L, 30, 17, 122, 4, 123}, 198 &versionInfo{37, M, 28, 29, 46, 14, 47}, 199 &versionInfo{37, Q, 30, 49, 24, 10, 25}, 200 &versionInfo{37, H, 30, 24, 15, 46, 16}, 201 &versionInfo{38, L, 30, 4, 122, 18, 123}, 202 &versionInfo{38, M, 28, 13, 46, 32, 47}, 203 &versionInfo{38, Q, 30, 48, 24, 14, 25}, 204 &versionInfo{38, H, 30, 42, 15, 32, 16}, 205 &versionInfo{39, L, 30, 20, 117, 4, 118}, 206 &versionInfo{39, M, 28, 40, 47, 7, 48}, 207 &versionInfo{39, Q, 30, 43, 24, 22, 25}, 208 &versionInfo{39, H, 30, 10, 15, 67, 16}, 209 &versionInfo{40, L, 30, 19, 118, 6, 119}, 210 &versionInfo{40, M, 28, 18, 47, 31, 48}, 211 &versionInfo{40, Q, 30, 34, 24, 34, 25}, 212 &versionInfo{40, H, 30, 20, 15, 61, 16}, 213 } 214 215 func (vi *versionInfo) totalDataBytes() int { 216 g1Data := int(vi.NumberOfBlocksInGroup1) * int(vi.DataCodeWordsPerBlockInGroup1) 217 g2Data := int(vi.NumberOfBlocksInGroup2) * int(vi.DataCodeWordsPerBlockInGroup2) 218 return (g1Data + g2Data) 219 } 220 221 func (vi *versionInfo) charCountBits(m encodingMode) byte { 222 switch m { 223 case numericMode: 224 if vi.Version < 10 { 225 return 10 226 } else if vi.Version < 27 { 227 return 12 228 } 229 return 14 230 231 case alphaNumericMode: 232 if vi.Version < 10 { 233 return 9 234 } else if vi.Version < 27 { 235 return 11 236 } 237 return 13 238 239 case byteMode: 240 if vi.Version < 10 { 241 return 8 242 } 243 return 16 244 245 case kanjiMode: 246 if vi.Version < 10 { 247 return 8 248 } else if vi.Version < 27 { 249 return 10 250 } 251 return 12 252 default: 253 return 0 254 } 255 } 256 257 func (vi *versionInfo) modulWidth() int { 258 return ((int(vi.Version) - 1) * 4) + 21 259 } 260 261 func (vi *versionInfo) alignmentPatternPlacements() []int { 262 if vi.Version == 1 { 263 return make([]int, 0) 264 } 265 266 first := 6 267 last := vi.modulWidth() - 7 268 space := float64(last - first) 269 count := int(math.Ceil(space/28)) + 1 270 271 result := make([]int, count) 272 result[0] = first 273 result[len(result)-1] = last 274 if count > 2 { 275 step := int(math.Ceil(float64(last-first) / float64(count-1))) 276 if step%2 == 1 { 277 frac := float64(last-first) / float64(count-1) 278 _, x := math.Modf(frac) 279 if x >= 0.5 { 280 frac = math.Ceil(frac) 281 } else { 282 frac = math.Floor(frac) 283 } 284 285 if int(frac)%2 == 0 { 286 step-- 287 } else { 288 step++ 289 } 290 } 291 292 for i := 1; i <= count-2; i++ { 293 result[i] = last - (step * (count - 1 - i)) 294 } 295 } 296 297 return result 298 } 299 300 func findSmallestVersionInfo(ecl ErrorCorrectionLevel, mode encodingMode, dataBits int) *versionInfo { 301 dataBits = dataBits + 4 // mode indicator 302 for _, vi := range versionInfos { 303 if vi.Level == ecl { 304 if (vi.totalDataBytes() * 8) >= (dataBits + int(vi.charCountBits(mode))) { 305 return vi 306 } 307 } 308 } 309 return nil 310 }