rev |
line source |
pascal@1161
|
1 /* qr.js -- QR code generator in Javascript (revision 2011-01-19)
|
pascal@1161
|
2 * Written by Kang Seonghoon <public+qrjs@mearie.org>.
|
pascal@1161
|
3 *
|
pascal@1161
|
4 * This source code is in the public domain; if your jurisdiction does not
|
pascal@1161
|
5 * recognize the public domain the terms of Creative Commons CC0 license
|
pascal@1161
|
6 * apply. In the other words, you can always do what you want.
|
pascal@1161
|
7 */
|
pascal@1161
|
8
|
pascal@1161
|
9 var QRCode = (function(){
|
pascal@1161
|
10
|
pascal@1161
|
11 /* Quick overview: QR code composed of 2D array of modules (a rectangular
|
pascal@1161
|
12 * area that conveys one bit of information); some modules are fixed to help
|
pascal@1161
|
13 * the recognition of the code, and remaining data modules are further divided
|
pascal@1161
|
14 * into 8-bit code words which are augumented by Reed-Solomon error correcting
|
pascal@1161
|
15 * codes (ECC). There could be multiple ECCs, in the case the code is so large
|
pascal@1161
|
16 * that it is helpful to split the raw data into several chunks.
|
pascal@1161
|
17 *
|
pascal@1161
|
18 * The number of modules is determined by the code's "version", ranging from 1
|
pascal@1161
|
19 * (21x21) to 40 (177x177). How many ECC bits are used is determined by the
|
pascal@1161
|
20 * ECC level (L/M/Q/H). The number and size (and thus the order of generator
|
pascal@1161
|
21 * polynomial) of ECCs depend to the version and ECC level.
|
pascal@1161
|
22 */
|
pascal@1161
|
23
|
pascal@1161
|
24 // per-version information (cf. JIS X 0510:2004 pp. 30--36, 71)
|
pascal@1161
|
25 //
|
pascal@1161
|
26 // [0]: the degree of generator polynomial by ECC levels
|
pascal@1161
|
27 // [1]: # of code blocks by ECC levels
|
pascal@1161
|
28 // [2]: left-top positions of alignment patterns
|
pascal@1161
|
29 //
|
pascal@1161
|
30 // the number in this table (in particular, [0]) does not exactly match with
|
pascal@1161
|
31 // the numbers in the specficiation. see augumenteccs below for the reason.
|
pascal@1161
|
32 var VERSIONS = [
|
pascal@1161
|
33 null,
|
pascal@1161
|
34 [[10, 7,17,13], [ 1, 1, 1, 1], []],
|
pascal@1161
|
35 [[16,10,28,22], [ 1, 1, 1, 1], [4,16]],
|
pascal@1161
|
36 [[26,15,22,18], [ 1, 1, 2, 2], [4,20]],
|
pascal@1161
|
37 [[18,20,16,26], [ 2, 1, 4, 2], [4,24]],
|
pascal@1161
|
38 [[24,26,22,18], [ 2, 1, 4, 4], [4,28]],
|
pascal@1161
|
39 [[16,18,28,24], [ 4, 2, 4, 4], [4,32]],
|
pascal@1161
|
40 [[18,20,26,18], [ 4, 2, 5, 6], [4,20,36]],
|
pascal@1161
|
41 [[22,24,26,22], [ 4, 2, 6, 6], [4,22,40]],
|
pascal@1161
|
42 [[22,30,24,20], [ 5, 2, 8, 8], [4,24,44]],
|
pascal@1161
|
43 [[26,18,28,24], [ 5, 4, 8, 8], [4,26,48]],
|
pascal@1161
|
44 [[30,20,24,28], [ 5, 4,11, 8], [4,28,52]],
|
pascal@1161
|
45 [[22,24,28,26], [ 8, 4,11,10], [4,30,56]],
|
pascal@1161
|
46 [[22,26,22,24], [ 9, 4,16,12], [4,32,60]],
|
pascal@1161
|
47 [[24,30,24,20], [ 9, 4,16,16], [4,24,44,64]],
|
pascal@1161
|
48 [[24,22,24,30], [10, 6,18,12], [4,24,46,68]],
|
pascal@1161
|
49 [[28,24,30,24], [10, 6,16,17], [4,24,48,72]],
|
pascal@1161
|
50 [[28,28,28,28], [11, 6,19,16], [4,28,52,76]],
|
pascal@1161
|
51 [[26,30,28,28], [13, 6,21,18], [4,28,54,80]],
|
pascal@1161
|
52 [[26,28,26,26], [14, 7,25,21], [4,28,56,84]],
|
pascal@1161
|
53 [[26,28,28,30], [16, 8,25,20], [4,32,60,88]],
|
pascal@1161
|
54 [[26,28,30,28], [17, 8,25,23], [4,26,48,70,92]],
|
pascal@1161
|
55 [[28,28,24,30], [17, 9,34,23], [4,24,48,72,96]],
|
pascal@1161
|
56 [[28,30,30,30], [18, 9,30,25], [4,28,52,76,100]],
|
pascal@1161
|
57 [[28,30,30,30], [20,10,32,27], [4,26,52,78,104]],
|
pascal@1161
|
58 [[28,26,30,30], [21,12,35,29], [4,30,56,82,108]],
|
pascal@1161
|
59 [[28,28,30,28], [23,12,37,34], [4,28,56,84,112]],
|
pascal@1161
|
60 [[28,30,30,30], [25,12,40,34], [4,32,60,88,116]],
|
pascal@1161
|
61 [[28,30,30,30], [26,13,42,35], [4,24,48,72,96,120]],
|
pascal@1161
|
62 [[28,30,30,30], [28,14,45,38], [4,28,52,76,100,124]],
|
pascal@1161
|
63 [[28,30,30,30], [29,15,48,40], [4,24,50,76,102,128]],
|
pascal@1161
|
64 [[28,30,30,30], [31,16,51,43], [4,28,54,80,106,132]],
|
pascal@1161
|
65 [[28,30,30,30], [33,17,54,45], [4,32,58,84,110,136]],
|
pascal@1161
|
66 [[28,30,30,30], [35,18,57,48], [4,28,56,84,112,140]],
|
pascal@1161
|
67 [[28,30,30,30], [37,19,60,51], [4,32,60,88,116,144]],
|
pascal@1161
|
68 [[28,30,30,30], [38,19,63,53], [4,28,52,76,100,124,148]],
|
pascal@1161
|
69 [[28,30,30,30], [40,20,66,56], [4,22,48,74,100,126,152]],
|
pascal@1161
|
70 [[28,30,30,30], [43,21,70,59], [4,26,52,78,104,130,156]],
|
pascal@1161
|
71 [[28,30,30,30], [45,22,74,62], [4,30,56,82,108,134,160]],
|
pascal@1161
|
72 [[28,30,30,30], [47,24,77,65], [4,24,52,80,108,136,164]],
|
pascal@1161
|
73 [[28,30,30,30], [49,25,81,68], [4,28,56,84,112,140,168]]];
|
pascal@1161
|
74
|
pascal@1161
|
75 // mode constants (cf. Table 2 in JIS X 0510:2004 p. 16)
|
pascal@1161
|
76 var MODE_TERMINATOR = 0;
|
pascal@1161
|
77 var MODE_NUMERIC = 1, MODE_ALPHANUMERIC = 2, MODE_OCTET = 4, MODE_KANJI = 8;
|
pascal@1161
|
78
|
pascal@1161
|
79 // validation regexps
|
pascal@1161
|
80 var NUMERIC_REGEXP = /^\d*$/;
|
pascal@1161
|
81 var ALPHANUMERIC_REGEXP = /^[A-Za-z0-9 $%*+\-./:]*$/;
|
pascal@1161
|
82 var ALPHANUMERIC_OUT_REGEXP = /^[A-Z0-9 $%*+\-./:]*$/;
|
pascal@1161
|
83
|
pascal@1161
|
84 // ECC levels (cf. Table 22 in JIS X 0510:2004 p. 45)
|
pascal@1161
|
85 var ECCLEVEL_L = 1, ECCLEVEL_M = 0, ECCLEVEL_Q = 3, ECCLEVEL_H = 2;
|
pascal@1161
|
86
|
pascal@1161
|
87 // GF(2^8)-to-integer mapping with a reducing polynomial x^8+x^4+x^3+x^2+1
|
pascal@1161
|
88 // invariant: GF256_MAP[GF256_INVMAP[i]] == i for all i in [1,256)
|
pascal@1161
|
89 var GF256_MAP = [], GF256_INVMAP = [-1];
|
pascal@1161
|
90 for (var i = 0, v = 1; i < 255; ++i) {
|
pascal@1161
|
91 GF256_MAP.push(v);
|
pascal@1161
|
92 GF256_INVMAP[v] = i;
|
pascal@1161
|
93 v = (v * 2) ^ (v >= 128 ? 0x11d : 0);
|
pascal@1161
|
94 }
|
pascal@1161
|
95
|
pascal@1161
|
96 // generator polynomials up to degree 30
|
pascal@1161
|
97 // (should match with polynomials in JIS X 0510:2004 Appendix A)
|
pascal@1161
|
98 //
|
pascal@1161
|
99 // generator polynomial of degree K is product of (x-\alpha^0), (x-\alpha^1),
|
pascal@1161
|
100 // ..., (x-\alpha^(K-1)). by convention, we omit the K-th coefficient (always 1)
|
pascal@1161
|
101 // from the result; also other coefficients are written in terms of the exponent
|
pascal@1161
|
102 // to \alpha to avoid the redundant calculation. (see also calculateecc below.)
|
pascal@1161
|
103 var GF256_GENPOLY = [[]];
|
pascal@1161
|
104 for (var i = 0; i < 30; ++i) {
|
pascal@1161
|
105 var prevpoly = GF256_GENPOLY[i], poly = [];
|
pascal@1161
|
106 for (var j = 0; j <= i; ++j) {
|
pascal@1161
|
107 var a = (j < i ? GF256_MAP[prevpoly[j]] : 0);
|
pascal@1161
|
108 var b = GF256_MAP[(i + (prevpoly[j-1] || 0)) % 255];
|
pascal@1161
|
109 poly.push(GF256_INVMAP[a ^ b]);
|
pascal@1161
|
110 }
|
pascal@1161
|
111 GF256_GENPOLY.push(poly);
|
pascal@1161
|
112 }
|
pascal@1161
|
113
|
pascal@1161
|
114 // alphanumeric character mapping (cf. Table 5 in JIS X 0510:2004 p. 19)
|
pascal@1161
|
115 var ALPHANUMERIC_MAP = {};
|
pascal@1161
|
116 for (var i = 0; i < 45; ++i) {
|
pascal@1161
|
117 ALPHANUMERIC_MAP['0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'.charAt(i)] = i;
|
pascal@1161
|
118 }
|
pascal@1161
|
119
|
pascal@1161
|
120 // mask functions in terms of row # and column #
|
pascal@1161
|
121 // (cf. Table 20 in JIS X 0510:2004 p. 42)
|
pascal@1161
|
122 var MASKFUNCS = [
|
pascal@1161
|
123 function(i,j) { return (i+j) % 2 == 0; },
|
pascal@1161
|
124 function(i,j) { return i % 2 == 0; },
|
pascal@1161
|
125 function(i,j) { return j % 3 == 0; },
|
pascal@1161
|
126 function(i,j) { return (i+j) % 3 == 0; },
|
pascal@1161
|
127 function(i,j) { return (((i/2)|0) + ((j/3)|0)) % 2 == 0; },
|
pascal@1161
|
128 function(i,j) { return (i*j) % 2 + (i*j) % 3 == 0; },
|
pascal@1161
|
129 function(i,j) { return ((i*j) % 2 + (i*j) % 3) % 2 == 0; },
|
pascal@1161
|
130 function(i,j) { return ((i+j) % 2 + (i*j) % 3) % 2 == 0; }];
|
pascal@1161
|
131
|
pascal@1161
|
132 // returns true when the version information has to be embeded.
|
pascal@1161
|
133 var needsverinfo = function(ver) { return ver > 6; };
|
pascal@1161
|
134
|
pascal@1161
|
135 // returns the size of entire QR code for given version.
|
pascal@1161
|
136 var getsizebyver = function(ver) { return 4 * ver + 17; };
|
pascal@1161
|
137
|
pascal@1161
|
138 // returns the number of bits available for code words in this version.
|
pascal@1161
|
139 var nfullbits = function(ver) {
|
pascal@1161
|
140 /*
|
pascal@1161
|
141 * |<--------------- n --------------->|
|
pascal@1161
|
142 * | |<----- n-17 ---->| |
|
pascal@1161
|
143 * +-------+ ///+-------+ ----
|
pascal@1161
|
144 * | | ///| | ^
|
pascal@1161
|
145 * | 9x9 | @@@@@ ///| 9x8 | |
|
pascal@1161
|
146 * | | # # # @5x5@ # # # | | |
|
pascal@1161
|
147 * +-------+ @@@@@ +-------+ |
|
pascal@1161
|
148 * # ---|
|
pascal@1161
|
149 * ^ |
|
pascal@1161
|
150 * # |
|
pascal@1161
|
151 * @@@@@ @@@@@ @@@@@ | n
|
pascal@1161
|
152 * @5x5@ @5x5@ @5x5@ n-17
|
pascal@1161
|
153 * @@@@@ @@@@@ @@@@@ | |
|
pascal@1161
|
154 * # | |
|
pascal@1161
|
155 * ////// v |
|
pascal@1161
|
156 * //////# ---|
|
pascal@1161
|
157 * +-------+ @@@@@ @@@@@ |
|
pascal@1161
|
158 * | | @5x5@ @5x5@ |
|
pascal@1161
|
159 * | 8x9 | @@@@@ @@@@@ |
|
pascal@1161
|
160 * | | v
|
pascal@1161
|
161 * +-------+ ----
|
pascal@1161
|
162 *
|
pascal@1161
|
163 * when the entire code has n^2 modules and there are m^2-3 alignment
|
pascal@1161
|
164 * patterns, we have:
|
pascal@1161
|
165 * - 225 (= 9x9 + 9x8 + 8x9) modules for finder patterns and
|
pascal@1161
|
166 * format information;
|
pascal@1161
|
167 * - 2n-34 (= 2(n-17)) modules for timing patterns;
|
pascal@1161
|
168 * - 36 (= 3x6 + 6x3) modules for version information, if any;
|
pascal@1161
|
169 * - 25m^2-75 (= (m^2-3)(5x5)) modules for alignment patterns
|
pascal@1161
|
170 * if any, but 10m-20 (= 2(m-2)x5) of them overlaps with
|
pascal@1161
|
171 * timing patterns.
|
pascal@1161
|
172 */
|
pascal@1161
|
173 var v = VERSIONS[ver];
|
pascal@1161
|
174 var nbits = 16*ver*ver + 128*ver + 64; // finder, timing and format info.
|
pascal@1161
|
175 if (needsverinfo(ver)) nbits -= 36; // version information
|
pascal@1161
|
176 if (v[2].length) { // alignment patterns
|
pascal@1161
|
177 nbits -= 25 * v[2].length * v[2].length - 10 * v[2].length - 55;
|
pascal@1161
|
178 }
|
pascal@1161
|
179 return nbits;
|
pascal@1161
|
180 };
|
pascal@1161
|
181
|
pascal@1161
|
182 // returns the number of bits available for data portions (i.e. excludes ECC
|
pascal@1161
|
183 // bits but includes mode and length bits) in this version and ECC level.
|
pascal@1161
|
184 var ndatabits = function(ver, ecclevel) {
|
pascal@1161
|
185 var nbits = nfullbits(ver) & ~7; // no sub-octet code words
|
pascal@1161
|
186 var v = VERSIONS[ver];
|
pascal@1161
|
187 nbits -= 8 * v[0][ecclevel] * v[1][ecclevel]; // ecc bits
|
pascal@1161
|
188 return nbits;
|
pascal@1161
|
189 }
|
pascal@1161
|
190
|
pascal@1161
|
191 // returns the number of bits required for the length of data.
|
pascal@1161
|
192 // (cf. Table 3 in JIS X 0510:2004 p. 16)
|
pascal@1161
|
193 var ndatalenbits = function(ver, mode) {
|
pascal@1161
|
194 switch (mode) {
|
pascal@1161
|
195 case MODE_NUMERIC: return (ver < 10 ? 10 : ver < 27 ? 12 : 14);
|
pascal@1161
|
196 case MODE_ALPHANUMERIC: return (ver < 10 ? 9 : ver < 27 ? 11 : 13);
|
pascal@1161
|
197 case MODE_OCTET: return (ver < 10 ? 8 : 16);
|
pascal@1161
|
198 case MODE_KANJI: return (ver < 10 ? 8 : ver < 27 ? 10 : 12);
|
pascal@1161
|
199 }
|
pascal@1161
|
200 };
|
pascal@1161
|
201
|
pascal@1161
|
202 // returns the maximum length of data possible in given configuration.
|
pascal@1161
|
203 var getmaxdatalen = function(ver, mode, ecclevel) {
|
pascal@1161
|
204 var nbits = ndatabits(ver, ecclevel) - 4 - ndatalenbits(ver, mode); // 4 for mode bits
|
pascal@1161
|
205 switch (mode) {
|
pascal@1161
|
206 case MODE_NUMERIC:
|
pascal@1161
|
207 return ((nbits/10) | 0) * 3 + (nbits%10 < 4 ? 0 : nbits%10 < 7 ? 1 : 2);
|
pascal@1161
|
208 case MODE_ALPHANUMERIC:
|
pascal@1161
|
209 return ((nbits/11) | 0) * 2 + (nbits%11 < 6 ? 0 : 1);
|
pascal@1161
|
210 case MODE_OCTET:
|
pascal@1161
|
211 return (nbits/8) | 0;
|
pascal@1161
|
212 case MODE_KANJI:
|
pascal@1161
|
213 return (nbits/13) | 0;
|
pascal@1161
|
214 }
|
pascal@1161
|
215 };
|
pascal@1161
|
216
|
pascal@1161
|
217 // checks if the given data can be encoded in given mode, and returns
|
pascal@1161
|
218 // the converted data for the further processing if possible. otherwise
|
pascal@1161
|
219 // returns null.
|
pascal@1161
|
220 //
|
pascal@1161
|
221 // this function does not check the length of data; it is a duty of
|
pascal@1161
|
222 // encode function below (as it depends on the version and ECC level too).
|
pascal@1161
|
223 var validatedata = function(mode, data) {
|
pascal@1161
|
224 switch (mode) {
|
pascal@1161
|
225 case MODE_NUMERIC:
|
pascal@1161
|
226 if (!data.match(NUMERIC_REGEXP)) return null;
|
pascal@1161
|
227 return data;
|
pascal@1161
|
228
|
pascal@1161
|
229 case MODE_ALPHANUMERIC:
|
pascal@1161
|
230 if (!data.match(ALPHANUMERIC_REGEXP)) return null;
|
pascal@1161
|
231 return data.toUpperCase();
|
pascal@1161
|
232
|
pascal@1161
|
233 case MODE_OCTET:
|
pascal@1161
|
234 if (typeof data === 'string') { // encode as utf-8 string
|
pascal@1161
|
235 var newdata = [];
|
pascal@1161
|
236 for (var i = 0; i < data.length; ++i) {
|
pascal@1161
|
237 var ch = data.charCodeAt(i);
|
pascal@1161
|
238 if (ch < 0x80) {
|
pascal@1161
|
239 newdata.push(ch);
|
pascal@1161
|
240 } else if (ch < 0x800) {
|
pascal@1161
|
241 newdata.push(0xc0 | (ch >> 6),
|
pascal@1161
|
242 0x80 | (ch & 0x3f));
|
pascal@1161
|
243 } else if (ch < 0x10000) {
|
pascal@1161
|
244 newdata.push(0xe0 | (ch >> 12),
|
pascal@1161
|
245 0x80 | ((ch >> 6) & 0x3f),
|
pascal@1161
|
246 0x80 | (ch & 0x3f));
|
pascal@1161
|
247 } else {
|
pascal@1161
|
248 newdata.push(0xf0 | (ch >> 18),
|
pascal@1161
|
249 0x80 | ((ch >> 12) & 0x3f),
|
pascal@1161
|
250 0x80 | ((ch >> 6) & 0x3f),
|
pascal@1161
|
251 0x80 | (ch & 0x3f));
|
pascal@1161
|
252 }
|
pascal@1161
|
253 }
|
pascal@1161
|
254 return newdata;
|
pascal@1161
|
255 } else {
|
pascal@1161
|
256 return data;
|
pascal@1161
|
257 }
|
pascal@1161
|
258 }
|
pascal@1161
|
259 };
|
pascal@1161
|
260
|
pascal@1161
|
261 // returns the code words (sans ECC bits) for given data and configurations.
|
pascal@1161
|
262 // requires data to be preprocessed by validatedata. no length check is
|
pascal@1161
|
263 // performed, and everything has to be checked before calling this function.
|
pascal@1161
|
264 var encode = function(ver, mode, data, maxbuflen) {
|
pascal@1161
|
265 var buf = [];
|
pascal@1161
|
266 var bits = 0, remaining = 8;
|
pascal@1161
|
267 var datalen = data.length;
|
pascal@1161
|
268
|
pascal@1161
|
269 // this function is intentionally no-op when n=0.
|
pascal@1161
|
270 var pack = function(x, n) {
|
pascal@1161
|
271 if (n >= remaining) {
|
pascal@1161
|
272 buf.push(bits | (x >> (n -= remaining)));
|
pascal@1161
|
273 while (n >= 8) buf.push((x >> (n -= 8)) & 255);
|
pascal@1161
|
274 bits = 0;
|
pascal@1161
|
275 remaining = 8;
|
pascal@1161
|
276 }
|
pascal@1161
|
277 if (n > 0) bits |= (x & ((1 << n) - 1)) << (remaining -= n);
|
pascal@1161
|
278 };
|
pascal@1161
|
279
|
pascal@1161
|
280 var nlenbits = ndatalenbits(ver, mode);
|
pascal@1161
|
281 pack(mode, 4);
|
pascal@1161
|
282 pack(datalen, nlenbits);
|
pascal@1161
|
283
|
pascal@1161
|
284 switch (mode) {
|
pascal@1161
|
285 case MODE_NUMERIC:
|
pascal@1161
|
286 for (var i = 2; i < datalen; i += 3) {
|
pascal@1161
|
287 pack(parseInt(data.substring(i-2,i+1), 10), 10);
|
pascal@1161
|
288 }
|
pascal@1161
|
289 pack(parseInt(data.substring(i-2), 10), [0,4,7][datalen%3]);
|
pascal@1161
|
290 break;
|
pascal@1161
|
291
|
pascal@1161
|
292 case MODE_ALPHANUMERIC:
|
pascal@1161
|
293 for (var i = 1; i < datalen; i += 2) {
|
pascal@1161
|
294 pack(ALPHANUMERIC_MAP[data.charAt(i-1)] * 45 +
|
pascal@1161
|
295 ALPHANUMERIC_MAP[data.charAt(i)], 11);
|
pascal@1161
|
296 }
|
pascal@1161
|
297 if (datalen % 2 == 1) {
|
pascal@1161
|
298 pack(ALPHANUMERIC_MAP[data.charAt(i-1)], 6);
|
pascal@1161
|
299 }
|
pascal@1161
|
300 break;
|
pascal@1161
|
301
|
pascal@1161
|
302 case MODE_OCTET:
|
pascal@1161
|
303 for (var i = 0; i < datalen; ++i) {
|
pascal@1161
|
304 pack(data[i], 8);
|
pascal@1161
|
305 }
|
pascal@1161
|
306 break;
|
pascal@1161
|
307 };
|
pascal@1161
|
308
|
pascal@1161
|
309 // final bits. it is possible that adding terminator causes the buffer
|
pascal@1161
|
310 // to overflow, but then the buffer truncated to the maximum size will
|
pascal@1161
|
311 // be valid as the truncated terminator mode bits and padding is
|
pascal@1161
|
312 // identical in appearance (cf. JIS X 0510:2004 sec 8.4.8).
|
pascal@1161
|
313 pack(MODE_TERMINATOR, 4);
|
pascal@1161
|
314 if (remaining < 8) buf.push(bits);
|
pascal@1161
|
315
|
pascal@1161
|
316 // the padding to fill up the remaining space. we should not add any
|
pascal@1161
|
317 // words when the overflow already occurred.
|
pascal@1161
|
318 while (buf.length + 1 < maxbuflen) buf.push(0xec, 0x11);
|
pascal@1161
|
319 if (buf.length < maxbuflen) buf.push(0xec);
|
pascal@1161
|
320 return buf;
|
pascal@1161
|
321 };
|
pascal@1161
|
322
|
pascal@1161
|
323 // calculates ECC code words for given code words and generator polynomial.
|
pascal@1161
|
324 //
|
pascal@1161
|
325 // this is quite similar to CRC calculation as both Reed-Solomon and CRC use
|
pascal@1161
|
326 // the certain kind of cyclic codes, which is effectively the division of
|
pascal@1161
|
327 // zero-augumented polynomial by the generator polynomial. the only difference
|
pascal@1161
|
328 // is that Reed-Solomon uses GF(2^8), instead of CRC's GF(2), and Reed-Solomon
|
pascal@1161
|
329 // uses the different generator polynomial than CRC's.
|
pascal@1161
|
330 var calculateecc = function(poly, genpoly) {
|
pascal@1161
|
331 var modulus = poly.slice(0);
|
pascal@1161
|
332 var polylen = poly.length, genpolylen = genpoly.length;
|
pascal@1161
|
333 for (var i = 0; i < genpolylen; ++i) modulus.push(0);
|
pascal@1161
|
334 for (var i = 0; i < polylen; ) {
|
pascal@1161
|
335 var quotient = GF256_INVMAP[modulus[i++]];
|
pascal@1161
|
336 if (quotient >= 0) {
|
pascal@1161
|
337 for (var j = 0; j < genpolylen; ++j) {
|
pascal@1161
|
338 modulus[i+j] ^= GF256_MAP[(quotient + genpoly[j]) % 255];
|
pascal@1161
|
339 }
|
pascal@1161
|
340 }
|
pascal@1161
|
341 }
|
pascal@1161
|
342 return modulus.slice(polylen);
|
pascal@1161
|
343 };
|
pascal@1161
|
344
|
pascal@1161
|
345 // auguments ECC code words to given code words. the resulting words are
|
pascal@1161
|
346 // ready to be encoded in the matrix.
|
pascal@1161
|
347 //
|
pascal@1161
|
348 // the much of actual augumenting procedure follows JIS X 0510:2004 sec 8.7.
|
pascal@1161
|
349 // the code is simplified using the fact that the size of each code & ECC
|
pascal@1161
|
350 // blocks is almost same; for example, when we have 4 blocks and 46 data words
|
pascal@1161
|
351 // the number of code words in those blocks are 11, 11, 12, 12 respectively.
|
pascal@1161
|
352 var augumenteccs = function(poly, nblocks, genpoly) {
|
pascal@1161
|
353 var subsizes = [];
|
pascal@1161
|
354 var subsize = (poly.length / nblocks) | 0, subsize0 = 0;
|
pascal@1161
|
355 var pivot = nblocks - poly.length % nblocks;
|
pascal@1161
|
356 for (var i = 0; i < pivot; ++i) {
|
pascal@1161
|
357 subsizes.push(subsize0);
|
pascal@1161
|
358 subsize0 += subsize;
|
pascal@1161
|
359 }
|
pascal@1161
|
360 for (var i = pivot; i < nblocks; ++i) {
|
pascal@1161
|
361 subsizes.push(subsize0);
|
pascal@1161
|
362 subsize0 += subsize+1;
|
pascal@1161
|
363 }
|
pascal@1161
|
364 subsizes.push(subsize0);
|
pascal@1161
|
365
|
pascal@1161
|
366 var eccs = [];
|
pascal@1161
|
367 for (var i = 0; i < nblocks; ++i) {
|
pascal@1161
|
368 eccs.push(calculateecc(poly.slice(subsizes[i], subsizes[i+1]), genpoly));
|
pascal@1161
|
369 }
|
pascal@1161
|
370
|
pascal@1161
|
371 var result = [];
|
pascal@1161
|
372 var nitemsperblock = (poly.length / nblocks) | 0;
|
pascal@1161
|
373 for (var i = 0; i < nitemsperblock; ++i) {
|
pascal@1161
|
374 for (var j = 0; j < nblocks; ++j) {
|
pascal@1161
|
375 result.push(poly[subsizes[j] + i]);
|
pascal@1161
|
376 }
|
pascal@1161
|
377 }
|
pascal@1161
|
378 for (var j = pivot; j < nblocks; ++j) {
|
pascal@1161
|
379 result.push(poly[subsizes[j+1] - 1]);
|
pascal@1161
|
380 }
|
pascal@1161
|
381 for (var i = 0; i < genpoly.length; ++i) {
|
pascal@1161
|
382 for (var j = 0; j < nblocks; ++j) {
|
pascal@1161
|
383 result.push(eccs[j][i]);
|
pascal@1161
|
384 }
|
pascal@1161
|
385 }
|
pascal@1161
|
386 return result;
|
pascal@1161
|
387 };
|
pascal@1161
|
388
|
pascal@1161
|
389 // auguments BCH(p+q,q) code to the polynomial over GF(2), given the proper
|
pascal@1161
|
390 // genpoly. the both input and output are in binary numbers, and unlike
|
pascal@1161
|
391 // calculateecc genpoly should include the 1 bit for the highest degree.
|
pascal@1161
|
392 //
|
pascal@1161
|
393 // actual polynomials used for this procedure are as follows:
|
pascal@1161
|
394 // - p=10, q=5, genpoly=x^10+x^8+x^5+x^4+x^2+x+1 (JIS X 0510:2004 Appendix C)
|
pascal@1161
|
395 // - p=18, q=6, genpoly=x^12+x^11+x^10+x^9+x^8+x^5+x^2+1 (ibid. Appendix D)
|
pascal@1161
|
396 var augumentbch = function(poly, p, genpoly, q) {
|
pascal@1161
|
397 var modulus = poly << q;
|
pascal@1161
|
398 for (var i = p - 1; i >= 0; --i) {
|
pascal@1161
|
399 if ((modulus >> (q+i)) & 1) modulus ^= genpoly << i;
|
pascal@1161
|
400 }
|
pascal@1161
|
401 return (poly << q) | modulus;
|
pascal@1161
|
402 };
|
pascal@1161
|
403
|
pascal@1161
|
404 // creates the base matrix for given version. it returns two matrices, one of
|
pascal@1161
|
405 // them is the actual one and the another represents the "reserved" portion
|
pascal@1161
|
406 // (e.g. finder and timing patterns) of the matrix.
|
pascal@1161
|
407 //
|
pascal@1161
|
408 // some entries in the matrix may be undefined, rather than 0 or 1. this is
|
pascal@1161
|
409 // intentional (no initialization needed!), and putdata below will fill
|
pascal@1161
|
410 // the remaining ones.
|
pascal@1161
|
411 var makebasematrix = function(ver) {
|
pascal@1161
|
412 var v = VERSIONS[ver], n = getsizebyver(ver);
|
pascal@1161
|
413 var matrix = [], reserved = [];
|
pascal@1161
|
414 for (var i = 0; i < n; ++i) {
|
pascal@1161
|
415 matrix.push([]);
|
pascal@1161
|
416 reserved.push([]);
|
pascal@1161
|
417 }
|
pascal@1161
|
418
|
pascal@1161
|
419 var blit = function(y, x, h, w, bits) {
|
pascal@1161
|
420 for (var i = 0; i < h; ++i) {
|
pascal@1161
|
421 for (var j = 0; j < w; ++j) {
|
pascal@1161
|
422 matrix[y+i][x+j] = (bits[i] >> j) & 1;
|
pascal@1161
|
423 reserved[y+i][x+j] = 1;
|
pascal@1161
|
424 }
|
pascal@1161
|
425 }
|
pascal@1161
|
426 };
|
pascal@1161
|
427
|
pascal@1161
|
428 // finder patterns and a part of timing patterns
|
pascal@1161
|
429 // will also mark the format information area (not yet written) as reserved.
|
pascal@1161
|
430 blit(0, 0, 9, 9, [0x7f, 0x41, 0x5d, 0x5d, 0x5d, 0x41, 0x17f, 0x00, 0x40]);
|
pascal@1161
|
431 blit(n-8, 0, 8, 9, [0x100, 0x7f, 0x41, 0x5d, 0x5d, 0x5d, 0x41, 0x7f]);
|
pascal@1161
|
432 blit(0, n-8, 9, 8, [0xfe, 0x82, 0xba, 0xba, 0xba, 0x82, 0xfe, 0x00, 0x00]);
|
pascal@1161
|
433
|
pascal@1161
|
434 // the rest of timing patterns
|
pascal@1161
|
435 for (var i = 9; i < n-8; ++i) {
|
pascal@1161
|
436 matrix[6][i] = matrix[i][6] = ~i & 1;
|
pascal@1161
|
437 reserved[6][i] = reserved[i][6] = 1;
|
pascal@1161
|
438 }
|
pascal@1161
|
439
|
pascal@1161
|
440 // alignment patterns
|
pascal@1161
|
441 var aligns = v[2], m = aligns.length;
|
pascal@1161
|
442 for (var i = 0; i < m; ++i) {
|
pascal@1161
|
443 var minj = (i==0 || i==m-1 ? 1 : 0), maxj = (i==0 ? m-1 : m);
|
pascal@1161
|
444 for (var j = minj; j < maxj; ++j) {
|
pascal@1161
|
445 blit(aligns[i], aligns[j], 5, 5, [0x1f, 0x11, 0x15, 0x11, 0x1f]);
|
pascal@1161
|
446 }
|
pascal@1161
|
447 }
|
pascal@1161
|
448
|
pascal@1161
|
449 // version information
|
pascal@1161
|
450 if (needsverinfo(ver)) {
|
pascal@1161
|
451 var code = augumentbch(ver, 6, 0x1f25, 12);
|
pascal@1161
|
452 var k = 0;
|
pascal@1161
|
453 for (var i = 0; i < 6; ++i) {
|
pascal@1161
|
454 for (var j = 0; j < 3; ++j) {
|
pascal@1161
|
455 matrix[i][(n-11)+j] = matrix[(n-11)+j][i] = (code >> k++) & 1;
|
pascal@1161
|
456 reserved[i][(n-11)+j] = reserved[(n-11)+j][i] = 1;
|
pascal@1161
|
457 }
|
pascal@1161
|
458 }
|
pascal@1161
|
459 }
|
pascal@1161
|
460
|
pascal@1161
|
461 return {matrix: matrix, reserved: reserved};
|
pascal@1161
|
462 };
|
pascal@1161
|
463
|
pascal@1161
|
464 // fills the data portion (i.e. unmarked in reserved) of the matrix with given
|
pascal@1161
|
465 // code words. the size of code words should be no more than available bits,
|
pascal@1161
|
466 // and remaining bits are padded to 0 (cf. JIS X 0510:2004 sec 8.7.3).
|
pascal@1161
|
467 var putdata = function(matrix, reserved, buf) {
|
pascal@1161
|
468 var n = matrix.length;
|
pascal@1161
|
469 var k = 0, dir = -1;
|
pascal@1161
|
470 for (var i = n-1; i >= 0; i -= 2) {
|
pascal@1161
|
471 if (i == 6) --i; // skip the entire timing pattern column
|
pascal@1161
|
472 var jj = (dir < 0 ? n-1 : 0);
|
pascal@1161
|
473 for (var j = 0; j < n; ++j) {
|
pascal@1161
|
474 for (var ii = i; ii > i-2; --ii) {
|
pascal@1161
|
475 if (!reserved[jj][ii]) {
|
pascal@1161
|
476 // may overflow, but (undefined >> x)
|
pascal@1161
|
477 // is 0 so it will auto-pad to zero.
|
pascal@1161
|
478 matrix[jj][ii] = (buf[k >> 3] >> (~k&7)) & 1;
|
pascal@1161
|
479 ++k;
|
pascal@1161
|
480 }
|
pascal@1161
|
481 }
|
pascal@1161
|
482 jj += dir;
|
pascal@1161
|
483 }
|
pascal@1161
|
484 dir = -dir;
|
pascal@1161
|
485 }
|
pascal@1161
|
486 return matrix;
|
pascal@1161
|
487 };
|
pascal@1161
|
488
|
pascal@1161
|
489 // XOR-masks the data portion of the matrix. repeating the call with the same
|
pascal@1161
|
490 // arguments will revert the prior call (convenient in the matrix evaluation).
|
pascal@1161
|
491 var maskdata = function(matrix, reserved, mask) {
|
pascal@1161
|
492 var maskf = MASKFUNCS[mask];
|
pascal@1161
|
493 var n = matrix.length;
|
pascal@1161
|
494 for (var i = 0; i < n; ++i) {
|
pascal@1161
|
495 for (var j = 0; j < n; ++j) {
|
pascal@1161
|
496 if (!reserved[i][j]) matrix[i][j] ^= maskf(i,j);
|
pascal@1161
|
497 }
|
pascal@1161
|
498 }
|
pascal@1161
|
499 return matrix;
|
pascal@1161
|
500 }
|
pascal@1161
|
501
|
pascal@1161
|
502 // puts the format information.
|
pascal@1161
|
503 var putformatinfo = function(matrix, reserved, ecclevel, mask) {
|
pascal@1161
|
504 var n = matrix.length;
|
pascal@1161
|
505 var code = augumentbch((ecclevel << 3) | mask, 5, 0x537, 10) ^ 0x5412;
|
pascal@1161
|
506 for (var i = 0; i < 15; ++i) {
|
pascal@1161
|
507 var r = [0,1,2,3,4,5,7,8,n-7,n-6,n-5,n-4,n-3,n-2,n-1][i];
|
pascal@1161
|
508 var c = [n-1,n-2,n-3,n-4,n-5,n-6,n-7,n-8,7,5,4,3,2,1,0][i];
|
pascal@1161
|
509 matrix[r][8] = matrix[8][c] = (code >> i) & 1;
|
pascal@1161
|
510 // we don't have to mark those bits reserved; always done
|
pascal@1161
|
511 // in makebasematrix above.
|
pascal@1161
|
512 }
|
pascal@1161
|
513 return matrix;
|
pascal@1161
|
514 };
|
pascal@1161
|
515
|
pascal@1161
|
516 // evaluates the resulting matrix and returns the score (lower is better).
|
pascal@1161
|
517 // (cf. JIS X 0510:2004 sec 8.8.2)
|
pascal@1161
|
518 //
|
pascal@1161
|
519 // the evaluation procedure tries to avoid the problematic patterns naturally
|
pascal@1161
|
520 // occuring from the original matrix. for example, it penaltizes the patterns
|
pascal@1161
|
521 // which just look like the finder pattern which will confuse the decoder.
|
pascal@1161
|
522 // we choose the mask which results in the lowest score among 8 possible ones.
|
pascal@1161
|
523 //
|
pascal@1161
|
524 // note: zxing seems to use the same procedure and in many cases its choice
|
pascal@1161
|
525 // agrees to ours, but sometimes it does not. practically it doesn't matter.
|
pascal@1161
|
526 var evaluatematrix = function(matrix) {
|
pascal@1161
|
527 // N1+(k-5) points for each consecutive row of k same-colored modules, where k >= 5. no overlapping row counts.
|
pascal@1161
|
528 var PENALTY_CONSECUTIVE = 3;
|
pascal@1161
|
529 // N2 points for each 2x2 block of same-colored modules. Overlapping block does count.
|
pascal@1161
|
530 var PENALTY_TWOBYTWO = 3;
|
pascal@1161
|
531 // N3 points for each pattern with >4W:1B:1W:3B:1W:1B or
|
pascal@1161
|
532 // 1B:1W:3B:1W:1B:>4W, or their multiples (e.g. highly unlikely, but 13W:3B:3W:9B:3W:3B counts).
|
pascal@1161
|
533 var PENALTY_FINDERLIKE = 40;
|
pascal@1161
|
534 // N4*k points for every (5*k)% deviation from 50% black density.
|
pascal@1161
|
535 // i.e. k=1 for 55~60% and 40~45%, k=2 for 60~65% and 35~40%, etc.
|
pascal@1161
|
536 var PENALTY_DENSITY = 10;
|
pascal@1161
|
537
|
pascal@1161
|
538 var evaluategroup = function(groups) { // assumes [W,B,W,B,W,...,B,W]
|
pascal@1161
|
539 var score = 0;
|
pascal@1161
|
540 for (var i = 0; i < groups.length; ++i) {
|
pascal@1161
|
541 if (groups[i] >= 5) score += PENALTY_CONSECUTIVE + (groups[i]-5);
|
pascal@1161
|
542 }
|
pascal@1161
|
543 for (var i = 5; i < groups.length; i += 2) {
|
pascal@1161
|
544 var p = groups[i];
|
pascal@1161
|
545 if (groups[i-1] == p && groups[i-2] == 3*p && groups[i-3] == p &&
|
pascal@1161
|
546 groups[i-4] == p && (groups[i-5] >= 4*p || groups[i+1] >= 4*p)) {
|
pascal@1161
|
547 // this part differs from zxing...
|
pascal@1161
|
548 score += PENALTY_FINDERLIKE;
|
pascal@1161
|
549 }
|
pascal@1161
|
550 }
|
pascal@1161
|
551 return score;
|
pascal@1161
|
552 };
|
pascal@1161
|
553
|
pascal@1161
|
554 var n = matrix.length;
|
pascal@1161
|
555 var score = 0, nblacks = 0;
|
pascal@1161
|
556 for (var i = 0; i < n; ++i) {
|
pascal@1161
|
557 var row = matrix[i];
|
pascal@1161
|
558 var groups;
|
pascal@1161
|
559
|
pascal@1161
|
560 // evaluate the current row
|
pascal@1161
|
561 groups = [0]; // the first empty group of white
|
pascal@1161
|
562 for (var j = 0; j < n; ) {
|
pascal@1161
|
563 var k;
|
pascal@1161
|
564 for (k = 0; j < n && row[j]; ++k) ++j;
|
pascal@1161
|
565 groups.push(k);
|
pascal@1161
|
566 for (k = 0; j < n && !row[j]; ++k) ++j;
|
pascal@1161
|
567 groups.push(k);
|
pascal@1161
|
568 }
|
pascal@1161
|
569 score += evaluategroup(groups);
|
pascal@1161
|
570
|
pascal@1161
|
571 // evaluate the current column
|
pascal@1161
|
572 groups = [0];
|
pascal@1161
|
573 for (var j = 0; j < n; ) {
|
pascal@1161
|
574 var k;
|
pascal@1161
|
575 for (k = 0; j < n && matrix[j][i]; ++k) ++j;
|
pascal@1161
|
576 groups.push(k);
|
pascal@1161
|
577 for (k = 0; j < n && !matrix[j][i]; ++k) ++j;
|
pascal@1161
|
578 groups.push(k);
|
pascal@1161
|
579 }
|
pascal@1161
|
580 score += evaluategroup(groups);
|
pascal@1161
|
581
|
pascal@1161
|
582 // check the 2x2 box and calculate the density
|
pascal@1161
|
583 var nextrow = matrix[i+1] || [];
|
pascal@1161
|
584 nblacks += row[0];
|
pascal@1161
|
585 for (var j = 1; j < n; ++j) {
|
pascal@1161
|
586 var p = row[j];
|
pascal@1161
|
587 nblacks += p;
|
pascal@1161
|
588 // at least comparison with next row should be strict...
|
pascal@1161
|
589 if (row[j-1] == p && nextrow[j] === p && nextrow[j-1] === p) {
|
pascal@1161
|
590 score += PENALTY_TWOBYTWO;
|
pascal@1161
|
591 }
|
pascal@1161
|
592 }
|
pascal@1161
|
593 }
|
pascal@1161
|
594
|
pascal@1161
|
595 score += PENALTY_DENSITY * ((Math.abs(nblacks / n / n - 0.5) / 0.05) | 0);
|
pascal@1161
|
596 return score;
|
pascal@1161
|
597 };
|
pascal@1161
|
598
|
pascal@1161
|
599 // returns the fully encoded QR code matrix which contains given data.
|
pascal@1161
|
600 // it also chooses the best mask automatically when mask is -1.
|
pascal@1161
|
601 var generate = function(data, ver, mode, ecclevel, mask) {
|
pascal@1161
|
602 var v = VERSIONS[ver];
|
pascal@1161
|
603 var buf = encode(ver, mode, data, ndatabits(ver, ecclevel) >> 3);
|
pascal@1161
|
604 buf = augumenteccs(buf, v[1][ecclevel], GF256_GENPOLY[v[0][ecclevel]]);
|
pascal@1161
|
605
|
pascal@1161
|
606 var result = makebasematrix(ver);
|
pascal@1161
|
607 var matrix = result.matrix, reserved = result.reserved;
|
pascal@1161
|
608 putdata(matrix, reserved, buf);
|
pascal@1161
|
609
|
pascal@1161
|
610 if (mask < 0) {
|
pascal@1161
|
611 // find the best mask
|
pascal@1161
|
612 maskdata(matrix, reserved, 0);
|
pascal@1161
|
613 putformatinfo(matrix, reserved, ecclevel, 0);
|
pascal@1161
|
614 var bestmask = 0, bestscore = evaluatematrix(matrix);
|
pascal@1161
|
615 maskdata(matrix, reserved, 0);
|
pascal@1161
|
616 for (mask = 1; mask < 8; ++mask) {
|
pascal@1161
|
617 maskdata(matrix, reserved, mask);
|
pascal@1161
|
618 putformatinfo(matrix, reserved, ecclevel, mask);
|
pascal@1161
|
619 var score = evaluatematrix(matrix);
|
pascal@1161
|
620 if (bestscore > score) {
|
pascal@1161
|
621 bestscore = score;
|
pascal@1161
|
622 bestmask = mask;
|
pascal@1161
|
623 }
|
pascal@1161
|
624 maskdata(matrix, reserved, mask);
|
pascal@1161
|
625 }
|
pascal@1161
|
626 mask = bestmask;
|
pascal@1161
|
627 }
|
pascal@1161
|
628
|
pascal@1161
|
629 maskdata(matrix, reserved, mask);
|
pascal@1161
|
630 putformatinfo(matrix, reserved, ecclevel, mask);
|
pascal@1161
|
631 return matrix;
|
pascal@1161
|
632 };
|
pascal@1161
|
633
|
pascal@1161
|
634 // the public interface is trivial; the options available are as follows:
|
pascal@1161
|
635 //
|
pascal@1161
|
636 // - version: an integer in [1,40]. when omitted (or -1) the smallest possible
|
pascal@1161
|
637 // version is chosen.
|
pascal@1161
|
638 // - mode: one of 'numeric', 'alphanumeric', 'octet'. when omitted the smallest
|
pascal@1161
|
639 // possible mode is chosen.
|
pascal@1161
|
640 // - ecclevel: one of 'L', 'M', 'Q', 'H'. defaults to 'L'.
|
pascal@1161
|
641 // - mask: an integer in [0,7]. when omitted (or -1) the best mask is chosen.
|
pascal@1161
|
642 //
|
pascal@1161
|
643 // for generate{HTML,PNG}:
|
pascal@1161
|
644 //
|
pascal@1161
|
645 // - modulesize: a number. this is a size of each modules in pixels, and
|
pascal@1161
|
646 // defaults to 5px.
|
pascal@1161
|
647 // - margin: a number. this is a size of margin in *modules*, and defaults to
|
pascal@1161
|
648 // 4 (white modules). the specficiation mandates the margin no less than 4
|
pascal@1161
|
649 // modules, so it is better not to alter this value unless you know what
|
pascal@1161
|
650 // you're doing.
|
pascal@1161
|
651 var QRCode = {
|
pascal@1161
|
652 'generate': function(data, options) {
|
pascal@1161
|
653 var MODES = {'numeric': MODE_NUMERIC, 'alphanumeric': MODE_ALPHANUMERIC,
|
pascal@1161
|
654 'octet': MODE_OCTET};
|
pascal@1161
|
655 var ECCLEVELS = {'L': ECCLEVEL_L, 'M': ECCLEVEL_M, 'Q': ECCLEVEL_Q,
|
pascal@1161
|
656 'H': ECCLEVEL_H};
|
pascal@1161
|
657
|
pascal@1161
|
658 options = options || {};
|
pascal@1161
|
659 var ver = options.version || -1;
|
pascal@1161
|
660 var ecclevel = ECCLEVELS[(options.ecclevel || 'L').toUpperCase()];
|
pascal@1161
|
661 var mode = options.mode ? MODES[options.mode.toLowerCase()] : -1;
|
pascal@1161
|
662 var mask = 'mask' in options ? options.mask : -1;
|
pascal@1161
|
663
|
pascal@1161
|
664 if (mode < 0) {
|
pascal@1161
|
665 if (typeof data === 'string') {
|
pascal@1161
|
666 if (data.match(NUMERIC_REGEXP)) {
|
pascal@1161
|
667 mode = MODE_NUMERIC;
|
pascal@1161
|
668 } else if (data.match(ALPHANUMERIC_OUT_REGEXP)) {
|
pascal@1161
|
669 // while encode supports case-insensitive
|
pascal@1161
|
670 // encoding, we restrict the data to be
|
pascal@1161
|
671 // uppercased when auto-selecting the mode.
|
pascal@1161
|
672 mode = MODE_ALPHANUMERIC;
|
pascal@1161
|
673 } else {
|
pascal@1161
|
674 mode = MODE_OCTET;
|
pascal@1161
|
675 }
|
pascal@1161
|
676 } else {
|
pascal@1161
|
677 mode = MODE_OCTET;
|
pascal@1161
|
678 }
|
pascal@1161
|
679 } else if (!(mode == MODE_NUMERIC || mode == MODE_ALPHANUMERIC ||
|
pascal@1161
|
680 mode == MODE_OCTET)) {
|
pascal@1161
|
681 throw 'invalid or unsupported mode';
|
pascal@1161
|
682 }
|
pascal@1161
|
683
|
pascal@1161
|
684 data = validatedata(mode, data);
|
pascal@1161
|
685 if (data === null) throw 'invalid data format';
|
pascal@1161
|
686
|
pascal@1161
|
687 if (ecclevel < 0 || ecclevel > 3) throw 'invalid ECC level';
|
pascal@1161
|
688
|
pascal@1161
|
689 if (ver < 0) {
|
pascal@1161
|
690 for (ver = 1; ver <= 40; ++ver) {
|
pascal@1161
|
691 if (data.length <= getmaxdatalen(ver, mode, ecclevel)) break;
|
pascal@1161
|
692 }
|
pascal@1161
|
693 if (ver > 40) throw 'too large data';
|
pascal@1161
|
694 } else if (ver < 1 || ver > 40) {
|
pascal@1161
|
695 throw 'invalid version';
|
pascal@1161
|
696 }
|
pascal@1161
|
697
|
pascal@1161
|
698 if (mask != -1 && (mask < 0 || mask > 8)) throw 'invalid mask';
|
pascal@1161
|
699
|
pascal@1161
|
700 return generate(data, ver, mode, ecclevel, mask);
|
pascal@1161
|
701 },
|
pascal@1161
|
702
|
pascal@1161
|
703
|
pascal@1161
|
704 'generatePNG': function(data, options) {
|
pascal@1161
|
705 options = options || {};
|
pascal@1161
|
706 var matrix = QRCode['generate'](data, options);
|
pascal@1161
|
707 var modsize = Math.max(options.modulesize || 5, 0.5);
|
pascal@1161
|
708 var margin = Math.max(options.margin || 4, 0.0);
|
pascal@1161
|
709 var n = matrix.length;
|
pascal@1161
|
710 var size = modsize * (n + 2 * margin);
|
pascal@1161
|
711
|
pascal@1161
|
712 var canvas = document.createElement('canvas'), context;
|
pascal@1161
|
713 canvas.width = canvas.height = size;
|
pascal@1161
|
714 context = canvas.getContext('2d');
|
pascal@1161
|
715 if (!context) throw 'canvas support is needed for PNG output';
|
pascal@1161
|
716
|
pascal@1161
|
717 context.fillStyle = '#fff';
|
pascal@1161
|
718 context.fillRect(0, 0, size, size);
|
pascal@1161
|
719 context.fillStyle = '#000';
|
pascal@1161
|
720 for (var i = 0; i < n; ++i) {
|
pascal@1161
|
721 for (var j = 0; j < n; ++j) {
|
pascal@1161
|
722 if (matrix[i][j]) {
|
pascal@1161
|
723 context.fillRect(modsize * (margin + j), modsize * (margin + i), modsize, modsize);
|
pascal@1161
|
724 }
|
pascal@1161
|
725 }
|
pascal@1161
|
726 }
|
pascal@1161
|
727 //context.fillText('evaluation: ' + evaluatematrix(matrix), 10, 10);
|
pascal@1161
|
728 return canvas.toDataURL();
|
pascal@1161
|
729 }
|
pascal@1161
|
730 };
|
pascal@1161
|
731
|
pascal@1161
|
732 return QRCode;
|
pascal@1161
|
733 })();
|