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