1//--------------------------------------------------------------------- 2// QRCode for JavaScript 3// 4// Copyright (c) 2009 Kazuhiko Arase 5// 6// URL: http://www.d-project.com/ 7// 8// Licensed under the MIT license: 9// http://www.opensource.org/licenses/mit-license.php 10// 11// The word "QR Code" is registered trademark of 12// DENSO WAVE INCORPORATED 13// http://www.denso-wave.com/qrcode/faqpatent-e.html 14// 15//--------------------------------------------------------------------- 16// Modified to work in node for this project (and some refactoring) 17//--------------------------------------------------------------------- 18 19var QR8bitByte = require('./QR8bitByte'); 20var QRUtil = require('./QRUtil'); 21var QRPolynomial = require('./QRPolynomial'); 22var QRRSBlock = require('./QRRSBlock'); 23var QRBitBuffer = require('./QRBitBuffer'); 24 25function QRCode(typeNumber, errorCorrectLevel) { 26 this.typeNumber = typeNumber; 27 this.errorCorrectLevel = errorCorrectLevel; 28 this.modules = null; 29 this.moduleCount = 0; 30 this.dataCache = null; 31 this.dataList = []; 32} 33 34QRCode.prototype = { 35 36 addData : function(data) { 37 var newData = new QR8bitByte(data); 38 this.dataList.push(newData); 39 this.dataCache = null; 40 }, 41 42 isDark : function(row, col) { 43 if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) { 44 throw new Error(row + "," + col); 45 } 46 return this.modules[row][col]; 47 }, 48 49 getModuleCount : function() { 50 return this.moduleCount; 51 }, 52 53 make : function() { 54 // Calculate automatically typeNumber if provided is < 1 55 if (this.typeNumber < 1 ){ 56 var typeNumber = 1; 57 for (typeNumber = 1; typeNumber < 40; typeNumber++) { 58 var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel); 59 60 var buffer = new QRBitBuffer(); 61 var totalDataCount = 0; 62 for (var i = 0; i < rsBlocks.length; i++) { 63 totalDataCount += rsBlocks[i].dataCount; 64 } 65 66 for (var x = 0; x < this.dataList.length; x++) { 67 var data = this.dataList[x]; 68 buffer.put(data.mode, 4); 69 buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) ); 70 data.write(buffer); 71 } 72 if (buffer.getLengthInBits() <= totalDataCount * 8) 73 break; 74 } 75 this.typeNumber = typeNumber; 76 } 77 this.makeImpl(false, this.getBestMaskPattern() ); 78 }, 79 80 makeImpl : function(test, maskPattern) { 81 82 this.moduleCount = this.typeNumber * 4 + 17; 83 this.modules = new Array(this.moduleCount); 84 85 for (var row = 0; row < this.moduleCount; row++) { 86 87 this.modules[row] = new Array(this.moduleCount); 88 89 for (var col = 0; col < this.moduleCount; col++) { 90 this.modules[row][col] = null;//(col + row) % 3; 91 } 92 } 93 94 this.setupPositionProbePattern(0, 0); 95 this.setupPositionProbePattern(this.moduleCount - 7, 0); 96 this.setupPositionProbePattern(0, this.moduleCount - 7); 97 this.setupPositionAdjustPattern(); 98 this.setupTimingPattern(); 99 this.setupTypeInfo(test, maskPattern); 100 101 if (this.typeNumber >= 7) { 102 this.setupTypeNumber(test); 103 } 104 105 if (this.dataCache === null) { 106 this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList); 107 } 108 109 this.mapData(this.dataCache, maskPattern); 110 }, 111 112 setupPositionProbePattern : function(row, col) { 113 114 for (var r = -1; r <= 7; r++) { 115 116 if (row + r <= -1 || this.moduleCount <= row + r) continue; 117 118 for (var c = -1; c <= 7; c++) { 119 120 if (col + c <= -1 || this.moduleCount <= col + c) continue; 121 122 if ( (0 <= r && r <= 6 && (c === 0 || c === 6) ) || 123 (0 <= c && c <= 6 && (r === 0 || r === 6) ) || 124 (2 <= r && r <= 4 && 2 <= c && c <= 4) ) { 125 this.modules[row + r][col + c] = true; 126 } else { 127 this.modules[row + r][col + c] = false; 128 } 129 } 130 } 131 }, 132 133 getBestMaskPattern : function() { 134 135 var minLostPoint = 0; 136 var pattern = 0; 137 138 for (var i = 0; i < 8; i++) { 139 140 this.makeImpl(true, i); 141 142 var lostPoint = QRUtil.getLostPoint(this); 143 144 if (i === 0 || minLostPoint > lostPoint) { 145 minLostPoint = lostPoint; 146 pattern = i; 147 } 148 } 149 150 return pattern; 151 }, 152 153 createMovieClip : function(target_mc, instance_name, depth) { 154 155 var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth); 156 var cs = 1; 157 158 this.make(); 159 160 for (var row = 0; row < this.modules.length; row++) { 161 162 var y = row * cs; 163 164 for (var col = 0; col < this.modules[row].length; col++) { 165 166 var x = col * cs; 167 var dark = this.modules[row][col]; 168 169 if (dark) { 170 qr_mc.beginFill(0, 100); 171 qr_mc.moveTo(x, y); 172 qr_mc.lineTo(x + cs, y); 173 qr_mc.lineTo(x + cs, y + cs); 174 qr_mc.lineTo(x, y + cs); 175 qr_mc.endFill(); 176 } 177 } 178 } 179 180 return qr_mc; 181 }, 182 183 setupTimingPattern : function() { 184 185 for (var r = 8; r < this.moduleCount - 8; r++) { 186 if (this.modules[r][6] !== null) { 187 continue; 188 } 189 this.modules[r][6] = (r % 2 === 0); 190 } 191 192 for (var c = 8; c < this.moduleCount - 8; c++) { 193 if (this.modules[6][c] !== null) { 194 continue; 195 } 196 this.modules[6][c] = (c % 2 === 0); 197 } 198 }, 199 200 setupPositionAdjustPattern : function() { 201 202 var pos = QRUtil.getPatternPosition(this.typeNumber); 203 204 for (var i = 0; i < pos.length; i++) { 205 206 for (var j = 0; j < pos.length; j++) { 207 208 var row = pos[i]; 209 var col = pos[j]; 210 211 if (this.modules[row][col] !== null) { 212 continue; 213 } 214 215 for (var r = -2; r <= 2; r++) { 216 217 for (var c = -2; c <= 2; c++) { 218 219 if (Math.abs(r) === 2 || 220 Math.abs(c) === 2 || 221 (r === 0 && c === 0) ) { 222 this.modules[row + r][col + c] = true; 223 } else { 224 this.modules[row + r][col + c] = false; 225 } 226 } 227 } 228 } 229 } 230 }, 231 232 setupTypeNumber : function(test) { 233 234 var bits = QRUtil.getBCHTypeNumber(this.typeNumber); 235 var mod; 236 237 for (var i = 0; i < 18; i++) { 238 mod = (!test && ( (bits >> i) & 1) === 1); 239 this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod; 240 } 241 242 for (var x = 0; x < 18; x++) { 243 mod = (!test && ( (bits >> x) & 1) === 1); 244 this.modules[x % 3 + this.moduleCount - 8 - 3][Math.floor(x / 3)] = mod; 245 } 246 }, 247 248 setupTypeInfo : function(test, maskPattern) { 249 250 var data = (this.errorCorrectLevel << 3) | maskPattern; 251 var bits = QRUtil.getBCHTypeInfo(data); 252 var mod; 253 254 // vertical 255 for (var v = 0; v < 15; v++) { 256 257 mod = (!test && ( (bits >> v) & 1) === 1); 258 259 if (v < 6) { 260 this.modules[v][8] = mod; 261 } else if (v < 8) { 262 this.modules[v + 1][8] = mod; 263 } else { 264 this.modules[this.moduleCount - 15 + v][8] = mod; 265 } 266 } 267 268 // horizontal 269 for (var h = 0; h < 15; h++) { 270 271 mod = (!test && ( (bits >> h) & 1) === 1); 272 273 if (h < 8) { 274 this.modules[8][this.moduleCount - h - 1] = mod; 275 } else if (h < 9) { 276 this.modules[8][15 - h - 1 + 1] = mod; 277 } else { 278 this.modules[8][15 - h - 1] = mod; 279 } 280 } 281 282 // fixed module 283 this.modules[this.moduleCount - 8][8] = (!test); 284 285 }, 286 287 mapData : function(data, maskPattern) { 288 289 var inc = -1; 290 var row = this.moduleCount - 1; 291 var bitIndex = 7; 292 var byteIndex = 0; 293 294 for (var col = this.moduleCount - 1; col > 0; col -= 2) { 295 296 if (col === 6) col--; 297 298 while (true) { 299 300 for (var c = 0; c < 2; c++) { 301 302 if (this.modules[row][col - c] === null) { 303 304 var dark = false; 305 306 if (byteIndex < data.length) { 307 dark = ( ( (data[byteIndex] >>> bitIndex) & 1) === 1); 308 } 309 310 var mask = QRUtil.getMask(maskPattern, row, col - c); 311 312 if (mask) { 313 dark = !dark; 314 } 315 316 this.modules[row][col - c] = dark; 317 bitIndex--; 318 319 if (bitIndex === -1) { 320 byteIndex++; 321 bitIndex = 7; 322 } 323 } 324 } 325 326 row += inc; 327 328 if (row < 0 || this.moduleCount <= row) { 329 row -= inc; 330 inc = -inc; 331 break; 332 } 333 } 334 } 335 336 } 337 338}; 339 340QRCode.PAD0 = 0xEC; 341QRCode.PAD1 = 0x11; 342 343QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) { 344 345 var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); 346 347 var buffer = new QRBitBuffer(); 348 349 for (var i = 0; i < dataList.length; i++) { 350 var data = dataList[i]; 351 buffer.put(data.mode, 4); 352 buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) ); 353 data.write(buffer); 354 } 355 356 // calc num max data. 357 var totalDataCount = 0; 358 for (var x = 0; x < rsBlocks.length; x++) { 359 totalDataCount += rsBlocks[x].dataCount; 360 } 361 362 if (buffer.getLengthInBits() > totalDataCount * 8) { 363 throw new Error("code length overflow. (" + 364 buffer.getLengthInBits() + 365 ">" + 366 totalDataCount * 8 + 367 ")"); 368 } 369 370 // end code 371 if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { 372 buffer.put(0, 4); 373 } 374 375 // padding 376 while (buffer.getLengthInBits() % 8 !== 0) { 377 buffer.putBit(false); 378 } 379 380 // padding 381 while (true) { 382 383 if (buffer.getLengthInBits() >= totalDataCount * 8) { 384 break; 385 } 386 buffer.put(QRCode.PAD0, 8); 387 388 if (buffer.getLengthInBits() >= totalDataCount * 8) { 389 break; 390 } 391 buffer.put(QRCode.PAD1, 8); 392 } 393 394 return QRCode.createBytes(buffer, rsBlocks); 395}; 396 397QRCode.createBytes = function(buffer, rsBlocks) { 398 399 var offset = 0; 400 401 var maxDcCount = 0; 402 var maxEcCount = 0; 403 404 var dcdata = new Array(rsBlocks.length); 405 var ecdata = new Array(rsBlocks.length); 406 407 for (var r = 0; r < rsBlocks.length; r++) { 408 409 var dcCount = rsBlocks[r].dataCount; 410 var ecCount = rsBlocks[r].totalCount - dcCount; 411 412 maxDcCount = Math.max(maxDcCount, dcCount); 413 maxEcCount = Math.max(maxEcCount, ecCount); 414 415 dcdata[r] = new Array(dcCount); 416 417 for (var i = 0; i < dcdata[r].length; i++) { 418 dcdata[r][i] = 0xff & buffer.buffer[i + offset]; 419 } 420 offset += dcCount; 421 422 var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); 423 var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1); 424 425 var modPoly = rawPoly.mod(rsPoly); 426 ecdata[r] = new Array(rsPoly.getLength() - 1); 427 for (var x = 0; x < ecdata[r].length; x++) { 428 var modIndex = x + modPoly.getLength() - ecdata[r].length; 429 ecdata[r][x] = (modIndex >= 0)? modPoly.get(modIndex) : 0; 430 } 431 432 } 433 434 var totalCodeCount = 0; 435 for (var y = 0; y < rsBlocks.length; y++) { 436 totalCodeCount += rsBlocks[y].totalCount; 437 } 438 439 var data = new Array(totalCodeCount); 440 var index = 0; 441 442 for (var z = 0; z < maxDcCount; z++) { 443 for (var s = 0; s < rsBlocks.length; s++) { 444 if (z < dcdata[s].length) { 445 data[index++] = dcdata[s][z]; 446 } 447 } 448 } 449 450 for (var xx = 0; xx < maxEcCount; xx++) { 451 for (var t = 0; t < rsBlocks.length; t++) { 452 if (xx < ecdata[t].length) { 453 data[index++] = ecdata[t][xx]; 454 } 455 } 456 } 457 458 return data; 459 460}; 461 462module.exports = QRCode; 463