├── .gitattributes ├── .gitignore ├── LICENSE ├── QRCode.d.ts ├── QRCode.js ├── QRCode.min.js ├── demo └── App.js ├── doc └── images │ ├── QR_Code_Structure.png │ ├── QR_Code_Structure.svg │ ├── demo-premium.png │ └── demo.png ├── package.json └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # temp 64 | 65 | # Test 66 | test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ray 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QRCode.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for QRCode.js 2 | // Project: [https://github.com/ushelp/EasyQRCode-React-Native] 3 | // Definitions by: Ray 4 | 5 | export class QRCode { 6 | constructor(canvas: any, vOption: any); 7 | 8 | makeCode(sText: any): void; 9 | 10 | static CorrectLevel: { 11 | H: number; 12 | L: number; 13 | M: number; 14 | Q: number; 15 | }; 16 | 17 | } 18 | 19 | export const Canvas: any; 20 | 21 | -------------------------------------------------------------------------------- /QRCode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * EasyQRCode-React-Native 3 | * 4 | * React Native QRCode generation component. Can generate standard QRCode image or base64 image data url text. Cross-browser QRCode generator for pure javascript. Support Dot style, Logo, Background image, Colorful, Title etc. settings. support binary mode. 5 | * 6 | * Version 4.0.11 7 | * 8 | * @author [ inthinkcolor@gmail.com ] 9 | * 10 | * @see https://github.com/ushelp/EasyQRCode-React-Native 11 | * @see http://www.easyproject.cn/easyqrcodejs/tryit.html 12 | * 13 | * Copyright 2017 Ray, EasyProject 14 | * Released under the MIT license 15 | * 16 | * [React Native] 17 | * 18 | */ 19 | import Canvas, { 20 | Image as CanvasImage 21 | } from 'react-native-canvas'; 22 | 23 | function QR8bitByte(data, binary, utf8WithoutBOM) { 24 | this.mode = QRMode.MODE_8BIT_BYTE; 25 | this.data = data; 26 | this.parsedData = []; 27 | 28 | function toUTF8Array(str) { 29 | var utf8 = []; 30 | for (var i = 0; i < str.length; i++) { 31 | var charcode = str.charCodeAt(i); 32 | if (charcode < 0x80) utf8.push(charcode); 33 | else if (charcode < 0x800) { 34 | utf8.push(0xc0 | (charcode >> 6), 35 | 0x80 | (charcode & 0x3f)); 36 | } else if (charcode < 0xd800 || charcode >= 0xe000) { 37 | utf8.push(0xe0 | (charcode >> 12), 38 | 0x80 | ((charcode >> 6) & 0x3f), 39 | 0x80 | (charcode & 0x3f)); 40 | } else { 41 | i++; 42 | charcode = 0x10000 + (((charcode & 0x3ff) << 10) | 43 | (str.charCodeAt(i) & 0x3ff)); 44 | utf8.push(0xf0 | (charcode >> 18), 45 | 0x80 | ((charcode >> 12) & 0x3f), 46 | 0x80 | ((charcode >> 6) & 0x3f), 47 | 0x80 | (charcode & 0x3f)); 48 | } 49 | } 50 | return utf8; 51 | } 52 | 53 | if (binary) { 54 | for (var i = 0, l = this.data.length; i < l; i++) { 55 | var byteArray = []; 56 | var code = this.data.charCodeAt(i); 57 | byteArray[0] = code; 58 | 59 | this.parsedData.push(byteArray); 60 | } 61 | this.parsedData = Array.prototype.concat.apply([], this.parsedData); 62 | 63 | } else { 64 | this.parsedData = toUTF8Array(data); 65 | } 66 | 67 | this.parsedData = Array.prototype.concat.apply([], this.parsedData); 68 | if (!utf8WithoutBOM && this.parsedData.length != this.data.length) { 69 | this.parsedData.unshift(191); 70 | this.parsedData.unshift(187); 71 | this.parsedData.unshift(239); 72 | } 73 | } 74 | 75 | QR8bitByte.prototype = { 76 | getLength: function(buffer) { 77 | return this.parsedData.length; 78 | }, 79 | write: function(buffer) { 80 | for (var i = 0, l = this.parsedData.length; i < l; i++) { 81 | buffer.put(this.parsedData[i], 8); 82 | } 83 | } 84 | }; 85 | 86 | function QRCodeModel(typeNumber, errorCorrectLevel) { 87 | this.typeNumber = typeNumber; 88 | this.errorCorrectLevel = errorCorrectLevel; 89 | this.modules = null; 90 | this.moduleCount = 0; 91 | this.dataCache = null; 92 | this.dataList = []; 93 | } 94 | 95 | QRCodeModel.prototype = { 96 | addData: function(data, binary, utf8WithoutBOM) { 97 | var newData = new QR8bitByte(data, binary, utf8WithoutBOM); 98 | this.dataList.push(newData); 99 | this.dataCache = null; 100 | }, 101 | isDark: function(row, col) { 102 | if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) { 103 | throw new Error(row + "," + col); 104 | } 105 | return this.modules[row][col][0]; 106 | }, 107 | getEye: function(row, col) { 108 | if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) { 109 | throw new Error(row + "," + col); 110 | } 111 | 112 | var block = this.modules[row][col]; // [isDark(ture/false), EyeOuterOrInner(O/I), Position(TL/TR/BL/A) ] 113 | 114 | if (block[1]) { 115 | var type = 'P' + block[1] + '_' + block[2]; //PO_TL, PI_TL, PO_TR, PI_TR, PO_BL, PI_BL 116 | if (block[2] == 'A') { 117 | type = 'A' + block[1]; // AI, AO 118 | } 119 | 120 | return { 121 | isDarkBlock: block[0], 122 | type: type 123 | }; 124 | } else { 125 | return null; 126 | } 127 | }, 128 | getModuleCount: function() { 129 | return this.moduleCount; 130 | }, 131 | make: function() { 132 | this.makeImpl(false, this.getBestMaskPattern()); 133 | }, 134 | makeImpl: function(test, maskPattern) { 135 | this.moduleCount = this.typeNumber * 4 + 17; 136 | this.modules = new Array(this.moduleCount); 137 | for (var row = 0; row < this.moduleCount; row++) { 138 | this.modules[row] = new Array(this.moduleCount); 139 | for (var col = 0; col < this.moduleCount; col++) { 140 | this.modules[row][col] = []; // [isDark(ture/false), EyeOuterOrInner(O/I), Position(TL/TR/BL) ] 141 | } 142 | } 143 | this.setupPositionProbePattern(0, 0, 'TL'); // TopLeft, TL 144 | this.setupPositionProbePattern(this.moduleCount - 7, 0, 'BL'); // BotoomLeft, BL 145 | this.setupPositionProbePattern(0, this.moduleCount - 7, 'TR'); // TopRight, TR 146 | this.setupPositionAdjustPattern('A'); // Alignment, A 147 | this.setupTimingPattern(); 148 | this.setupTypeInfo(test, maskPattern); 149 | if (this.typeNumber >= 7) { 150 | this.setupTypeNumber(test); 151 | } 152 | if (this.dataCache == null) { 153 | this.dataCache = QRCodeModel.createData(this.typeNumber, this.errorCorrectLevel, this.dataList); 154 | } 155 | this.mapData(this.dataCache, maskPattern); 156 | }, 157 | setupPositionProbePattern: function(row, col, posName) { 158 | for (var r = -1; r <= 7; r++) { 159 | if (row + r <= -1 || this.moduleCount <= row + r) continue; 160 | for (var c = -1; c <= 7; c++) { 161 | if (col + c <= -1 || this.moduleCount <= col + c) continue; 162 | if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || ( 163 | 2 <= 164 | r && r <= 4 && 165 | 2 <= c && c <= 4)) { 166 | this.modules[row + r][col + c][0] = true; 167 | 168 | this.modules[row + r][col + c][2] = posName; // Position 169 | if (r == -0 || c == -0 || r == 6 || c == 6) { 170 | this.modules[row + r][col + c][1] = 'O'; // Position Outer 171 | } else { 172 | this.modules[row + r][col + c][1] = 'I'; // Position Inner 173 | } 174 | } else { 175 | this.modules[row + r][col + c][0] = false; 176 | } 177 | } 178 | } 179 | }, 180 | getBestMaskPattern: function() { 181 | var minLostPoint = 0; 182 | var pattern = 0; 183 | for (var i = 0; i < 8; i++) { 184 | this.makeImpl(true, i); 185 | var lostPoint = QRUtil.getLostPoint(this); 186 | if (i == 0 || minLostPoint > lostPoint) { 187 | minLostPoint = lostPoint; 188 | pattern = i; 189 | } 190 | } 191 | return pattern; 192 | }, 193 | createMovieClip: function(target_mc, instance_name, depth) { 194 | var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth); 195 | var cs = 1; 196 | this.make(); 197 | for (var row = 0; row < this.modules.length; row++) { 198 | var y = row * cs; 199 | for (var col = 0; col < this.modules[row].length; col++) { 200 | var x = col * cs; 201 | var dark = this.modules[row][col][0]; 202 | if (dark) { 203 | qr_mc.beginFill(0, 100); 204 | qr_mc.moveTo(x, y); 205 | qr_mc.lineTo(x + cs, y); 206 | qr_mc.lineTo(x + cs, y + cs); 207 | qr_mc.lineTo(x, y + cs); 208 | qr_mc.endFill(); 209 | } 210 | } 211 | } 212 | return qr_mc; 213 | }, 214 | setupTimingPattern: function() { 215 | for (var r = 8; r < this.moduleCount - 8; r++) { 216 | if (this.modules[r][6][0] != null) { 217 | continue; 218 | } 219 | this.modules[r][6][0] = (r % 2 == 0); 220 | } 221 | for (var c = 8; c < this.moduleCount - 8; c++) { 222 | if (this.modules[6][c][0] != null) { 223 | continue; 224 | } 225 | this.modules[6][c][0] = (c % 2 == 0); 226 | } 227 | }, 228 | setupPositionAdjustPattern: function(posName) { 229 | var pos = QRUtil.getPatternPosition(this.typeNumber); 230 | for (var i = 0; i < pos.length; i++) { 231 | for (var j = 0; j < pos.length; j++) { 232 | var row = pos[i]; 233 | var col = pos[j]; 234 | if (this.modules[row][col][0] != null) { 235 | continue; 236 | } 237 | for (var r = -2; r <= 2; r++) { 238 | for (var c = -2; c <= 2; c++) { 239 | if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) { 240 | this.modules[row + r][col + c][0] = true; 241 | this.modules[row + r][col + c][2] = posName; // Position 242 | if (r == -2 || c == -2 || r == 2 || c == 2) { 243 | this.modules[row + r][col + c][1] = 'O'; // Position Outer 244 | } else { 245 | this.modules[row + r][col + c][1] = 'I'; // Position Inner 246 | } 247 | } else { 248 | this.modules[row + r][col + c][0] = false; 249 | } 250 | } 251 | } 252 | } 253 | } 254 | }, 255 | setupTypeNumber: function(test) { 256 | var bits = QRUtil.getBCHTypeNumber(this.typeNumber); 257 | for (var i = 0; i < 18; i++) { 258 | var mod = (!test && ((bits >> i) & 1) == 1); 259 | this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3][0] = mod; 260 | } 261 | for (var i = 0; i < 18; i++) { 262 | var mod = (!test && ((bits >> i) & 1) == 1); 263 | this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)][0] = mod; 264 | } 265 | }, 266 | setupTypeInfo: function(test, maskPattern) { 267 | var data = (this.errorCorrectLevel << 3) | maskPattern; 268 | var bits = QRUtil.getBCHTypeInfo(data); 269 | for (var i = 0; i < 15; i++) { 270 | var mod = (!test && ((bits >> i) & 1) == 1); 271 | if (i < 6) { 272 | this.modules[i][8][0] = mod; 273 | } else if (i < 8) { 274 | this.modules[i + 1][8][0] = mod; 275 | } else { 276 | this.modules[this.moduleCount - 15 + i][8][0] = mod; 277 | } 278 | } 279 | for (var i = 0; i < 15; i++) { 280 | var mod = (!test && ((bits >> i) & 1) == 1); 281 | if (i < 8) { 282 | this.modules[8][this.moduleCount - i - 1][0] = mod; 283 | } else if (i < 9) { 284 | this.modules[8][15 - i - 1 + 1][0] = mod; 285 | } else { 286 | this.modules[8][15 - i - 1][0] = mod; 287 | } 288 | } 289 | this.modules[this.moduleCount - 8][8][0] = (!test); 290 | }, 291 | mapData: function(data, maskPattern) { 292 | var inc = -1; 293 | var row = this.moduleCount - 1; 294 | var bitIndex = 7; 295 | var byteIndex = 0; 296 | for (var col = this.moduleCount - 1; col > 0; col -= 2) { 297 | if (col == 6) col--; 298 | while (true) { 299 | for (var c = 0; c < 2; c++) { 300 | if (this.modules[row][col - c][0] == null) { 301 | var dark = false; 302 | if (byteIndex < data.length) { 303 | dark = (((data[byteIndex] >>> bitIndex) & 1) == 1); 304 | } 305 | var mask = QRUtil.getMask(maskPattern, row, col - c); 306 | if (mask) { 307 | dark = !dark; 308 | } 309 | this.modules[row][col - c][0] = dark; 310 | bitIndex--; 311 | if (bitIndex == -1) { 312 | byteIndex++; 313 | bitIndex = 7; 314 | } 315 | } 316 | } 317 | row += inc; 318 | if (row < 0 || this.moduleCount <= row) { 319 | row -= inc; 320 | inc = -inc; 321 | break; 322 | } 323 | } 324 | } 325 | } 326 | }; 327 | QRCodeModel.PAD0 = 0xEC; 328 | QRCodeModel.PAD1 = 0x11; 329 | QRCodeModel.createData = function(typeNumber, errorCorrectLevel, dataList) { 330 | var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); 331 | var buffer = new QRBitBuffer(); 332 | for (var i = 0; i < dataList.length; i++) { 333 | var data = dataList[i]; 334 | buffer.put(data.mode, 4); 335 | buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber)); 336 | data.write(buffer); 337 | } 338 | var totalDataCount = 0; 339 | for (var i = 0; i < rsBlocks.length; i++) { 340 | totalDataCount += rsBlocks[i].dataCount; 341 | } 342 | if (buffer.getLengthInBits() > totalDataCount * 8) { 343 | throw new Error("code length overflow. (" + 344 | buffer.getLengthInBits() + 345 | ">" + 346 | totalDataCount * 8 + 347 | ")"); 348 | } 349 | if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { 350 | buffer.put(0, 4); 351 | } 352 | while (buffer.getLengthInBits() % 8 != 0) { 353 | buffer.putBit(false); 354 | } 355 | while (true) { 356 | if (buffer.getLengthInBits() >= totalDataCount * 8) { 357 | break; 358 | } 359 | buffer.put(QRCodeModel.PAD0, 8); 360 | if (buffer.getLengthInBits() >= totalDataCount * 8) { 361 | break; 362 | } 363 | buffer.put(QRCodeModel.PAD1, 8); 364 | } 365 | return QRCodeModel.createBytes(buffer, rsBlocks); 366 | }; 367 | QRCodeModel.createBytes = function(buffer, rsBlocks) { 368 | var offset = 0; 369 | var maxDcCount = 0; 370 | var maxEcCount = 0; 371 | var dcdata = new Array(rsBlocks.length); 372 | var ecdata = new Array(rsBlocks.length); 373 | for (var r = 0; r < rsBlocks.length; r++) { 374 | var dcCount = rsBlocks[r].dataCount; 375 | var ecCount = rsBlocks[r].totalCount - dcCount; 376 | maxDcCount = Math.max(maxDcCount, dcCount); 377 | maxEcCount = Math.max(maxEcCount, ecCount); 378 | dcdata[r] = new Array(dcCount); 379 | for (var i = 0; i < dcdata[r].length; i++) { 380 | dcdata[r][i] = 0xff & buffer.buffer[i + offset]; 381 | } 382 | offset += dcCount; 383 | var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); 384 | var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1); 385 | var modPoly = rawPoly.mod(rsPoly); 386 | ecdata[r] = new Array(rsPoly.getLength() - 1); 387 | for (var i = 0; i < ecdata[r].length; i++) { 388 | var modIndex = i + modPoly.getLength() - ecdata[r].length; 389 | ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0; 390 | } 391 | } 392 | var totalCodeCount = 0; 393 | for (var i = 0; i < rsBlocks.length; i++) { 394 | totalCodeCount += rsBlocks[i].totalCount; 395 | } 396 | var data = new Array(totalCodeCount); 397 | var index = 0; 398 | for (var i = 0; i < maxDcCount; i++) { 399 | for (var r = 0; r < rsBlocks.length; r++) { 400 | if (i < dcdata[r].length) { 401 | data[index++] = dcdata[r][i]; 402 | } 403 | } 404 | } 405 | for (var i = 0; i < maxEcCount; i++) { 406 | for (var r = 0; r < rsBlocks.length; r++) { 407 | if (i < ecdata[r].length) { 408 | data[index++] = ecdata[r][i]; 409 | } 410 | } 411 | } 412 | return data; 413 | }; 414 | var QRMode = { 415 | MODE_NUMBER: 1 << 0, 416 | MODE_ALPHA_NUM: 1 << 1, 417 | MODE_8BIT_BYTE: 1 << 2, 418 | MODE_KANJI: 1 << 3 419 | }; 420 | var QRErrorCorrectLevel = { 421 | L: 1, 422 | M: 0, 423 | Q: 3, 424 | H: 2 425 | }; 426 | var QRMaskPattern = { 427 | PATTERN000: 0, 428 | PATTERN001: 1, 429 | PATTERN010: 2, 430 | PATTERN011: 3, 431 | PATTERN100: 4, 432 | PATTERN101: 5, 433 | PATTERN110: 6, 434 | PATTERN111: 7 435 | }; 436 | var QRUtil = { 437 | PATTERN_POSITION_TABLE: [ 438 | [], 439 | [6, 18], 440 | [6, 22], 441 | [6, 26], 442 | [6, 30], 443 | [6, 34], 444 | [6, 22, 38], 445 | [6, 24, 42], 446 | [6, 26, 46], 447 | [6, 28, 50], 448 | [6, 30, 54], 449 | [6, 32, 58], 450 | [6, 34, 62], 451 | [6, 26, 46, 66], 452 | [6, 26, 48, 70], 453 | [6, 26, 50, 74], 454 | [6, 30, 54, 78], 455 | [6, 30, 56, 82], 456 | [6, 30, 58, 86], 457 | [6, 34, 62, 90], 458 | [6, 28, 50, 72, 94], 459 | [6, 26, 50, 74, 98], 460 | [6, 30, 54, 78, 102], 461 | [6, 28, 54, 80, 106], 462 | [6, 32, 58, 84, 110], 463 | [6, 30, 58, 86, 114], 464 | [6, 34, 62, 90, 118], 465 | [6, 26, 50, 74, 98, 122], 466 | [6, 30, 54, 78, 102, 126], 467 | [6, 26, 52, 78, 104, 130], 468 | [6, 30, 56, 82, 108, 134], 469 | [6, 34, 60, 86, 112, 138], 470 | [6, 30, 58, 86, 114, 142], 471 | [6, 34, 62, 90, 118, 146], 472 | [6, 30, 54, 78, 102, 126, 150], 473 | [6, 24, 50, 76, 102, 128, 154], 474 | [6, 28, 54, 80, 106, 132, 158], 475 | [6, 32, 58, 84, 110, 136, 162], 476 | [6, 26, 54, 82, 110, 138, 166], 477 | [6, 30, 58, 86, 114, 142, 170] 478 | ], 479 | G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0), 480 | G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0), 481 | G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1), 482 | getBCHTypeInfo: function(data) { 483 | var d = data << 10; 484 | while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) { 485 | d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15))); 486 | } 487 | return ((data << 10) | d) ^ QRUtil.G15_MASK; 488 | }, 489 | getBCHTypeNumber: function(data) { 490 | var d = data << 12; 491 | while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) { 492 | d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18))); 493 | } 494 | return (data << 12) | d; 495 | }, 496 | getBCHDigit: function(data) { 497 | var digit = 0; 498 | while (data != 0) { 499 | digit++; 500 | data >>>= 1; 501 | } 502 | return digit; 503 | }, 504 | getPatternPosition: function(typeNumber) { 505 | return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]; 506 | }, 507 | getMask: function(maskPattern, i, j) { 508 | switch (maskPattern) { 509 | case QRMaskPattern.PATTERN000: 510 | return (i + j) % 2 == 0; 511 | case QRMaskPattern.PATTERN001: 512 | return i % 2 == 0; 513 | case QRMaskPattern.PATTERN010: 514 | return j % 3 == 0; 515 | case QRMaskPattern.PATTERN011: 516 | return (i + j) % 3 == 0; 517 | case QRMaskPattern.PATTERN100: 518 | return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0; 519 | case QRMaskPattern.PATTERN101: 520 | return (i * j) % 2 + (i * j) % 3 == 0; 521 | case QRMaskPattern.PATTERN110: 522 | return ((i * j) % 2 + (i * j) % 3) % 2 == 0; 523 | case QRMaskPattern.PATTERN111: 524 | return ((i * j) % 3 + (i + j) % 2) % 2 == 0; 525 | default: 526 | throw new Error("bad maskPattern:" + maskPattern); 527 | } 528 | }, 529 | getErrorCorrectPolynomial: function(errorCorrectLength) { 530 | var a = new QRPolynomial([1], 0); 531 | for (var i = 0; i < errorCorrectLength; i++) { 532 | a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0)); 533 | } 534 | return a; 535 | }, 536 | getLengthInBits: function(mode, type) { 537 | if (1 <= type && type < 10) { 538 | switch (mode) { 539 | case QRMode.MODE_NUMBER: 540 | return 10; 541 | case QRMode.MODE_ALPHA_NUM: 542 | return 9; 543 | case QRMode.MODE_8BIT_BYTE: 544 | return 8; 545 | case QRMode.MODE_KANJI: 546 | return 8; 547 | default: 548 | throw new Error("mode:" + mode); 549 | } 550 | } else if (type < 27) { 551 | switch (mode) { 552 | case QRMode.MODE_NUMBER: 553 | return 12; 554 | case QRMode.MODE_ALPHA_NUM: 555 | return 11; 556 | case QRMode.MODE_8BIT_BYTE: 557 | return 16; 558 | case QRMode.MODE_KANJI: 559 | return 10; 560 | default: 561 | throw new Error("mode:" + mode); 562 | } 563 | } else if (type < 41) { 564 | switch (mode) { 565 | case QRMode.MODE_NUMBER: 566 | return 14; 567 | case QRMode.MODE_ALPHA_NUM: 568 | return 13; 569 | case QRMode.MODE_8BIT_BYTE: 570 | return 16; 571 | case QRMode.MODE_KANJI: 572 | return 12; 573 | default: 574 | throw new Error("mode:" + mode); 575 | } 576 | } else { 577 | throw new Error("type:" + type); 578 | } 579 | }, 580 | getLostPoint: function(qrCode) { 581 | var moduleCount = qrCode.getModuleCount(); 582 | var lostPoint = 0; 583 | for (var row = 0; row < moduleCount; row++) { 584 | for (var col = 0; col < moduleCount; col++) { 585 | var sameCount = 0; 586 | var dark = qrCode.isDark(row, col); 587 | for (var r = -1; r <= 1; r++) { 588 | if (row + r < 0 || moduleCount <= row + r) { 589 | continue; 590 | } 591 | for (var c = -1; c <= 1; c++) { 592 | if (col + c < 0 || moduleCount <= col + c) { 593 | continue; 594 | } 595 | if (r == 0 && c == 0) { 596 | continue; 597 | } 598 | if (dark == qrCode.isDark(row + r, col + c)) { 599 | sameCount++; 600 | } 601 | } 602 | } 603 | if (sameCount > 5) { 604 | lostPoint += (3 + sameCount - 5); 605 | } 606 | } 607 | } 608 | for (var row = 0; row < moduleCount - 1; row++) { 609 | for (var col = 0; col < moduleCount - 1; col++) { 610 | var count = 0; 611 | if (qrCode.isDark(row, col)) count++; 612 | if (qrCode.isDark(row + 1, col)) count++; 613 | if (qrCode.isDark(row, col + 1)) count++; 614 | if (qrCode.isDark(row + 1, col + 1)) count++; 615 | if (count == 0 || count == 4) { 616 | lostPoint += 3; 617 | } 618 | } 619 | } 620 | for (var row = 0; row < moduleCount; row++) { 621 | for (var col = 0; col < moduleCount - 6; col++) { 622 | if (qrCode.isDark(row, col) && !qrCode.isDark(row, col + 1) && qrCode.isDark(row, col + 2) && 623 | qrCode.isDark( 624 | row, 625 | col + 3) && qrCode.isDark(row, col + 4) && !qrCode.isDark(row, col + 5) && qrCode 626 | .isDark( 627 | row, col + 6)) { 628 | lostPoint += 40; 629 | } 630 | } 631 | } 632 | for (var col = 0; col < moduleCount; col++) { 633 | for (var row = 0; row < moduleCount - 6; row++) { 634 | if (qrCode.isDark(row, col) && !qrCode.isDark(row + 1, col) && qrCode.isDark(row + 2, col) && 635 | qrCode.isDark( 636 | row + 637 | 3, col) && qrCode.isDark(row + 4, col) && !qrCode.isDark(row + 5, col) && qrCode.isDark( 638 | row + 6, col)) { 639 | lostPoint += 40; 640 | } 641 | } 642 | } 643 | var darkCount = 0; 644 | for (var col = 0; col < moduleCount; col++) { 645 | for (var row = 0; row < moduleCount; row++) { 646 | if (qrCode.isDark(row, col)) { 647 | darkCount++; 648 | } 649 | } 650 | } 651 | var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; 652 | lostPoint += ratio * 10; 653 | return lostPoint; 654 | } 655 | }; 656 | var QRMath = { 657 | glog: function(n) { 658 | if (n < 1) { 659 | throw new Error("glog(" + n + ")"); 660 | } 661 | return QRMath.LOG_TABLE[n]; 662 | }, 663 | gexp: function(n) { 664 | while (n < 0) { 665 | n += 255; 666 | } 667 | while (n >= 256) { 668 | n -= 255; 669 | } 670 | return QRMath.EXP_TABLE[n]; 671 | }, 672 | EXP_TABLE: new Array(256), 673 | LOG_TABLE: new Array(256) 674 | }; 675 | for (var i = 0; i < 8; i++) { 676 | QRMath.EXP_TABLE[i] = 1 << i; 677 | } 678 | for (var i = 8; i < 256; i++) { 679 | QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath 680 | .EXP_TABLE[ 681 | i - 8]; 682 | } 683 | for (var i = 0; i < 255; i++) { 684 | QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i; 685 | } 686 | 687 | function QRPolynomial(num, shift) { 688 | if (num.length == undefined) { 689 | throw new Error(num.length + "/" + shift); 690 | } 691 | var offset = 0; 692 | while (offset < num.length && num[offset] == 0) { 693 | offset++; 694 | } 695 | this.num = new Array(num.length - offset + shift); 696 | for (var i = 0; i < num.length - offset; i++) { 697 | this.num[i] = num[i + offset]; 698 | } 699 | } 700 | QRPolynomial.prototype = { 701 | get: function(index) { 702 | return this.num[index]; 703 | }, 704 | getLength: function() { 705 | return this.num.length; 706 | }, 707 | multiply: function(e) { 708 | var num = new Array(this.getLength() + e.getLength() - 1); 709 | for (var i = 0; i < this.getLength(); i++) { 710 | for (var j = 0; j < e.getLength(); j++) { 711 | num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j))); 712 | } 713 | } 714 | return new QRPolynomial(num, 0); 715 | }, 716 | mod: function(e) { 717 | if (this.getLength() - e.getLength() < 0) { 718 | return this; 719 | } 720 | var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0)); 721 | var num = new Array(this.getLength()); 722 | for (var i = 0; i < this.getLength(); i++) { 723 | num[i] = this.get(i); 724 | } 725 | for (var i = 0; i < e.getLength(); i++) { 726 | num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio); 727 | } 728 | return new QRPolynomial(num, 0).mod(e); 729 | } 730 | }; 731 | 732 | function QRRSBlock(totalCount, dataCount) { 733 | this.totalCount = totalCount; 734 | this.dataCount = dataCount; 735 | } 736 | QRRSBlock.RS_BLOCK_TABLE = [ 737 | [1, 26, 19], 738 | [1, 26, 16], 739 | [1, 26, 13], 740 | [1, 26, 9], 741 | [1, 44, 34], 742 | [1, 44, 28], 743 | [1, 44, 22], 744 | [1, 44, 16], 745 | [1, 70, 55], 746 | [1, 70, 44], 747 | [2, 35, 17], 748 | [2, 35, 13], 749 | [1, 100, 80], 750 | [2, 50, 32], 751 | [2, 50, 24], 752 | [4, 25, 9], 753 | [1, 134, 108], 754 | [2, 67, 43], 755 | [2, 33, 15, 2, 34, 16], 756 | [2, 33, 11, 2, 34, 12], 757 | [2, 86, 68], 758 | [4, 43, 27], 759 | [4, 43, 19], 760 | [4, 43, 15], 761 | [2, 98, 78], 762 | [4, 49, 31], 763 | [2, 32, 14, 4, 33, 15], 764 | [4, 39, 13, 1, 40, 14], 765 | [2, 121, 97], 766 | [2, 60, 38, 2, 61, 39], 767 | [4, 40, 18, 2, 41, 19], 768 | [4, 40, 14, 2, 41, 15], 769 | [2, 146, 116], 770 | [3, 58, 36, 2, 59, 37], 771 | [4, 36, 16, 4, 37, 17], 772 | [4, 36, 12, 4, 37, 13], 773 | [2, 86, 68, 2, 87, 69], 774 | [4, 69, 43, 1, 70, 44], 775 | [6, 43, 19, 2, 44, 20], 776 | [6, 43, 15, 2, 44, 16], 777 | [4, 101, 81], 778 | [1, 80, 50, 4, 81, 51], 779 | [4, 50, 22, 4, 51, 23], 780 | [3, 36, 12, 8, 37, 13], 781 | [2, 116, 92, 2, 117, 93], 782 | [6, 58, 36, 2, 59, 37], 783 | [4, 46, 20, 6, 47, 21], 784 | [7, 42, 14, 4, 43, 15], 785 | [4, 133, 107], 786 | [8, 59, 37, 1, 60, 38], 787 | [8, 44, 20, 4, 45, 21], 788 | [12, 33, 11, 4, 34, 12], 789 | [3, 145, 115, 1, 146, 116], 790 | [4, 64, 40, 5, 65, 41], 791 | [11, 36, 16, 5, 37, 17], 792 | [11, 36, 12, 5, 37, 13], 793 | [5, 109, 87, 1, 110, 88], 794 | [5, 65, 41, 5, 66, 42], 795 | [5, 54, 24, 7, 55, 25], 796 | [11, 36, 12, 7, 37, 13], 797 | [5, 122, 98, 1, 123, 99], 798 | [7, 73, 45, 3, 74, 46], 799 | [15, 43, 19, 2, 44, 20], 800 | [3, 45, 15, 13, 46, 16], 801 | [1, 135, 107, 5, 136, 108], 802 | [10, 74, 46, 1, 75, 47], 803 | [1, 50, 22, 15, 51, 23], 804 | [2, 42, 14, 17, 43, 15], 805 | [5, 150, 120, 1, 151, 121], 806 | [9, 69, 43, 4, 70, 44], 807 | [17, 50, 22, 1, 51, 23], 808 | [2, 42, 14, 19, 43, 15], 809 | [3, 141, 113, 4, 142, 114], 810 | [3, 70, 44, 11, 71, 45], 811 | [17, 47, 21, 4, 48, 22], 812 | [9, 39, 13, 16, 40, 14], 813 | [3, 135, 107, 5, 136, 108], 814 | [3, 67, 41, 13, 68, 42], 815 | [15, 54, 24, 5, 55, 25], 816 | [15, 43, 15, 10, 44, 16], 817 | [4, 144, 116, 4, 145, 117], 818 | [17, 68, 42], 819 | [17, 50, 22, 6, 51, 23], 820 | [19, 46, 16, 6, 47, 17], 821 | [2, 139, 111, 7, 140, 112], 822 | [17, 74, 46], 823 | [7, 54, 24, 16, 55, 25], 824 | [34, 37, 13], 825 | [4, 151, 121, 5, 152, 122], 826 | [4, 75, 47, 14, 76, 48], 827 | [11, 54, 24, 14, 55, 25], 828 | [16, 45, 15, 14, 46, 16], 829 | [6, 147, 117, 4, 148, 118], 830 | [6, 73, 45, 14, 74, 46], 831 | [11, 54, 24, 16, 55, 25], 832 | [30, 46, 16, 2, 47, 17], 833 | [8, 132, 106, 4, 133, 107], 834 | [8, 75, 47, 13, 76, 48], 835 | [7, 54, 24, 22, 55, 25], 836 | [22, 45, 15, 13, 46, 16], 837 | [10, 142, 114, 2, 143, 115], 838 | [19, 74, 46, 4, 75, 47], 839 | [28, 50, 22, 6, 51, 23], 840 | [33, 46, 16, 4, 47, 17], 841 | [8, 152, 122, 4, 153, 123], 842 | [22, 73, 45, 3, 74, 46], 843 | [8, 53, 23, 26, 54, 24], 844 | [12, 45, 15, 28, 46, 16], 845 | [3, 147, 117, 10, 148, 118], 846 | [3, 73, 45, 23, 74, 46], 847 | [4, 54, 24, 31, 55, 25], 848 | [11, 45, 15, 31, 46, 16], 849 | [7, 146, 116, 7, 147, 117], 850 | [21, 73, 45, 7, 74, 46], 851 | [1, 53, 23, 37, 54, 24], 852 | [19, 45, 15, 26, 46, 16], 853 | [5, 145, 115, 10, 146, 116], 854 | [19, 75, 47, 10, 76, 48], 855 | [15, 54, 24, 25, 55, 25], 856 | [23, 45, 15, 25, 46, 16], 857 | [13, 145, 115, 3, 146, 116], 858 | [2, 74, 46, 29, 75, 47], 859 | [42, 54, 24, 1, 55, 25], 860 | [23, 45, 15, 28, 46, 16], 861 | [17, 145, 115], 862 | [10, 74, 46, 23, 75, 47], 863 | [10, 54, 24, 35, 55, 25], 864 | [19, 45, 15, 35, 46, 16], 865 | [17, 145, 115, 1, 146, 116], 866 | [14, 74, 46, 21, 75, 47], 867 | [29, 54, 24, 19, 55, 25], 868 | [11, 45, 15, 46, 46, 16], 869 | [13, 145, 115, 6, 146, 116], 870 | [14, 74, 46, 23, 75, 47], 871 | [44, 54, 24, 7, 55, 25], 872 | [59, 46, 16, 1, 47, 17], 873 | [12, 151, 121, 7, 152, 122], 874 | [12, 75, 47, 26, 76, 48], 875 | [39, 54, 24, 14, 55, 25], 876 | [22, 45, 15, 41, 46, 16], 877 | [6, 151, 121, 14, 152, 122], 878 | [6, 75, 47, 34, 76, 48], 879 | [46, 54, 24, 10, 55, 25], 880 | [2, 45, 15, 64, 46, 16], 881 | [17, 152, 122, 4, 153, 123], 882 | [29, 74, 46, 14, 75, 47], 883 | [49, 54, 24, 10, 55, 25], 884 | [24, 45, 15, 46, 46, 16], 885 | [4, 152, 122, 18, 153, 123], 886 | [13, 74, 46, 32, 75, 47], 887 | [48, 54, 24, 14, 55, 25], 888 | [42, 45, 15, 32, 46, 16], 889 | [20, 147, 117, 4, 148, 118], 890 | [40, 75, 47, 7, 76, 48], 891 | [43, 54, 24, 22, 55, 25], 892 | [10, 45, 15, 67, 46, 16], 893 | [19, 148, 118, 6, 149, 119], 894 | [18, 75, 47, 31, 76, 48], 895 | [34, 54, 24, 34, 55, 25], 896 | [20, 45, 15, 61, 46, 16] 897 | ]; 898 | QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) { 899 | var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel); 900 | if (rsBlock == undefined) { 901 | throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel); 902 | } 903 | var length = rsBlock.length / 3; 904 | var list = []; 905 | for (var i = 0; i < length; i++) { 906 | var count = rsBlock[i * 3 + 0]; 907 | var totalCount = rsBlock[i * 3 + 1]; 908 | var dataCount = rsBlock[i * 3 + 2]; 909 | for (var j = 0; j < count; j++) { 910 | list.push(new QRRSBlock(totalCount, dataCount)); 911 | } 912 | } 913 | return list; 914 | }; 915 | QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) { 916 | switch (errorCorrectLevel) { 917 | case QRErrorCorrectLevel.L: 918 | return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; 919 | case QRErrorCorrectLevel.M: 920 | return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; 921 | case QRErrorCorrectLevel.Q: 922 | return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; 923 | case QRErrorCorrectLevel.H: 924 | return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; 925 | default: 926 | return undefined; 927 | } 928 | }; 929 | 930 | function QRBitBuffer() { 931 | this.buffer = []; 932 | this.length = 0; 933 | } 934 | QRBitBuffer.prototype = { 935 | get: function(index) { 936 | var bufIndex = Math.floor(index / 8); 937 | return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1) == 1; 938 | }, 939 | put: function(num, length) { 940 | for (var i = 0; i < length; i++) { 941 | this.putBit(((num >>> (length - i - 1)) & 1) == 1); 942 | } 943 | }, 944 | getLengthInBits: function() { 945 | return this.length; 946 | }, 947 | putBit: function(bit) { 948 | var bufIndex = Math.floor(this.length / 8); 949 | if (this.buffer.length <= bufIndex) { 950 | this.buffer.push(0); 951 | } 952 | if (bit) { 953 | this.buffer[bufIndex] |= (0x80 >>> (this.length % 8)); 954 | } 955 | this.length++; 956 | } 957 | }; 958 | var QRCodeLimitLength = [ 959 | [17, 14, 11, 7], 960 | [32, 26, 20, 14], 961 | [53, 42, 32, 24], 962 | [78, 62, 46, 34], 963 | [106, 84, 60, 44], 964 | [134, 106, 74, 58], 965 | [154, 122, 86, 64], 966 | [192, 152, 108, 84], 967 | [230, 180, 130, 98], 968 | [271, 213, 151, 119], 969 | [321, 251, 177, 137], 970 | [367, 287, 203, 155], 971 | [425, 331, 241, 177], 972 | [458, 362, 258, 194], 973 | [520, 412, 292, 220], 974 | [586, 450, 322, 250], 975 | [644, 504, 364, 280], 976 | [718, 560, 394, 310], 977 | [792, 624, 442, 338], 978 | [858, 666, 482, 382], 979 | [929, 711, 509, 403], 980 | [1003, 779, 565, 439], 981 | [1091, 857, 611, 461], 982 | [1171, 911, 661, 511], 983 | [1273, 997, 715, 535], 984 | [1367, 1059, 751, 593], 985 | [1465, 1125, 805, 625], 986 | [1528, 1190, 868, 658], 987 | [1628, 1264, 908, 698], 988 | [1732, 1370, 982, 742], 989 | [1840, 1452, 1030, 790], 990 | [1952, 1538, 1112, 842], 991 | [2068, 1628, 1168, 898], 992 | [2188, 1722, 1228, 958], 993 | [2303, 1809, 1283, 983], 994 | [2431, 1911, 1351, 1051], 995 | [2563, 1989, 1423, 1093], 996 | [2699, 2099, 1499, 1139], 997 | [2809, 2213, 1579, 1219], 998 | [2953, 2331, 1663, 1273] 999 | ]; 1000 | 1001 | /** 1002 | * Get the type by string length 1003 | * 1004 | * @private 1005 | * @param {String} sText 1006 | * @param {Number} nCorrectLevel 1007 | * @return {Number} type 1008 | */ 1009 | function _getTypeNumber(sText, _htOption) { 1010 | 1011 | var nCorrectLevel = _htOption.correctLevel; 1012 | 1013 | 1014 | var nType = 1; 1015 | var length = _getUTF8Length(sText); 1016 | 1017 | for (var i = 0, len = QRCodeLimitLength.length; i < len; i++) { 1018 | var nLimit = 0; 1019 | 1020 | switch (nCorrectLevel) { 1021 | case QRErrorCorrectLevel.L: 1022 | nLimit = QRCodeLimitLength[i][0]; 1023 | break; 1024 | case QRErrorCorrectLevel.M: 1025 | nLimit = QRCodeLimitLength[i][1]; 1026 | break; 1027 | case QRErrorCorrectLevel.Q: 1028 | nLimit = QRCodeLimitLength[i][2]; 1029 | break; 1030 | case QRErrorCorrectLevel.H: 1031 | nLimit = QRCodeLimitLength[i][3]; 1032 | break; 1033 | } 1034 | 1035 | if (length <= nLimit) { 1036 | break; 1037 | } else { 1038 | nType++; 1039 | } 1040 | } 1041 | if (nType > QRCodeLimitLength.length) { 1042 | throw new Error("Too long data. the CorrectLevel." + ['M', 'L', 'H', 'Q'][nCorrectLevel] + 1043 | " limit length is " + nLimit); 1044 | } 1045 | 1046 | if (_htOption.version != 0) { 1047 | if (nType <= _htOption.version) { 1048 | nType = _htOption.version; 1049 | _htOption.runVersion = nType; 1050 | } else { 1051 | console.warn("QR Code version " + _htOption.version + " too small, run version use " + nType); 1052 | _htOption.runVersion = nType; 1053 | } 1054 | } 1055 | 1056 | 1057 | return nType; 1058 | } 1059 | 1060 | function _getUTF8Length(sText) { 1061 | var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a'); 1062 | return replacedText.length + (replacedText.length != sText.length ? 3 : 0); 1063 | } 1064 | 1065 | /** 1066 | * Drawing QRCode by using canvas 1067 | * 1068 | * @constructor 1069 | * @param {HTMLElement} el 1070 | * @param {Object} htOption QRCode Options 1071 | */ 1072 | var Drawing = function(canvas, htOption) { 1073 | this._bIsPainted = false; 1074 | this._htOption = htOption; 1075 | this._canvas = canvas; 1076 | this._bSupportDataURI = null; 1077 | }; 1078 | 1079 | /** 1080 | * Draw the QRCode 1081 | * 1082 | * @param {QRCode} oQRCode 1083 | */ 1084 | Drawing.prototype.draw = function(oQRCode) { 1085 | var _htOption = this._htOption; 1086 | if (_htOption.onRenderingStart) { 1087 | _htOption.onRenderingStart(_htOption) 1088 | } 1089 | 1090 | var nCount = oQRCode.getModuleCount(); 1091 | var nWidth = Math.round(_htOption.width / nCount); 1092 | var nHeight = Math.round((_htOption.height - _htOption.titleHeight) / nCount); 1093 | if (nWidth <= 1) { 1094 | nWidth = 1; 1095 | } 1096 | if (nHeight <= 1) { 1097 | nHeight = 1; 1098 | } 1099 | 1100 | _htOption.quietZone = Math.round(_htOption.quietZone); 1101 | 1102 | _htOption.width = nWidth * nCount; 1103 | _htOption.height = nHeight * nCount + _htOption.titleHeight; 1104 | this._canvas.height = _htOption.height + _htOption.quietZone * 2; 1105 | this._canvas.width = _htOption.width + _htOption.quietZone * 2; 1106 | 1107 | var autoColorDark = _htOption.autoColorDark; 1108 | var autoColorLight = _htOption.autoColorLight; 1109 | var notAutoColorLight = "rgba(0,0,0,0)"; 1110 | 1111 | this._oContext = this._canvas.getContext("2d"); 1112 | this._oContext.patternQuality = 'best'; //'fast'|'good'|'best'|'nearest'|'bilinear' 1113 | this._oContext.quality = 'best'; //'fast'|'good'|'best'|'nearest'|'bilinear' 1114 | this._oContext.textDrawingMode = 'path'; // 'path'|'glyph' 1115 | this._oContext.antialias = 'gray'; // 'default'|'none'|'gray'|'subpixel' 1116 | 1117 | var _oContext = this._oContext; 1118 | _oContext.lineWidth = 0; 1119 | _oContext.fillStyle = _htOption.colorLight; 1120 | _oContext.fillRect(0, 0, this._canvas.width, this._canvas.height); 1121 | _oContext.clearRect(_htOption.quietZone, _htOption.quietZone, _htOption.width, _htOption.titleHeight); 1122 | 1123 | var t = this; 1124 | 1125 | function drawQuietZoneColor() { 1126 | if (_htOption.quietZone > 0 && _htOption.quietZoneColor) { 1127 | // top 1128 | _oContext.lineWidth = 0; 1129 | _oContext.fillStyle = _htOption.quietZoneColor; 1130 | 1131 | _oContext.fillRect(0, 0, t._canvas.width, _htOption.quietZone); 1132 | // left 1133 | _oContext.fillRect(0, _htOption.quietZone, _htOption.quietZone, t._canvas.height - _htOption.quietZone * 1134 | 2); 1135 | // right 1136 | _oContext.fillRect(t._canvas.width - _htOption.quietZone, _htOption.quietZone, _htOption.quietZone, t 1137 | ._canvas 1138 | .height - _htOption.quietZone * 2); 1139 | // bottom 1140 | _oContext.fillRect(0, t._canvas.height - _htOption.quietZone, t._canvas.width, _htOption.quietZone); 1141 | } 1142 | } 1143 | 1144 | if (_htOption.backgroundImage) { 1145 | 1146 | // backgroundImage 1147 | var bgImg = new CanvasImage(t._canvas); 1148 | 1149 | bgImg.addEventListener('load', () => { 1150 | _oContext.globalAlpha = 1; 1151 | _oContext.globalAlpha = _htOption.backgroundImageAlpha; 1152 | _oContext.drawImage(bgImg, 0, _htOption.titleHeight, _htOption.width + _htOption.quietZone * 2, 1153 | _htOption.height + 1154 | _htOption.quietZone * 2 - _htOption.titleHeight); 1155 | _oContext.globalAlpha = 1; 1156 | 1157 | drawQrcode.call(t, oQRCode); 1158 | }); 1159 | bgImg.addEventListener('error', () => { 1160 | console.error("Background image can not load!"); 1161 | }); 1162 | bgImg.src = _htOption.backgroundImage; 1163 | 1164 | // if(!(_htOption.backgroundImage.startsWith("http://") || _htOption.backgroundImage.startsWith("https://") || _htOption.backgroundImage.startsWith("ftp://"))){ 1165 | // imageSrc = Image.resolveAssetSource(require(_htOption.backgroundImage)).uri; 1166 | // } 1167 | 1168 | // DoSomething 1169 | } else { 1170 | drawQrcode.call(t, oQRCode); 1171 | } 1172 | 1173 | function drawQrcode(oQRCode) { 1174 | 1175 | for (var row = 0; row < nCount; row++) { 1176 | for (var col = 0; col < nCount; col++) { 1177 | var nLeft = col * nWidth + _htOption.quietZone; 1178 | var nTop = row * nHeight + _htOption.quietZone; 1179 | 1180 | var bIsDark = oQRCode.isDark(row, col); 1181 | 1182 | var eye = oQRCode.getEye(row, 1183 | col); // { isDark: true/false, type: PO_TL, PI_TL, PO_TR, PI_TR, PO_BL, PI_BL }; 1184 | 1185 | var nowDotScale = _htOption.dotScale; 1186 | 1187 | _oContext.lineWidth = 0; 1188 | 1189 | // Color handler 1190 | var dColor; 1191 | var lColor; 1192 | if (eye) { 1193 | dColor = _htOption[eye.type] || _htOption[eye.type.substring( 1194 | 0, 2)] || 1195 | _htOption.colorDark; 1196 | lColor = _htOption.colorLight; 1197 | } else { 1198 | if (_htOption.backgroundImage) { 1199 | 1200 | lColor = "rgba(0,0,0,0)"; 1201 | if (row == 6) { 1202 | // dColor = _htOption.timing_H || _htOption.timing || _htOption.colorDark; 1203 | if (_htOption.autoColor) { 1204 | dColor = _htOption.timing_H || _htOption.timing || _htOption.autoColorDark; 1205 | lColor = _htOption.autoColorLight; 1206 | } else { 1207 | dColor = _htOption.timing_H || _htOption.timing || _htOption.colorDark; 1208 | } 1209 | } else if (col == 6) { 1210 | // dColor = _htOption.timing_V || _htOption.timing || _htOption.colorDark; 1211 | if (_htOption.autoColor) { 1212 | dColor = _htOption.timing_V || _htOption.timing || _htOption.autoColorDark; 1213 | lColor = _htOption.autoColorLight; 1214 | } else { 1215 | dColor = _htOption.timing_V || _htOption.timing || 1216 | _htOption.colorDark; 1217 | } 1218 | } else { 1219 | if (_htOption.autoColor) { 1220 | dColor = _htOption.autoColorDark; 1221 | lColor = _htOption.autoColorLight; 1222 | } else { 1223 | dColor = _htOption.colorDark; 1224 | } 1225 | } 1226 | 1227 | } else { 1228 | if (row == 6) { 1229 | dColor = _htOption.timing_H || _htOption.timing || _htOption.colorDark; 1230 | } else if (col == 6) { 1231 | dColor = _htOption.timing_V || _htOption.timing || 1232 | _htOption.colorDark; 1233 | } else { 1234 | dColor = _htOption.colorDark; 1235 | } 1236 | lColor = _htOption.colorLight; 1237 | } 1238 | } 1239 | _oContext.strokeStyle = bIsDark ? dColor : 1240 | lColor; 1241 | _oContext.fillStyle = bIsDark ? dColor : 1242 | lColor; 1243 | 1244 | if (eye) { 1245 | // Is eye 1246 | bIsDark = eye.isDarkBlock; 1247 | var type = eye.type; 1248 | if (eye.type == 'AO') { 1249 | nowDotScale = _htOption.dotScaleAO; 1250 | } else if (eye.type == 'AI') { 1251 | nowDotScale = _htOption.dotScaleAI; 1252 | } else { 1253 | nowDotScale = 1; 1254 | } 1255 | 1256 | if (_htOption.backgroundImage && _htOption.autoColor) { 1257 | dColor = ((eye.type == 'AO') ? _htOption.AI : _htOption.AO) || 1258 | _htOption.autoColorDark; 1259 | lColor = _htOption.autoColorLight; 1260 | } else { 1261 | dColor = ((eye.type == 'AO') ? _htOption.AI : _htOption.AO) || 1262 | dColor; 1263 | } 1264 | 1265 | // _oContext.fillRect(nLeft, _htOption.titleHeight + nTop, nWidth, nHeight); 1266 | _oContext.fillRect(nLeft + nWidth * (1 - nowDotScale) / 2, _htOption.titleHeight + 1267 | nTop + nHeight * (1 - 1268 | nowDotScale) / 2, nWidth * nowDotScale, nHeight * 1269 | nowDotScale); 1270 | } else { 1271 | if (row == 6) { 1272 | // Timing Pattern 1273 | nowDotScale = _htOption.dotScaleTiming_H; 1274 | 1275 | _oContext.fillRect(nLeft + nWidth * (1 - nowDotScale) / 2, _htOption.titleHeight + nTop + 1276 | nHeight * (1 - 1277 | nowDotScale) / 2, nWidth * nowDotScale, nHeight * nowDotScale); 1278 | } else if (col == 6) { 1279 | // Timing Pattern 1280 | nowDotScale = _htOption.dotScaleTiming_V; 1281 | _oContext.fillRect(nLeft + nWidth * (1 - nowDotScale) / 2, _htOption.titleHeight + nTop + 1282 | nHeight * (1 - 1283 | nowDotScale) / 2, nWidth * nowDotScale, nHeight * nowDotScale); 1284 | } else { 1285 | 1286 | if (_htOption.backgroundImage) { 1287 | 1288 | _oContext.fillRect(nLeft + nWidth * (1 - nowDotScale) / 2, _htOption.titleHeight + 1289 | nTop + 1290 | nHeight * (1 - 1291 | nowDotScale) / 2, nWidth * nowDotScale, nHeight * nowDotScale); 1292 | 1293 | } else { 1294 | 1295 | _oContext.fillRect(nLeft + nWidth * (1 - nowDotScale) / 2, _htOption.titleHeight + 1296 | nTop + 1297 | nHeight * (1 - 1298 | nowDotScale) / 2, nWidth * nowDotScale, nHeight * nowDotScale); 1299 | 1300 | } 1301 | } 1302 | } 1303 | 1304 | if (_htOption.dotScale != 1 && !eye) { 1305 | _oContext.strokeStyle = _htOption.colorLight; 1306 | } 1307 | 1308 | } 1309 | } 1310 | 1311 | if (_htOption.title) { 1312 | _oContext.fillStyle = _htOption.titleBackgroundColor; 1313 | // _oContext.fillRect(0, 0, t._canvas.width, _htOption.titleHeight + this._htOption.quietZone); 1314 | _oContext.fillRect(_htOption.quietZone, _htOption.quietZone, _htOption.width, _htOption.titleHeight); 1315 | 1316 | _oContext.font = _htOption.titleFont; 1317 | _oContext.fillStyle = _htOption.titleColor; 1318 | _oContext.textAlign = 'center'; 1319 | _oContext.fillText(_htOption.title, t._canvas.width / 2, this._htOption.quietZone + this._htOption 1320 | .titleTop); 1321 | } 1322 | 1323 | if (_htOption.subTitle) { 1324 | _oContext.font = _htOption.subTitleFont; 1325 | _oContext.fillStyle = _htOption.subTitleColor; 1326 | _oContext.fillText(_htOption.subTitle, t._canvas.width / 2, this._htOption.quietZone + this._htOption 1327 | .subTitleTop); 1328 | } 1329 | 1330 | if (_htOption.logo) { 1331 | var logoImg = new CanvasImage(t._canvas); 1332 | 1333 | function generateLogoImg(img) { 1334 | var imgContainerW = Math.round(_htOption.width / 3.5); 1335 | var imgContainerH = Math.round(_htOption.height / 3.5); 1336 | if (imgContainerW !== imgContainerH) { 1337 | imgContainerW = imgContainerH; 1338 | } 1339 | 1340 | if (_htOption.logoMaxWidth) { 1341 | imgContainerW = Math.round(_htOption.logoMaxWidth); 1342 | } else if (_htOption.logoWidth) { 1343 | imgContainerW = Math.round(_htOption.logoWidth); 1344 | } 1345 | 1346 | if (_htOption.logoMaxHeight) { 1347 | imgContainerH = Math.round(_htOption.logoMaxHeight); 1348 | } else if (_htOption.logoHeight) { 1349 | imgContainerH = Math.round(_htOption.logoHeight); 1350 | } 1351 | 1352 | var nw; 1353 | var nh; 1354 | if (typeof img.naturalWidth == "undefined") { 1355 | // IE 6/7/8 1356 | nw = img.width; 1357 | nh = img.height; 1358 | } else { 1359 | // HTML5 browsers 1360 | nw = img.naturalWidth; 1361 | nh = img.naturalHeight; 1362 | } 1363 | 1364 | if (_htOption.logoMaxWidth || _htOption.logoMaxHeight) { 1365 | if (_htOption.logoMaxWidth && nw <= imgContainerW) { 1366 | imgContainerW = nw; 1367 | } 1368 | 1369 | if (_htOption.logoMaxHeight && nh <= imgContainerH) { 1370 | imgContainerH = nh; 1371 | } 1372 | if (nw <= imgContainerW && nh <= imgContainerH) { 1373 | imgContainerW = nw; 1374 | imgContainerH = nh; 1375 | } 1376 | } 1377 | 1378 | var imgContainerX = (_htOption.width + _htOption.quietZone * 2 - imgContainerW) / 2; 1379 | var imgContainerY = (_htOption.height + _htOption.titleHeight + _htOption.quietZone * 1380 | 2 - imgContainerH) / 2; 1381 | 1382 | var imgScale = Math.min(imgContainerW / nw, imgContainerH / nh); 1383 | var imgW = nw * imgScale; 1384 | var imgH = nh * imgScale; 1385 | 1386 | if (_htOption.logoMaxWidth || _htOption.logoMaxHeight) { 1387 | imgContainerW = imgW; 1388 | imgContainerH = imgH; 1389 | imgContainerX = (_htOption.width + _htOption.quietZone * 2 - imgContainerW) / 2; 1390 | imgContainerY = (_htOption.height + _htOption.titleHeight + _htOption 1391 | .quietZone * 1392 | 2 - imgContainerH) / 2; 1393 | 1394 | } 1395 | 1396 | // Did Not Use Transparent Logo Image 1397 | if (!_htOption.logoBackgroundTransparent) { 1398 | //if (!_htOption.logoBackgroundColor) { 1399 | //_htOption.logoBackgroundColor = '#ffffff'; 1400 | //} 1401 | _oContext.fillStyle = _htOption.logoBackgroundColor; 1402 | 1403 | _oContext.fillRect(imgContainerX, imgContainerY, imgContainerW, imgContainerH); 1404 | } 1405 | _oContext.drawImage(img, imgContainerX + (imgContainerW - imgW) / 2, imgContainerY + 1406 | (imgContainerH - imgH) / 2, imgW, imgH); 1407 | 1408 | t._bIsPainted = true; 1409 | 1410 | drawQuietZoneColor(); 1411 | if (_htOption.onRenderingEnd) { 1412 | _htOption.onRenderingEnd(_htOption, function() { 1413 | return t._canvas.toDataURL(); 1414 | }); 1415 | } 1416 | } 1417 | 1418 | logoImg.addEventListener('load', () => { 1419 | generateLogoImg(logoImg); 1420 | }); 1421 | logoImg.addEventListener('error', () => { 1422 | console.error("Logo image can not load!"); 1423 | }); 1424 | 1425 | logoImg.src = _htOption.logo; 1426 | } else { 1427 | drawQuietZoneColor(); 1428 | t._bIsPainted = true; 1429 | 1430 | if (_htOption.onRenderingEnd) { 1431 | _htOption.onRenderingEnd(_htOption, function() { 1432 | return t._canvas.toDataURL(); 1433 | }); 1434 | } 1435 | } 1436 | 1437 | } 1438 | 1439 | }; 1440 | 1441 | 1442 | 1443 | /** 1444 | * Return whether the QRCode is painted or not 1445 | * 1446 | * @return {Boolean} 1447 | */ 1448 | Drawing.prototype.isPainted = function() { 1449 | return this._bIsPainted; 1450 | }; 1451 | 1452 | 1453 | /** 1454 | * @private 1455 | * @param {Number} nNumber 1456 | */ 1457 | Drawing.prototype.round = function(nNumber) { 1458 | if (!nNumber) { 1459 | return nNumber; 1460 | } 1461 | return Math.floor(nNumber * 1000) / 1000; 1462 | }; 1463 | 1464 | 1465 | function QRCode(canvas, vOption) { 1466 | this.canvas = canvas; 1467 | this._htOption = { 1468 | width: 256, 1469 | height: 256, 1470 | typeNumber: 4, 1471 | colorDark: "#000000", 1472 | colorLight: "#ffffff", 1473 | correctLevel: QRErrorCorrectLevel.H, 1474 | 1475 | dotScale: 1, // For body block, must be greater than 0, less than or equal to 1. default is 1 1476 | 1477 | dotScaleTiming: 1, // Dafault for timing block , must be greater than 0, less than or equal to 1. default is 1 1478 | dotScaleTiming_H: undefined, // For horizontal timing block, must be greater than 0, less than or equal to 1. default is 1 1479 | dotScaleTiming_V: undefined, // For vertical timing block, must be greater than 0, less than or equal to 1. default is 1 1480 | 1481 | dotScaleA: 1, // Dafault for alignment block, must be greater than 0, less than or equal to 1. default is 1 1482 | dotScaleAO: undefined, // For alignment outer block, must be greater than 0, less than or equal to 1. default is 1 1483 | dotScaleAI: undefined, // For alignment inner block, must be greater than 0, less than or equal to 1. default is 1 1484 | 1485 | quietZone: 0, 1486 | quietZoneColor: "rgba(0,0,0,0)", 1487 | 1488 | title: "", 1489 | titleFont: "normal normal bold 16px Arial", 1490 | titleColor: "#000000", 1491 | titleBackgroundColor: "#ffffff", 1492 | titleHeight: 0, // Title Height, Include subTitle 1493 | titleTop: 30, // draws y coordinates. default is 30 1494 | 1495 | subTitle: "", 1496 | subTitleFont: "normal normal normal 14px Arial", 1497 | subTitleColor: "#4F4F4F", 1498 | subTitleTop: 60, // draws y coordinates. default is 0 1499 | 1500 | logo: undefined, 1501 | logoWidth: undefined, 1502 | logoHeight: undefined, 1503 | logoMaxWidth: undefined, 1504 | logoMaxHeight: undefined, 1505 | logoBackgroundColor: '#ffffff', 1506 | logoBackgroundTransparent: false, 1507 | 1508 | // === Posotion Pattern(Eye) Color 1509 | PO: undefined, // Global Posotion Outer color. if not set, the defaut is `colorDark` 1510 | PI: undefined, // Global Posotion Inner color. if not set, the defaut is `colorDark` 1511 | PO_TL: undefined, // Posotion Outer - Top Left 1512 | PI_TL: undefined, // Posotion Inner - Top Left 1513 | PO_TR: undefined, // Posotion Outer - Top Right 1514 | PI_TR: undefined, // Posotion Inner - Top Right 1515 | PO_BL: undefined, // Posotion Outer - Bottom Left 1516 | PI_BL: undefined, // Posotion Inner - Bottom Left 1517 | 1518 | // === Alignment Color 1519 | AO: undefined, // Alignment Outer. if not set, the defaut is `colorDark` 1520 | AI: undefined, // Alignment Inner. if not set, the defaut is `colorDark` 1521 | 1522 | // === Timing Pattern Color 1523 | timing: undefined, // Global Timing color. if not set, the defaut is `colorDark` 1524 | timing_H: undefined, // Horizontal timing color 1525 | timing_V: undefined, // Vertical timing color 1526 | 1527 | // ==== Backgroud Image 1528 | backgroundImage: undefined, // Background Image 1529 | backgroundImageAlpha: 1, // Background image transparency, value between 0 and 1. default is 1. 1530 | autoColor: false, // Automatic color adjustment(for data block) 1531 | autoColorDark: "rgba(0, 0, 0, .6)", // Automatic color: dark CSS color 1532 | autoColorLight: "rgba(255, 255, 255, .7)", // Automatic color: light CSS color 1533 | 1534 | // ==== Event Handler 1535 | onRenderingStart: undefined, 1536 | onRenderingEnd: undefined, 1537 | 1538 | // ==== Versions 1539 | version: 0, // The symbol versions of QR Code range from Version 1 to Version 40. default 0 means automatically choose the closest version based on the text length. 1540 | 1541 | // ==== binary(hex) data mode 1542 | binary: false, // Whether it is binary mode, default is text mode. 1543 | 1544 | // UTF-8 without BOM 1545 | utf8WithoutBOM: true 1546 | 1547 | }; 1548 | if (typeof vOption === 'string') { 1549 | vOption = { 1550 | text: vOption 1551 | }; 1552 | } 1553 | 1554 | // Overwrites options 1555 | if (vOption) { 1556 | for (var i in vOption) { 1557 | this._htOption[i] = vOption[i]; 1558 | } 1559 | } 1560 | 1561 | if (!this._htOption.title && !this._htOption.subTitle) { 1562 | this._htOption.titleHeight = 0; 1563 | } 1564 | 1565 | if (this._htOption.version < 0 || this._htOption.version > 40) { 1566 | console.warn("QR Code version '" + this._htOption.version + "' is invalidate, reset to 0") 1567 | this._htOption.version = 0; 1568 | } 1569 | 1570 | 1571 | if (this._htOption.dotScale < 0 || this._htOption.dotScale > 1) { 1572 | console.warn(this._htOption.dotScale + 1573 | " , is invalidate, dotScale must greater than 0, less than or equal to 1, now reset to 1. ") 1574 | this._htOption.dotScale = 1; 1575 | } 1576 | 1577 | if (this._htOption.dotScaleTiming < 0 || this._htOption.dotScaleTiming > 1) { 1578 | console.warn(this._htOption.dotScaleTiming + 1579 | " , is invalidate, dotScaleTiming must greater than 0, less than or equal to 1, now reset to 1. " 1580 | ) 1581 | this._htOption.dotScaleTiming = 1; 1582 | } 1583 | if (this._htOption.dotScaleTiming_H) { 1584 | if (this._htOption.dotScaleTiming_H < 0 || this._htOption.dotScaleTiming_H > 1) { 1585 | console.warn(this._htOption.dotScaleTiming_H + 1586 | " , is invalidate, dotScaleTiming_H must greater than 0, less than or equal to 1, now reset to 1. " 1587 | ) 1588 | this._htOption.dotScaleTiming_H = 1; 1589 | } 1590 | } else { 1591 | this._htOption.dotScaleTiming_H = this._htOption.dotScaleTiming; 1592 | } 1593 | 1594 | if (this._htOption.dotScaleTiming_V) { 1595 | if (this._htOption.dotScaleTiming_V < 0 || this._htOption.dotScaleTiming_V > 1) { 1596 | console.warn(this._htOption.dotScaleTiming_V + 1597 | " , is invalidate, dotScaleTiming_V must greater than 0, less than or equal to 1, now reset to 1. " 1598 | ) 1599 | this._htOption.dotScaleTiming_V = 1; 1600 | } 1601 | } else { 1602 | this._htOption.dotScaleTiming_V = this._htOption.dotScaleTiming; 1603 | } 1604 | 1605 | 1606 | 1607 | if (this._htOption.dotScaleA < 0 || this._htOption.dotScaleA > 1) { 1608 | console.warn(this._htOption.dotScaleA + 1609 | " , is invalidate, dotScaleA must greater than 0, less than or equal to 1, now reset to 1. " 1610 | ) 1611 | this._htOption.dotScaleA = 1; 1612 | } 1613 | if (this._htOption.dotScaleAO) { 1614 | if (this._htOption.dotScaleAO < 0 || this._htOption.dotScaleAO > 1) { 1615 | console.warn(this._htOption.dotScaleAO + 1616 | " , is invalidate, dotScaleAO must greater than 0, less than or equal to 1, now reset to 1. " 1617 | ) 1618 | this._htOption.dotScaleAO = 1; 1619 | } 1620 | } else { 1621 | this._htOption.dotScaleAO = this._htOption.dotScaleA; 1622 | } 1623 | if (this._htOption.dotScaleAI) { 1624 | if (this._htOption.dotScaleAI < 0 || this._htOption.dotScaleAI > 1) { 1625 | console.warn(this._htOption.dotScaleAI + 1626 | " , is invalidate, dotScaleAI must greater than 0, less than or equal to 1, now reset to 1. " 1627 | ) 1628 | this._htOption.dotScaleAI = 1; 1629 | } 1630 | } else { 1631 | this._htOption.dotScaleAI = this._htOption.dotScaleA; 1632 | } 1633 | 1634 | 1635 | 1636 | if (this._htOption.backgroundImageAlpha < 0 || this._htOption.backgroundImageAlpha > 1) { 1637 | console.warn(this._htOption.backgroundImageAlpha + 1638 | " , is invalidate, backgroundImageAlpha must between 0 and 1, now reset to 1. ") 1639 | this._htOption.backgroundImageAlpha = 1; 1640 | } 1641 | 1642 | this._htOption.height = this._htOption.height + this._htOption.titleHeight; 1643 | 1644 | this._oQRCode = null; 1645 | this._oQRCode = new QRCodeModel(_getTypeNumber(this._htOption.text, this._htOption), this._htOption.correctLevel); 1646 | this._oQRCode.addData(this._htOption.text, this._htOption.binary, this._htOption.utf8WithoutBOM); 1647 | this._oQRCode.make(); 1648 | this._show(); 1649 | } 1650 | 1651 | 1652 | QRCode.prototype._show = function() { 1653 | var _oDrawing = new Drawing(this.canvas, Object.assign({}, this._htOption)); 1654 | this._oDrawing = _oDrawing; 1655 | _oDrawing.draw(this._oQRCode); 1656 | }; 1657 | 1658 | /** 1659 | * Make the QRCode 1660 | * 1661 | * @param {String} sText link data 1662 | */ 1663 | QRCode.prototype.makeCode = function(sText) { 1664 | 1665 | this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption), this._htOption.correctLevel); 1666 | this._oQRCode.addData(sText, this._htOption.binary, this._htOption.utf8WithoutBOM); 1667 | this._oQRCode.make(); 1668 | if (this._htOption.tooltip) { 1669 | this._el.title = sText; 1670 | } 1671 | this._oDrawing.draw(this._oQRCode); 1672 | // this.makeImage(); 1673 | }; 1674 | 1675 | 1676 | 1677 | /** 1678 | * @name QRCode.CorrectLevel 1679 | */ 1680 | QRCode.CorrectLevel = QRErrorCorrectLevel; 1681 | 1682 | export { 1683 | QRCode, 1684 | Canvas 1685 | }; -------------------------------------------------------------------------------- /QRCode.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * EasyQRCode-React-Native 3 | * 4 | * React Native QRCode generation component. Can generate standard QRCode image or base64 image data url text. Cross-browser QRCode generator for pure javascript. Support Dot style, Logo, Background image, Colorful, Title etc. settings. support binary mode. 5 | * 6 | * Version 4.0.11 7 | * 8 | * @author [ inthinkcolor@gmail.com ] 9 | * 10 | * @see https://github.com/ushelp/EasyQRCode-React-Native 11 | * @see http://www.easyproject.cn/easyqrcodejs/tryit.html 12 | * 13 | * Copyright 2017 Ray, EasyProject 14 | * Released under the MIT license 15 | * 16 | * [React Native] 17 | * 18 | */ 19 | import Canvas,{Image as CanvasImage}from"react-native-canvas";function QR8bitByte(data,binary,utf8WithoutBOM){if(this.mode=QRMode.MODE_8BIT_BYTE,this.data=data,this.parsedData=[],binary){for(var i=0,l=this.data.length;i>6,128|63&charcode):charcode<55296||57344<=charcode?utf8.push(224|charcode>>12,128|charcode>>6&63,128|63&charcode):(i++,charcode=65536+((1023&charcode)<<10|1023&str.charCodeAt(i)),utf8.push(240|charcode>>18,128|charcode>>12&63,128|charcode>>6&63,128|63&charcode))}return utf8}(data);this.parsedData=Array.prototype.concat.apply([],this.parsedData),utf8WithoutBOM||this.parsedData.length==this.data.length||(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function QRCodeModel(typeNumber,errorCorrectLevel){this.typeNumber=typeNumber,this.errorCorrectLevel=errorCorrectLevel,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}QR8bitByte.prototype={getLength:function(buffer){return this.parsedData.length},write:function(buffer){for(var i=0,l=this.parsedData.length;i>i&1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3][0]=mod}for(i=0;i<18;i++){mod=!test&&1==(bits>>i&1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)][0]=mod}},setupTypeInfo:function(test,maskPattern){for(var maskPattern=this.errorCorrectLevel<<3|maskPattern,bits=QRUtil.getBCHTypeInfo(maskPattern),i=0;i<15;i++){var mod=!test&&1==(bits>>i&1);i<6?this.modules[i][8][0]=mod:i<8?this.modules[i+1][8][0]=mod:this.modules[this.moduleCount-15+i][8][0]=mod}for(i=0;i<15;i++){mod=!test&&1==(bits>>i&1);i<8?this.modules[8][this.moduleCount-i-1][0]=mod:i<9?this.modules[8][15-i-1+1][0]=mod:this.modules[8][15-i-1][0]=mod}this.modules[this.moduleCount-8][8][0]=!test},mapData:function(data,maskPattern){for(var inc=-1,row=this.moduleCount-1,bitIndex=7,byteIndex=0,col=this.moduleCount-1;0>>bitIndex&1)),mask=QRUtil.getMask(maskPattern,row,col-c),this.modules[row][col-c][0]=dark=mask?!dark:dark,-1==--bitIndex)&&(byteIndex++,bitIndex=7);if((row+=inc)<0||this.moduleCount<=row){row-=inc,inc=-inc;break}}}},QRCodeModel.PAD0=236,QRCodeModel.PAD1=17,QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){for(var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel),buffer=new QRBitBuffer,i=0;i8*totalDataCount)throw new Error("code length overflow. ("+buffer.getLengthInBits()+">"+8*totalDataCount+")");for(buffer.getLengthInBits()+4<=8*totalDataCount&&buffer.put(0,4);buffer.getLengthInBits()%8!=0;)buffer.putBit(!1);for(;;){if(buffer.getLengthInBits()>=8*totalDataCount)break;if(buffer.put(QRCodeModel.PAD0,8),buffer.getLengthInBits()>=8*totalDataCount)break;buffer.put(QRCodeModel.PAD1,8)}return QRCodeModel.createBytes(buffer,rsBlocks)},QRCodeModel.createBytes=function(buffer,rsBlocks){for(var offset=0,maxDcCount=0,maxEcCount=0,dcdata=new Array(rsBlocks.length),ecdata=new Array(rsBlocks.length),r=0;r>>=1;return digit},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1]},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return i*j%2+i*j%3==0;case QRMaskPattern.PATTERN110:return(i*j%2+i*j%3)%2==0;case QRMaskPattern.PATTERN111:return(i*j%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern)}},getErrorCorrectPolynomial:function(errorCorrectLength){for(var a=new QRPolynomial([1],0),i=0;i>>7-index%8&1)},put:function(num,length){for(var i=0;i>>length-i-1&1))},getLengthInBits:function(){return this.length},putBit:function(bit){var bufIndex=Math.floor(this.length/8);this.buffer.length<=bufIndex&&this.buffer.push(0),bit&&(this.buffer[bufIndex]|=128>>>this.length%8),this.length++}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]];function _getTypeNumber(sText,_htOption){for(var nCorrectLevel=_htOption.correctLevel,nType=1,length=_getUTF8Length(sText),i=0,len=QRCodeLimitLength.length;iQRCodeLimitLength.length)throw new Error("Too long data. the CorrectLevel."+["M","L","H","Q"][nCorrectLevel]+" limit length is "+nLimit);return 0!=_htOption.version&&(nType<=_htOption.version?nType=_htOption.version:console.warn("QR Code version "+_htOption.version+" too small, run version use "+nType),_htOption.runVersion=nType),nType}function _getUTF8Length(sText){var replacedText=encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return replacedText.length+(replacedText.length!=sText.length?3:0)}var Drawing=function(canvas,htOption){this._bIsPainted=!1,this._htOption=htOption,this._canvas=canvas,this._bSupportDataURI=null};function QRCode(canvas,vOption){if(this.canvas=canvas,this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:QRErrorCorrectLevel.H,dotScale:1,dotScaleTiming:1,dotScaleTiming_H:void 0,dotScaleTiming_V:void 0,dotScaleA:1,dotScaleAO:void 0,dotScaleAI:void 0,quietZone:0,quietZoneColor:"rgba(0,0,0,0)",title:"",titleFont:"normal normal bold 16px Arial",titleColor:"#000000",titleBackgroundColor:"#ffffff",titleHeight:0,titleTop:30,subTitle:"",subTitleFont:"normal normal normal 14px Arial",subTitleColor:"#4F4F4F",subTitleTop:60,logo:void 0,logoWidth:void 0,logoHeight:void 0,logoMaxWidth:void 0,logoMaxHeight:void 0,logoBackgroundColor:"#ffffff",logoBackgroundTransparent:!1,PO:void 0,PI:void 0,PO_TL:void 0,PI_TL:void 0,PO_TR:void 0,PI_TR:void 0,PO_BL:void 0,PI_BL:void 0,AO:void 0,AI:void 0,timing:void 0,timing_H:void 0,timing_V:void 0,backgroundImage:void 0,backgroundImageAlpha:1,autoColor:!1,autoColorDark:"rgba(0, 0, 0, .6)",autoColorLight:"rgba(255, 255, 255, .7)",onRenderingStart:void 0,onRenderingEnd:void 0,version:0,binary:!1,utf8WithoutBOM:!0},vOption="string"==typeof vOption?{text:vOption}:vOption)for(var i in vOption)this._htOption[i]=vOption[i];this._htOption.title||this._htOption.subTitle||(this._htOption.titleHeight=0),(this._htOption.version<0||40{var img,imgContainerW,imgContainerH,imgContainerX,imgContainerY,imgScale,nw,nh;img=logoImg,imgContainerW=Math.round(_htOption.width/3.5),imgContainerH=Math.round(_htOption.height/3.5),imgContainerW!==imgContainerH&&(imgContainerW=imgContainerH),_htOption.logoMaxWidth?imgContainerW=Math.round(_htOption.logoMaxWidth):_htOption.logoWidth&&(imgContainerW=Math.round(_htOption.logoWidth)),_htOption.logoMaxHeight?imgContainerH=Math.round(_htOption.logoMaxHeight):_htOption.logoHeight&&(imgContainerH=Math.round(_htOption.logoHeight)),nh=void 0===img.naturalWidth?(nw=img.width,img.height):(nw=img.naturalWidth,img.naturalHeight),(_htOption.logoMaxWidth||_htOption.logoMaxHeight)&&(_htOption.logoMaxWidth&&nw<=imgContainerW&&(imgContainerW=nw),_htOption.logoMaxHeight&&nh<=imgContainerH&&(imgContainerH=nh),nw<=imgContainerW)&&nh<=imgContainerH&&(imgContainerW=nw,imgContainerH=nh),imgContainerX=(_htOption.width+2*_htOption.quietZone-imgContainerW)/2,imgContainerY=(_htOption.height+_htOption.titleHeight+2*_htOption.quietZone-imgContainerH)/2,imgScale=Math.min(imgContainerW/nw,imgContainerH/nh),nw*=imgScale,nh*=imgScale,(_htOption.logoMaxWidth||_htOption.logoMaxHeight)&&(imgContainerX=(_htOption.width+2*_htOption.quietZone-(imgContainerW=nw))/2,imgContainerY=(_htOption.height+_htOption.titleHeight+2*_htOption.quietZone-(imgContainerH=nh))/2),_htOption.logoBackgroundTransparent||(_oContext.fillStyle=_htOption.logoBackgroundColor,_oContext.fillRect(imgContainerX,imgContainerY,imgContainerW,imgContainerH)),_oContext.drawImage(img,imgContainerX+(imgContainerW-nw)/2,imgContainerY+(imgContainerH-nh)/2,nw,nh),t._bIsPainted=!0,drawQuietZoneColor(),_htOption.onRenderingEnd&&_htOption.onRenderingEnd(_htOption,function(){return t._canvas.toDataURL()})}),logoImg.addEventListener("error",()=>{console.error("Logo image can not load!")}),logoImg.src=_htOption.logo):(drawQuietZoneColor(),t._bIsPainted=!0,_htOption.onRenderingEnd&&_htOption.onRenderingEnd(_htOption,function(){return t._canvas.toDataURL()}))}_htOption.backgroundImage?((bgImg=new CanvasImage(t._canvas)).addEventListener("load",()=>{_oContext.globalAlpha=1,_oContext.globalAlpha=_htOption.backgroundImageAlpha,_oContext.drawImage(bgImg,0,_htOption.titleHeight,_htOption.width+2*_htOption.quietZone,_htOption.height+2*_htOption.quietZone-_htOption.titleHeight),_oContext.globalAlpha=1,drawQrcode.call(t,oQRCode)}),bgImg.addEventListener("error",()=>{console.error("Background image can not load!")}),bgImg.src=_htOption.backgroundImage):drawQrcode.call(t,oQRCode)},Drawing.prototype.isPainted=function(){return this._bIsPainted},Drawing.prototype.round=function(nNumber){return nNumber&&Math.floor(1e3*nNumber)/1e3},QRCode.prototype._show=function(){var _oDrawing=new Drawing(this.canvas,Object.assign({},this._htOption));(this._oDrawing=_oDrawing).draw(this._oQRCode)},QRCode.prototype.makeCode=function(sText){this._oQRCode=new QRCodeModel(_getTypeNumber(sText,this._htOption),this._htOption.correctLevel),this._oQRCode.addData(sText,this._htOption.binary,this._htOption.utf8WithoutBOM),this._oQRCode.make(),this._htOption.tooltip&&(this._el.title=sText),this._oDrawing.draw(this._oQRCode)},QRCode.CorrectLevel=QRErrorCorrectLevel;export{QRCode,Canvas}; -------------------------------------------------------------------------------- /demo/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | View 4 | } from 'react-native'; 5 | 6 | // 1. Import 7 | import {QRCode, Canvas} from 'easyqrcode-react-native'; 8 | 9 | class App extends Component{ 10 | 11 | // 3. Generate QRCode 12 | generateQRCode = (canvas) => { 13 | if (canvas !== null){ 14 | // QRCode options 15 | var options = { 16 | text: "www.easyproject.cn/donation", 17 | }; 18 | // Create QRCode Object 19 | var qrCode = new QRCode(canvas, options); 20 | } 21 | } 22 | 23 | render() { 24 | return ( 25 | 26 | {/* 2. QRCode Canvas */} 27 | 28 | 29 | ); 30 | } 31 | }; 32 | 33 | export default App; -------------------------------------------------------------------------------- /doc/images/QR_Code_Structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ushelp/EasyQRCode-React-Native/8a341e991bd22b0b9294f17bb6ea099ac98e6da4/doc/images/QR_Code_Structure.png -------------------------------------------------------------------------------- /doc/images/QR_Code_Structure.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | image/svg+xml 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 1. Version information 20 | 2. Format information 21 | 3. Data and error correction keys 22 | 4. Required patterns 23 | 4.1. Position 24 | 4.2. Alignment 25 | 4.3. Timing 26 | 5. Quiet zone 27 | -------------------------------------------------------------------------------- /doc/images/demo-premium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ushelp/EasyQRCode-React-Native/8a341e991bd22b0b9294f17bb6ea099ac98e6da4/doc/images/demo-premium.png -------------------------------------------------------------------------------- /doc/images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ushelp/EasyQRCode-React-Native/8a341e991bd22b0b9294f17bb6ea099ac98e6da4/doc/images/demo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easyqrcode-react-native", 3 | "version": "4.0.11", 4 | "description": "React Native QRCode generation component. Can get standard base64 image data url text or save image to file. Cross-browser QRCode generator for pure javascript. Support Dot style, Logo, Background image, Colorful, Title etc. settings. support binary mode.", 5 | "main": "QRCode.min.js", 6 | "scripts": {}, 7 | "homepage": "https://github.com/ushelp/EasyQRCode-React-Native#readme", 8 | "author": "Ray ", 9 | "directories": { 10 | "dist": "dist", 11 | "src": "src", 12 | "demo": "demo" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/ushelp/EasyQRCode-React-Native.git" 17 | }, 18 | "keywords": [ 19 | "react native qrcode", 20 | "react native easyqrcode", 21 | "qrcode", 22 | "qrcodejs", 23 | "react native", 24 | "reactnative", 25 | "rn qrcode", 26 | "reactnative qrcode", 27 | "qrcode react native", 28 | "javascript qrcode", 29 | "qrcode logo", 30 | "qrcode generator", 31 | "easyqrcode", 32 | "easyqrcodejs" 33 | ], 34 | "bugs": { 35 | "url": "https://github.com/ushelp/EasyQRCode-React-Native/issues " 36 | }, 37 | "license": "MIT", 38 | "dependencies": { 39 | "react-native-canvas": "^0.1.39", 40 | "react-native-svg": "^13.14.0" 41 | }, 42 | "devDependencies": {}, 43 | "peerDependencies": { 44 | "react-native-webview": ">=5.10.0 || >=6.1.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # EasyQRCode React Native 2 | 3 | A QRCode component for React Native. Support Dot style, Logo, Background image, Colorful, Title etc. settings. Support Angular, Vue.js, React, Next.js framework. Support binary(hex) data mode. 4 | 5 | 6 | ## Table of contents 7 | 8 | - [Choose what you need](#choose-what-you-need) 9 | - [Feature](#feature) 10 | - [Try It!](#try-it) 11 | - [Demo preview](#demo-preview) 12 | - [QR Code Structure](#qr-code-structure) 13 | - [Installation](#installation) 14 | - [Basic Usages](#basic-usages) 15 | - [QRCode API](#qrcode-api) 16 | - [Object](#object) 17 | - [Options](#options) 18 | - [Method](#method) 19 | - [How to load Images?](#how-to-load-images) 20 | - [Get Base64 data of QRCode](#get-base64-data-of-qrcode) 21 | - [License](#license) 22 | - [EasyQRCode-React-Native-Premium](#react-native-easyqrcode-premium) 23 | - [End](#end) 24 | 25 | 26 | ## Choose what you need 27 | 28 | | Project | Support | 29 | | --- | --- | 30 | | [EasyQRCodeJS](https://github.com/ushelp/EasyQRCodeJS) | **Running with DOM on CLIENT-SIDE .** Browser(IE6+, Chrome, Firefox, Safari, Opera, Mobile Safari, Android, Windows Mobile, ETC.), Electron, NW.js, ETC. | 31 | | [EasyQRCodeJS-NodeJS](https://github.com/ushelp/EasyQRCodeJS-NodeJS) | **Running without DOM on SERVER-SIDE**. Save image to file(PNG/JPEG/SVG) or get data url text. NodeJS, Electron, NW.js, ETC.| 32 | | [EasyQRCode-React-Native](https://github.com/ushelp/EasyQRCode-React-Native) | **A QRCode generator for React Native**. Generate QRCode image or get base64 data url text. | 33 | 34 | 35 | ## Feature 36 | 37 | - **English** 38 | 39 | - Required Patterns that support dot style 40 | 41 | - Support unicode character set: `😊❤️👍👨‍💻 Hello, こんにちは, こんにちは, Γεια, Привет, नमस्ते, สวัสดี, Привіт, سلام, Здравей, ສະບາຍດີ, Përshëndetje, Բարեւ, 你好` 42 | 43 | - Support for Quiet Zone settings 44 | 45 | - Support custom Position Pattern inner fill and outer border color 46 | 47 | - Support custom Alignment Pattern inner fill and outer border color 48 | 49 | - Support custom Timing Patterns vertical, horizontal color 50 | 51 | - Support Logo images (including transparent PNG images) 52 | 53 | - Support Background Image 54 | 55 | - Support for title, subtitle settings 56 | 57 | - Support binary(hex) data mode 58 | 59 | - Support TypeScript 60 | 61 | - **中文** 62 | 63 | - 支持点形风格的 Required Patterns 64 | 65 | - 支持 Unicode 字符集: `😊❤️👍👨‍💻 Hello, こんにちは, こんにちは, Γεια, Привет, नमस्ते, สวัสดี, Привіт, سلام, Здравей, ສະບາຍດີ, Përshëndetje, Բարեւ, 你好` 66 | 67 | - 支持 Quiet Zone 设置 68 | 69 | - 支持自定义 Position Pattern 内填充和外边框颜色 70 | 71 | - 支持自定义 Alignment Pattern 内填充和外边框颜色 72 | 73 | - 支持自定义 Timing Patterns 垂直,水平颜色 74 | 75 | - 支持 Logo 图片(包括背景透明的 PNG 图片) 76 | 77 | - 支持 Background Image 背景图片 78 | 79 | - 支持标题,副标题设置 80 | 81 | - 二进制数据模式支持 82 | 83 | - TypeScript 支持 84 | 85 | ## Try It! 86 | 87 | [Try It!](http://www.easyproject.cn/easyqrcodejs/tryit.html "EasyQRCodeJS Try It!") 88 | 89 | ## Demo preview 90 | 91 | ![Demo preview](doc/images/demo.png) 92 | 93 | ## QR Code Structure 94 | 95 | ![QR Code Structure](doc/images/QR_Code_Structure.png) 96 | 97 | 98 | ## Installation 99 | 100 | ```BASH 101 | npm install react-native-webview 102 | react-native link react-native-webview 103 | 104 | npm install easyqrcode-react-native 105 | ``` 106 | 107 | ## Basic Usages 108 | ```JS 109 | import React, { Component } from 'react'; 110 | import { 111 | View 112 | } from 'react-native'; 113 | 114 | // 1. Import 115 | import {QRCode, Canvas} from 'easyqrcode-react-native'; 116 | 117 | class App extends Component{ 118 | 119 | // 3. Generate QRCode 120 | generateQRCode = (canvas) => { 121 | if (canvas !== null){ 122 | // QRCode options 123 | var options = { 124 | text: "www.easyproject.cn/donation", 125 | }; 126 | // Create QRCode Object 127 | var qrCode = new QRCode(canvas, options); 128 | } 129 | } 130 | 131 | render() { 132 | return ( 133 | 134 | {/* 2. QRCode Canvas */} 135 | 136 | 137 | ); 138 | } 139 | }; 140 | 141 | export default App; 142 | ``` 143 | 144 | ## QRCode API 145 | 146 | ### Object 147 | 148 | ```JS 149 | var qrcode = new QRCode(canvas_object, options_object); 150 | ``` 151 | 152 | 153 | ### Options 154 | 155 | ```JS 156 | var options_object = { 157 | // ====== Basic 158 | text: "https://github.com/ushelp/EasyQRCodeJS", 159 | width: 256, 160 | height: 256, 161 | colorDark : "#000000", 162 | colorLight : "#ffffff", 163 | correctLevel : QRCode.CorrectLevel.H, // L, M, Q, H 164 | 165 | // ====== dotScale 166 | /* 167 | dotScale: 1, // For body block, must be greater than 0, less than or equal to 1. default is 1 168 | 169 | dotScaleTiming: 1, // Dafault for timing block , must be greater than 0, less than or equal to 1. default is 1 170 | dotScaleTiming_H: undefined, // For horizontal timing block, must be greater than 0, less than or equal to 1. default is 1 171 | dotScaleTiming_V: undefined, // For vertical timing block, must be greater than 0, less than or equal to 1. default is 1 172 | 173 | dotScaleA: 1, // Dafault for alignment block, must be greater than 0, less than or equal to 1. default is 1 174 | dotScaleAO: undefined, // For alignment outer block, must be greater than 0, less than or equal to 1. default is 1 175 | dotScaleAI: undefined, // For alignment inner block, must be greater than 0, less than or equal to 1. default is 1 176 | */ 177 | 178 | // ====== Quiet Zone 179 | /* 180 | quietZone: 0, 181 | quietZoneColor: "rgba(0,0,0,0)", 182 | */ 183 | 184 | // ====== Logo 185 | /* 186 | logo: "https://avatars1.githubusercontent.com/u/4082017?s=160&v=4", // support: Static Image Resources, Network Images, Base64 Uri Data Images 187 | logoWidth: 80, // fixed logo width. default is `width/3.5` 188 | logoHeight: 80, // fixed logo height. default is `heigth/3.5` 189 | logoMaxWidth: undefined, // Maximum logo width. if set will ignore `logoWidth` value 190 | logoMaxHeight: undefined, // Maximum logo height. if set will ignore `logoHeight` value 191 | logoBackgroundColor: '#fffff', // Logo backgroud color, Invalid when `logBgTransparent` is true; default is '#ffffff' 192 | logoBackgroundTransparent: false, // Whether use transparent image, default is false 193 | */ 194 | 195 | // ====== Backgroud Image 196 | /* 197 | backgroundImage: '', // support: Static Image Resources, Network Images, Base64 Uri Data Images 198 | backgroundImageAlpha: 1, // Background image transparency, value between 0 and 1. default is 1. 199 | autoColor: false, // Automatic color adjustment(for data block) 200 | autoColorDark: "rgba(0, 0, 0, .6)", // Automatic color: dark CSS color 201 | autoColorLight: "rgba(255, 255, 255, .7)", // Automatic color: light CSS color 202 | */ 203 | 204 | // ====== Colorful 205 | // === Posotion Pattern(Eye) Color 206 | /* 207 | PO: '#e1622f', // Global Posotion Outer color. if not set, the defaut is `colorDark` 208 | PI: '#aa5b71', // Global Posotion Inner color. if not set, the defaut is `colorDark` 209 | PO_TL:'', // Posotion Outer color - Top Left 210 | PI_TL:'', // Posotion Inner color - Top Left 211 | PO_TR:'', // Posotion Outer color - Top Right 212 | PI_TR:'', // Posotion Inner color - Top Right 213 | PO_BL:'', // Posotion Outer color - Bottom Left 214 | PI_BL:'', // Posotion Inner color - Bottom Left 215 | */ 216 | // === Alignment Color 217 | /* 218 | AO: '', // Alignment Outer. if not set, the defaut is `colorDark` 219 | AI: '', // Alignment Inner. if not set, the defaut is `colorDark` 220 | */ 221 | // === Timing Pattern Color 222 | /* 223 | timing: '#e1622f', // Global Timing color. if not set, the defaut is `colorDark` 224 | timing_H: '', // Horizontal timing color 225 | timing_V: '', // Vertical timing color 226 | */ 227 | 228 | // ====== Title 229 | /* 230 | title: 'QR Title', // content 231 | titleFont: "normal normal bold 18px Arial", //font. default is "bold 16px Arial" 232 | titleColor: "#004284", // color. default is "#000" 233 | titleBackgroundColor: "#fff", // background color. default is "#fff" 234 | titleHeight: 70, // height, including subTitle. default is 0 235 | titleTop: 25, // draws y coordinates. default is 30 236 | */ 237 | 238 | // ====== SubTitle 239 | /* 240 | subTitle: 'QR subTitle', // content 241 | subTitleFont: "normal normal normal 14px Arial", // font. default is "14px Arial" 242 | subTitleColor: "#004284", // color. default is "4F4F4F" 243 | subTitleTop: 40, // draws y coordinates. default is 0 244 | */ 245 | 246 | // ===== Event Handler 247 | /* 248 | onRenderingStart: undefined, 249 | onRenderingEnd: undefined, 250 | */ 251 | 252 | // ===== Versions 253 | /* 254 | version: 0, // The symbol versions of QR Code range from Version 1 to Version 40. default 0 means automatically choose the closest version based on the text length. 255 | */ 256 | 257 | // ===== Binary(hex) data mode 258 | /* 259 | binary: false, // Whether it is binary mode, default is text mode. 260 | */ 261 | 262 | // ===== UTF-8 without BOM 263 | /* 264 | utf8WithoutBOM: true 265 | */ 266 | } 267 | ``` 268 | 269 | | Option | Required | Type | Defaults | Description | 270 | | --- | --- |--- | --- |--- | 271 | | Basic options| --- | ---|---|---| 272 | | **text** | Y | String |`''` | Text | 273 | | **width** | N | Number | `256` | Width | 274 | | **height** | N | Number | `256` | Height | 275 | | **colorDark** | N | String | `#000000` | Dark CSS color, `rgba(0,0,0,0)`| 276 | | **colorLight** | N | String | `#ffffff` | Light CSS color, `rgba(0,0,0,0)` | 277 | | **correctLevel** | N | Enum | `QRCode.CorrectLevel.H` | `QRCode.CorrectLevel.H`
`QRCode.CorrectLevel.Q`
`QRCode.CorrectLevel.M`
`QRCode.CorrectLevel.L`| 278 | | **dotScale** | N | Number | `1.0` |Dot style required Patterns. Ranges: `0-1.0` | 279 | | Dot style| --- | ---|---|---| 280 | | **dotScale** | N | Number | `1.0` |Dot style scale. Ranges: `0-1.0` | 281 | | **dotScaleTiming** | N | Number | `1.0` |Dot style scale for timing. Ranges: `0-1.0` | 282 | | **dotScaleTiming_V** | N | Number | `undefined` |Dot style scale for horizontal timing. Ranges: `0-1.0` | 283 | | **dotScaleTiming_H** | N | Number | `undefined` |Dot style scale for vertical timing. Ranges: `0-1.0` | 284 | | **dotScaleA** | N | Number | `1.0` |Dot style scale for alignment. Ranges: `0-1.0` | 285 | | **dotScaleAO** | N | Number | `undefined` |Dot style scale for alignment outer. Ranges: `0-1.0` | 286 | | **dotScaleAI** | N | Number | `undefined` |Dot style scale for alignment inner. Ranges: `0-1.0` | 287 | | Quiet Zone| --- | ---|---|---| 288 | | **quietZone** | N | Number | `0` | Quiet Zone size | 289 | | **quietZoneColor** | N | String | `rgba(0,0,0,0)` | Background CSS color to Quiet Zone | 290 | | Logo options| --- | ---|---|---| 291 | | **logo** | N | String | `undefined` | support: Static Image Resources, Network Images(`http://`, `https://`, `ftp://`), Base64 Uri Data Images | 292 | | **logoWidth** | N | Number | `width/3.5` | Fixed logo width. | 293 | | **logoHeight** | N | Number | `height/3.5` | fixed logo height. | 294 | | **logoMaxWidth** | N | Number | `undefined` | Maximum logo width. if set will ignore `logoWidth` value. | 295 | | **logoMaxHeight** | N | Number | `undefined` | Maximum logo height. if set will ignore `logoHeight` value. | 296 | | **logoBackgroundTransparent** | N | Boolean | `false` | Whether the background transparent image(`PNG`) shows transparency. When `true`, `logoBackgroundColor` is invalid | 297 | | **logoBackgroundColor** | N | String | `#ffffff` | Set Background CSS Color when image background transparent. Valid when `logoBackgroundTransparent` is `false` | 298 | | Backgroud Image options| ---|--- |---|---| 299 | | **backgroundImage** | N | String | `undefined` | Background Image Path or Base64 encoded Image. If use relative address, relative to `easy.qrcode.min.js` | 300 | | **backgroundImageAlpha** | N | Number | `1.0` | Background image transparency. Ranges: `0-1.0` | 301 | | **autoColor** | N | Boolean | `false` | Automatic color adjustment(for data block) | 302 | | **autoColorDark** | N | String | `rgba(0, 0, 0, .6)` | Automatic color: dark CSS color | 303 | | **autoColorLight** | N | String | `rgba(255, 255, 255, .7)` | Automatic color: light CSS color | 304 | | Posotion Pattern Color options| --- | ---|---|---| 305 | | **PO** | N | String | `undefined` | Global Posotion Outer CSS color. if not set, the defaut is `colorDark` | 306 | | **PI** | N | String | `undefined` | Global Posotion Inner CSS color. if not set, the defaut is `colorDark` | 307 | | **PO_TL** | N | String | `undefined` | Posotion Outer CSS color - Top Left | 308 | | **PI_TL** | N | String | `undefined` | Posotion Inner CSS color - Top Left | 309 | | **PO_TR** | N | String | `undefined` | Posotion Outer CSS color - Top Right | 310 | | **PI_TR** | N | String | `undefined` | Posotion Inner CSS color - Top Right | 311 | | **PO_BL** | N | String | `undefined` | Posotion Outer CSS color - Bottom Left | 312 | | **PI_BL** | N | String | `undefined` | Posotion Inner CSS color - Bottom Left | 313 | | Alignment Color options| --- |--- |---|---| 314 | | **AO** | N | String | `undefined` | Alignment Outer CSS color. if not set, the defaut is `colorDark` | 315 | | **AI** | N | String | `undefined` | Alignment Inner CSS color. if not set, the defaut is `colorDark` | 316 | | Timing Pattern Color options| --- | ---|---|---| 317 | | **timing** | N | String | `undefined` | Global Timing CSS color. if not set, the defaut is `colorDark` | 318 | | **timing_H** | N | String | `undefined` | Horizontal timing CSS color | 319 | | **timing_V** | N | String | `undefined` | Vertical timing CSS color | 320 | | Title options| --- | ---|---|---| 321 | | **title** | N | String | `''` | | 322 | | **titleFont** | N | String | `normal normal bold 16px Arial` | CSS Font | 323 | | **titleColor** | N | String | `#000000` | CSS color | 324 | | **titleBackgroundColor** | N | String | `#ffffff` | CSS color| 325 | | **titleHeight** | N | Number | `0` | Title Height, Include subTitle | 326 | | **titleTop** | N | Number | `30` | draws y coordinates.| 327 | | SubTitle options| --- | ---|---|---| 328 | | **subTitle** | N | String | `''` | | 329 | | **subTitleFont** | N | String | `normal normal normal 14px Arial` | CSS Font | 330 | | **subTitleColor** | N | String | `#4F4F4F` | CSS color | 331 | | **subTitleTop** | N | Number | `0` | draws y coordinates. default is 0| 332 | | Event Handler options| --- | ---|---|---| 333 | | **onRenderingStart(qrCodeOptions)** | N | Function | `undefined` | Callback function when the rendering start. can use to hide loading state or handling. | 334 | | **onRenderingEnd(qrCodeOptions, base64DataFn)** | N | Function | `undefined` | Callback function when the rendering ends. `base64DataFn` parameter is the base64 data execution function of QRCode, returns a Promise that resolves to DataURL. | 335 | | Version options| --- | ---|---|---| 336 | | **version** | N | Number | `0` | The symbol versions of QR Code range from Version `1` to Version `40`. default 0 means automatically choose the closest version based on the text length. [Information capacity and versions of QR Codes](https://www.qrcode.com/en/about/version.html) **NOTE**: If you set a value less than the minimum version available for text, the minimum version is automatically used. | 337 | | Tooltip options| --- | ---|---|---| 338 | | **tooltip** | N | Boolean | `false` | Whether set the QRCode Text as the title attribute value of the QRCode div. | 339 | | UTF-8 options| --- | ---|---|---| 340 | | **utf8WithoutBOM** | N | Boolean | `true` | Use UTF-8 without BOM. set to `false` value will use BOM in UFT-8.| 341 | | Binary(hex) data model options| --- | ---|---|---| 342 | | **binary** | N | Boolean | `false` | Whether it is binary mode, default is text mode. | 343 | 344 | 345 | ### Method 346 | 347 | - makeCode(text) 348 | 349 | ```JS 350 | qrcode.makeCode("https://github.com/ushelp/EasyQRCodeJS"); // make another code text. 351 | ``` 352 | 353 | 354 | ## How to load Images? 355 | 356 | You can use **Static Image Resources**, **Network Images**, **Base64 Data Uri Resources** to load images. 357 | 358 | ```JS 359 | // Static Image Resources 360 | const logoImage = Image.resolveAssetSource(require('./logo.png')).uri; 361 | 362 | // Network Images 363 | const logoImage = "https://avatars1.githubusercontent.com/u/4082017?s=160&v=4"; 364 | 365 | // Base64 Data Uri Resources 366 | const logoImage = "data:image/png;base64,iVBORw0KGgoAAAAN..."; 367 | 368 | var options = { 369 | // ... 370 | logo: logoImage 371 | 372 | // ... 373 | } 374 | ``` 375 | 376 | 377 | ## Get Base64 data of QRCode 378 | 379 | Use **onRenderingEnd(qrCodeOptions, base64DataFn)** to get Base64 data url of the QRCode. `base64DataFn` parameter is the base64 data execution function of QRCode, returns a Promise that resolves to DataURL. 380 | 381 | ```JS 382 | var options = { 383 | 384 | // ... 385 | 386 | onRenderingEnd: function(qrCodeOptions, base64DataFn){ 387 | 388 | base64DataFn().then(base64DataURL=>{ 389 | console.log(base64DataURL); 390 | }) 391 | 392 | } 393 | 394 | // ... 395 | } 396 | ``` 397 | 398 | If you encounter `Tainted canvases may not be exported` error. Please convert your images(`logo`, `background`, `...`) to Base64 Data URL. 399 | 400 | ``` 401 | Error: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported. 402 | ``` 403 | 404 | ## License 405 | MIT License 406 | 407 | 408 | ## EasyQRCode-React-Native-Premium 409 | ## 410 | *Let you draw freely!* 411 | 412 | **EasyQRCodeJS-Premium** is a more powerful and comprehensive enterprise version. You can use Canvas to customize any element, such as eye frame shape, eyeball shape, QR code block shape, and more. Also supports excavation (to prevent the QRcode overlap with the logo), random block mode. 413 | 414 | If you need more functions, we can provide you with customized development of API libraries or products. please contact me to buy the business enterprise edition. 415 | 416 | **EasyQRCodeJS-Premium** 是功能更加强大和全面的商业/企业版本。让您可以在 QRCode 中通过 Canvas 自定义任何喜欢的元素,例如 Eye frame 形状, Eye ball 形状, QR Body block 形状等等。 还支持 Logo 挖取(excavation,防止二维码与 Logo 重叠)和 Random bolock mode. 417 | 418 | 如果您需要更多功能,我们可以为您提供 API 库或产品的定制开发。请联系我购买商业/企业版本。 419 | 420 | ![Premium demo preview](doc/images/demo-premium.png) 421 | 422 | 423 | ## End 424 | 425 | Email: 426 | 427 | [http://www.easyproject.cn](http://www.easyproject.cn "EasyProject Home") 428 | 429 | 430 | **Donation/捐助:** 431 | 432 | 433 | 
434 | 支付宝/微信/QQ/云闪付/PayPal 扫码支付 435 |
支付宝/微信/QQ/云闪付/PayPal
436 | 437 |
438 | 439 | 我们相信,每个人的点滴贡献,都将是推动产生更多、更好免费开源产品的一大步。 440 | 441 | **感谢慷慨捐助,以支持服务器运行和鼓励更多社区成员。** 442 | 443 | We believe that the contribution of each bit by bit, will be driven to produce more and better free and open source products a big step. 444 | 445 | **Thank you donation to support the server running and encourage more community members.** 446 | 447 | 448 | --------------------------------------------------------------------------------