Blog/wwwroot/qrcode.js 44.3 K · 1075 lines · raw · history

1 /**
2 * @fileoverview
3 * - Using the 'QRCode for Javascript library'
4 * - Fixed dataset of 'QRCode for Javascript library' for support full-spec.
5 * - this library has no dependencies.
6 *
7 * @author davidshimjs
8 * @see <a href="http://www.d-project.com/" target="_blank">http://www.d-project.com/</a>
9 * @see <a href="http://jeromeetienne.github.com/jquery-qrcode/" target="_blank">http://jeromeetienne.github.com/jquery-qrcode/</a>
10 */
11 let QRCode;
12
13 (function () {
14 //---------------------------------------------------------------------
15 // QRCode for JavaScript
16 //
17 // Copyright (c) 2009 Kazuhiko Arase
18 //
19 // URL: http://www.d-project.com/
20 //
21 // Licensed under the MIT license:
22 // http://www.opensource.org/licenses/mit-license.php
23 //
24 // The word "QR Code" is registered trademark of
25 // DENSO WAVE INCORPORATED
26 // http://www.denso-wave.com/qrcode/faqpatent-e.html
27 //
28 //---------------------------------------------------------------------
29 function QR8bitByte(data) {
30 this.mode = QRMode.MODE_8BIT_BYTE;
31 this.data = data;
32 this.parsedData = [];
33
34 // Added to support UTF-8 Characters
35 const l = this.data.length;
36 for (let i = 0; i < l; i++) {
37 const byteArray = [];
38 const code = this.data.charCodeAt(i);
39
40 if (code > 0x10000) {
41 byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
42 byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
43 byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
44 byteArray[3] = 0x80 | (code & 0x3F);
45 } else if (code > 0x800) {
46 byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
47 byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
48 byteArray[2] = 0x80 | (code & 0x3F);
49 } else if (code > 0x80) {
50 byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
51 byteArray[1] = 0x80 | (code & 0x3F);
52 } else {
53 byteArray[0] = code;
54 }
55
56 this.parsedData.push(byteArray);
57 }
58
59 this.parsedData = Array.prototype.concat.apply([], this.parsedData);
60
61 if (this.parsedData.length !== this.data.length) {
62 this.parsedData.unshift(191);
63 this.parsedData.unshift(187);
64 this.parsedData.unshift(239);
65 }
66 }
67
68 QR8bitByte.prototype = {
69 getLength: function (_) {
70 return this.parsedData.length;
71 },
72 write: function (buffer) {
73 const l = this.parsedData.length;
74 for (let i = 0; i < l; i++) {
75 buffer.put(this.parsedData[i], 8);
76 }
77 }
78 };
79
80 function QRCodeModel(typeNumber, errorCorrectLevel) {
81 this.typeNumber = typeNumber;
82 this.errorCorrectLevel = errorCorrectLevel;
83 this.modules = null;
84 this.moduleCount = 0;
85 this.dataCache = null;
86 this.dataList = [];
87 }
88
89 QRCodeModel.prototype = {
90 addData: function (data) {
91 const newData = new QR8bitByte(data);
92 this.dataList.push(newData);
93 this.dataCache = null;
94 }, isDark: function (row, col) {
95 if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
96 throw new Error(row + "," + col);
97 }
98 return this.modules[row][col];
99 }, getModuleCount: function () {
100 return this.moduleCount;
101 }, make: function () {
102 this.makeImpl(false, this.getBestMaskPattern());
103 }, makeImpl: function (test, maskPattern) {
104 this.moduleCount = this.typeNumber * 4 + 17;
105 this.modules = new Array(this.moduleCount);
106 for (let row = 0; row < this.moduleCount; row++) {
107 this.modules[row] = new Array(this.moduleCount);
108 for (let col = 0; col < this.moduleCount; col++) {
109 this.modules[row][col] = null;
110 }
111 }
112 this.setupPositionProbePattern(0, 0);
113 this.setupPositionProbePattern(this.moduleCount - 7, 0);
114 this.setupPositionProbePattern(0, this.moduleCount - 7);
115 this.setupPositionAdjustPattern();
116 this.setupTimingPattern();
117 this.setupTypeInfo(test, maskPattern);
118 if (this.typeNumber >= 7) {
119 this.setupTypeNumber(test);
120 }
121 if (this.dataCache == null) {
122 this.dataCache = QRCodeModel.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
123 }
124 this.mapData(this.dataCache, maskPattern);
125 }, setupPositionProbePattern: function (row, col) {
126 for (let r = -1; r <= 7; r++) {
127 if (row + r <= -1 || this.moduleCount <= row + r) continue;
128 for (let c = -1; c <= 7; c++) {
129 if (col + c <= -1 || this.moduleCount <= col + c) continue;
130 this.modules[row + r][col + c] = (0 <= r && r <= 6 && (c === 0 || c === 6)) || (0 <= c && c <= 6 && (r === 0 || r === 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4);
131 }
132 }
133 }, getBestMaskPattern: function () {
134 let minLostPoint = 0;
135 let pattern = 0;
136 for (let i = 0; i < 8; i++) {
137 this.makeImpl(true, i);
138 const lostPoint = QRUtil.getLostPoint(this);
139 if (i === 0 || minLostPoint > lostPoint) {
140 minLostPoint = lostPoint;
141 pattern = i;
142 }
143 }
144 return pattern;
145 }, setupTimingPattern: function () {
146 for (let r = 8; r < this.moduleCount - 8; r++) {
147 if (this.modules[r][6] != null) {
148 continue;
149 }
150 this.modules[r][6] = (r % 2 === 0);
151 }
152 for (let c = 8; c < this.moduleCount - 8; c++) {
153 if (this.modules[6][c] != null) {
154 continue;
155 }
156 this.modules[6][c] = (c % 2 === 0);
157 }
158 }, setupPositionAdjustPattern: function () {
159 const pos = QRUtil.getPatternPosition(this.typeNumber);
160 for (let i = 0; i < pos.length; i++) {
161 for (let j = 0; j < pos.length; j++) {
162 const row = pos[i];
163 const col = pos[j];
164 if (this.modules[row][col] != null) {
165 continue;
166 }
167 for (let r = -2; r <= 2; r++) {
168 for (let c = -2; c <= 2; c++) {
169 this.modules[row + r][col + c] = r === -2 || r === 2 || c === -2 || c === 2 || (r === 0 && c === 0);
170 }
171 }
172 }
173 }
174 }, setupTypeNumber: function (test) {
175 const bits = QRUtil.getBCHTypeNumber(this.typeNumber);
176 for (let i = 0; i < 18; i++) {
177 this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = (!test && ((bits >> i) & 1) === 1);
178 }
179 for (let i = 0; i < 18; i++) {
180 this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = (!test && ((bits >> i) & 1) === 1);
181 }
182 }, setupTypeInfo: function (test, maskPattern) {
183 const data = (this.errorCorrectLevel << 3) | maskPattern;
184 const bits = QRUtil.getBCHTypeInfo(data);
185 for (let i = 0; i < 15; i++) {
186 let mod = (!test && ((bits >> i) & 1) === 1);
187 if (i < 6) {
188 this.modules[i][8] = mod;
189 } else if (i < 8) {
190 this.modules[i + 1][8] = mod;
191 } else {
192 this.modules[this.moduleCount - 15 + i][8] = mod;
193 }
194 }
195 for (let i = 0; i < 15; i++) {
196 let mod = (!test && ((bits >> i) & 1) === 1);
197 if (i < 8) {
198 this.modules[8][this.moduleCount - i - 1] = mod;
199 } else if (i < 9) {
200 this.modules[8][15 - i - 1 + 1] = mod;
201 } else {
202 this.modules[8][15 - i - 1] = mod;
203 }
204 }
205 this.modules[this.moduleCount - 8][8] = (!test);
206 }, mapData: function (data, maskPattern) {
207 let inc = -1;
208 let row = this.moduleCount - 1;
209 let bitIndex = 7;
210 let byteIndex = 0;
211 for (let col = this.moduleCount - 1; col > 0; col -= 2) {
212 if (col === 6) col--;
213 while (true) {
214 for (let c = 0; c < 2; c++) {
215 if (this.modules[row][col - c] == null) {
216 let dark = false;
217 if (byteIndex < data.length) {
218 dark = (((data[byteIndex] >>> bitIndex) & 1) === 1);
219 }
220 const mask = QRUtil.getMask(maskPattern, row, col - c);
221 if (mask) {
222 dark = !dark;
223 }
224 this.modules[row][col - c] = dark;
225 bitIndex--;
226 if (bitIndex === -1) {
227 byteIndex++;
228 bitIndex = 7;
229 }
230 }
231 }
232 row += inc;
233 if (row < 0 || this.moduleCount <= row) {
234 row -= inc;
235 inc = -inc;
236 break;
237 }
238 }
239 }
240 }
241 };
242 QRCodeModel.PAD0 = 0xEC;
243 QRCodeModel.PAD1 = 0x11;
244 QRCodeModel.createData = function (typeNumber, errorCorrectLevel, dataList) {
245 const rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
246 const buffer = new QRBitBuffer();
247 for (let i = 0; i < dataList.length; i++) {
248 const data = dataList[i];
249 buffer.put(data.mode, 4);
250 buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
251 data.write(buffer);
252 }
253 let totalDataCount = 0;
254 for (let i = 0; i < rsBlocks.length; i++) {
255 totalDataCount += rsBlocks[i].dataCount;
256 }
257 if (buffer.getLengthInBits() > totalDataCount * 8) {
258 throw new Error("code length overflow. ("
259 + buffer.getLengthInBits()
260 + ">"
261 + totalDataCount * 8
262 + ")");
263 }
264 if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
265 buffer.put(0, 4);
266 }
267 while (buffer.getLengthInBits() % 8 !== 0) {
268 buffer.putBit(false);
269 }
270 while (true) {
271 if (buffer.getLengthInBits() >= totalDataCount * 8) {
272 break;
273 }
274 buffer.put(QRCodeModel.PAD0, 8);
275 if (buffer.getLengthInBits() >= totalDataCount * 8) {
276 break;
277 }
278 buffer.put(QRCodeModel.PAD1, 8);
279 }
280 return QRCodeModel.createBytes(buffer, rsBlocks);
281 };
282 QRCodeModel.createBytes = function (buffer, rsBlocks) {
283 let offset = 0;
284 let maxDcCount = 0;
285 let maxEcCount = 0;
286 const dcdata = new Array(rsBlocks.length);
287 const ecdata = new Array(rsBlocks.length);
288 for (let r = 0; r < rsBlocks.length; r++) {
289 const dcCount = rsBlocks[r].dataCount;
290 const ecCount = rsBlocks[r].totalCount - dcCount;
291 maxDcCount = Math.max(maxDcCount, dcCount);
292 maxEcCount = Math.max(maxEcCount, ecCount);
293 dcdata[r] = new Array(dcCount);
294 for (let i = 0; i < dcdata[r].length; i++) {
295 dcdata[r][i] = 0xff & buffer.buffer[i + offset];
296 }
297 offset += dcCount;
298 const rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
299 const rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
300 const modPoly = rawPoly.mod(rsPoly);
301 ecdata[r] = new Array(rsPoly.getLength() - 1);
302 for (let i = 0; i < ecdata[r].length; i++) {
303 const modIndex = i + modPoly.getLength() - ecdata[r].length;
304 ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
305 }
306 }
307 let totalCodeCount = 0;
308 for (let i = 0; i < rsBlocks.length; i++) {
309 totalCodeCount += rsBlocks[i].totalCount;
310 }
311 const data = new Array(totalCodeCount);
312 let index = 0;
313 for (let i = 0; i < maxDcCount; i++) {
314 for (let r = 0; r < rsBlocks.length; r++) {
315 if (i < dcdata[r].length) {
316 data[index++] = dcdata[r][i];
317 }
318 }
319 }
320 for (let i = 0; i < maxEcCount; i++) {
321 for (let r = 0; r < rsBlocks.length; r++) {
322 if (i < ecdata[r].length) {
323 data[index++] = ecdata[r][i];
324 }
325 }
326 }
327 return data;
328 };
329 let QRMode = {MODE_NUMBER: 1 << 0, MODE_ALPHA_NUM: 1 << 1, MODE_8BIT_BYTE: 1 << 2, MODE_KANJI: 1 << 3};
330 const QRErrorCorrectLevel = {L: 1, M: 0, Q: 3, H: 2};
331 const QRMaskPattern = {
332 PATTERN000: 0,
333 PATTERN001: 1,
334 PATTERN010: 2,
335 PATTERN011: 3,
336 PATTERN100: 4,
337 PATTERN101: 5,
338 PATTERN110: 6,
339 PATTERN111: 7
340 };
341 let QRUtil = {
342 PATTERN_POSITION_TABLE: [[], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]],
343 G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
344 G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
345 G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
346 getBCHTypeInfo: function (data) {
347 let d = data << 10;
348 while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
349 d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15)));
350 }
351 return ((data << 10) | d) ^ QRUtil.G15_MASK;
352 },
353 getBCHTypeNumber: function (data) {
354 let d = data << 12;
355 while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
356 d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18)));
357 }
358 return (data << 12) | d;
359 },
360 getBCHDigit: function (data) {
361 let digit = 0;
362 while (data !== 0) {
363 digit++;
364 data >>>= 1;
365 }
366 return digit;
367 },
368 getPatternPosition: function (typeNumber) {
369 return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
370 },
371 getMask: function (maskPattern, i, j) {
372 switch (maskPattern) {
373 case QRMaskPattern.PATTERN000:
374 return (i + j) % 2 === 0;
375 case QRMaskPattern.PATTERN001:
376 return i % 2 === 0;
377 case QRMaskPattern.PATTERN010:
378 return j % 3 === 0;
379 case QRMaskPattern.PATTERN011:
380 return (i + j) % 3 === 0;
381 case QRMaskPattern.PATTERN100:
382 return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0;
383 case QRMaskPattern.PATTERN101:
384 return (i * j) % 2 + (i * j) % 3 === 0;
385 case QRMaskPattern.PATTERN110:
386 return ((i * j) % 2 + (i * j) % 3) % 2 === 0;
387 case QRMaskPattern.PATTERN111:
388 return ((i * j) % 3 + (i + j) % 2) % 2 === 0;
389 default:
390 throw new Error("bad maskPattern:" + maskPattern);
391 }
392 },
393 getErrorCorrectPolynomial: function (errorCorrectLength) {
394 let a = new QRPolynomial([1], 0);
395 for (let i = 0; i < errorCorrectLength; i++) {
396 a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
397 }
398 return a;
399 },
400 getLengthInBits: function (mode, type) {
401 if (1 <= type && type < 10) {
402 switch (mode) {
403 case QRMode.MODE_NUMBER:
404 return 10;
405 case QRMode.MODE_ALPHA_NUM:
406 return 9;
407 case QRMode.MODE_8BIT_BYTE:
408 return 8;
409 case QRMode.MODE_KANJI:
410 return 8;
411 default:
412 throw new Error("mode:" + mode);
413 }
414 } else if (type < 27) {
415 switch (mode) {
416 case QRMode.MODE_NUMBER:
417 return 12;
418 case QRMode.MODE_ALPHA_NUM:
419 return 11;
420 case QRMode.MODE_8BIT_BYTE:
421 return 16;
422 case QRMode.MODE_KANJI:
423 return 10;
424 default:
425 throw new Error("mode:" + mode);
426 }
427 } else if (type < 41) {
428 switch (mode) {
429 case QRMode.MODE_NUMBER:
430 return 14;
431 case QRMode.MODE_ALPHA_NUM:
432 return 13;
433 case QRMode.MODE_8BIT_BYTE:
434 return 16;
435 case QRMode.MODE_KANJI:
436 return 12;
437 default:
438 throw new Error("mode:" + mode);
439 }
440 } else {
441 throw new Error("type:" + type);
442 }
443 },
444 getLostPoint: function (qrCode) {
445 const moduleCount = qrCode.getModuleCount();
446 let lostPoint = 0;
447 for (let row = 0; row < moduleCount; row++) {
448 for (let col = 0; col < moduleCount; col++) {
449 let sameCount = 0;
450 const dark = qrCode.isDark(row, col);
451 for (let r = -1; r <= 1; r++) {
452 if (row + r < 0 || moduleCount <= row + r) {
453 continue;
454 }
455 for (let c = -1; c <= 1; c++) {
456 if (col + c < 0 || moduleCount <= col + c) {
457 continue;
458 }
459 if (r === 0 && c === 0) {
460 continue;
461 }
462 if (dark === qrCode.isDark(row + r, col + c)) {
463 sameCount++;
464 }
465 }
466 }
467 if (sameCount > 5) {
468 lostPoint += (3 + sameCount - 5);
469 }
470 }
471 }
472 for (let row = 0; row < moduleCount - 1; row++) {
473 for (let col = 0; col < moduleCount - 1; col++) {
474 let count = 0;
475 if (qrCode.isDark(row, col)) count++;
476 if (qrCode.isDark(row + 1, col)) count++;
477 if (qrCode.isDark(row, col + 1)) count++;
478 if (qrCode.isDark(row + 1, col + 1)) count++;
479 if (count === 0 || count === 4) {
480 lostPoint += 3;
481 }
482 }
483 }
484 for (let row = 0; row < moduleCount; row++) {
485 for (let col = 0; col < moduleCount - 6; col++) {
486 if (qrCode.isDark(row, col) && !qrCode.isDark(row, col + 1) && qrCode.isDark(row, col + 2) && qrCode.isDark(row, col + 3) && qrCode.isDark(row, col + 4) && !qrCode.isDark(row, col + 5) && qrCode.isDark(row, col + 6)) {
487 lostPoint += 40;
488 }
489 }
490 }
491 for (let col = 0; col < moduleCount; col++) {
492 for (let row = 0; row < moduleCount - 6; row++) {
493 if (qrCode.isDark(row, col) && !qrCode.isDark(row + 1, col) && qrCode.isDark(row + 2, col) && qrCode.isDark(row + 3, col) && qrCode.isDark(row + 4, col) && !qrCode.isDark(row + 5, col) && qrCode.isDark(row + 6, col)) {
494 lostPoint += 40;
495 }
496 }
497 }
498 let darkCount = 0;
499 for (let col = 0; col < moduleCount; col++) {
500 for (let row = 0; row < moduleCount; row++) {
501 if (qrCode.isDark(row, col)) {
502 darkCount++;
503 }
504 }
505 }
506 const ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
507 lostPoint += ratio * 10;
508 return lostPoint;
509 }
510 };
511 let QRMath = {
512 glog: function (n) {
513 if (n < 1) {
514 throw new Error("glog(" + n + ")");
515 }
516 return QRMath.LOG_TABLE[n];
517 }, gexp: function (n) {
518 while (n < 0) {
519 n += 255;
520 }
521 while (n >= 256) {
522 n -= 255;
523 }
524 return QRMath.EXP_TABLE[n];
525 }, EXP_TABLE: new Array(256), LOG_TABLE: new Array(256)
526 };
527 for (let i = 0; i < 8; i++) {
528 QRMath.EXP_TABLE[i] = 1 << i;
529 }
530 for (let i = 8; i < 256; i++) {
531 QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
532 }
533 for (let i = 0; i < 255; i++) {
534 QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
535 }
536
537 function QRPolynomial(num, shift) {
538 if (num.length === undefined) {
539 throw new Error(num.length + "/" + shift);
540 }
541 let offset = 0;
542 while (offset < num.length && num[offset] === 0) {
543 offset++;
544 }
545 this.num = new Array(num.length - offset + shift);
546 for (let i = 0; i < num.length - offset; i++) {
547 this.num[i] = num[i + offset];
548 }
549 }
550
551 QRPolynomial.prototype = {
552 get: function (index) {
553 return this.num[index];
554 }, getLength: function () {
555 return this.num.length;
556 }, multiply: function (e) {
557 const num = new Array(this.getLength() + e.getLength() - 1);
558 for (let i = 0; i < this.getLength(); i++) {
559 for (let j = 0; j < e.getLength(); j++) {
560 num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
561 }
562 }
563 return new QRPolynomial(num, 0);
564 }, mod: function (e) {
565 if (this.getLength() - e.getLength() < 0) {
566 return this;
567 }
568 const ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
569 const num = new Array(this.getLength());
570 for (let i = 0; i < this.getLength(); i++) {
571 num[i] = this.get(i);
572 }
573 for (let i = 0; i < e.getLength(); i++) {
574 num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
575 }
576 return new QRPolynomial(num, 0).mod(e);
577 }
578 };
579
580 function QRRSBlock(totalCount, dataCount) {
581 this.totalCount = totalCount;
582 this.dataCount = dataCount;
583 }
584
585 QRRSBlock.RS_BLOCK_TABLE = [[1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]];
586 QRRSBlock.getRSBlocks = function (typeNumber, errorCorrectLevel) {
587 const rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
588 if (rsBlock === undefined) {
589 throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel);
590 }
591 const length = rsBlock.length / 3;
592 const list = [];
593 for (let i = 0; i < length; i++) {
594 const count = rsBlock[i * 3];
595 const totalCount = rsBlock[i * 3 + 1];
596 const dataCount = rsBlock[i * 3 + 2];
597 for (let j = 0; j < count; j++) {
598 list.push(new QRRSBlock(totalCount, dataCount));
599 }
600 }
601 return list;
602 };
603 QRRSBlock.getRsBlockTable = function (typeNumber, errorCorrectLevel) {
604 switch (errorCorrectLevel) {
605 case QRErrorCorrectLevel.L:
606 return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4];
607 case QRErrorCorrectLevel.M:
608 return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
609 case QRErrorCorrectLevel.Q:
610 return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
611 case QRErrorCorrectLevel.H:
612 return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
613 default:
614 return undefined;
615 }
616 };
617
618 function QRBitBuffer() {
619 this.buffer = [];
620 this.length = 0;
621 }
622
623 QRBitBuffer.prototype = {
624 get: function (index) {
625 const bufIndex = Math.floor(index / 8);
626 return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1) === 1;
627 }, put: function (num, length) {
628 for (let i = 0; i < length; i++) {
629 this.putBit(((num >>> (length - i - 1)) & 1) === 1);
630 }
631 }, getLengthInBits: function () {
632 return this.length;
633 }, putBit: function (bit) {
634 const bufIndex = Math.floor(this.length / 8);
635 if (this.buffer.length <= bufIndex) {
636 this.buffer.push(0);
637 }
638 if (bit) {
639 this.buffer[bufIndex] |= (0x80 >>> (this.length % 8));
640 }
641 this.length++;
642 }
643 };
644 const 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]];
645
646 function _isSupportCanvas() {
647 return typeof CanvasRenderingContext2D != "undefined";
648 }
649
650 const svgDrawer = (function () {
651
652 const Drawing = function (el, htOption) {
653 this._el = el;
654 this._htOption = htOption;
655 };
656
657 Drawing.prototype.draw = function (oQRCode) {
658 const _htOption = this._htOption;
659 const _el = this._el;
660 const nCount = oQRCode.getModuleCount();
661
662 this.clear();
663
664 function makeSVG(tag, attrs) {
665 const el = document.createElementNS('http://www.w3.org/2000/svg', tag);
666 for (let k in attrs)
667 if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]);
668 return el;
669 }
670
671 const svg = makeSVG("svg", {
672 'viewBox': '0 0 ' + String(nCount) + " " + String(nCount),
673 'width': '100%',
674 'height': '100%',
675 'fill': _htOption.colorLight
676 });
677 svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
678 _el.appendChild(svg);
679
680 svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"}));
681 svg.appendChild(makeSVG("rect", {
682 "fill": _htOption.colorDark,
683 "width": "1",
684 "height": "1",
685 "id": "template"
686 }));
687
688 for (let row = 0; row < nCount; row++) {
689 for (let col = 0; col < nCount; col++) {
690 if (oQRCode.isDark(row, col)) {
691 const child = makeSVG("use", {"x": String(col), "y": String(row)});
692 child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template")
693 svg.appendChild(child);
694 }
695 }
696 }
697 };
698 Drawing.prototype.clear = function () {
699 while (this._el.hasChildNodes())
700 this._el.removeChild(this._el.lastChild);
701 };
702 return Drawing;
703 })();
704
705 const useSVG = document.documentElement.tagName.toLowerCase() === "svg";
706
707 // Drawing in DOM by using Table tag
708 let Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () {
709 const Drawing = function (el, htOption) {
710 this._el = el;
711 this._htOption = htOption;
712 };
713
714 /**
715 * Draw the QRCode
716 *
717 * @param {QRCode} oQRCode
718 */
719 Drawing.prototype.draw = function (oQRCode) {
720 const _htOption = this._htOption;
721 const _el = this._el;
722 const nCount = oQRCode.getModuleCount();
723 const nWidth = Math.floor(_htOption.width / nCount);
724 const nHeight = Math.floor(_htOption.height / nCount);
725 const aHTML = ['<table style="border:0;border-collapse:collapse;">'];
726
727 for (let row = 0; row < nCount; row++) {
728 aHTML.push('<tr>');
729
730 for (let col = 0; col < nCount; col++) {
731 aHTML.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' + nWidth + 'px;height:' + nHeight + 'px;background-color:' + (oQRCode.isDark(row, col) ? _htOption.colorDark : _htOption.colorLight) + ';"></td>');
732 }
733
734 aHTML.push('</tr>');
735 }
736
737 aHTML.push('</table>');
738 _el.innerHTML = aHTML.join('');
739
740 // Fix the margin values as real size.
741 const elTable = _el.childNodes[0];
742 const nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2;
743 const nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;
744
745 if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
746 elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px";
747 }
748 };
749
750 /**
751 * Clear the QRCode
752 */
753 Drawing.prototype.clear = function () {
754 this._el.innerHTML = '';
755 };
756
757 return Drawing;
758 })() : (function () { // Drawing in Canvas
759 function _onMakeImage() {
760 this._elImage.src = this._elCanvas.toDataURL("image/png");
761 this._elImage.style.display = "block";
762 this._elCanvas.style.display = "none";
763 }
764
765 /**
766 * Check whether the user's browser supports Data URI or not
767 *
768 * @private
769 * @param {Function} fSuccess Occurs if it supports Data URI
770 * @param {Function} fFail Occurs if it doesn't support Data URI
771 */
772 function _safeSetDataURI(fSuccess, fFail) {
773 const self = this;
774 self._fFail = fFail;
775 self._fSuccess = fSuccess;
776
777 // Check it just once
778 if (self._bSupportDataURI === null) {
779 const el = document.createElement("img");
780 const fOnError = function () {
781 self._bSupportDataURI = false;
782
783 if (self._fFail) {
784 self._fFail.call(self);
785 }
786 };
787 const fOnSuccess = function () {
788 self._bSupportDataURI = true;
789
790 if (self._fSuccess) {
791 self._fSuccess.call(self);
792 }
793 };
794
795 el.onabort = fOnError;
796 el.onerror = fOnError;
797 el.onload = fOnSuccess;
798 el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data.
799 } else if (self._bSupportDataURI === true && self._fSuccess) {
800 self._fSuccess.call(self);
801 } else if (self._bSupportDataURI === false && self._fFail) {
802 self._fFail.call(self);
803 }
804 };
805
806 /**
807 * Drawing QRCode by using canvas
808 *
809 * @constructor
810 * @param {HTMLElement} el
811 * @param {Object} htOption QRCode Options
812 */
813 const Drawing = function (el, htOption) {
814 this._bIsPainted = false;
815 this._htOption = htOption;
816 this._elCanvas = document.createElement("canvas");
817 this._elCanvas.width = htOption.width;
818 this._elCanvas.height = htOption.height;
819 el.appendChild(this._elCanvas);
820 this._el = el;
821 this._oContext = this._elCanvas.getContext("2d");
822 this._bIsPainted = false;
823 this._elImage = document.createElement("img");
824 this._elImage.alt = "Scan me!";
825 this._elImage.style.display = "none";
826 this._el.appendChild(this._elImage);
827 this._bSupportDataURI = null;
828 };
829
830 /**
831 * Draw the QRCode
832 *
833 * @param {QRCode} oQRCode
834 */
835 Drawing.prototype.draw = function (oQRCode) {
836 const _elImage = this._elImage;
837 const _oContext = this._oContext;
838 const _htOption = this._htOption;
839
840 const nCount = oQRCode.getModuleCount();
841 const nWidth = _htOption.width / nCount;
842 const nHeight = _htOption.height / nCount;
843 const nRoundedWidth = Math.round(nWidth);
844 const nRoundedHeight = Math.round(nHeight);
845
846 _elImage.style.display = "none";
847 this.clear();
848
849 for (let row = 0; row < nCount; row++) {
850 for (let col = 0; col < nCount; col++) {
851 const bIsDark = oQRCode.isDark(row, col);
852 const nLeft = col * nWidth;
853 const nTop = row * nHeight;
854 _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
855 _oContext.lineWidth = 1;
856 _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
857 _oContext.fillRect(nLeft, nTop, nWidth, nHeight);
858
859 // 안티 앨리어싱 방지 처리
860 _oContext.strokeRect(
861 Math.floor(nLeft) + 0.5,
862 Math.floor(nTop) + 0.5,
863 nRoundedWidth,
864 nRoundedHeight
865 );
866
867 _oContext.strokeRect(
868 Math.ceil(nLeft) - 0.5,
869 Math.ceil(nTop) - 0.5,
870 nRoundedWidth,
871 nRoundedHeight
872 );
873 }
874 }
875
876 this._bIsPainted = true;
877 };
878
879 /**
880 * Make the image from Canvas if the browser supports Data URI.
881 */
882 Drawing.prototype.makeImage = function () {
883 if (this._bIsPainted) {
884 _safeSetDataURI.call(this, _onMakeImage);
885 }
886 };
887
888 /**
889 * Return whether the QRCode is painted or not
890 *
891 * @return {Boolean}
892 */
893 Drawing.prototype.isPainted = function () {
894 return this._bIsPainted;
895 };
896
897 /**
898 * Clear the QRCode
899 */
900 Drawing.prototype.clear = function () {
901 this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height);
902 this._bIsPainted = false;
903 };
904
905 /**
906 * @private
907 * @param {Number} nNumber
908 */
909 Drawing.prototype.round = function (nNumber) {
910 if (!nNumber) {
911 return nNumber;
912 }
913
914 return Math.floor(nNumber * 1000) / 1000;
915 };
916
917 return Drawing;
918 })();
919
920 /**
921 * Get the type by string length
922 *
923 * @private
924 * @param {String} sText
925 * @param {Number} nCorrectLevel
926 * @return {Number} type
927 */
928 function _getTypeNumber(sText, nCorrectLevel) {
929 let nType = 1;
930 const length = _getUTF8Length(sText);
931
932 let i = 0;
933 const len = QRCodeLimitLength.length;
934 for (; i <= len; i++) {
935 let nLimit = 0;
936
937 switch (nCorrectLevel) {
938 case QRErrorCorrectLevel.L:
939 nLimit = QRCodeLimitLength[i][0];
940 break;
941 case QRErrorCorrectLevel.M:
942 nLimit = QRCodeLimitLength[i][1];
943 break;
944 case QRErrorCorrectLevel.Q:
945 nLimit = QRCodeLimitLength[i][2];
946 break;
947 case QRErrorCorrectLevel.H:
948 nLimit = QRCodeLimitLength[i][3];
949 break;
950 }
951
952 if (length <= nLimit) {
953 break;
954 } else {
955 nType++;
956 }
957 }
958
959 if (nType > QRCodeLimitLength.length) {
960 throw new Error("Too long data");
961 }
962
963 return nType;
964 }
965
966 function _getUTF8Length(sText) {
967 const replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a');
968 return replacedText.length + (replacedText.length !== sText ? 3 : 0);
969 }
970
971 /**
972 * @class QRCode
973 * @constructor
974 * @example
975 * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie");
976 *
977 * @example
978 * let oQRCode = new QRCode("test", {
979 * text : "http://naver.com",
980 * width : 128,
981 * height : 128
982 * });
983 *
984 * oQRCode.clear(); // Clear the QRCode.
985 * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode.
986 *
987 * @param {HTMLElement|String} el target element or 'id' attribute of element.
988 * @param {Object|String} vOption
989 * @param {String} vOption.text QRCode link data
990 * @param {Number} [vOption.width=256]
991 * @param {Number} [vOption.height=256]
992 * @param {String} [vOption.colorDark="#000000"]
993 * @param {String} [vOption.colorLight="#ffffff"]
994 * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]
995 */
996 QRCode = function (el, vOption) {
997 this._htOption = {
998 width: 256,
999 height: 256,
1000 typeNumber: 4,
1001 colorDark: "#000000",
1002 colorLight: "#ffffff",
1003 correctLevel: QRErrorCorrectLevel.H
1004 };
1005
1006 if (typeof vOption === 'string') {
1007 vOption = {
1008 text: vOption
1009 };
1010 }
1011
1012 // Overwrites options
1013 if (vOption) {
1014 for (let i in vOption) {
1015 this._htOption[i] = vOption[i];
1016 }
1017 }
1018
1019 if (typeof el == "string") {
1020 el = document.getElementById(el);
1021 }
1022
1023 if (this._htOption.useSVG) {
1024 Drawing = svgDrawer;
1025 }
1026
1027 this._el = el;
1028 this._oQRCode = null;
1029 this._oDrawing = new Drawing(this._el, this._htOption);
1030
1031 if (this._htOption.text) {
1032 this.makeCode(this._htOption.text);
1033 }
1034 };
1035
1036 /**
1037 * Make the QRCode
1038 *
1039 * @param {String} sText link data
1040 */
1041 QRCode.prototype.makeCode = function (sText) {
1042 this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel);
1043 this._oQRCode.addData(sText);
1044 this._oQRCode.make();
1045 this._el.title = sText;
1046 this._oDrawing.draw(this._oQRCode);
1047 this.makeImage();
1048 };
1049
1050 /**
1051 * Make the Image from Canvas element
1052 * - It occurs automatically
1053 *
1054 * @private
1055 */
1056 QRCode.prototype.makeImage = function () {
1057 if (typeof this._oDrawing.makeImage == "function") {
1058 this._oDrawing.makeImage();
1059 }
1060 };
1061
1062 /**
1063 * Clear the QRCode
1064 */
1065 QRCode.prototype.clear = function () {
1066 this._oDrawing.clear();
1067 };
1068
1069 /**
1070 * @name QRCode.CorrectLevel
1071 */
1072 QRCode.CorrectLevel = QRErrorCorrectLevel;
1073 })();
1074
1075 export {QRCode};