@vkontakte/vk-qr 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,194 +1,398 @@
1
- import { _ as _define_property__ } from "@swc/helpers/_/_define_property";
2
- const MULTI = 1;
3
- const QR_BORDER = 7;
4
- const TILE_SIZE = 96 * MULTI;
5
- const SMALL_QR_SIZE = 25;
6
- const getPixel = (x, y, size, modules, isLogoShowed)=>{
7
- if (x < QR_BORDER && y < QR_BORDER) return false;
8
- if (x >= size - QR_BORDER && y < QR_BORDER) return false;
9
- if (x < QR_BORDER && y >= size - QR_BORDER) return false;
10
- if (isLogoShowed) {
11
- let imageTiles = QR_BORDER + 2;
12
- if (size <= SMALL_QR_SIZE) imageTiles--;
13
- let paddingTiles = (size - 2 * QR_BORDER - imageTiles) / 2 - 1;
14
- if (size <= SMALL_QR_SIZE) paddingTiles++;
15
- if (x > QR_BORDER + paddingTiles && x < size - QR_BORDER - paddingTiles - 1 && y > QR_BORDER + paddingTiles && y < size - QR_BORDER - paddingTiles - 1) return false;
16
- return 0 <= x && x < size && 0 <= y && y < size && modules[y][x];
1
+ function isFinderPattern(figure, size) {
2
+ const { x, y, pixels } = figure;
3
+ const width = pixels[0]?.length ?? 0;
4
+ const height = pixels.length;
5
+ const right = x + width - 1;
6
+ const bottom = y + height - 1;
7
+ const finderRegions = [
8
+ [
9
+ 0,
10
+ 0,
11
+ 6,
12
+ 6
13
+ ],
14
+ [
15
+ size - 7,
16
+ 0,
17
+ size - 1,
18
+ 6
19
+ ],
20
+ [
21
+ 0,
22
+ size - 7,
23
+ 6,
24
+ size - 1
25
+ ]
26
+ ];
27
+ for (const [rx1, ry1, rx2, ry2] of finderRegions)if (x >= rx1 && y >= ry1 && right <= rx2 && bottom <= ry2) return true;
28
+ return false;
29
+ }
30
+ function extractFigures(image) {
31
+ const height = image.length;
32
+ const width = image[0]?.length ?? 0;
33
+ const visited = Array.from({
34
+ length: height
35
+ }, ()=>Array(width).fill(false));
36
+ const figures = [];
37
+ const directions = [
38
+ [
39
+ 0,
40
+ -1
41
+ ],
42
+ [
43
+ 1,
44
+ 0
45
+ ],
46
+ [
47
+ 0,
48
+ 1
49
+ ],
50
+ [
51
+ -1,
52
+ 0
53
+ ]
54
+ ];
55
+ function isInside(x, y) {
56
+ return y >= 0 && y < height && x >= 0 && x < width;
17
57
  }
18
- return modules[y]?.[x];
19
- };
20
- const getNeighbors = (x, y, size, modules, isLogoShowed = true)=>({
21
- l: getPixel(x - 1, y, size, modules, isLogoShowed),
22
- r: getPixel(x + 1, y, size, modules, isLogoShowed),
23
- t: getPixel(x, y - 1, size, modules, isLogoShowed),
24
- b: getPixel(x, y + 1, size, modules, isLogoShowed),
25
- current: getPixel(x, y, size, modules, isLogoShowed)
26
- });
27
- const convertSegmentsToSvgString = (qrCode, options)=>{
28
- if ('number' != typeof options.qrSize) throw new Error('Size should be a number');
29
- if ('string' != typeof options.className) throw new Error('Classname should be a string');
30
- const _2 = 2 * MULTI;
31
- const _12_7 = 12.7 * MULTI;
32
- const _12_8 = 12.8 * MULTI;
33
- const _14_7 = 14.7 * MULTI;
34
- const _14_8 = 14.8 * MULTI;
35
- const _15_9 = 15.9 * MULTI;
36
- const _28_6 = 28.6 * MULTI;
37
- const _30_5 = 30.5 * MULTI;
38
- const _84_7 = 84.7776815 * MULTI;
39
- const _87_3 = 87.3 * MULTI;
40
- const _71_4 = 71.4 * MULTI;
41
- const _42_9 = 42.9 * MULTI;
42
- const _87_2 = 87.2 * MULTI;
43
- const _85_2 = 85.2 * MULTI;
44
- const _85_3 = 85.3 * MULTI;
45
- const _69_5 = 69.5 * MULTI;
46
- const _98 = 98 * MULTI;
47
- const _100 = 100 * MULTI;
48
- const parts = [];
49
- let leftPadding = 0;
50
- let topPadding = 0;
51
- let xCoord = 0;
52
- let yCoord = 0;
53
- for(let y = 0; y < qrCode.size; y++){
54
- leftPadding = 0;
55
- for(let x = 0; x < qrCode.size; x++){
56
- xCoord = x + leftPadding;
57
- leftPadding += TILE_SIZE;
58
- yCoord = y + topPadding;
59
- const neighbors = getNeighbors(x, y, qrCode.size, qrCode.modules, options.isShowLogo);
60
- let path = '';
61
- let selector = '';
62
- if (neighbors.current) {
63
- selector = selector || neighbors.l || neighbors.r || neighbors.t || neighbors.b ? '' : 'empty';
64
- selector = !selector && neighbors.l && neighbors.r || neighbors.t && neighbors.b ? 'rect' : '';
65
- if (!selector) {
66
- selector += neighbors.l ? 'l' : neighbors.r ? 'r' : '';
67
- selector += neighbors.t ? 't' : neighbors.b ? 'b' : '';
68
- if (!selector) selector = 'empty';
58
+ for(let y = 0; y < height; y++)for(let x = 0; x < width; x++){
59
+ if (visited[y][x]) continue;
60
+ if (!image[y][x]) continue;
61
+ const cells = [
62
+ [
63
+ x,
64
+ y
65
+ ]
66
+ ];
67
+ visited[y][x] = true;
68
+ let minX = x;
69
+ let maxX = x;
70
+ let minY = y;
71
+ let maxY = y;
72
+ for(let head = 0; head < cells.length; head++){
73
+ const [cx, cy] = cells[head];
74
+ minX = Math.min(minX, cx);
75
+ maxX = Math.max(maxX, cx);
76
+ minY = Math.min(minY, cy);
77
+ maxY = Math.max(maxY, cy);
78
+ for (const [dx, dy] of directions){
79
+ const nx = cx + dx;
80
+ const ny = cy + dy;
81
+ if (isInside(nx, ny)) {
82
+ if (!visited[ny][nx]) {
83
+ if (image[ny][nx]) {
84
+ visited[ny][nx] = true;
85
+ cells.push([
86
+ nx,
87
+ ny
88
+ ]);
89
+ }
90
+ }
69
91
  }
70
- } else {
71
- selector = !selector && neighbors.l && neighbors.t && getPixel(x - 1, y - 1, qrCode.size, qrCode.modules, options.isShowLogo) ? 'n_lt' : '';
72
- selector = !selector && neighbors.l && neighbors.b && getPixel(x - 1, y + 1, qrCode.size, qrCode.modules, options.isShowLogo) ? 'n_lb' : '';
73
- selector = !selector && neighbors.r && neighbors.t && getPixel(x + 1, y - 1, qrCode.size, qrCode.modules, options.isShowLogo) ? 'n_rt' : '';
74
- selector = !selector && neighbors.r && neighbors.b && getPixel(x + 1, y + 1, qrCode.size, qrCode.modules, options.isShowLogo) ? 'n_rb' : '';
75
92
  }
76
- if (selector) {
77
- path = `<use xlink:href="#${selector}-${options.suffix}"/>`;
78
- parts.push(`<g transform="translate(${xCoord},${yCoord})">${path}</g>`);
93
+ }
94
+ const figureWidth = maxX - minX + 1;
95
+ const figureHeight = maxY - minY + 1;
96
+ const pixels = Array.from({
97
+ length: figureHeight
98
+ }, ()=>Array(figureWidth).fill(false));
99
+ for (const [cellX, cellY] of cells)pixels[cellY - minY][cellX - minX] = true;
100
+ figures.push({
101
+ x: minX,
102
+ y: minY,
103
+ pixels
104
+ });
105
+ }
106
+ return figures;
107
+ }
108
+ function figureToSvgPath(figure, options = {}) {
109
+ const pixelSize = options.pixelSize ?? 100;
110
+ const radius = options.radius ?? 35;
111
+ const contours = buildContours(getBoundaryEdges(figure, pixelSize));
112
+ const cornerRadius = createCornerRadiusResolver(figure, pixelSize, radius);
113
+ return contours.map((contour)=>contourToRoundedPath(contour, cornerRadius)).join(' ');
114
+ }
115
+ function hasPixel(pixels, x, y) {
116
+ return pixels[y]?.[x] ?? false;
117
+ }
118
+ function getBoundaryEdges(figure, pixelSize) {
119
+ const pixels = figure.pixels;
120
+ const height = pixels.length;
121
+ const width = pixels[0]?.length ?? 0;
122
+ const edges = [];
123
+ function px(x) {
124
+ return (figure.x + x) * pixelSize;
125
+ }
126
+ function py(y) {
127
+ return (figure.y + y) * pixelSize;
128
+ }
129
+ for(let y = 0; y < height; y++)for(let x = 0; x < width; x++){
130
+ if (!pixels[y][x]) continue;
131
+ const left = px(x);
132
+ const right = px(x + 1);
133
+ const top = py(y);
134
+ const bottom = py(y + 1);
135
+ if (!hasPixel(pixels, x, y - 1)) edges.push({
136
+ from: {
137
+ x: left,
138
+ y: top
139
+ },
140
+ to: {
141
+ x: right,
142
+ y: top
143
+ }
144
+ });
145
+ if (!hasPixel(pixels, x + 1, y)) edges.push({
146
+ from: {
147
+ x: right,
148
+ y: top
149
+ },
150
+ to: {
151
+ x: right,
152
+ y: bottom
153
+ }
154
+ });
155
+ if (!hasPixel(pixels, x, y + 1)) edges.push({
156
+ from: {
157
+ x: right,
158
+ y: bottom
159
+ },
160
+ to: {
161
+ x: left,
162
+ y: bottom
163
+ }
164
+ });
165
+ if (!hasPixel(pixels, x - 1, y)) edges.push({
166
+ from: {
167
+ x: left,
168
+ y: bottom
169
+ },
170
+ to: {
171
+ x: left,
172
+ y: top
173
+ }
174
+ });
175
+ }
176
+ return edges;
177
+ }
178
+ function hasFilledCorner(pixels, x, y, dx, dy) {
179
+ return hasPixel(pixels, x + dx, y) && hasPixel(pixels, x, y + dy) && hasPixel(pixels, x + dx, y + dy);
180
+ }
181
+ function createCornerRadiusResolver(figure, pixelSize, radius) {
182
+ return (prev, current, next)=>{
183
+ if (getTurn(prev, current, next) < 0) {
184
+ const x = Math.round(Math.min(prev.x, current.x, next.x) / pixelSize) - figure.x;
185
+ const y = Math.round(Math.min(prev.y, current.y, next.y) / pixelSize) - figure.y;
186
+ const topLeft = hasFilledCorner(figure.pixels, x, y, -1, -1);
187
+ const bottomLeft = hasFilledCorner(figure.pixels, x, y, -1, 1);
188
+ const topRight = hasFilledCorner(figure.pixels, x, y, 1, -1);
189
+ const bottomRight = hasFilledCorner(figure.pixels, x, y, 1, 1);
190
+ const roundsBottomRight = bottomRight && (!topRight || bottomLeft && !topLeft);
191
+ return roundsBottomRight && current.x === (figure.x + (x + 1)) * pixelSize && current.y === (figure.y + (y + 1)) * pixelSize ? radius : 0;
192
+ }
193
+ return radius;
194
+ };
195
+ }
196
+ function buildContours(edges) {
197
+ const edgeMap = new Map();
198
+ for (const edge of edges){
199
+ const key = pointKey(edge.from);
200
+ const list = edgeMap.get(key);
201
+ if (list) list.push(edge.to);
202
+ else edgeMap.set(key, [
203
+ edge.to
204
+ ]);
205
+ }
206
+ const contours = [];
207
+ while(true){
208
+ const startKey = edgeMap.keys().next().value;
209
+ if (void 0 === startKey) break;
210
+ const [x, y] = startKey.split(',').map(Number);
211
+ const start = {
212
+ x,
213
+ y
214
+ };
215
+ const contour = [
216
+ start
217
+ ];
218
+ let current = start;
219
+ while(true){
220
+ const candidates = edgeMap.get(pointKey(current));
221
+ if (!candidates || 0 === candidates.length) throw new Error('Broken contour: edge chain is not closed');
222
+ let nextIndex = 0;
223
+ if (candidates.length > 1 && contour.length > 1) {
224
+ const prev = contour[contour.length - 2];
225
+ const rightTurn = candidates.findIndex((candidate)=>getTurn(prev, current, candidate) > 0);
226
+ if (-1 !== rightTurn) nextIndex = rightTurn;
79
227
  }
228
+ const [next] = candidates.splice(nextIndex, 1);
229
+ if (0 === candidates.length) edgeMap.delete(pointKey(current));
230
+ if (samePoint(next, start)) break;
231
+ contour.push(next);
232
+ current = next;
80
233
  }
81
- topPadding += TILE_SIZE;
234
+ contours.push(contour);
235
+ }
236
+ return contours;
237
+ }
238
+ function contourToRoundedPath(contour, cornerRadius) {
239
+ const n = contour.length;
240
+ if (0 === n) return '';
241
+ const commands = [];
242
+ for(let i = 0; i < n; i++){
243
+ const prev = contour[(i - 1 + n) % n];
244
+ const current = contour[i];
245
+ const next = contour[(i + 1) % n];
246
+ const incomingLength = distance(current, prev);
247
+ const outgoingLength = distance(current, next);
248
+ const r = Math.min(cornerRadius(prev, current, next), incomingLength / 2, outgoingLength / 2);
249
+ const beforeCorner = moveTowards(current, prev, r);
250
+ const afterCorner = moveTowards(current, next, r);
251
+ commands.push(`${0 === i ? 'M' : 'L'} ${beforeCorner.x} ${beforeCorner.y}`);
252
+ if (r > 0) commands.push(`Q ${current.x} ${current.y} ${afterCorner.x} ${afterCorner.y}`);
82
253
  }
254
+ commands.push('Z');
255
+ return commands.join(' ');
256
+ }
257
+ function moveTowards(from, to, distanceValue) {
258
+ const length = distance(from, to);
259
+ if (0 === length) return {
260
+ ...from
261
+ };
262
+ const ratio = distanceValue / length;
263
+ return {
264
+ x: from.x + (to.x - from.x) * ratio,
265
+ y: from.y + (to.y - from.y) * ratio
266
+ };
267
+ }
268
+ function distance(a, b) {
269
+ return Math.hypot(a.x - b.x, a.y - b.y);
270
+ }
271
+ function pointKey(point) {
272
+ return `${point.x},${point.y}`;
273
+ }
274
+ function samePoint(a, b) {
275
+ return a.x === b.x && a.y === b.y;
276
+ }
277
+ function getTurn(prev, current, next) {
278
+ return (current.x - prev.x) * (next.y - current.y) - (current.y - prev.y) * (next.x - current.x);
279
+ }
280
+ const INC_TILE_SIZE = 100;
281
+ function convertSegmentsToSvgString(qrCode, options) {
282
+ if ('number' != typeof options.qrSize) throw new Error('Size should be a number');
283
+ if ('string' != typeof options.className) throw new Error('Classname should be a string');
284
+ const background = getBackgroundLayout(qrCode.size, options);
285
+ const parts = renderFinderPatterns(qrCode.size, options);
286
+ if (options.isShowLogo) parts.push(renderLogo(qrCode.size, options));
287
+ return `
288
+ <svg
289
+ version="1.1"
290
+ viewBox="${-background.padding} ${-background.padding} ${background.size} ${background.size}"
291
+ width="${options.qrSize}px"
292
+ height="${options.qrSize}px"
293
+ style="display: block"
294
+ ${options.className ? `class="${options.className}"` : ''}
295
+ xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
296
+ >
297
+ ${renderDefinitions(options)}
298
+
299
+ ${renderBackground(background, options)}
300
+
301
+ <g${background.hideQr ? ' display="none"' : ''}>
302
+ ${renderModules(qrCode, options)}
303
+ ${parts.join('\n')}
304
+ </g>
305
+
306
+ </svg>`;
307
+ }
308
+ function getVisibleModules(qrCode, isShowLogo) {
309
+ if (!isShowLogo) return qrCode.modules;
310
+ const logoAreaSize = qrCode.size <= 25 ? 7 : 9;
311
+ const logoAreaStart = (qrCode.size - logoAreaSize) / 2;
312
+ return qrCode.modules.map((row, y)=>row.map((pixel, x)=>pixel && !(x >= logoAreaStart && x < logoAreaStart + logoAreaSize && y >= logoAreaStart && y < logoAreaStart + logoAreaSize)));
313
+ }
314
+ function renderModules(qrCode, options) {
315
+ return extractFigures(getVisibleModules(qrCode, options.isShowLogo)).filter((figure)=>!isFinderPattern(figure, qrCode.size)).map((figure)=>`
316
+ <path
317
+ d="${figureToSvgPath(figure)}"
318
+ fill="${options.foregroundColor}"
319
+ fill-rule="evenodd"
320
+ />`).join('');
321
+ }
322
+ function renderFinderPatterns(size, options) {
323
+ const pointPosition = (size - 7) * 100;
324
+ return [
325
+ `<use fill-rule="evenodd" transform="translate(0,0)" xlink:href="#point-${options.suffix}"/>`,
326
+ `<use fill-rule="evenodd" transform="translate(${pointPosition},0)" xlink:href="#point-${options.suffix}"/>`,
327
+ `<use fill-rule="evenodd" transform="translate(0,${pointPosition})" xlink:href="#point-${options.suffix}"/>`
328
+ ];
329
+ }
330
+ function renderLogo(size, options) {
83
331
  let scale = '';
84
- let position = (qrCode.size - 3 * QR_BORDER) / 2 * TILE_SIZE + TILE_SIZE * QR_BORDER - 10;
85
- if (qrCode.size <= SMALL_QR_SIZE) {
332
+ let position = (size - 21) / 2 * 100 + 700 - 10;
333
+ if (size <= 25) {
86
334
  scale = 'scale(0.85)';
87
335
  position += 50;
88
336
  }
89
- const pointPosition = (qrCode.size - QR_BORDER) * options.incTileSize;
90
- parts.push(`<use fill-rule="evenodd" transform="translate(0,0)" xlink:href="#point-${options.suffix}"/>`);
91
- parts.push(`<use fill-rule="evenodd" transform="translate(${pointPosition},0)" xlink:href="#point-${options.suffix}"/>`);
92
- parts.push(`<use fill-rule="evenodd" transform="translate(0,${pointPosition})" xlink:href="#point-${options.suffix}"/>`);
93
- if (options.isShowLogo) if (options.logoData) parts.push(`
337
+ if (!options.logoData) return `
338
+ <use style="width: 750px; height: 750px;" width="750" height="750"
339
+ fill="none"
340
+ fill-rule="evenodd"
341
+ transform="translate(${position},${position}) ${scale}" xlink:href="#vk_logo-${options.suffix}"
342
+ />
343
+ `;
344
+ return `
94
345
  <image
95
346
  preserveAspectRatio="xMidYMid slice" clip-path="url(#logo-mask-${options.suffix})"
96
347
  style="width: 750px; height: 750px;" width="750" height="750"
97
348
  transform="translate(${position},${position}) ${scale}"
98
349
  xlink:href="${options.logoData}"
99
350
  />
100
- `);
101
- else parts.push(`
102
- <use style="width: 750px; height: 750px;" width="750" height="750"
103
- fill="none"
104
- fill-rule="evenodd"
105
- transform="translate(${position},${position}) ${scale}" xlink:href="#vk_logo-${options.suffix}"
106
- />
107
- `);
108
- const backgroundSize = 99 * qrCode.size;
109
- let qrBackground = '';
110
- let qrTransform = 'translate(0,0)';
111
- if (options.isShowBackground) {
112
- const qrScale = (options.qrSize - 40) / options.qrSize;
113
- const padding = backgroundSize / options.qrSize * 21;
114
- const radius = Math.ceil(backgroundSize / (options.qrSize / 36));
115
- qrBackground = `
351
+ `;
352
+ }
353
+ function getBackgroundLayout(size, options) {
354
+ const contentSize = 100 * size;
355
+ if (!options.isShowBackground) return {
356
+ size: contentSize,
357
+ padding: 0,
358
+ radius: 0,
359
+ hideQr: false
360
+ };
361
+ const frameWidth = Math.min(options.backgroundPadding, options.qrSize / 2);
362
+ const contentWidth = options.qrSize - 2 * frameWidth;
363
+ const hideQr = contentWidth <= 0;
364
+ const backgroundSize = hideQr ? contentSize : contentSize * options.qrSize / contentWidth;
365
+ const padding = (backgroundSize - contentSize) / 2;
366
+ const radius = Math.min(options.backgroundRadius, options.qrSize / 2) / options.qrSize * backgroundSize;
367
+ return {
368
+ size: backgroundSize,
369
+ padding,
370
+ radius,
371
+ hideQr
372
+ };
373
+ }
374
+ function renderBackground(layout, options) {
375
+ if (!options.isShowBackground) return '';
376
+ const { size, padding, radius } = layout;
377
+ return `
116
378
  <rect
117
- x="0"
118
- width="${backgroundSize}"
119
- height="${backgroundSize}"
379
+ x="${-padding}"
380
+ y="${-padding}"
381
+ width="${size}"
382
+ height="${size}"
120
383
  rx="${radius}"
121
384
  fill="${options.backgroundColor}"
122
385
  />`;
123
- qrTransform = `translate(${padding}, ${padding}) scale(${qrScale})`;
124
- }
125
- const oneSide = `M0,0 L66,0 C${_84_7},-3.44940413e-15 ${_100},15.2223185 ${_100},34 L${_100},66 C${_100},${_84_7} \
126
- ${_84_7},${_100} 66,${_100} L0,${_100} L0,0 Z`;
127
- const twoSides = `M0,0 L${_100},0 L${_100},66 C${_100},${_84_7} ${_84_7},${_100} 66,${_100} L0,${_100} L0,0 Z`;
128
- return `
129
- <svg
130
- version="1.1"
131
- viewBox="0 0 ${backgroundSize} ${backgroundSize}"
132
- width="${options.qrSize}px"
133
- height="${options.qrSize}px"
134
- ${options.className ? `class="${options.className}"` : ''}
135
- xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
136
- >
137
- <defs>
138
- <rect id="rect-${options.suffix}" width="100" height="100" fill="${options.foregroundColor}"/>
139
- <path
140
- id="empty-${options.suffix}"
141
- d="M0,${_28_6}v${_42_9}C0,${_87_3},${_12_8},${_100},${_28_6},${_100}h${_42_9}c${_15_9},0,${_28_6}-${_12_8},\
142
- ${_28_6}-${_28_6}V${_28_6}C${_100},${_12_7},${_87_2},0,${_71_4},0H${_28_6} C${_12_8},0,0,${_12_8},0,${_28_6}z"
143
- fill="${options.foregroundColor}"
144
- />
145
- <path id="b-${options.suffix}" d="${oneSide}" transform="rotate(-90 50 50)" fill="${options.foregroundColor}"/>
146
- <path id="r-${options.suffix}" d="${oneSide}" transform="rotate(-180 50 50)" fill="${options.foregroundColor}"/>
147
- <path id="l-${options.suffix}" d="${oneSide}" fill="${options.foregroundColor}"/>
148
- <path id="t-${options.suffix}" d="${oneSide}" transform="rotate(90 50 50)" fill="${options.foregroundColor}"/>
149
- <path id="l-${options.suffix}" d="${twoSides}" transform="rotate(-90 50 50)" fill="${options.foregroundColor}"/>
150
- <path id="lt-${options.suffix}" d="${twoSides}" fill="${options.foregroundColor}"/>
151
- <path id="lb-${options.suffix}" d="${twoSides}" transform="rotate(-90 50 50)" fill="${options.foregroundColor}"/>
152
- <path id="rb-${options.suffix}" d="${twoSides}" transform="rotate(-180 50 50)" fill="${options.foregroundColor}"/>
153
- <path id="rt-${options.suffix}" d="${twoSides}" transform="rotate(90 50 50)" fill="${options.foregroundColor}"/>
154
- <path
155
- id="n_lt-${options.suffix}"
156
- d="M${_30_5},${_2}V0H0v${_30_5}h${_2}C${_2},${_14_7},${_14_8},${_2},${_30_5},${_2}z"
157
- fill="${options.foregroundColor}"
158
- />
159
- <path
160
- id="n_lb-${options.suffix}"
161
- d="M${_2},${_69_5}H0V${_100}h${_30_5}v-${_2}C${_14_7},${_98},${_2},${_85_2},${_2},${_69_5}z"
162
- fill="${options.foregroundColor}"
163
- />
164
- <path
165
- id="n_rt-${options.suffix}"
166
- d="M${_98},${_30_5}h${_2}V0H${_69_5}v${_2}C${_85_3},${_2},${_98},${_14_8},${_98},${_30_5}z"
167
- fill="${options.foregroundColor}"
168
- />
169
- <path id="n_rb-${options.suffix}"
170
- d="M${_69_5},${_98}v${_2}H${_100}V${_69_5}h-${_2}C${_98},${_85_3},${_85_2},${_98},${_69_5},${_98}z"
171
- fill="${options.foregroundColor}"
172
- />
173
- <path
386
+ }
387
+ function renderDefinitions(options) {
388
+ return `<defs>
389
+ <g
174
390
  id="point-${options.suffix}"
175
391
  fill="${options.foregroundColor}"
176
- d="M600.001786,457.329333 L600.001786,242.658167 C600.001786,147.372368 587.039517,124.122784 \
177
- 581.464617,118.535383 C575.877216,112.960483 552.627632,99.9982143 457.329333,99.9982143 \
178
- L242.670667,99.9982143 C147.372368,99.9982143 124.122784,112.960483 118.547883,118.535383 \
179
- C112.972983,124.122784 99.9982143,147.372368 99.9982143,242.658167 L99.9982143,457.329333 \
180
- C99.9982143,552.627632 112.972983,575.877216 118.547883,581.464617 C124.122784,587.027017 \
181
- 147.372368,600.001786 242.670667,600.001786 L457.329333,600.001786 C552.627632,600.001786 \
182
- 575.877216,587.027017 581.464617,581.464617 C587.039517,575.877216 600.001786,552.627632 \
183
- 600.001786,457.329333 Z M457.329333,0 C653.338333,0 700,46.6616668 700,242.658167 C700,438.667167 \
184
- 700,261.332833 700,457.329333 C700,653.338333 653.338333,700 457.329333,700 C261.332833,700 438.667167,700 \
185
- 242.670667,700 C46.6616668,700 0,653.338333 0,457.329333 C0,261.332833 0,352.118712 0,242.658167 \
186
- C0,46.6616668 46.6616668,0 242.670667,0 C438.667167,0 261.332833,0 457.329333,0 Z M395.996667,200 \
187
- C480.004166,200 500,220.008332 500,303.990835 C500,387.998334 500,312.001666 500,395.996667 \
188
- C500,479.991668 480.004166,500 395.996667,500 C312.001666,500 387.998334,500 304.003333,500 C220.008332,500 \
189
- 200,479.991668 200,395.996667 C200,312.001666 200,350.906061 200,303.990835 C200,220.008332 220.008332,200 \
190
- 304.003333,200 C387.998334,200 312.001666,200 395.996667,200 Z"
191
- />
392
+ >
393
+ <rect width="300" height="300" x="200" y="200" rx="67" />
394
+ <rect width="600" height="600" x="50" y="50" rx="134" stroke-width="100" fill="none" stroke="${options.foregroundColor}" />
395
+ </g>
192
396
  <g id="vk_logo-${options.suffix}">
193
397
  <path
194
398
  fill="${options.logoColor}"
@@ -209,358 +413,511 @@ C500,479.991668 480.004166,500 395.996667,500 C312.001666,500 387.998334,500 304
209
413
  <clipPath id="logo-mask-${options.suffix}">
210
414
  <rect x="0" y="0" width="750" height="750" />
211
415
  </clipPath>
212
- </defs>
213
-
214
- ${qrBackground}
215
-
216
- <g transform="${qrTransform}">
217
- ${parts.join('\n')}
218
- </g>
219
- </svg>`;
220
- };
221
- class QrCode {
222
- static encodeText(text, ecl) {
223
- const segs = QrSegment.makeSegments(text);
224
- return QrCode.encodeSegments(segs, ecl);
416
+ </defs>`;
417
+ }
418
+ const QrCodeEcc = {
419
+ LOW: {
420
+ ordinal: 0,
421
+ formatBits: 1
422
+ },
423
+ MEDIUM: {
424
+ ordinal: 1,
425
+ formatBits: 0
426
+ },
427
+ QUARTILE: {
428
+ ordinal: 2,
429
+ formatBits: 3
430
+ },
431
+ HIGH: {
432
+ ordinal: 3,
433
+ formatBits: 2
225
434
  }
226
- static encodeBinary(data, ecl) {
227
- const seg = QrSegment.makeBytes(data);
228
- return QrCode.encodeSegments([
229
- seg
230
- ], ecl);
435
+ };
436
+ const QrSegmentMode = {
437
+ NUMERIC: {
438
+ modeBits: 0x1,
439
+ numBitsCharCount: [
440
+ 10,
441
+ 12,
442
+ 14
443
+ ]
444
+ },
445
+ ALPHANUMERIC: {
446
+ modeBits: 0x2,
447
+ numBitsCharCount: [
448
+ 9,
449
+ 11,
450
+ 13
451
+ ]
452
+ },
453
+ BYTE: {
454
+ modeBits: 0x4,
455
+ numBitsCharCount: [
456
+ 8,
457
+ 16,
458
+ 16
459
+ ]
460
+ },
461
+ KANJI: {
462
+ modeBits: 0x8,
463
+ numBitsCharCount: [
464
+ 8,
465
+ 10,
466
+ 12
467
+ ]
468
+ },
469
+ ECI: {
470
+ modeBits: 0x7,
471
+ numBitsCharCount: [
472
+ 0,
473
+ 0,
474
+ 0
475
+ ]
231
476
  }
232
- static encodeSegments(segs, ecl, minVersion = 1, maxVersion = 40, mask = -1, boostEcl = true) {
233
- if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION) || mask < -1 || mask > 7) throw 'Invalid value';
234
- let version;
235
- let dataUsedBits;
236
- for(version = minVersion;; version++){
237
- const dataCapacityBits = 8 * QrCode.getNumDataCodewords(version, ecl);
238
- const usedBits = QrSegment.getTotalBits(segs, version);
239
- if (usedBits <= dataCapacityBits) {
240
- dataUsedBits = usedBits;
241
- break;
242
- }
243
- if (version >= maxVersion) throw 'Data too long';
244
- }
245
- for (const newEcl of [
246
- QrCodeEcc.MEDIUM,
247
- QrCodeEcc.QUARTILE,
248
- QrCodeEcc.HIGH
249
- ])if (boostEcl && dataUsedBits <= 8 * QrCode.getNumDataCodewords(version, newEcl)) ecl = newEcl;
250
- const bb = new BitBuffer();
251
- for (const seg of segs){
252
- bb.appendBits(seg.mode.modeBits, 4);
253
- bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version));
254
- for (const b of seg.getData())bb.array.push(b);
477
+ };
478
+ function numCharCountBits(mode, ver) {
479
+ return mode.numBitsCharCount[Math.floor((ver + 7) / 17)];
480
+ }
481
+ function encodeSegments(segs, ecl, minVersion = 1, maxVersion = 40, mask = -1, boostEcl = true) {
482
+ if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) throw 'Invalid value';
483
+ let version;
484
+ let dataUsedBits;
485
+ for(version = minVersion;; version++){
486
+ const dataCapacityBits = 8 * getNumDataCodewords(version, ecl);
487
+ const usedBits = getTotalBits(segs, version);
488
+ if (usedBits <= dataCapacityBits) {
489
+ dataUsedBits = usedBits;
490
+ break;
255
491
  }
256
- if (bb.array.length !== dataUsedBits) throw 'Assertion error';
257
- const dataCapacityBits = 8 * QrCode.getNumDataCodewords(version, ecl);
258
- if (bb.array.length > dataCapacityBits) throw 'Assertion error';
259
- bb.appendBits(0, Math.min(4, dataCapacityBits - bb.array.length));
260
- bb.appendBits(0, (8 - bb.array.length % 8) % 8);
261
- if (bb.array.length % 8 !== 0) throw 'Assertion error';
262
- for(let padByte = 0xec; bb.array.length < dataCapacityBits; padByte ^= 253)bb.appendBits(padByte, 8);
263
- const dataCodewords = [];
264
- while(8 * dataCodewords.length < bb.array.length)dataCodewords.push(0);
265
- bb.array.forEach((b, i)=>{
266
- dataCodewords[i >>> 3] |= b << 7 - (7 & i);
267
- });
268
- return new QrCode(version, ecl, dataCodewords, mask);
492
+ if (version >= maxVersion) throw 'Data too long';
493
+ }
494
+ let finalEcl = ecl;
495
+ for (const newEcl of [
496
+ QrCodeEcc.MEDIUM,
497
+ QrCodeEcc.QUARTILE,
498
+ QrCodeEcc.HIGH
499
+ ])if (boostEcl && dataUsedBits <= 8 * getNumDataCodewords(version, newEcl)) finalEcl = newEcl;
500
+ const bb = createBitBuffer();
501
+ for (const seg of segs){
502
+ bb.appendBits(seg.mode.modeBits, 4);
503
+ bb.appendBits(seg.numChars, numCharCountBits(seg.mode, version));
504
+ for (const b of seg.getData())bb.array.push(b);
269
505
  }
270
- getModule(x, y) {
271
- return 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x];
506
+ if (bb.array.length !== dataUsedBits) throw 'Assertion error';
507
+ const dataCapacityBits = 8 * getNumDataCodewords(version, finalEcl);
508
+ if (bb.array.length > dataCapacityBits) throw 'Assertion error';
509
+ bb.appendBits(0, Math.min(4, dataCapacityBits - bb.array.length));
510
+ bb.appendBits(0, (8 - bb.array.length % 8) % 8);
511
+ if (bb.array.length % 8 !== 0) throw 'Assertion error';
512
+ for(let padByte = 0xec; bb.array.length < dataCapacityBits; padByte ^= 253)bb.appendBits(padByte, 8);
513
+ const dataCodewords = [];
514
+ while(8 * dataCodewords.length < bb.array.length)dataCodewords.push(0);
515
+ bb.array.forEach((b, i)=>{
516
+ dataCodewords[i >>> 3] |= b << 7 - (7 & i);
517
+ });
518
+ return createQrCode(version, finalEcl, dataCodewords, mask);
519
+ }
520
+ function createQrCode(version, errorCorrectionLevel, dataCodewords, mask) {
521
+ if (version < MIN_VERSION || version > MAX_VERSION) throw 'Version value out of range';
522
+ if (mask < -1 || mask > 7) throw 'Mask value out of range';
523
+ const size = 4 * version + 17;
524
+ const row = [];
525
+ for(let i = 0; i < size; i++)row.push(false);
526
+ const modules = [];
527
+ const isFunction = [];
528
+ for(let i = 0; i < size; i++){
529
+ modules.push(row.slice());
530
+ isFunction.push(row.slice());
272
531
  }
273
- toSvgString(border) {
274
- if (border < 0) throw 'Border must be non-negative';
275
- const parts = [];
276
- for(let y = 0; y < this.size; y++)for(let x = 0; x < this.size; x++)if (this.getModule(x, y)) parts.push(`M${x + border},${y + border}h1v1h-1z`);
277
- return `<?xml version="1.0" encoding="UTF-8"?>
278
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
279
- <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 ${this.size + 2 * border} ${this.size + 2 * border}" stroke="none">
280
- <rect width="100%" height="100%" fill="#FFFFFF"/>
281
- <path d="${parts.join(' ')}" fill="#000000"/>
282
- </svg>
283
- `;
532
+ const setFunctionModule = (x, y, isBlack)=>{
533
+ modules[y][x] = isBlack;
534
+ isFunction[y][x] = true;
535
+ };
536
+ for(let i = 0; i < size; i++){
537
+ setFunctionModule(6, i, i % 2 === 0);
538
+ setFunctionModule(i, 6, i % 2 === 0);
284
539
  }
285
- drawFunctionPatterns() {
286
- for(let i = 0; i < this.size; i++){
287
- this.setFunctionModule(6, i, i % 2 === 0);
288
- this.setFunctionModule(i, 6, i % 2 === 0);
540
+ const drawFinderPattern = (x, y)=>{
541
+ for(let dy = -4; dy <= 4; dy++)for(let dx = -4; dx <= 4; dx++){
542
+ const dist = Math.max(Math.abs(dx), Math.abs(dy));
543
+ const xx = x + dx;
544
+ const yy = y + dy;
545
+ if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, 2 !== dist && 4 !== dist);
289
546
  }
290
- this.drawFinderPattern(3, 3);
291
- this.drawFinderPattern(this.size - 4, 3);
292
- this.drawFinderPattern(3, this.size - 4);
293
- const alignPatPos = this.getAlignmentPatternPositions();
294
- const numAlign = alignPatPos.length;
295
- for(let i = 0; i < numAlign; i++)for(let j = 0; j < numAlign; j++)if (!(0 === i && 0 === j || 0 === i && j === numAlign - 1 || i === numAlign - 1 && 0 === j)) this.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);
296
- this.drawFormatBits(0);
297
- this.drawVersion();
547
+ };
548
+ drawFinderPattern(3, 3);
549
+ drawFinderPattern(size - 4, 3);
550
+ drawFinderPattern(3, size - 4);
551
+ const alignPatPos = getAlignmentPatternPositions(version, size);
552
+ const numAlign = alignPatPos.length;
553
+ for(let i = 0; i < numAlign; i++)for(let j = 0; j < numAlign; j++)if (!(0 === i && 0 === j || 0 === i && j === numAlign - 1 || i === numAlign - 1 && 0 === j)) {
554
+ const drawAlignmentPattern = (x, y)=>{
555
+ for(let dy = -2; dy <= 2; dy++)for(let dx = -2; dx <= 2; dx++)setFunctionModule(x + dx, y + dy, 1 !== Math.max(Math.abs(dx), Math.abs(dy)));
556
+ };
557
+ drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);
298
558
  }
299
- drawFormatBits(mask) {
300
- const data = this.errorCorrectionLevel.formatBits << 3 | mask;
559
+ const drawFormatBits = (ecl, msk)=>{
560
+ const data = ecl.formatBits << 3 | msk;
301
561
  let rem = data;
302
562
  for(let i = 0; i < 10; i++)rem = rem << 1 ^ (rem >>> 9) * 0x537;
303
563
  const bits = (data << 10 | rem) ^ 0x5412;
304
564
  if (bits >>> 15 !== 0) throw 'Assertion error';
305
- for(let i = 0; i <= 5; i++)this.setFunctionModule(8, i, getBit(bits, i));
306
- this.setFunctionModule(8, 7, getBit(bits, 6));
307
- this.setFunctionModule(8, 8, getBit(bits, 7));
308
- this.setFunctionModule(7, 8, getBit(bits, 8));
309
- for(let i = 9; i < 15; i++)this.setFunctionModule(14 - i, 8, getBit(bits, i));
310
- for(let i = 0; i < 8; i++)this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i));
311
- for(let i = 8; i < 15; i++)this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i));
312
- this.setFunctionModule(8, this.size - 8, true);
313
- }
314
- drawVersion() {
315
- if (this.version < 7) return;
316
- let rem = this.version;
565
+ for(let i = 0; i <= 5; i++)setFunctionModule(8, i, getBit(bits, i));
566
+ setFunctionModule(8, 7, getBit(bits, 6));
567
+ setFunctionModule(8, 8, getBit(bits, 7));
568
+ setFunctionModule(7, 8, getBit(bits, 8));
569
+ for(let i = 9; i < 15; i++)setFunctionModule(14 - i, 8, getBit(bits, i));
570
+ for(let i = 0; i < 8; i++)setFunctionModule(size - 1 - i, 8, getBit(bits, i));
571
+ for(let i = 8; i < 15; i++)setFunctionModule(8, size - 15 + i, getBit(bits, i));
572
+ setFunctionModule(8, size - 8, true);
573
+ };
574
+ drawFormatBits(errorCorrectionLevel, 0);
575
+ if (version >= 7) {
576
+ let rem = version;
317
577
  for(let i = 0; i < 12; i++)rem = rem << 1 ^ (rem >>> 11) * 0x1f25;
318
- const bits = this.version << 12 | rem;
578
+ const bits = version << 12 | rem;
319
579
  if (bits >>> 18 !== 0) throw 'Assertion error';
320
580
  for(let i = 0; i < 18; i++){
321
581
  const color = getBit(bits, i);
322
- const a = this.size - 11 + i % 3;
582
+ const a = size - 11 + i % 3;
323
583
  const b = Math.floor(i / 3);
324
- this.setFunctionModule(a, b, color);
325
- this.setFunctionModule(b, a, color);
584
+ setFunctionModule(a, b, color);
585
+ setFunctionModule(b, a, color);
326
586
  }
327
587
  }
328
- drawFinderPattern(x, y) {
329
- for(let dy = -4; dy <= 4; dy++)for(let dx = -4; dx <= 4; dx++){
330
- const dist = Math.max(Math.abs(dx), Math.abs(dy));
331
- const xx = x + dx;
332
- const yy = y + dy;
333
- if (0 <= xx && xx < this.size && 0 <= yy && yy < this.size) this.setFunctionModule(xx, yy, 2 !== dist && 4 !== dist);
588
+ const allCodewords = addEccAndInterleave(version, errorCorrectionLevel, dataCodewords);
589
+ if (allCodewords.length !== Math.floor(getNumRawDataModules(version) / 8)) throw 'Invalid argument';
590
+ let bitIndex = 0;
591
+ for(let right = size - 1; right >= 1; right -= 2){
592
+ if (6 === right) right = 5;
593
+ for(let vert = 0; vert < size; vert++)for(let j = 0; j < 2; j++){
594
+ const x = right - j;
595
+ const upward = (right + 1 & 2) === 0;
596
+ const y = upward ? size - 1 - vert : vert;
597
+ if (!isFunction[y][x] && bitIndex < 8 * allCodewords.length) {
598
+ modules[y][x] = getBit(allCodewords[bitIndex >>> 3], 7 - (7 & bitIndex));
599
+ bitIndex++;
600
+ }
334
601
  }
335
602
  }
336
- drawAlignmentPattern(x, y) {
337
- for(let dy = -2; dy <= 2; dy++)for(let dx = -2; dx <= 2; dx++)this.setFunctionModule(x + dx, y + dy, 1 !== Math.max(Math.abs(dx), Math.abs(dy)));
603
+ if (bitIndex !== 8 * allCodewords.length) throw 'Assertion error';
604
+ let finalMask = mask;
605
+ if (-1 === finalMask) {
606
+ let minPenalty = 1000000000;
607
+ for(let i = 0; i < 8; i++){
608
+ applyMask(modules, isFunction, size, i);
609
+ drawFormatBits(errorCorrectionLevel, i);
610
+ const penalty = getPenaltyScore(modules, size);
611
+ if (penalty < minPenalty) {
612
+ finalMask = i;
613
+ minPenalty = penalty;
614
+ }
615
+ applyMask(modules, isFunction, size, i);
616
+ }
338
617
  }
339
- setFunctionModule(x, y, isBlack) {
340
- this.modules[y][x] = isBlack;
341
- this.isFunction[y][x] = true;
618
+ if (finalMask < 0 || finalMask > 7) throw 'Assertion error';
619
+ applyMask(modules, isFunction, size, finalMask);
620
+ {
621
+ const data = errorCorrectionLevel.formatBits << 3 | finalMask;
622
+ let rem = data;
623
+ for(let i = 0; i < 10; i++)rem = rem << 1 ^ (rem >>> 9) * 0x537;
624
+ const bits = (data << 10 | rem) ^ 0x5412;
625
+ if (bits >>> 15 !== 0) throw 'Assertion error';
626
+ for(let i = 0; i <= 5; i++)modules[i][8] = getBit(bits, i);
627
+ modules[7][8] = getBit(bits, 6);
628
+ modules[8][8] = getBit(bits, 7);
629
+ modules[8][7] = getBit(bits, 8);
630
+ for(let i = 9; i < 15; i++)modules[8][14 - i] = getBit(bits, i);
631
+ for(let i = 0; i < 8; i++)modules[8][size - 1 - i] = getBit(bits, i);
632
+ for(let i = 8; i < 15; i++)modules[size - 15 + i][8] = getBit(bits, i);
633
+ modules[size - 8][8] = true;
342
634
  }
343
- addEccAndInterleave(data) {
344
- const ver = this.version;
345
- const ecl = this.errorCorrectionLevel;
346
- if (data.length !== QrCode.getNumDataCodewords(ver, ecl)) throw 'Invalid argument';
347
- const numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];
348
- const blockEccLen = QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver];
349
- const rawCodewords = Math.floor(QrCode.getNumRawDataModules(ver) / 8);
350
- const numShortBlocks = numBlocks - rawCodewords % numBlocks;
351
- const shortBlockLen = Math.floor(rawCodewords / numBlocks);
352
- const blocks = [];
353
- const rs = new ReedSolomonGenerator(blockEccLen);
354
- for(let i = 0, k = 0; i < numBlocks; i++){
355
- const dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));
356
- k += dat.length;
357
- const ecc = rs.getRemainder(dat);
358
- if (i < numShortBlocks) dat.push(0);
359
- blocks.push(dat.concat(ecc));
360
- }
361
- const result = [];
362
- for(let i = 0; i < blocks[0].length; i++)for(let j = 0; j < blocks.length; j++)if (i !== shortBlockLen - blockEccLen || j >= numShortBlocks) result.push(blocks[j][i]);
363
- if (result.length !== rawCodewords) throw 'Assertion error';
635
+ return {
636
+ version,
637
+ errorCorrectionLevel,
638
+ size,
639
+ mask: finalMask,
640
+ modules
641
+ };
642
+ }
643
+ function getAlignmentPatternPositions(version, size) {
644
+ if (1 === version) return [];
645
+ else {
646
+ const numAlign = Math.floor(version / 7) + 2;
647
+ const step = 32 === version ? 26 : 2 * Math.ceil((size - 13) / (2 * numAlign - 2));
648
+ const result = [
649
+ 6
650
+ ];
651
+ for(let pos = size - 7; result.length < numAlign; pos -= step)result.splice(1, 0, pos);
364
652
  return result;
365
653
  }
366
- drawCodewords(data) {
367
- if (data.length !== Math.floor(QrCode.getNumRawDataModules(this.version) / 8)) throw 'Invalid argument';
368
- let i = 0;
369
- for(let right = this.size - 1; right >= 1; right -= 2){
370
- if (6 === right) right = 5;
371
- for(let vert = 0; vert < this.size; vert++)for(let j = 0; j < 2; j++){
372
- const x = right - j;
373
- const upward = (right + 1 & 2) === 0;
374
- const y = upward ? this.size - 1 - vert : vert;
375
- if (!this.isFunction[y][x] && i < 8 * data.length) {
376
- this.modules[y][x] = getBit(data[i >>> 3], 7 - (7 & i));
377
- i++;
378
- }
379
- }
654
+ }
655
+ function applyMask(modules, isFunction, size, mask) {
656
+ if (mask < 0 || mask > 7) throw 'Mask value out of range';
657
+ for(let y = 0; y < size; y++)for(let x = 0; x < size; x++){
658
+ let invert;
659
+ switch(mask){
660
+ case 0:
661
+ invert = (x + y) % 2 === 0;
662
+ break;
663
+ case 1:
664
+ invert = y % 2 === 0;
665
+ break;
666
+ case 2:
667
+ invert = x % 3 === 0;
668
+ break;
669
+ case 3:
670
+ invert = (x + y) % 3 === 0;
671
+ break;
672
+ case 4:
673
+ invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0;
674
+ break;
675
+ case 5:
676
+ invert = x * y % 2 + x * y % 3 === 0;
677
+ break;
678
+ case 6:
679
+ invert = (x * y % 2 + x * y % 3) % 2 === 0;
680
+ break;
681
+ case 7:
682
+ invert = ((x + y) % 2 + x * y % 3) % 2 === 0;
683
+ break;
684
+ default:
685
+ throw 'Assertion error';
380
686
  }
381
- if (i !== 8 * data.length) throw 'Assertion error';
687
+ if (!isFunction[y][x] && invert) modules[y][x] = !modules[y][x];
382
688
  }
383
- applyMask(mask) {
384
- if (mask < 0 || mask > 7) throw 'Mask value out of range';
385
- for(let y = 0; y < this.size; y++)for(let x = 0; x < this.size; x++){
386
- let invert;
387
- switch(mask){
388
- case 0:
389
- invert = (x + y) % 2 === 0;
390
- break;
391
- case 1:
392
- invert = y % 2 === 0;
393
- break;
394
- case 2:
395
- invert = x % 3 === 0;
396
- break;
397
- case 3:
398
- invert = (x + y) % 3 === 0;
399
- break;
400
- case 4:
401
- invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0;
402
- break;
403
- case 5:
404
- invert = x * y % 2 + x * y % 3 === 0;
405
- break;
406
- case 6:
407
- invert = (x * y % 2 + x * y % 3) % 2 === 0;
408
- break;
409
- case 7:
410
- invert = ((x + y) % 2 + x * y % 3) % 2 === 0;
411
- break;
412
- default:
413
- throw 'Assertion error';
414
- }
415
- if (!this.isFunction[y][x] && invert) this.modules[y][x] = !this.modules[y][x];
689
+ }
690
+ function getPenaltyScore(modules, size) {
691
+ let result = 0;
692
+ for(let y = 0; y < size; y++){
693
+ const runHistory = [
694
+ 0,
695
+ 0,
696
+ 0,
697
+ 0,
698
+ 0,
699
+ 0,
700
+ 0
701
+ ];
702
+ let color = false;
703
+ let runX = 0;
704
+ for(let x = 0; x < size; x++)if (modules[y][x] === color) {
705
+ runX++;
706
+ if (5 === runX) result += PENALTY_N1;
707
+ else if (runX > 5) result++;
708
+ } else {
709
+ addRunToHistory(runX, runHistory);
710
+ if (!color && hasFinderLikePattern(runHistory)) result += PENALTY_N3;
711
+ color = modules[y][x];
712
+ runX = 1;
416
713
  }
714
+ addRunToHistory(runX, runHistory);
715
+ if (color) addRunToHistory(0, runHistory);
716
+ if (hasFinderLikePattern(runHistory)) result += PENALTY_N3;
417
717
  }
418
- getPenaltyScore() {
419
- let result = 0;
420
- for(let y = 0; y < this.size; y++){
421
- const runHistory = [
422
- 0,
423
- 0,
424
- 0,
425
- 0,
426
- 0,
427
- 0,
428
- 0
429
- ];
430
- let color = false;
431
- let runX = 0;
432
- for(let x = 0; x < this.size; x++)if (this.modules[y][x] === color) {
433
- runX++;
434
- if (5 === runX) result += QrCode.PENALTY_N1;
435
- else if (runX > 5) result++;
436
- } else {
437
- QrCode.addRunToHistory(runX, runHistory);
438
- if (!color && QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3;
439
- color = this.modules[y][x];
440
- runX = 1;
441
- }
442
- QrCode.addRunToHistory(runX, runHistory);
443
- if (color) QrCode.addRunToHistory(0, runHistory);
444
- if (QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3;
445
- }
446
- for(let x = 0; x < this.size; x++){
447
- const runHistory = [
448
- 0,
449
- 0,
450
- 0,
451
- 0,
452
- 0,
453
- 0,
454
- 0
455
- ];
456
- let color = false;
457
- let runY = 0;
458
- for(let y = 0; y < this.size; y++)if (this.modules[y][x] === color) {
459
- runY++;
460
- if (5 === runY) result += QrCode.PENALTY_N1;
461
- else if (runY > 5) result++;
462
- } else {
463
- QrCode.addRunToHistory(runY, runHistory);
464
- if (!color && QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3;
465
- color = this.modules[y][x];
466
- runY = 1;
467
- }
468
- QrCode.addRunToHistory(runY, runHistory);
469
- if (color) QrCode.addRunToHistory(0, runHistory);
470
- if (QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3;
471
- }
472
- for(let y = 0; y < this.size - 1; y++)for(let x = 0; x < this.size - 1; x++){
473
- const color = this.modules[y][x];
474
- if (color === this.modules[y][x + 1] && color === this.modules[y + 1][x] && color === this.modules[y + 1][x + 1]) result += QrCode.PENALTY_N2;
718
+ for(let x = 0; x < size; x++){
719
+ const runHistory = [
720
+ 0,
721
+ 0,
722
+ 0,
723
+ 0,
724
+ 0,
725
+ 0,
726
+ 0
727
+ ];
728
+ let color = false;
729
+ let runY = 0;
730
+ for(let y = 0; y < size; y++)if (modules[y][x] === color) {
731
+ runY++;
732
+ if (5 === runY) result += PENALTY_N1;
733
+ else if (runY > 5) result++;
734
+ } else {
735
+ addRunToHistory(runY, runHistory);
736
+ if (!color && hasFinderLikePattern(runHistory)) result += PENALTY_N3;
737
+ color = modules[y][x];
738
+ runY = 1;
475
739
  }
476
- let black = 0;
477
- for (const row of this.modules)for (const color of row)if (color) black++;
478
- const total = this.size * this.size;
479
- const k = Math.ceil(Math.abs(20 * black - 10 * total) / total) - 1;
480
- result += k * QrCode.PENALTY_N4;
481
- return result;
740
+ addRunToHistory(runY, runHistory);
741
+ if (color) addRunToHistory(0, runHistory);
742
+ if (hasFinderLikePattern(runHistory)) result += PENALTY_N3;
482
743
  }
483
- getAlignmentPatternPositions() {
484
- if (1 === this.version) return [];
485
- {
486
- const numAlign = Math.floor(this.version / 7) + 2;
487
- const step = 32 === this.version ? 26 : 2 * Math.ceil((this.size - 13) / (2 * numAlign - 2));
488
- const result = [
489
- 6
490
- ];
491
- for(let pos = this.size - 7; result.length < numAlign; pos -= step)result.splice(1, 0, pos);
492
- return result;
493
- }
744
+ for(let y = 0; y < size - 1; y++)for(let x = 0; x < size - 1; x++){
745
+ const color = modules[y][x];
746
+ if (color === modules[y][x + 1] && color === modules[y + 1][x] && color === modules[y + 1][x + 1]) result += PENALTY_N2;
494
747
  }
495
- static getNumRawDataModules(ver) {
496
- if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) throw 'Version number out of range';
497
- let result = (16 * ver + 128) * ver + 64;
498
- if (ver >= 2) {
499
- const numAlign = Math.floor(ver / 7) + 2;
500
- result -= (25 * numAlign - 10) * numAlign - 55;
501
- if (ver >= 7) result -= 36;
502
- }
503
- return result;
748
+ let black = 0;
749
+ for (const row of modules)for (const color of row)if (color) black++;
750
+ const total = size * size;
751
+ const k = Math.ceil(Math.abs(20 * black - 10 * total) / total) - 1;
752
+ result += k * PENALTY_N4;
753
+ return result;
754
+ }
755
+ function addRunToHistory(run, history) {
756
+ history.pop();
757
+ history.unshift(run);
758
+ }
759
+ function hasFinderLikePattern(runHistory) {
760
+ const n = runHistory[1];
761
+ return n > 0 && runHistory[2] === n && runHistory[4] === n && runHistory[5] === n && runHistory[3] === 3 * n && Math.max(runHistory[0], runHistory[6]) >= 4 * n;
762
+ }
763
+ function addEccAndInterleave(version, ecl, data) {
764
+ if (data.length !== getNumDataCodewords(version, ecl)) throw 'Invalid argument';
765
+ const numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][version];
766
+ const blockEccLen = ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][version];
767
+ const rawCodewords = Math.floor(getNumRawDataModules(version) / 8);
768
+ const numShortBlocks = numBlocks - rawCodewords % numBlocks;
769
+ const shortBlockLen = Math.floor(rawCodewords / numBlocks);
770
+ const blocks = [];
771
+ const rs = createReedSolomonGenerator(blockEccLen);
772
+ for(let i = 0, k = 0; i < numBlocks; i++){
773
+ const dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));
774
+ k += dat.length;
775
+ const ecc = rs.getRemainder(dat);
776
+ if (i < numShortBlocks) dat.push(0);
777
+ blocks.push(dat.concat(ecc));
504
778
  }
505
- static getNumDataCodewords(ver, ecl) {
506
- return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];
779
+ const result = [];
780
+ for(let i = 0; i < blocks[0].length; i++)for(let j = 0; j < blocks.length; j++)if (i !== shortBlockLen - blockEccLen || j >= numShortBlocks) result.push(blocks[j][i]);
781
+ if (result.length !== rawCodewords) throw 'Assertion error';
782
+ return result;
783
+ }
784
+ function getNumRawDataModules(ver) {
785
+ if (ver < MIN_VERSION || ver > MAX_VERSION) throw 'Version number out of range';
786
+ let result = (16 * ver + 128) * ver + 64;
787
+ if (ver >= 2) {
788
+ const numAlign = Math.floor(ver / 7) + 2;
789
+ result -= (25 * numAlign - 10) * numAlign - 55;
790
+ if (ver >= 7) result -= 36;
507
791
  }
508
- static addRunToHistory(run, history) {
509
- history.pop();
510
- history.unshift(run);
792
+ return result;
793
+ }
794
+ function getNumDataCodewords(ver, ecl) {
795
+ return Math.floor(getNumRawDataModules(ver) / 8) - ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];
796
+ }
797
+ function getBit(x, i) {
798
+ return (x >>> i & 1) !== 0;
799
+ }
800
+ function createQrSegment(mode, numChars, bitData) {
801
+ if (numChars < 0) throw 'Invalid argument';
802
+ const data = bitData.slice();
803
+ return {
804
+ mode,
805
+ numChars,
806
+ getData: ()=>data.slice()
807
+ };
808
+ }
809
+ function makeBytes(data) {
810
+ const bb = createBitBuffer();
811
+ for (const b of data)bb.appendBits(b, 8);
812
+ return createQrSegment(QrSegmentMode.BYTE, data.length, bb.array);
813
+ }
814
+ function makeNumeric(digits) {
815
+ if (!NUMERIC_REGEX.test(digits)) throw 'String contains non-numeric characters';
816
+ const bb = createBitBuffer();
817
+ for(let i = 0; i < digits.length;){
818
+ const n = Math.min(digits.length - i, 3);
819
+ bb.appendBits(parseInt(digits.substr(i, n), 10), 3 * n + 1);
820
+ i += n;
511
821
  }
512
- static hasFinderLikePattern(runHistory) {
513
- const n = runHistory[1];
514
- return n > 0 && runHistory[2] === n && runHistory[4] === n && runHistory[5] === n && runHistory[3] === 3 * n && Math.max(runHistory[0], runHistory[6]) >= 4 * n;
822
+ return createQrSegment(QrSegmentMode.NUMERIC, digits.length, bb.array);
823
+ }
824
+ function makeAlphanumeric(text) {
825
+ if (!ALPHANUMERIC_REGEX.test(text)) throw 'String contains unencodable characters in alphanumeric mode';
826
+ const bb = createBitBuffer();
827
+ let i;
828
+ for(i = 0; i + 2 <= text.length; i += 2){
829
+ let temp = 45 * ALPHANUMERIC_CHARSET.indexOf(text.charAt(i));
830
+ temp += ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));
831
+ bb.appendBits(temp, 11);
515
832
  }
516
- constructor(version, errorCorrectionLevel, dataCodewords, mask){
517
- _define_property__(this, "size", void 0);
518
- _define_property__(this, "modules", []);
519
- _define_property__(this, "isFunction", []);
520
- _define_property__(this, "version", void 0);
521
- _define_property__(this, "errorCorrectionLevel", void 0);
522
- _define_property__(this, "mask", void 0);
523
- if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) throw 'Version value out of range';
524
- if (mask < -1 || mask > 7) throw 'Mask value out of range';
525
- this.version = version;
526
- this.errorCorrectionLevel = errorCorrectionLevel;
527
- this.size = 4 * version + 17;
528
- const row = [];
529
- for(let i = 0; i < this.size; i++)row.push(false);
530
- for(let i = 0; i < this.size; i++){
531
- this.modules.push(row.slice());
532
- this.isFunction.push(row.slice());
833
+ if (i < text.length) bb.appendBits(ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6);
834
+ return createQrSegment(QrSegmentMode.ALPHANUMERIC, text.length, bb.array);
835
+ }
836
+ function makeSegments(text) {
837
+ if ('' === text) return [];
838
+ else if (NUMERIC_REGEX.test(text)) return [
839
+ makeNumeric(text)
840
+ ];
841
+ else if (ALPHANUMERIC_REGEX.test(text)) return [
842
+ makeAlphanumeric(text)
843
+ ];
844
+ else return [
845
+ makeBytes(toUtf8ByteArray(text))
846
+ ];
847
+ }
848
+ function getTotalBits(segs, version) {
849
+ let result = 0;
850
+ for (const seg of segs){
851
+ const ccbits = numCharCountBits(seg.mode, version);
852
+ if (seg.numChars >= 1 << ccbits) return 1 / 0;
853
+ result += 4 + ccbits + seg.getData().length;
854
+ }
855
+ return result;
856
+ }
857
+ function toUtf8ByteArray(str) {
858
+ str = encodeURI(str);
859
+ const result = [];
860
+ for(let i = 0; i < str.length; i++)if ('%' !== str.charAt(i)) result.push(str.charCodeAt(i));
861
+ else {
862
+ result.push(parseInt(str.substr(i + 1, 2), 16));
863
+ i += 2;
864
+ }
865
+ return result;
866
+ }
867
+ function createBitBuffer() {
868
+ const array = [];
869
+ return {
870
+ array,
871
+ appendBits (val, len) {
872
+ if (len < 0 || len > 31 || val >>> len !== 0) throw 'Value out of range';
873
+ for(let i = len - 1; i >= 0; i--)array.push(val >>> i & 1);
533
874
  }
534
- this.drawFunctionPatterns();
535
- const allCodewords = this.addEccAndInterleave(dataCodewords);
536
- this.drawCodewords(allCodewords);
537
- if (-1 === mask) {
538
- let minPenalty = 1000000000;
539
- for(let i = 0; i < 8; i++){
540
- this.applyMask(i);
541
- this.drawFormatBits(i);
542
- const penalty = this.getPenaltyScore();
543
- if (penalty < minPenalty) {
544
- mask = i;
545
- minPenalty = penalty;
546
- }
547
- this.applyMask(i);
875
+ };
876
+ }
877
+ function createReedSolomonGenerator(degree) {
878
+ if (degree < 1 || degree > 255) throw 'Degree out of range';
879
+ const coefficients = [];
880
+ for(let i = 0; i < degree - 1; i++)coefficients.push(0);
881
+ coefficients.push(1);
882
+ let root = 1;
883
+ for(let i = 0; i < degree; i++){
884
+ for(let j = 0; j < coefficients.length; j++){
885
+ coefficients[j] = rsMultiply(coefficients[j], root);
886
+ if (j + 1 < coefficients.length) coefficients[j] ^= coefficients[j + 1];
887
+ }
888
+ root = rsMultiply(root, 0x02);
889
+ }
890
+ return {
891
+ getRemainder (data) {
892
+ const result = coefficients.map(()=>0);
893
+ for (const b of data){
894
+ const factor = b ^ result.shift();
895
+ result.push(0);
896
+ coefficients.forEach((coef, i)=>{
897
+ result[i] ^= rsMultiply(coef, factor);
898
+ });
548
899
  }
900
+ return result;
549
901
  }
550
- if (mask < 0 || mask > 7) throw 'Assertion error';
551
- this.mask = mask;
552
- this.applyMask(mask);
553
- this.drawFormatBits(mask);
554
- this.isFunction = [];
902
+ };
903
+ }
904
+ function rsMultiply(x, y) {
905
+ if (x >>> 8 !== 0 || y >>> 8 !== 0) throw 'Byte out of range';
906
+ let z = 0;
907
+ for(let i = 7; i >= 0; i--){
908
+ z = z << 1 ^ (z >>> 7) * 0x11d;
909
+ z ^= (y >>> i & 1) * x;
555
910
  }
911
+ if (z >>> 8 !== 0) throw 'Assertion error';
912
+ return z;
556
913
  }
557
- _define_property__(QrCode, "MIN_VERSION", 1);
558
- _define_property__(QrCode, "MAX_VERSION", 40);
559
- _define_property__(QrCode, "PENALTY_N1", 3);
560
- _define_property__(QrCode, "PENALTY_N2", 3);
561
- _define_property__(QrCode, "PENALTY_N3", 40);
562
- _define_property__(QrCode, "PENALTY_N4", 10);
563
- _define_property__(QrCode, "ECC_CODEWORDS_PER_BLOCK", [
914
+ const MIN_VERSION = 1;
915
+ const MAX_VERSION = 40;
916
+ const PENALTY_N1 = 3;
917
+ const PENALTY_N2 = 3;
918
+ const PENALTY_N3 = 40;
919
+ const PENALTY_N4 = 10;
920
+ const ECC_CODEWORDS_PER_BLOCK = [
564
921
  [
565
922
  -1,
566
923
  7,
@@ -733,8 +1090,8 @@ _define_property__(QrCode, "ECC_CODEWORDS_PER_BLOCK", [
733
1090
  30,
734
1091
  30
735
1092
  ]
736
- ]);
737
- _define_property__(QrCode, "NUM_ERROR_CORRECTION_BLOCKS", [
1093
+ ];
1094
+ const NUM_ERROR_CORRECTION_BLOCKS = [
738
1095
  [
739
1096
  -1,
740
1097
  1,
@@ -907,193 +1264,10 @@ _define_property__(QrCode, "NUM_ERROR_CORRECTION_BLOCKS", [
907
1264
  77,
908
1265
  81
909
1266
  ]
910
- ]);
911
- function getBit(x, i) {
912
- return (x >>> i & 1) !== 0;
913
- }
914
- class QrSegment {
915
- static makeBytes(data) {
916
- const bb = new BitBuffer();
917
- for (const b of data)bb.appendBits(b, 8);
918
- return new QrSegment(QrSegmentMode.BYTE, data.length, bb.array);
919
- }
920
- static makeNumeric(digits) {
921
- if (!QrSegment.NUMERIC_REGEX.test(digits)) throw 'String contains non-numeric characters';
922
- const bb = new BitBuffer();
923
- for(let i = 0; i < digits.length;){
924
- const n = Math.min(digits.length - i, 3);
925
- bb.appendBits(parseInt(digits.substr(i, n), 10), 3 * n + 1);
926
- i += n;
927
- }
928
- return new QrSegment(QrSegmentMode.NUMERIC, digits.length, bb.array);
929
- }
930
- static makeAlphanumeric(text) {
931
- if (!QrSegment.ALPHANUMERIC_REGEX.test(text)) throw 'String contains unencodable characters in alphanumeric mode';
932
- const bb = new BitBuffer();
933
- let i;
934
- for(i = 0; i + 2 <= text.length; i += 2){
935
- let temp = 45 * QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i));
936
- temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));
937
- bb.appendBits(temp, 11);
938
- }
939
- if (i < text.length) bb.appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6);
940
- return new QrSegment(QrSegmentMode.ALPHANUMERIC, text.length, bb.array);
941
- }
942
- static makeSegments(text) {
943
- if ('' === text) return [];
944
- if (QrSegment.NUMERIC_REGEX.test(text)) return [
945
- QrSegment.makeNumeric(text)
946
- ];
947
- if (QrSegment.ALPHANUMERIC_REGEX.test(text)) return [
948
- QrSegment.makeAlphanumeric(text)
949
- ];
950
- return [
951
- QrSegment.makeBytes(QrSegment.toUtf8ByteArray(text))
952
- ];
953
- }
954
- static makeEci(assignVal) {
955
- const bb = new BitBuffer();
956
- if (assignVal < 0) throw 'ECI assignment value out of range';
957
- if (assignVal < 128) bb.appendBits(assignVal, 8);
958
- else if (assignVal < 16384) {
959
- bb.appendBits(2, 2);
960
- bb.appendBits(assignVal, 14);
961
- } else if (assignVal < 1000000) {
962
- bb.appendBits(6, 3);
963
- bb.appendBits(assignVal, 21);
964
- } else throw 'ECI assignment value out of range';
965
- return new QrSegment(QrSegmentMode.ECI, 0, bb.array);
966
- }
967
- getData() {
968
- return this.bitData.slice();
969
- }
970
- static getTotalBits(segs, version) {
971
- let result = 0;
972
- for (const seg of segs){
973
- const ccbits = seg.mode.numCharCountBits(version);
974
- if (seg.numChars >= 1 << ccbits) return 1 / 0;
975
- result += 4 + ccbits + seg.bitData.length;
976
- }
977
- return result;
978
- }
979
- static toUtf8ByteArray(str) {
980
- str = encodeURI(str);
981
- const result = [];
982
- for(let i = 0; i < str.length; i++)if ('%' !== str.charAt(i)) result.push(str.charCodeAt(i));
983
- else {
984
- result.push(parseInt(str.substr(i + 1, 2), 16));
985
- i += 2;
986
- }
987
- return result;
988
- }
989
- constructor(mode, numChars, bitData){
990
- _define_property__(this, "mode", void 0);
991
- _define_property__(this, "numChars", void 0);
992
- _define_property__(this, "bitData", void 0);
993
- if (numChars < 0) throw 'Invalid argument';
994
- this.mode = mode;
995
- this.numChars = numChars;
996
- this.bitData = bitData.slice();
997
- }
998
- }
999
- _define_property__(QrSegment, "NUMERIC_REGEX", /^[0-9]*$/);
1000
- _define_property__(QrSegment, "ALPHANUMERIC_REGEX", /^[A-Z0-9 $%*+./:-]*$/);
1001
- _define_property__(QrSegment, "ALPHANUMERIC_CHARSET", '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:');
1002
- class ReedSolomonGenerator {
1003
- getRemainder(data) {
1004
- const result = this.coefficients.map((_)=>0);
1005
- for (const b of data){
1006
- const factor = b ^ result.shift();
1007
- result.push(0);
1008
- this.coefficients.forEach((coef, i)=>{
1009
- result[i] ^= ReedSolomonGenerator.multiply(coef, factor);
1010
- });
1011
- }
1012
- return result;
1013
- }
1014
- static multiply(x, y) {
1015
- if (x >>> 8 !== 0 || y >>> 8 !== 0) throw 'Byte out of range';
1016
- let z = 0;
1017
- for(let i = 7; i >= 0; i--){
1018
- z = z << 1 ^ (z >>> 7) * 0x11d;
1019
- z ^= (y >>> i & 1) * x;
1020
- }
1021
- if (z >>> 8 !== 0) throw 'Assertion error';
1022
- return z;
1023
- }
1024
- constructor(degree){
1025
- _define_property__(this, "coefficients", []);
1026
- if (degree < 1 || degree > 255) throw 'Degree out of range';
1027
- const coefs = this.coefficients;
1028
- for(let i = 0; i < degree - 1; i++)coefs.push(0);
1029
- coefs.push(1);
1030
- let root = 1;
1031
- for(let i = 0; i < degree; i++){
1032
- for(let j = 0; j < coefs.length; j++){
1033
- coefs[j] = ReedSolomonGenerator.multiply(coefs[j], root);
1034
- if (j + 1 < coefs.length) coefs[j] ^= coefs[j + 1];
1035
- }
1036
- root = ReedSolomonGenerator.multiply(root, 0x02);
1037
- }
1038
- }
1039
- }
1040
- class BitBuffer {
1041
- appendBits(val, len) {
1042
- if (len < 0 || len > 31 || val >>> len !== 0) throw 'Value out of range';
1043
- for(let i = len - 1; i >= 0; i--)this.array.push(val >>> i & 1);
1044
- }
1045
- constructor(){
1046
- _define_property__(this, "array", []);
1047
- }
1048
- }
1049
- class QrCodeEcc {
1050
- constructor(ordinal, formatBits){
1051
- _define_property__(this, "ordinal", void 0);
1052
- _define_property__(this, "formatBits", void 0);
1053
- this.ordinal = ordinal;
1054
- this.formatBits = formatBits;
1055
- }
1056
- }
1057
- _define_property__(QrCodeEcc, "LOW", new QrCodeEcc(0, 1));
1058
- _define_property__(QrCodeEcc, "MEDIUM", new QrCodeEcc(1, 0));
1059
- _define_property__(QrCodeEcc, "QUARTILE", new QrCodeEcc(2, 3));
1060
- _define_property__(QrCodeEcc, "HIGH", new QrCodeEcc(3, 2));
1061
- class QrSegmentMode {
1062
- numCharCountBits(ver) {
1063
- return this.numBitsCharCount[Math.floor((ver + 7) / 17)];
1064
- }
1065
- constructor(modeBits, numBitsCharCount){
1066
- _define_property__(this, "modeBits", void 0);
1067
- _define_property__(this, "numBitsCharCount", void 0);
1068
- this.modeBits = modeBits;
1069
- this.numBitsCharCount = numBitsCharCount;
1070
- }
1071
- }
1072
- _define_property__(QrSegmentMode, "NUMERIC", new QrSegmentMode(0x1, [
1073
- 10,
1074
- 12,
1075
- 14
1076
- ]));
1077
- _define_property__(QrSegmentMode, "ALPHANUMERIC", new QrSegmentMode(0x2, [
1078
- 9,
1079
- 11,
1080
- 13
1081
- ]));
1082
- _define_property__(QrSegmentMode, "BYTE", new QrSegmentMode(0x4, [
1083
- 8,
1084
- 16,
1085
- 16
1086
- ]));
1087
- _define_property__(QrSegmentMode, "KANJI", new QrSegmentMode(0x8, [
1088
- 8,
1089
- 10,
1090
- 12
1091
- ]));
1092
- _define_property__(QrSegmentMode, "ECI", new QrSegmentMode(0x7, [
1093
- 0,
1094
- 0,
1095
- 0
1096
- ]));
1267
+ ];
1268
+ const NUMERIC_REGEX = /^[0-9]*$/;
1269
+ const ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+./:-]*$/;
1270
+ const ALPHANUMERIC_CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
1097
1271
  const DEFAULT_SIZE = 128;
1098
1272
  const BACKGROUND_COLOR_DEFAULT = '#ffffff';
1099
1273
  const FOREGROUND_COLOR_DEFAULT = '#000000';
@@ -1119,14 +1293,16 @@ function createQR(text, qrSizeOrOptions, classNameLegacy, legacyOptions) {
1119
1293
  isShowBackground: !!options.isShowBackground || false,
1120
1294
  foregroundColor: 'string' == typeof options.foregroundColor ? options.foregroundColor : FOREGROUND_COLOR_DEFAULT,
1121
1295
  backgroundColor: 'string' == typeof options.backgroundColor ? options.backgroundColor : BACKGROUND_COLOR_DEFAULT,
1296
+ backgroundPadding: 'number' == typeof options.backgroundPadding && Number.isFinite(options.backgroundPadding) && options.backgroundPadding >= 0 ? options.backgroundPadding : 20,
1297
+ backgroundRadius: 'number' == typeof options.backgroundRadius && Number.isFinite(options.backgroundRadius) && options.backgroundRadius >= 0 ? options.backgroundRadius : 36,
1122
1298
  logoColor: 'string' == typeof options.logoColor ? options.logoColor : LOGO_COLOR_DEFAULT,
1123
1299
  suffix: options.suffix ? options.suffix.toString() : '0',
1124
1300
  logoData: 'string' == typeof options.logoData ? options.logoData : null,
1125
1301
  ecc: 'number' == typeof options.ecc && eccLevels[options.ecc] ? options.ecc : 3,
1126
- incTileSize: 'number' == typeof options.incTileSize ? options.incTileSize : 96
1302
+ incTileSize: 'number' == typeof options.incTileSize ? options.incTileSize : INC_TILE_SIZE
1127
1303
  };
1128
- const segments = QrSegment.makeSegments(text);
1129
- const qrCode = QrCode.encodeSegments(segments, eccLevels[fallbackOptions.ecc], 1, 40, -1, true);
1304
+ const segments = makeSegments(text);
1305
+ const qrCode = encodeSegments(segments, eccLevels[fallbackOptions.ecc], 1, 40, -1, true);
1130
1306
  const svgCode = convertSegmentsToSvgString(qrCode, fallbackOptions);
1131
1307
  return svgCode;
1132
1308
  }