@sythos/js_barcode_universal 1.5.8 → 1.5.9
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/LICENSE +5 -4
- package/NOTICE.md +2 -2
- package/README.md +17 -13
- package/bundle/sythos-barcode.esm.js +11658 -11244
- package/bundle/sythos-barcode.js +11658 -11244
- package/package.json +10 -3
- package/src/index.d.ts +1 -1
- package/src/index.js +508 -516
- package/src/js/aztec/decoder.js +208 -199
- package/src/js/aztec/detector.js +153 -126
- package/src/js/aztec/encoder.js +172 -153
- package/src/js/aztec/high-level.js +106 -101
- package/src/js/aztec/index.js +1 -12
- package/src/js/aztec/tables.js +106 -120
- package/src/js/aztecrune/decoder.js +76 -85
- package/src/js/aztecrune/detector.js +107 -93
- package/src/js/aztecrune/encoder.js +50 -68
- package/src/js/aztecrune/index.js +1 -16
- package/src/js/aztecrune/tables.js +59 -54
- package/src/js/compactpdf417/decoder.js +69 -74
- package/src/js/compactpdf417/detector.js +74 -75
- package/src/js/compactpdf417/encoder.js +68 -81
- package/src/js/compactpdf417/index.js +1 -13
- package/src/js/compactpdf417/tables.js +71 -80
- package/src/js/core/bit-buffer.js +106 -122
- package/src/js/core/bit-matrix.js +196 -194
- package/src/js/core/errors.js +13 -15
- package/src/js/core/galois-field.js +119 -133
- package/src/js/core/index.js +2 -19
- package/src/js/core/reed-solomon.js +195 -204
- package/src/js/databar/codec.js +93 -108
- package/src/js/databar/decoder.js +149 -140
- package/src/js/databar/encoder.js +47 -55
- package/src/js/databar/gs1.js +122 -123
- package/src/js/databar/index.js +4 -36
- package/src/js/databar/patterns.js +81 -85
- package/src/js/databar/tables.js +51 -52
- package/src/js/datamatrix/decoder.js +216 -182
- package/src/js/datamatrix/detector.js +161 -132
- package/src/js/datamatrix/encoder.js +192 -133
- package/src/js/datamatrix/index.js +1 -9
- package/src/js/datamatrix/tables.js +68 -69
- package/src/js/frameqr/decoder.js +102 -125
- package/src/js/frameqr/detector.js +101 -94
- package/src/js/frameqr/encoder.js +67 -80
- package/src/js/frameqr/index.js +1 -10
- package/src/js/frameqr/tables.js +145 -163
- package/src/js/image/binarizer.js +146 -162
- package/src/js/image/grid-sampler.js +63 -79
- package/src/js/image/index.js +0 -2
- package/src/js/image/luminance.js +129 -140
- package/src/js/image/perspective.js +112 -149
- package/src/js/micropdf417/compaction.js +64 -65
- package/src/js/micropdf417/decoder.js +106 -109
- package/src/js/micropdf417/detector.js +73 -70
- package/src/js/micropdf417/encoder.js +129 -154
- package/src/js/micropdf417/error-correction.js +6 -11
- package/src/js/micropdf417/index.js +2 -16
- package/src/js/micropdf417/tables.js +112 -99
- package/src/js/microqr/decoder.js +177 -182
- package/src/js/microqr/detector.js +240 -256
- package/src/js/microqr/encoder.js +239 -194
- package/src/js/microqr/index.js +0 -2
- package/src/js/microqr/tables.js +208 -200
- package/src/js/oned/addons.js +167 -191
- package/src/js/oned/index.js +27 -58
- package/src/js/oned/patterns.js +194 -213
- package/src/js/oned/reader.js +1003 -984
- package/src/js/oned/writers.js +411 -466
- package/src/js/pdf417/compaction.js +315 -222
- package/src/js/pdf417/decoder.js +71 -36
- package/src/js/pdf417/detector.js +377 -361
- package/src/js/pdf417/encoder.js +57 -46
- package/src/js/pdf417/error-correction.js +5 -8
- package/src/js/pdf417/tables.js +248 -250
- package/src/js/qr/decoder.js +311 -347
- package/src/js/qr/detector.js +378 -395
- package/src/js/qr/encoder.js +514 -599
- package/src/js/qr/index.js +0 -2
- package/src/js/qr/tables.js +375 -422
- package/src/js/render/image-data.js +57 -62
- package/src/js/render/index.js +21 -23
- package/src/js/render/options.js +82 -90
- package/src/js/render/png.js +144 -169
- package/src/js/render/svg.js +41 -44
- package/src/js/render/webgl.js +103 -109
- package/src/js/render/webgpu.js +205 -217
- package/src/js/rmqr/decoder.js +161 -53
- package/src/js/rmqr/detector.js +73 -41
- package/src/js/rmqr/encoder.js +253 -104
- package/src/js/rmqr/index.js +1 -5
- package/src/js/rmqr/tables.js +108 -85
- package/src/ts/aztec/decoder.ts +317 -0
- package/src/ts/aztec/detector.ts +224 -0
- package/src/ts/aztec/encoder.ts +257 -0
- package/src/ts/aztec/high-level.ts +211 -0
- package/src/ts/aztec/index.ts +45 -0
- package/src/ts/aztec/tables.ts +210 -0
- package/src/ts/aztecrune/decoder.ts +155 -0
- package/src/ts/aztecrune/detector.ts +166 -0
- package/src/ts/aztecrune/encoder.ts +121 -0
- package/src/ts/aztecrune/index.ts +49 -0
- package/src/ts/aztecrune/tables.ts +137 -0
- package/src/ts/compactpdf417/decoder.ts +128 -0
- package/src/ts/compactpdf417/detector.ts +139 -0
- package/src/ts/compactpdf417/encoder.ts +140 -0
- package/src/ts/compactpdf417/index.ts +17 -0
- package/src/ts/compactpdf417/tables.ts +173 -0
- package/src/ts/core/bit-buffer.ts +174 -0
- package/src/ts/core/bit-matrix.ts +241 -0
- package/src/ts/core/errors.ts +61 -0
- package/src/ts/core/galois-field.ts +207 -0
- package/src/ts/core/index.ts +56 -0
- package/src/ts/core/reed-solomon.ts +327 -0
- package/src/ts/databar/codec.ts +181 -0
- package/src/ts/databar/decoder.ts +215 -0
- package/src/ts/databar/encoder.ts +96 -0
- package/src/ts/databar/gs1.ts +177 -0
- package/src/ts/databar/index.ts +68 -0
- package/src/ts/databar/patterns.ts +143 -0
- package/src/ts/databar/tables.ts +124 -0
- package/src/ts/datamatrix/decoder.ts +262 -0
- package/src/ts/datamatrix/detector.ts +225 -0
- package/src/ts/datamatrix/encoder.ts +191 -0
- package/src/ts/datamatrix/index.ts +42 -0
- package/src/ts/datamatrix/tables.ts +123 -0
- package/src/ts/frameqr/decoder.ts +239 -0
- package/src/ts/frameqr/detector.ts +192 -0
- package/src/ts/frameqr/encoder.ts +156 -0
- package/src/ts/frameqr/index.ts +42 -0
- package/src/ts/frameqr/tables.ts +270 -0
- package/src/ts/image/binarizer.ts +270 -0
- package/src/ts/image/grid-sampler.ts +164 -0
- package/src/ts/image/index.ts +40 -0
- package/src/ts/image/luminance.ts +196 -0
- package/src/ts/image/perspective.ts +195 -0
- package/src/ts/index.d.ts +1 -1
- package/src/ts/index.ts +790 -0
- package/src/ts/micropdf417/compaction.ts +116 -0
- package/src/ts/micropdf417/decoder.ts +183 -0
- package/src/ts/micropdf417/detector.ts +149 -0
- package/src/ts/micropdf417/encoder.ts +209 -0
- package/src/ts/micropdf417/error-correction.ts +55 -0
- package/src/ts/micropdf417/index.ts +49 -0
- package/src/ts/micropdf417/tables.ts +184 -0
- package/src/ts/microqr/decoder.ts +245 -0
- package/src/ts/microqr/detector.ts +355 -0
- package/src/ts/microqr/encoder.ts +269 -0
- package/src/ts/microqr/index.ts +36 -0
- package/src/ts/microqr/tables.ts +316 -0
- package/src/ts/oned/addons.ts +420 -0
- package/src/ts/oned/index.ts +106 -0
- package/src/ts/oned/patterns.ts +384 -0
- package/src/ts/oned/reader.ts +1364 -0
- package/src/ts/oned/writers.ts +746 -0
- package/src/ts/pdf417/compaction.ts +298 -0
- package/src/ts/pdf417/decoder.ts +75 -0
- package/src/ts/pdf417/detector.ts +468 -0
- package/src/ts/pdf417/encoder.ts +91 -0
- package/src/ts/pdf417/error-correction.ts +47 -0
- package/src/ts/pdf417/index.ts +6 -0
- package/src/ts/pdf417/tables.ts +317 -0
- package/src/ts/qr/decoder.ts +575 -0
- package/src/ts/qr/detector.ts +630 -0
- package/src/ts/qr/encoder.ts +958 -0
- package/src/ts/qr/index.ts +44 -0
- package/src/ts/qr/tables.ts +737 -0
- package/src/ts/render/image-data.ts +125 -0
- package/src/ts/render/index.ts +130 -0
- package/src/ts/render/options.ts +160 -0
- package/src/ts/render/png.ts +295 -0
- package/src/ts/render/svg.ts +120 -0
- package/src/ts/render/webgl.ts +206 -0
- package/src/ts/render/webgpu.ts +369 -0
- package/src/ts/rmqr/decoder.ts +101 -0
- package/src/ts/rmqr/detector.ts +90 -0
- package/src/ts/rmqr/encoder.ts +172 -0
- package/src/ts/rmqr/index.ts +37 -0
- package/src/ts/rmqr/tables.ts +154 -0
package/src/js/qr/encoder.js
CHANGED
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
*
|
|
28
28
|
* Original work. No code from any other barcode implementation.
|
|
29
29
|
*/
|
|
30
|
-
|
|
31
30
|
/**
|
|
32
31
|
* QR Code encoder.
|
|
33
32
|
*
|
|
@@ -51,44 +50,22 @@
|
|
|
51
50
|
*
|
|
52
51
|
* @module qr/encoder
|
|
53
52
|
*/
|
|
54
|
-
|
|
55
53
|
import { BitMatrix } from '../core/bit-matrix.js';
|
|
56
54
|
import { BitWriter } from '../core/bit-buffer.js';
|
|
57
55
|
import { EncodeError } from '../core/errors.js';
|
|
58
56
|
import { GF256_QR } from '../core/galois-field.js';
|
|
59
57
|
import { rsEncode } from '../core/reed-solomon.js';
|
|
60
|
-
import {
|
|
61
|
-
ECC_LEVELS,
|
|
62
|
-
ECC_LEVEL_BITS,
|
|
63
|
-
MAX_VERSION,
|
|
64
|
-
MIN_VERSION,
|
|
65
|
-
MODE,
|
|
66
|
-
VERSION_INFO_MIN,
|
|
67
|
-
alignmentCentres,
|
|
68
|
-
blockLayout,
|
|
69
|
-
countBits,
|
|
70
|
-
dataBitCapacity,
|
|
71
|
-
dataModuleOrder,
|
|
72
|
-
formatInfoPositions,
|
|
73
|
-
maskBit,
|
|
74
|
-
versionSize,
|
|
75
|
-
} from './tables.js';
|
|
76
|
-
|
|
58
|
+
import { ECC_LEVELS, ECC_LEVEL_BITS, MAX_VERSION, MIN_VERSION, MODE, VERSION_INFO_MIN, alignmentCentres, blockLayout, countBits, dataBitCapacity, dataModuleOrder, formatInfoPositions, maskBit, versionSize, } from './tables.js';
|
|
77
59
|
/** Alphanumeric mode character set; a character's index is its encoded value. */
|
|
78
60
|
export const ALPHANUMERIC_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
|
|
79
|
-
|
|
80
61
|
/** ECI designator for UTF-8. */
|
|
81
62
|
export const ECI_UTF8 = 26;
|
|
82
|
-
|
|
83
63
|
/** BCH(15,5) generator for the format information: x^10+x^8+x^5+x^4+x^2+x+1. */
|
|
84
64
|
const FORMAT_GENERATOR = 0x537;
|
|
85
|
-
|
|
86
65
|
/** Applied to the format information so an all-zero payload is not all-zero. */
|
|
87
66
|
const FORMAT_MASK = 0x5412;
|
|
88
|
-
|
|
89
67
|
/** BCH(18,6) generator for the version information. */
|
|
90
68
|
const VERSION_GENERATOR = 0x1f25;
|
|
91
|
-
|
|
92
69
|
/**
|
|
93
70
|
* @typedef {object} EncodeOptions
|
|
94
71
|
* @property {'L'|'M'|'Q'|'H'} [ecc] Error correction level. Default 'M'.
|
|
@@ -100,27 +77,23 @@ const VERSION_GENERATOR = 0x1f25;
|
|
|
100
77
|
* @property {boolean} [kanji] Allow kanji mode. Default true; ignored when the
|
|
101
78
|
* platform cannot supply a Shift_JIS codec.
|
|
102
79
|
*/
|
|
103
|
-
|
|
104
80
|
/* ------------------------------------------------------------------ *
|
|
105
81
|
* Character classification
|
|
106
82
|
* ------------------------------------------------------------------ */
|
|
107
|
-
|
|
108
83
|
/**
|
|
109
84
|
* @param {string} ch Single code point.
|
|
110
85
|
* @returns {boolean}
|
|
111
86
|
*/
|
|
112
87
|
function isNumeric(ch) {
|
|
113
|
-
|
|
88
|
+
return ch >= '0' && ch <= '9';
|
|
114
89
|
}
|
|
115
|
-
|
|
116
90
|
/**
|
|
117
91
|
* @param {string} ch Single code point.
|
|
118
92
|
* @returns {number} Alphanumeric value, or -1.
|
|
119
93
|
*/
|
|
120
94
|
function alphanumericValue(ch) {
|
|
121
|
-
|
|
95
|
+
return ALPHANUMERIC_CHARS.indexOf(ch);
|
|
122
96
|
}
|
|
123
|
-
|
|
124
97
|
/**
|
|
125
98
|
* UTF-8 length of a code point, without allocating.
|
|
126
99
|
*
|
|
@@ -128,16 +101,17 @@ function alphanumericValue(ch) {
|
|
|
128
101
|
* @returns {number} 1-4.
|
|
129
102
|
*/
|
|
130
103
|
function utf8Length(cp) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
104
|
+
if (cp < 0x80)
|
|
105
|
+
return 1;
|
|
106
|
+
if (cp < 0x800)
|
|
107
|
+
return 2;
|
|
108
|
+
if (cp < 0x10000)
|
|
109
|
+
return 3;
|
|
110
|
+
return 4;
|
|
135
111
|
}
|
|
136
|
-
|
|
137
112
|
/* ------------------------------------------------------------------ *
|
|
138
113
|
* Shift_JIS
|
|
139
114
|
* ------------------------------------------------------------------ */
|
|
140
|
-
|
|
141
115
|
/**
|
|
142
116
|
* Pack a Shift_JIS double byte into the 13-bit kanji mode value.
|
|
143
117
|
*
|
|
@@ -149,27 +123,27 @@ function utf8Length(cp) {
|
|
|
149
123
|
* @returns {number} 13-bit value, or -1 if outside the kanji mode ranges.
|
|
150
124
|
*/
|
|
151
125
|
export function sjisToThirteenBits(sjis) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
126
|
+
// The trail byte must be a real Shift_JIS one. This is not defensive
|
|
127
|
+
// decoration: the packing is only injective while the rebased low byte stays
|
|
128
|
+
// below 0xC0, which holds for trail bytes 0x40-0xFC and fails for anything
|
|
129
|
+
// below 0x40. Accepting those would silently map two inputs to one value.
|
|
130
|
+
const trail = sjis & 0xff;
|
|
131
|
+
if (trail < 0x40 || trail === 0x7f || trail > 0xfc)
|
|
132
|
+
return -1;
|
|
133
|
+
let v;
|
|
134
|
+
if (sjis >= 0x8140 && sjis <= 0x9ffc)
|
|
135
|
+
v = sjis - 0x8140;
|
|
136
|
+
else if (sjis >= 0xe040 && sjis <= 0xebbf)
|
|
137
|
+
v = sjis - 0xc140;
|
|
138
|
+
else
|
|
139
|
+
return -1;
|
|
140
|
+
const packed = ((v >> 8) * 0xc0) + (v & 0xff);
|
|
141
|
+
return packed > 0x1fff ? -1 : packed;
|
|
166
142
|
}
|
|
167
|
-
|
|
168
143
|
/** @type {Map<string, number> | null} Unicode code point -> Shift_JIS. */
|
|
169
144
|
let sjisReverseMap = null;
|
|
170
145
|
/** @type {boolean} True once we have tried and know whether it worked. */
|
|
171
146
|
let sjisReverseTried = false;
|
|
172
|
-
|
|
173
147
|
/**
|
|
174
148
|
* Build the Unicode -> Shift_JIS map by inverting the platform's decoder.
|
|
175
149
|
*
|
|
@@ -182,64 +156,64 @@ let sjisReverseTried = false;
|
|
|
182
156
|
* Shift_JIS decoder, in which case kanji mode is simply not offered.
|
|
183
157
|
*/
|
|
184
158
|
function getSjisReverseMap() {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
159
|
+
if (sjisReverseTried)
|
|
160
|
+
return sjisReverseMap;
|
|
161
|
+
sjisReverseTried = true;
|
|
162
|
+
let decoder;
|
|
163
|
+
try {
|
|
164
|
+
decoder = new TextDecoder('shift_jis', { fatal: true });
|
|
165
|
+
// Some runtimes accept the label and then decode everything to U+FFFD.
|
|
166
|
+
if (decoder.decode(new Uint8Array([0x82, 0xa0])) !== 'あ')
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
const map = new Map();
|
|
173
|
+
const buf = new Uint8Array(2);
|
|
174
|
+
const ranges = [[0x8140, 0x9ffc], [0xe040, 0xebbf]];
|
|
175
|
+
for (let r = 0; r < ranges.length; r++) {
|
|
176
|
+
for (let sjis = ranges[r][0]; sjis <= ranges[r][1]; sjis++) {
|
|
177
|
+
const lo = sjis & 0xff;
|
|
178
|
+
// Shift_JIS trail bytes never take these values.
|
|
179
|
+
if (lo < 0x40 || lo === 0x7f || lo > 0xfc)
|
|
180
|
+
continue;
|
|
181
|
+
if (sjisToThirteenBits(sjis) < 0)
|
|
182
|
+
continue;
|
|
183
|
+
buf[0] = sjis >> 8;
|
|
184
|
+
buf[1] = lo;
|
|
185
|
+
let text;
|
|
186
|
+
try {
|
|
187
|
+
text = decoder.decode(buf);
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
// Reject anything that did not decode to exactly one code point, and
|
|
193
|
+
// keep the first (lowest) encoding when a character has several.
|
|
194
|
+
if (text.length === 0 || Array.from(text).length !== 1)
|
|
195
|
+
continue;
|
|
196
|
+
if (!map.has(text))
|
|
197
|
+
map.set(text, sjis);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
sjisReverseMap = map;
|
|
201
|
+
return map;
|
|
224
202
|
}
|
|
225
|
-
|
|
226
203
|
/* ------------------------------------------------------------------ *
|
|
227
204
|
* Segment analysis
|
|
228
205
|
* ------------------------------------------------------------------ */
|
|
229
|
-
|
|
230
206
|
/** Modes considered by the segmentation search, in table order. */
|
|
231
207
|
const SEARCH_MODES = [MODE.NUMERIC, MODE.ALPHANUMERIC, MODE.BYTE, MODE.KANJI];
|
|
232
|
-
|
|
233
208
|
/**
|
|
234
209
|
* Cost unit: one sixth of a bit, so numeric (10 bits per 3 characters) and
|
|
235
210
|
* alphanumeric (11 bits per 2) are both exact integers.
|
|
236
211
|
*/
|
|
237
212
|
const UNIT = 6;
|
|
238
|
-
const COST_NUMERIC = 20;
|
|
213
|
+
const COST_NUMERIC = 20; // 10/3 bits
|
|
239
214
|
const COST_ALPHANUMERIC = 33; // 11/2 bits
|
|
240
|
-
const COST_KANJI = 78;
|
|
215
|
+
const COST_KANJI = 78; // 13 bits
|
|
241
216
|
const INFEASIBLE = Number.MAX_SAFE_INTEGER / 4;
|
|
242
|
-
|
|
243
217
|
/**
|
|
244
218
|
* @typedef {object} CharInfo
|
|
245
219
|
* @property {string[]} points Code points.
|
|
@@ -249,7 +223,6 @@ const INFEASIBLE = Number.MAX_SAFE_INTEGER / 4;
|
|
|
249
223
|
* @property {Int32Array} kanji 13-bit kanji value, or -1.
|
|
250
224
|
* @property {boolean} utf8
|
|
251
225
|
*/
|
|
252
|
-
|
|
253
226
|
/**
|
|
254
227
|
* @param {string} text
|
|
255
228
|
* @param {boolean} utf8
|
|
@@ -257,32 +230,28 @@ const INFEASIBLE = Number.MAX_SAFE_INTEGER / 4;
|
|
|
257
230
|
* @returns {CharInfo}
|
|
258
231
|
*/
|
|
259
232
|
function classify(text, utf8, allowKanji) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return { points, numeric, alnum, byteLen, kanji, utf8 };
|
|
233
|
+
const points = Array.from(text);
|
|
234
|
+
const n = points.length;
|
|
235
|
+
const numeric = new Uint8Array(n);
|
|
236
|
+
const alnum = new Int32Array(n);
|
|
237
|
+
const byteLen = new Int32Array(n);
|
|
238
|
+
const kanji = new Int32Array(n);
|
|
239
|
+
const reverse = allowKanji ? getSjisReverseMap() : null;
|
|
240
|
+
for (let i = 0; i < n; i++) {
|
|
241
|
+
const ch = points[i];
|
|
242
|
+
const cp = ch.codePointAt(0);
|
|
243
|
+
numeric[i] = isNumeric(ch) ? 1 : 0;
|
|
244
|
+
alnum[i] = alphanumericValue(ch);
|
|
245
|
+
byteLen[i] = utf8 ? utf8Length(cp) : 1;
|
|
246
|
+
kanji[i] = -1;
|
|
247
|
+
if (reverse) {
|
|
248
|
+
const sjis = reverse.get(ch);
|
|
249
|
+
if (sjis !== undefined)
|
|
250
|
+
kanji[i] = sjisToThirteenBits(sjis);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return { points, numeric, alnum, byteLen, kanji, utf8 };
|
|
284
254
|
}
|
|
285
|
-
|
|
286
255
|
/**
|
|
287
256
|
* Cost of one character in a mode, in sixths of a bit.
|
|
288
257
|
*
|
|
@@ -290,22 +259,20 @@ function classify(text, utf8, allowKanji) {
|
|
|
290
259
|
* @returns {number}
|
|
291
260
|
*/
|
|
292
261
|
function charCost(info, i, mode) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
262
|
+
switch (mode) {
|
|
263
|
+
case MODE.NUMERIC: return info.numeric[i] ? COST_NUMERIC : INFEASIBLE;
|
|
264
|
+
case MODE.ALPHANUMERIC: return info.alnum[i] >= 0 ? COST_ALPHANUMERIC : INFEASIBLE;
|
|
265
|
+
case MODE.BYTE: return info.byteLen[i] * 8 * UNIT;
|
|
266
|
+
case MODE.KANJI: return info.kanji[i] >= 0 ? COST_KANJI : INFEASIBLE;
|
|
267
|
+
default: return INFEASIBLE;
|
|
268
|
+
}
|
|
300
269
|
}
|
|
301
|
-
|
|
302
270
|
/**
|
|
303
271
|
* @typedef {object} Segment
|
|
304
272
|
* @property {number} mode
|
|
305
273
|
* @property {number} start Inclusive index into the code point array.
|
|
306
274
|
* @property {number} end Exclusive.
|
|
307
275
|
*/
|
|
308
|
-
|
|
309
276
|
/**
|
|
310
277
|
* Cheapest segmentation for a given version band.
|
|
311
278
|
*
|
|
@@ -318,81 +285,74 @@ function charCost(info, i, mode) {
|
|
|
318
285
|
* @returns {Segment[]}
|
|
319
286
|
*/
|
|
320
287
|
function segmentize(info, version) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
for (let m = 0; m < M; m++) {
|
|
327
|
-
header[m] = (4 + countBits(SEARCH_MODES[m], version)) * UNIT;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
let cost = new Array(M);
|
|
331
|
-
for (let m = 0; m < M; m++) cost[m] = header[m];
|
|
332
|
-
|
|
333
|
-
// from[i * M + m] is the mode we were in before character i-1 was appended
|
|
334
|
-
// in mode m. Int8Array is plenty for four modes and keeps this cheap on
|
|
335
|
-
// long payloads.
|
|
336
|
-
const from = new Int8Array((n + 1) * M).fill(-1);
|
|
337
|
-
|
|
338
|
-
for (let i = 0; i < n; i++) {
|
|
339
|
-
const next = new Array(M);
|
|
288
|
+
const n = info.points.length;
|
|
289
|
+
if (n === 0)
|
|
290
|
+
return [{ mode: MODE.BYTE, start: 0, end: 0 }];
|
|
291
|
+
const M = SEARCH_MODES.length;
|
|
292
|
+
const header = new Array(M);
|
|
340
293
|
for (let m = 0; m < M; m++) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
294
|
+
header[m] = (4 + countBits(SEARCH_MODES[m], version)) * UNIT;
|
|
295
|
+
}
|
|
296
|
+
let cost = new Array(M);
|
|
297
|
+
for (let m = 0; m < M; m++)
|
|
298
|
+
cost[m] = header[m];
|
|
299
|
+
// from[i * M + m] is the mode we were in before character i-1 was appended
|
|
300
|
+
// in mode m. Int8Array is plenty for four modes and keeps this cheap on
|
|
301
|
+
// long payloads.
|
|
302
|
+
const from = new Int8Array((n + 1) * M).fill(-1);
|
|
303
|
+
for (let i = 0; i < n; i++) {
|
|
304
|
+
const next = new Array(M);
|
|
305
|
+
for (let m = 0; m < M; m++) {
|
|
306
|
+
const cc = charCost(info, i, SEARCH_MODES[m]);
|
|
307
|
+
if (cc >= INFEASIBLE) {
|
|
308
|
+
next[m] = INFEASIBLE;
|
|
309
|
+
from[(i + 1) * M + m] = -1;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
let bestCost = cost[m]; // stay in this mode
|
|
313
|
+
let bestPrev = m;
|
|
314
|
+
for (let p = 0; p < M; p++) {
|
|
315
|
+
if (p === m)
|
|
316
|
+
continue;
|
|
317
|
+
const candidate = cost[p] + header[m];
|
|
318
|
+
if (candidate < bestCost) {
|
|
319
|
+
bestCost = candidate;
|
|
320
|
+
bestPrev = p;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
next[m] = bestCost >= INFEASIBLE ? INFEASIBLE : bestCost + cc;
|
|
324
|
+
from[(i + 1) * M + m] = bestPrev;
|
|
325
|
+
}
|
|
326
|
+
cost = next;
|
|
327
|
+
}
|
|
328
|
+
let bestMode = -1;
|
|
329
|
+
let bestCost = INFEASIBLE;
|
|
330
|
+
for (let m = 0; m < M; m++) {
|
|
331
|
+
if (cost[m] < bestCost) {
|
|
332
|
+
bestCost = cost[m];
|
|
333
|
+
bestMode = m;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (bestMode < 0) {
|
|
337
|
+
throw new EncodeError('QR: no mode can represent this text');
|
|
338
|
+
}
|
|
339
|
+
// Walk the parent pointers back, collecting mode runs.
|
|
340
|
+
const modeAt = new Int8Array(n);
|
|
341
|
+
let m = bestMode;
|
|
342
|
+
for (let i = n; i > 0; i--) {
|
|
343
|
+
modeAt[i - 1] = m;
|
|
344
|
+
m = from[i * M + m];
|
|
345
|
+
}
|
|
346
|
+
const segments = [];
|
|
347
|
+
let start = 0;
|
|
348
|
+
for (let i = 1; i <= n; i++) {
|
|
349
|
+
if (i === n || modeAt[i] !== modeAt[start]) {
|
|
350
|
+
segments.push({ mode: SEARCH_MODES[modeAt[start]], start, end: i });
|
|
351
|
+
start = i;
|
|
356
352
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
next[m] = bestCost >= INFEASIBLE ? INFEASIBLE : bestCost + cc;
|
|
360
|
-
from[(i + 1) * M + m] = bestPrev;
|
|
361
|
-
}
|
|
362
|
-
cost = next;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
let bestMode = -1;
|
|
366
|
-
let bestCost = INFEASIBLE;
|
|
367
|
-
for (let m = 0; m < M; m++) {
|
|
368
|
-
if (cost[m] < bestCost) {
|
|
369
|
-
bestCost = cost[m];
|
|
370
|
-
bestMode = m;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
if (bestMode < 0) {
|
|
374
|
-
throw new EncodeError('QR: no mode can represent this text');
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Walk the parent pointers back, collecting mode runs.
|
|
378
|
-
const modeAt = new Int8Array(n);
|
|
379
|
-
let m = bestMode;
|
|
380
|
-
for (let i = n; i > 0; i--) {
|
|
381
|
-
modeAt[i - 1] = m;
|
|
382
|
-
m = from[i * M + m];
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const segments = [];
|
|
386
|
-
let start = 0;
|
|
387
|
-
for (let i = 1; i <= n; i++) {
|
|
388
|
-
if (i === n || modeAt[i] !== modeAt[start]) {
|
|
389
|
-
segments.push({ mode: SEARCH_MODES[modeAt[start]], start, end: i });
|
|
390
|
-
start = i;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return segments;
|
|
353
|
+
}
|
|
354
|
+
return segments;
|
|
394
355
|
}
|
|
395
|
-
|
|
396
356
|
/**
|
|
397
357
|
* Exact encoded length of a segment.
|
|
398
358
|
*
|
|
@@ -400,28 +360,28 @@ function segmentize(info, version) {
|
|
|
400
360
|
* @returns {number} Bits, including the mode indicator and count field.
|
|
401
361
|
*/
|
|
402
362
|
function segmentBits(seg, info, version) {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
363
|
+
const n = seg.end - seg.start;
|
|
364
|
+
let payload;
|
|
365
|
+
switch (seg.mode) {
|
|
366
|
+
case MODE.NUMERIC:
|
|
367
|
+
payload = 10 * Math.floor(n / 3) + [0, 4, 7][n % 3];
|
|
368
|
+
break;
|
|
369
|
+
case MODE.ALPHANUMERIC:
|
|
370
|
+
payload = 11 * Math.floor(n / 2) + (n % 2) * 6;
|
|
371
|
+
break;
|
|
372
|
+
case MODE.KANJI:
|
|
373
|
+
payload = 13 * n;
|
|
374
|
+
break;
|
|
375
|
+
default: {
|
|
376
|
+
let bytes = 0;
|
|
377
|
+
for (let i = seg.start; i < seg.end; i++)
|
|
378
|
+
bytes += info.byteLen[i];
|
|
379
|
+
payload = bytes * 8;
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return 4 + countBits(seg.mode, version) + payload;
|
|
423
384
|
}
|
|
424
|
-
|
|
425
385
|
/**
|
|
426
386
|
* Character count field value — bytes for byte mode, characters otherwise.
|
|
427
387
|
*
|
|
@@ -429,16 +389,16 @@ function segmentBits(seg, info, version) {
|
|
|
429
389
|
* @returns {number}
|
|
430
390
|
*/
|
|
431
391
|
function segmentCount(seg, info) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
392
|
+
if (seg.mode !== MODE.BYTE)
|
|
393
|
+
return seg.end - seg.start;
|
|
394
|
+
let bytes = 0;
|
|
395
|
+
for (let i = seg.start; i < seg.end; i++)
|
|
396
|
+
bytes += info.byteLen[i];
|
|
397
|
+
return bytes;
|
|
436
398
|
}
|
|
437
|
-
|
|
438
399
|
/* ------------------------------------------------------------------ *
|
|
439
400
|
* Bitstream
|
|
440
401
|
* ------------------------------------------------------------------ */
|
|
441
|
-
|
|
442
402
|
/**
|
|
443
403
|
* Serialise segments into the data codewords for a version and level.
|
|
444
404
|
*
|
|
@@ -447,96 +407,83 @@ function segmentCount(seg, info) {
|
|
|
447
407
|
* @returns {Uint8Array} Exactly `dataCodewords(version, ecc)` bytes.
|
|
448
408
|
*/
|
|
449
409
|
function writeBitstream(segments, info, version, ecc, withEci) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
if (count >= (1 << width)) {
|
|
464
|
-
throw new EncodeError(
|
|
465
|
-
`QR: segment of ${count} does not fit a ${width}-bit count field at version ${version}`
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
writer.put(seg.mode, 4);
|
|
470
|
-
writer.put(count, width);
|
|
471
|
-
|
|
472
|
-
switch (seg.mode) {
|
|
473
|
-
case MODE.NUMERIC: {
|
|
474
|
-
let i = seg.start;
|
|
475
|
-
while (i + 2 < seg.end) {
|
|
476
|
-
writer.put(
|
|
477
|
-
+info.points[i] * 100 + +info.points[i + 1] * 10 + +info.points[i + 2],
|
|
478
|
-
10
|
|
479
|
-
);
|
|
480
|
-
i += 3;
|
|
481
|
-
}
|
|
482
|
-
const left = seg.end - i;
|
|
483
|
-
if (left === 2) writer.put(+info.points[i] * 10 + +info.points[i + 1], 7);
|
|
484
|
-
else if (left === 1) writer.put(+info.points[i], 4);
|
|
485
|
-
break;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
case MODE.ALPHANUMERIC: {
|
|
489
|
-
let i = seg.start;
|
|
490
|
-
while (i + 1 < seg.end) {
|
|
491
|
-
writer.put(info.alnum[i] * 45 + info.alnum[i + 1], 11);
|
|
492
|
-
i += 2;
|
|
410
|
+
const writer = new BitWriter();
|
|
411
|
+
const capacity = dataBitCapacity(version, ecc);
|
|
412
|
+
const encoder = info.utf8 ? new TextEncoder() : null;
|
|
413
|
+
if (withEci) {
|
|
414
|
+
writer.put(MODE.ECI, 4);
|
|
415
|
+
writer.put(ECI_UTF8, 8); // single-byte designator form, values 0-127
|
|
416
|
+
}
|
|
417
|
+
for (let s = 0; s < segments.length; s++) {
|
|
418
|
+
const seg = segments[s];
|
|
419
|
+
const count = segmentCount(seg, info);
|
|
420
|
+
const width = countBits(seg.mode, version);
|
|
421
|
+
if (count >= (1 << width)) {
|
|
422
|
+
throw new EncodeError(`QR: segment of ${count} does not fit a ${width}-bit count field at version ${version}`);
|
|
493
423
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
424
|
+
writer.put(seg.mode, 4);
|
|
425
|
+
writer.put(count, width);
|
|
426
|
+
switch (seg.mode) {
|
|
427
|
+
case MODE.NUMERIC: {
|
|
428
|
+
let i = seg.start;
|
|
429
|
+
while (i + 2 < seg.end) {
|
|
430
|
+
writer.put(+info.points[i] * 100 + +info.points[i + 1] * 10 + +info.points[i + 2], 10);
|
|
431
|
+
i += 3;
|
|
432
|
+
}
|
|
433
|
+
const left = seg.end - i;
|
|
434
|
+
if (left === 2)
|
|
435
|
+
writer.put(+info.points[i] * 10 + +info.points[i + 1], 7);
|
|
436
|
+
else if (left === 1)
|
|
437
|
+
writer.put(+info.points[i], 4);
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
case MODE.ALPHANUMERIC: {
|
|
441
|
+
let i = seg.start;
|
|
442
|
+
while (i + 1 < seg.end) {
|
|
443
|
+
writer.put(info.alnum[i] * 45 + info.alnum[i + 1], 11);
|
|
444
|
+
i += 2;
|
|
445
|
+
}
|
|
446
|
+
if (i < seg.end)
|
|
447
|
+
writer.put(info.alnum[i], 6);
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case MODE.KANJI: {
|
|
451
|
+
for (let i = seg.start; i < seg.end; i++)
|
|
452
|
+
writer.put(info.kanji[i], 13);
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
default: {
|
|
456
|
+
for (let i = seg.start; i < seg.end; i++) {
|
|
457
|
+
const ch = info.points[i];
|
|
458
|
+
if (encoder) {
|
|
459
|
+
writer.putBytes(encoder.encode(ch));
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
writer.put(ch.codePointAt(0) & 0xff, 8);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
511
467
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
);
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const out = new Uint8Array(target);
|
|
531
|
-
out.set(bytes.subarray(0, Math.min(bytes.length, target)));
|
|
532
|
-
|
|
533
|
-
// Pad with the specified alternating filler.
|
|
534
|
-
for (let i = bytes.length; i < target; i++) {
|
|
535
|
-
out[i] = (i - bytes.length) % 2 === 0 ? 0xec : 0x11;
|
|
536
|
-
}
|
|
537
|
-
return out;
|
|
468
|
+
}
|
|
469
|
+
if (writer.length > capacity) {
|
|
470
|
+
throw new EncodeError(`QR: ${writer.length} bits exceed the ${capacity}-bit capacity of version ${version}-${ecc}`);
|
|
471
|
+
}
|
|
472
|
+
// Terminator: up to four zero bits, truncated if the symbol is nearly full.
|
|
473
|
+
const terminator = Math.min(4, capacity - writer.length);
|
|
474
|
+
if (terminator > 0)
|
|
475
|
+
writer.put(0, terminator);
|
|
476
|
+
writer.padToByte();
|
|
477
|
+
const bytes = writer.toBytes();
|
|
478
|
+
const target = capacity / 8;
|
|
479
|
+
const out = new Uint8Array(target);
|
|
480
|
+
out.set(bytes.subarray(0, Math.min(bytes.length, target)));
|
|
481
|
+
// Pad with the specified alternating filler.
|
|
482
|
+
for (let i = bytes.length; i < target; i++) {
|
|
483
|
+
out[i] = (i - bytes.length) % 2 === 0 ? 0xec : 0x11;
|
|
484
|
+
}
|
|
485
|
+
return out;
|
|
538
486
|
}
|
|
539
|
-
|
|
540
487
|
/**
|
|
541
488
|
* Split into blocks, add Reed-Solomon parity, and interleave.
|
|
542
489
|
*
|
|
@@ -549,48 +496,44 @@ function writeBitstream(segments, info, version, ecc, withEci) {
|
|
|
549
496
|
* @returns {Uint8Array}
|
|
550
497
|
*/
|
|
551
498
|
function interleave(data, layout) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
return out;
|
|
499
|
+
const blocks = [];
|
|
500
|
+
let offset = 0;
|
|
501
|
+
for (let b = 0; b < layout.blockCount; b++) {
|
|
502
|
+
const count = b < layout.group1Blocks ? layout.group1DataCount : layout.group2DataCount;
|
|
503
|
+
const chunk = data.subarray(offset, offset + count);
|
|
504
|
+
offset += count;
|
|
505
|
+
blocks.push({ data: chunk, ecc: rsEncode(chunk, layout.eccPerBlock, GF256_QR, 0) });
|
|
506
|
+
}
|
|
507
|
+
const out = new Uint8Array(layout.totalCodewords);
|
|
508
|
+
let n = 0;
|
|
509
|
+
const maxData = layout.group2Blocks > 0 ? layout.group2DataCount : layout.group1DataCount;
|
|
510
|
+
for (let i = 0; i < maxData; i++) {
|
|
511
|
+
for (let b = 0; b < blocks.length; b++) {
|
|
512
|
+
if (i < blocks[b].data.length)
|
|
513
|
+
out[n++] = blocks[b].data[i];
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
for (let i = 0; i < layout.eccPerBlock; i++) {
|
|
517
|
+
for (let b = 0; b < blocks.length; b++)
|
|
518
|
+
out[n++] = blocks[b].ecc[i];
|
|
519
|
+
}
|
|
520
|
+
return out;
|
|
575
521
|
}
|
|
576
|
-
|
|
577
522
|
/* ------------------------------------------------------------------ *
|
|
578
523
|
* BCH
|
|
579
524
|
* ------------------------------------------------------------------ */
|
|
580
|
-
|
|
581
525
|
/**
|
|
582
526
|
* @param {number} v
|
|
583
527
|
* @returns {number} Position of the highest set bit, plus one.
|
|
584
528
|
*/
|
|
585
529
|
function bitLength(v) {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
530
|
+
let n = 0;
|
|
531
|
+
while (v !== 0) {
|
|
532
|
+
n++;
|
|
533
|
+
v >>>= 1;
|
|
534
|
+
}
|
|
535
|
+
return n;
|
|
592
536
|
}
|
|
593
|
-
|
|
594
537
|
/**
|
|
595
538
|
* Remainder of `value` modulo a BCH generator polynomial, over GF(2).
|
|
596
539
|
*
|
|
@@ -598,14 +541,13 @@ function bitLength(v) {
|
|
|
598
541
|
* @returns {number}
|
|
599
542
|
*/
|
|
600
543
|
function bchRemainder(value, generator) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
544
|
+
const degree = bitLength(generator) - 1;
|
|
545
|
+
let v = value;
|
|
546
|
+
while (bitLength(v) > degree) {
|
|
547
|
+
v ^= generator << (bitLength(v) - degree - 1);
|
|
548
|
+
}
|
|
549
|
+
return v;
|
|
607
550
|
}
|
|
608
|
-
|
|
609
551
|
/**
|
|
610
552
|
* The 15-bit masked format information.
|
|
611
553
|
*
|
|
@@ -613,12 +555,12 @@ function bchRemainder(value, generator) {
|
|
|
613
555
|
* @returns {number}
|
|
614
556
|
*/
|
|
615
557
|
export function formatInfoBits(ecc, mask) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
558
|
+
const level = ECC_LEVEL_BITS[ecc];
|
|
559
|
+
if (level === undefined)
|
|
560
|
+
throw new EncodeError(`QR: unknown error correction level "${ecc}"`);
|
|
561
|
+
const data = (level << 3) | mask;
|
|
562
|
+
return ((data << 10) | bchRemainder(data << 10, FORMAT_GENERATOR)) ^ FORMAT_MASK;
|
|
620
563
|
}
|
|
621
|
-
|
|
622
564
|
/**
|
|
623
565
|
* The 18-bit version information, for versions 7 and up.
|
|
624
566
|
*
|
|
@@ -626,13 +568,11 @@ export function formatInfoBits(ecc, mask) {
|
|
|
626
568
|
* @returns {number}
|
|
627
569
|
*/
|
|
628
570
|
export function versionInfoBits(version) {
|
|
629
|
-
|
|
571
|
+
return (version << 12) | bchRemainder(version << 12, VERSION_GENERATOR);
|
|
630
572
|
}
|
|
631
|
-
|
|
632
573
|
/* ------------------------------------------------------------------ *
|
|
633
574
|
* Module layout
|
|
634
575
|
* ------------------------------------------------------------------ */
|
|
635
|
-
|
|
636
576
|
/**
|
|
637
577
|
* Clear a rectangle. The counterpart of `setRegion`, which BitMatrix does not
|
|
638
578
|
* need often enough to carry.
|
|
@@ -641,81 +581,75 @@ export function versionInfoBits(version) {
|
|
|
641
581
|
* @param {number} w @param {number} h
|
|
642
582
|
*/
|
|
643
583
|
function clearRegion(m, x, y, w, h) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
584
|
+
for (let j = y; j < y + h; j++) {
|
|
585
|
+
for (let i = x; i < x + w; i++)
|
|
586
|
+
m.unset(i, j);
|
|
587
|
+
}
|
|
647
588
|
}
|
|
648
|
-
|
|
649
589
|
/**
|
|
650
590
|
* Draw finders, separators, timing, alignment and the dark module.
|
|
651
591
|
*
|
|
652
592
|
* @param {BitMatrix} m @param {number} version
|
|
653
593
|
*/
|
|
654
594
|
function drawFunctionPatterns(m, version) {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
m.set(
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// The dark module, which is always set and carries no information.
|
|
686
|
-
m.set(8, size - 8);
|
|
595
|
+
const size = versionSize(version);
|
|
596
|
+
// Finder patterns: 7x7 dark ring, light ring, 3x3 dark core. Separators are
|
|
597
|
+
// simply left light, which they already are.
|
|
598
|
+
const corners = [[0, 0], [size - 7, 0], [0, size - 7]];
|
|
599
|
+
for (let c = 0; c < corners.length; c++) {
|
|
600
|
+
const [x, y] = corners[c];
|
|
601
|
+
m.setRegion(x, y, 7, 7);
|
|
602
|
+
clearRegion(m, x + 1, y + 1, 5, 5);
|
|
603
|
+
m.setRegion(x + 2, y + 2, 3, 3);
|
|
604
|
+
}
|
|
605
|
+
// Timing patterns: dark on even coordinates, which keeps them in phase with
|
|
606
|
+
// the finder edges they run between.
|
|
607
|
+
for (let i = 8; i < size - 8; i++) {
|
|
608
|
+
if ((i & 1) === 0) {
|
|
609
|
+
m.set(i, 6);
|
|
610
|
+
m.set(6, i);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
// Alignment patterns: 5x5 dark ring, light ring, single dark centre.
|
|
614
|
+
const centres = alignmentCentres(version);
|
|
615
|
+
for (let c = 0; c < centres.length; c++) {
|
|
616
|
+
const [cx, cy] = centres[c];
|
|
617
|
+
m.setRegion(cx - 2, cy - 2, 5, 5);
|
|
618
|
+
clearRegion(m, cx - 1, cy - 1, 3, 3);
|
|
619
|
+
m.set(cx, cy);
|
|
620
|
+
}
|
|
621
|
+
// The dark module, which is always set and carries no information.
|
|
622
|
+
m.set(8, size - 8);
|
|
687
623
|
}
|
|
688
|
-
|
|
689
624
|
/**
|
|
690
625
|
* @param {BitMatrix} m @param {number} version @param {string} ecc @param {number} mask
|
|
691
626
|
*/
|
|
692
627
|
function drawFormatInfo(m, version, ecc, mask) {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
628
|
+
const size = versionSize(version);
|
|
629
|
+
const bits = formatInfoBits(ecc, mask);
|
|
630
|
+
const [a, b] = formatInfoPositions(size);
|
|
631
|
+
for (let i = 0; i < 15; i++) {
|
|
632
|
+
const on = ((bits >> i) & 1) === 1;
|
|
633
|
+
m.setValue(a[i][0], a[i][1], on);
|
|
634
|
+
m.setValue(b[i][0], b[i][1], on);
|
|
635
|
+
}
|
|
701
636
|
}
|
|
702
|
-
|
|
703
637
|
/**
|
|
704
638
|
* @param {BitMatrix} m @param {number} version
|
|
705
639
|
*/
|
|
706
640
|
function drawVersionInfo(m, version) {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
641
|
+
if (version < VERSION_INFO_MIN)
|
|
642
|
+
return;
|
|
643
|
+
const size = versionSize(version);
|
|
644
|
+
const bits = versionInfoBits(version);
|
|
645
|
+
for (let i = 0; i < 18; i++) {
|
|
646
|
+
const on = ((bits >> i) & 1) === 1;
|
|
647
|
+
const major = Math.floor(i / 3);
|
|
648
|
+
const minor = i % 3;
|
|
649
|
+
m.setValue(major, size - 11 + minor, on); // bottom-left block
|
|
650
|
+
m.setValue(size - 11 + minor, major, on); // top-right block, transposed
|
|
651
|
+
}
|
|
717
652
|
}
|
|
718
|
-
|
|
719
653
|
/**
|
|
720
654
|
* Lay the interleaved codewords along the zig-zag path, applying the mask.
|
|
721
655
|
*
|
|
@@ -723,28 +657,25 @@ function drawVersionInfo(m, version) {
|
|
|
723
657
|
* @param {number} mask
|
|
724
658
|
*/
|
|
725
659
|
function placeData(m, version, codewords, mask) {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
660
|
+
const order = dataModuleOrder(version);
|
|
661
|
+
const available = codewords.length * 8;
|
|
662
|
+
for (let p = 0, bit = 0; p < order.length; p += 2, bit++) {
|
|
663
|
+
const x = order[p];
|
|
664
|
+
const y = order[p + 1];
|
|
665
|
+
// Past the last codeword are the remainder bits, which are always zero.
|
|
666
|
+
let dark = bit < available &&
|
|
667
|
+
((codewords[bit >> 3] >> (7 - (bit & 7))) & 1) === 1;
|
|
668
|
+
if (maskBit(mask, x, y))
|
|
669
|
+
dark = !dark;
|
|
670
|
+
m.setValue(x, y, dark);
|
|
671
|
+
}
|
|
738
672
|
}
|
|
739
|
-
|
|
740
673
|
/* ------------------------------------------------------------------ *
|
|
741
674
|
* Mask scoring
|
|
742
675
|
* ------------------------------------------------------------------ */
|
|
743
|
-
|
|
744
676
|
/** The 11-module finder-lookalike runs that rule 3 punishes. */
|
|
745
677
|
const RULE3_A = 0b10111010000;
|
|
746
678
|
const RULE3_B = 0b00001011101;
|
|
747
|
-
|
|
748
679
|
/**
|
|
749
680
|
* The four penalty rules. Lower is better.
|
|
750
681
|
*
|
|
@@ -758,68 +689,66 @@ const RULE3_B = 0b00001011101;
|
|
|
758
689
|
* @returns {number}
|
|
759
690
|
*/
|
|
760
691
|
export function maskPenalty(m) {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
692
|
+
const size = m.width;
|
|
693
|
+
let penalty = 0;
|
|
694
|
+
// Rule 1: runs of five or more identical modules, per row and per column.
|
|
695
|
+
for (let axis = 0; axis < 2; axis++) {
|
|
696
|
+
for (let a = 0; a < size; a++) {
|
|
697
|
+
let run = 1;
|
|
698
|
+
let prev = axis === 0 ? m.get(0, a) : m.get(a, 0);
|
|
699
|
+
for (let b = 1; b < size; b++) {
|
|
700
|
+
const cur = axis === 0 ? m.get(b, a) : m.get(a, b);
|
|
701
|
+
if (cur === prev) {
|
|
702
|
+
run++;
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
if (run >= 5)
|
|
706
|
+
penalty += 3 + (run - 5);
|
|
707
|
+
prev = cur;
|
|
708
|
+
run = 1;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (run >= 5)
|
|
712
|
+
penalty += 3 + (run - 5);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
// Rule 2: every 2x2 block of one colour.
|
|
716
|
+
for (let y = 0; y < size - 1; y++) {
|
|
717
|
+
for (let x = 0; x < size - 1; x++) {
|
|
718
|
+
const v = m.get(x, y);
|
|
719
|
+
if (v === m.get(x + 1, y) && v === m.get(x, y + 1) && v === m.get(x + 1, y + 1)) {
|
|
720
|
+
penalty += 3;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
// Rule 3: the 1:1:3:1:1 finder ratio with four light modules on one side.
|
|
725
|
+
// An 11-module sliding window in each direction catches both orientations.
|
|
726
|
+
for (let axis = 0; axis < 2; axis++) {
|
|
727
|
+
for (let a = 0; a < size; a++) {
|
|
728
|
+
let window = 0;
|
|
729
|
+
for (let b = 0; b < size; b++) {
|
|
730
|
+
const bit = (axis === 0 ? m.get(b, a) : m.get(a, b)) ? 1 : 0;
|
|
731
|
+
window = ((window << 1) | bit) & 0x7ff;
|
|
732
|
+
if (b >= 10 && (window === RULE3_A || window === RULE3_B))
|
|
733
|
+
penalty += 40;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
// Rule 4: departure from an even split of dark and light.
|
|
738
|
+
let dark = 0;
|
|
739
|
+
for (let y = 0; y < size; y++) {
|
|
740
|
+
for (let x = 0; x < size; x++) {
|
|
741
|
+
if (m.get(x, y))
|
|
742
|
+
dark++;
|
|
777
743
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
// Rule 2: every 2x2 block of one colour.
|
|
784
|
-
for (let y = 0; y < size - 1; y++) {
|
|
785
|
-
for (let x = 0; x < size - 1; x++) {
|
|
786
|
-
const v = m.get(x, y);
|
|
787
|
-
if (v === m.get(x + 1, y) && v === m.get(x, y + 1) && v === m.get(x + 1, y + 1)) {
|
|
788
|
-
penalty += 3;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Rule 3: the 1:1:3:1:1 finder ratio with four light modules on one side.
|
|
794
|
-
// An 11-module sliding window in each direction catches both orientations.
|
|
795
|
-
for (let axis = 0; axis < 2; axis++) {
|
|
796
|
-
for (let a = 0; a < size; a++) {
|
|
797
|
-
let window = 0;
|
|
798
|
-
for (let b = 0; b < size; b++) {
|
|
799
|
-
const bit = (axis === 0 ? m.get(b, a) : m.get(a, b)) ? 1 : 0;
|
|
800
|
-
window = ((window << 1) | bit) & 0x7ff;
|
|
801
|
-
if (b >= 10 && (window === RULE3_A || window === RULE3_B)) penalty += 40;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
// Rule 4: departure from an even split of dark and light.
|
|
807
|
-
let dark = 0;
|
|
808
|
-
for (let y = 0; y < size; y++) {
|
|
809
|
-
for (let x = 0; x < size; x++) {
|
|
810
|
-
if (m.get(x, y)) dark++;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
const percent = (dark * 100) / (size * size);
|
|
814
|
-
penalty += Math.floor(Math.abs(percent - 50) / 5) * 10;
|
|
815
|
-
|
|
816
|
-
return penalty;
|
|
744
|
+
}
|
|
745
|
+
const percent = (dark * 100) / (size * size);
|
|
746
|
+
penalty += Math.floor(Math.abs(percent - 50) / 5) * 10;
|
|
747
|
+
return penalty;
|
|
817
748
|
}
|
|
818
|
-
|
|
819
749
|
/* ------------------------------------------------------------------ *
|
|
820
750
|
* Public API
|
|
821
751
|
* ------------------------------------------------------------------ */
|
|
822
|
-
|
|
823
752
|
/**
|
|
824
753
|
* Build the module grid for a version, level, mask and payload.
|
|
825
754
|
*
|
|
@@ -828,14 +757,13 @@ export function maskPenalty(m) {
|
|
|
828
757
|
* @returns {BitMatrix}
|
|
829
758
|
*/
|
|
830
759
|
function buildMatrix(version, ecc, mask, codewords) {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
760
|
+
const m = new BitMatrix(versionSize(version));
|
|
761
|
+
drawFunctionPatterns(m, version);
|
|
762
|
+
placeData(m, version, codewords, mask);
|
|
763
|
+
drawFormatInfo(m, version, ecc, mask);
|
|
764
|
+
drawVersionInfo(m, version);
|
|
765
|
+
return m;
|
|
837
766
|
}
|
|
838
|
-
|
|
839
767
|
/**
|
|
840
768
|
* Encode text as a QR Code.
|
|
841
769
|
*
|
|
@@ -850,109 +778,96 @@ function buildMatrix(version, ecc, mask, codewords) {
|
|
|
850
778
|
* @throws {EncodeError} If the text does not fit, or the options are invalid.
|
|
851
779
|
*/
|
|
852
780
|
export function encodeQR(text, options = {}) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const forcedVersion = options.version;
|
|
863
|
-
if (forcedVersion !== undefined) {
|
|
864
|
-
if (!Number.isInteger(forcedVersion) || forcedVersion < MIN_VERSION || forcedVersion > MAX_VERSION) {
|
|
865
|
-
throw new EncodeError(`QR: version must be an integer 1-40, got ${forcedVersion}`);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
const forcedMask = options.mask;
|
|
870
|
-
if (forcedMask !== undefined && (!Number.isInteger(forcedMask) || forcedMask < 0 || forcedMask > 7)) {
|
|
871
|
-
throw new EncodeError(`QR: mask must be an integer 0-7, got ${forcedMask}`);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// Byte mode interpretation. ISO-8859-1 is the default ECI, so Latin-1 text
|
|
875
|
-
// needs no header; anything else goes out as UTF-8 with ECI 26 announced.
|
|
876
|
-
const charset = options.charset ?? 'auto';
|
|
877
|
-
let utf8;
|
|
878
|
-
if (charset === 'utf-8') utf8 = true;
|
|
879
|
-
else if (charset === 'iso-8859-1') utf8 = false;
|
|
880
|
-
else if (charset === 'auto') utf8 = !isLatin1(text);
|
|
881
|
-
else throw new EncodeError(`QR: unknown charset "${charset}"`);
|
|
882
|
-
|
|
883
|
-
if (!utf8 && !isLatin1(text)) {
|
|
884
|
-
throw new EncodeError('QR: text contains characters outside ISO-8859-1');
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
const allowKanji = options.kanji !== false;
|
|
888
|
-
const info = classify(text, utf8, allowKanji);
|
|
889
|
-
|
|
890
|
-
// Solve each version band once — the count field widths, and therefore the
|
|
891
|
-
// cheapest segmentation, are constant within a band.
|
|
892
|
-
const bands = [[1, 9], [10, 26], [27, 40]];
|
|
893
|
-
let chosen = null;
|
|
894
|
-
|
|
895
|
-
for (let b = 0; b < bands.length && !chosen; b++) {
|
|
896
|
-
const [lo, hi] = bands[b];
|
|
897
|
-
if (forcedVersion !== undefined && (forcedVersion < lo || forcedVersion > hi)) continue;
|
|
898
|
-
|
|
899
|
-
const segments = segmentize(info, lo);
|
|
900
|
-
const needsEci = utf8 && segments.some((s) => s.mode === MODE.BYTE);
|
|
901
|
-
|
|
902
|
-
let bits = needsEci ? 12 : 0; // ECI mode indicator plus one designator byte
|
|
903
|
-
for (let s = 0; s < segments.length; s++) bits += segmentBits(segments[s], info, lo);
|
|
904
|
-
|
|
905
|
-
const from = forcedVersion !== undefined ? forcedVersion : lo;
|
|
906
|
-
const to = forcedVersion !== undefined ? forcedVersion : hi;
|
|
907
|
-
for (let v = from; v <= to; v++) {
|
|
908
|
-
if (bits <= dataBitCapacity(v, ecc)) {
|
|
909
|
-
chosen = { version: v, segments, needsEci, bits };
|
|
910
|
-
break;
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
if (!chosen) {
|
|
781
|
+
if (typeof text !== 'string') {
|
|
782
|
+
throw new EncodeError('QR: text must be a string');
|
|
783
|
+
}
|
|
784
|
+
const ecc = options.ecc ?? 'M';
|
|
785
|
+
if (ECC_LEVELS.indexOf(ecc) === -1) {
|
|
786
|
+
throw new EncodeError(`QR: error correction level must be L, M, Q or H, got "${ecc}"`);
|
|
787
|
+
}
|
|
788
|
+
const forcedVersion = options.version;
|
|
916
789
|
if (forcedVersion !== undefined) {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
790
|
+
if (!Number.isInteger(forcedVersion) || forcedVersion < MIN_VERSION || forcedVersion > MAX_VERSION) {
|
|
791
|
+
throw new EncodeError(`QR: version must be an integer 1-40, got ${forcedVersion}`);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
const forcedMask = options.mask;
|
|
795
|
+
if (forcedMask !== undefined && (!Number.isInteger(forcedMask) || forcedMask < 0 || forcedMask > 7)) {
|
|
796
|
+
throw new EncodeError(`QR: mask must be an integer 0-7, got ${forcedMask}`);
|
|
797
|
+
}
|
|
798
|
+
// Byte mode interpretation. ISO-8859-1 is the default ECI, so Latin-1 text
|
|
799
|
+
// needs no header; anything else goes out as UTF-8 with ECI 26 announced.
|
|
800
|
+
const charset = options.charset ?? 'auto';
|
|
801
|
+
let utf8;
|
|
802
|
+
if (charset === 'utf-8')
|
|
803
|
+
utf8 = true;
|
|
804
|
+
else if (charset === 'iso-8859-1')
|
|
805
|
+
utf8 = false;
|
|
806
|
+
else if (charset === 'auto')
|
|
807
|
+
utf8 = !isLatin1(text);
|
|
808
|
+
else
|
|
809
|
+
throw new EncodeError(`QR: unknown charset "${charset}"`);
|
|
810
|
+
if (!utf8 && !isLatin1(text)) {
|
|
811
|
+
throw new EncodeError('QR: text contains characters outside ISO-8859-1');
|
|
812
|
+
}
|
|
813
|
+
const allowKanji = options.kanji !== false;
|
|
814
|
+
const info = classify(text, utf8, allowKanji);
|
|
815
|
+
// Solve each version band once — the count field widths, and therefore the
|
|
816
|
+
// cheapest segmentation, are constant within a band.
|
|
817
|
+
const bands = [[1, 9], [10, 26], [27, 40]];
|
|
818
|
+
let chosen = null;
|
|
819
|
+
for (let b = 0; b < bands.length && !chosen; b++) {
|
|
820
|
+
const [lo, hi] = bands[b];
|
|
821
|
+
if (forcedVersion !== undefined && (forcedVersion < lo || forcedVersion > hi))
|
|
822
|
+
continue;
|
|
823
|
+
const segments = segmentize(info, lo);
|
|
824
|
+
const needsEci = utf8 && segments.some((s) => s.mode === MODE.BYTE);
|
|
825
|
+
let bits = needsEci ? 12 : 0; // ECI mode indicator plus one designator byte
|
|
826
|
+
for (let s = 0; s < segments.length; s++)
|
|
827
|
+
bits += segmentBits(segments[s], info, lo);
|
|
828
|
+
const from = forcedVersion !== undefined ? forcedVersion : lo;
|
|
829
|
+
const to = forcedVersion !== undefined ? forcedVersion : hi;
|
|
830
|
+
for (let v = from; v <= to; v++) {
|
|
831
|
+
if (bits <= dataBitCapacity(v, ecc)) {
|
|
832
|
+
chosen = { version: v, segments, needsEci, bits };
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (!chosen) {
|
|
838
|
+
if (forcedVersion !== undefined) {
|
|
839
|
+
throw new EncodeError(`QR: text does not fit version ${forcedVersion}-${ecc} ` +
|
|
840
|
+
`(capacity ${dataBitCapacity(forcedVersion, ecc)} bits)`);
|
|
841
|
+
}
|
|
842
|
+
throw new EncodeError(`QR: text is too long for any version at error correction level ${ecc}`);
|
|
843
|
+
}
|
|
844
|
+
const { version, segments, needsEci } = chosen;
|
|
845
|
+
const layout = blockLayout(version, ecc);
|
|
846
|
+
const data = writeBitstream(segments, info, version, ecc, needsEci);
|
|
847
|
+
const codewords = interleave(data, layout);
|
|
848
|
+
if (forcedMask !== undefined) {
|
|
849
|
+
return buildMatrix(version, ecc, forcedMask, codewords);
|
|
850
|
+
}
|
|
851
|
+
let best = null;
|
|
852
|
+
let bestScore = Infinity;
|
|
853
|
+
for (let mask = 0; mask < 8; mask++) {
|
|
854
|
+
const candidate = buildMatrix(version, ecc, mask, codewords);
|
|
855
|
+
const score = maskPenalty(candidate);
|
|
856
|
+
if (score < bestScore) {
|
|
857
|
+
bestScore = score;
|
|
858
|
+
best = candidate;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return best;
|
|
947
862
|
}
|
|
948
|
-
|
|
949
863
|
/**
|
|
950
864
|
* @param {string} text
|
|
951
865
|
* @returns {boolean} True if every code point fits one ISO-8859-1 byte.
|
|
952
866
|
*/
|
|
953
867
|
function isLatin1(text) {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
868
|
+
for (let i = 0; i < text.length; i++) {
|
|
869
|
+
if (text.charCodeAt(i) > 0xff)
|
|
870
|
+
return false;
|
|
871
|
+
}
|
|
872
|
+
return true;
|
|
958
873
|
}
|