UNPKG

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