@sythos/js_barcode_universal 1.5.8 → 1.5.10

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.
Files changed (179) hide show
  1. package/LICENSE +5 -4
  2. package/NOTICE.md +2 -2
  3. package/README.md +61 -13
  4. package/bundle/sythos-barcode.esm.js +11658 -11244
  5. package/bundle/sythos-barcode.js +11658 -11244
  6. package/package.json +10 -3
  7. package/src/index.d.ts +1 -1
  8. package/src/index.js +508 -516
  9. package/src/js/aztec/decoder.js +208 -199
  10. package/src/js/aztec/detector.js +153 -126
  11. package/src/js/aztec/encoder.js +172 -153
  12. package/src/js/aztec/high-level.js +106 -101
  13. package/src/js/aztec/index.js +1 -12
  14. package/src/js/aztec/tables.js +106 -120
  15. package/src/js/aztecrune/decoder.js +76 -85
  16. package/src/js/aztecrune/detector.js +107 -93
  17. package/src/js/aztecrune/encoder.js +50 -68
  18. package/src/js/aztecrune/index.js +1 -16
  19. package/src/js/aztecrune/tables.js +59 -54
  20. package/src/js/compactpdf417/decoder.js +69 -74
  21. package/src/js/compactpdf417/detector.js +74 -75
  22. package/src/js/compactpdf417/encoder.js +68 -81
  23. package/src/js/compactpdf417/index.js +1 -13
  24. package/src/js/compactpdf417/tables.js +71 -80
  25. package/src/js/core/bit-buffer.js +106 -122
  26. package/src/js/core/bit-matrix.js +196 -194
  27. package/src/js/core/errors.js +13 -15
  28. package/src/js/core/galois-field.js +119 -133
  29. package/src/js/core/index.js +2 -19
  30. package/src/js/core/reed-solomon.js +195 -204
  31. package/src/js/databar/codec.js +93 -108
  32. package/src/js/databar/decoder.js +149 -140
  33. package/src/js/databar/encoder.js +47 -55
  34. package/src/js/databar/gs1.js +122 -123
  35. package/src/js/databar/index.js +4 -36
  36. package/src/js/databar/patterns.js +81 -85
  37. package/src/js/databar/tables.js +51 -52
  38. package/src/js/datamatrix/decoder.js +216 -182
  39. package/src/js/datamatrix/detector.js +161 -132
  40. package/src/js/datamatrix/encoder.js +192 -133
  41. package/src/js/datamatrix/index.js +1 -9
  42. package/src/js/datamatrix/tables.js +68 -69
  43. package/src/js/frameqr/decoder.js +102 -125
  44. package/src/js/frameqr/detector.js +101 -94
  45. package/src/js/frameqr/encoder.js +67 -80
  46. package/src/js/frameqr/index.js +1 -10
  47. package/src/js/frameqr/tables.js +145 -163
  48. package/src/js/image/binarizer.js +146 -162
  49. package/src/js/image/grid-sampler.js +63 -79
  50. package/src/js/image/index.js +0 -2
  51. package/src/js/image/luminance.js +129 -140
  52. package/src/js/image/perspective.js +112 -149
  53. package/src/js/micropdf417/compaction.js +64 -65
  54. package/src/js/micropdf417/decoder.js +106 -109
  55. package/src/js/micropdf417/detector.js +73 -70
  56. package/src/js/micropdf417/encoder.js +129 -154
  57. package/src/js/micropdf417/error-correction.js +6 -11
  58. package/src/js/micropdf417/index.js +2 -16
  59. package/src/js/micropdf417/tables.js +112 -99
  60. package/src/js/microqr/decoder.js +177 -182
  61. package/src/js/microqr/detector.js +240 -256
  62. package/src/js/microqr/encoder.js +239 -194
  63. package/src/js/microqr/index.js +0 -2
  64. package/src/js/microqr/tables.js +208 -200
  65. package/src/js/oned/addons.js +167 -191
  66. package/src/js/oned/index.js +27 -58
  67. package/src/js/oned/patterns.js +194 -213
  68. package/src/js/oned/reader.js +1003 -984
  69. package/src/js/oned/writers.js +411 -466
  70. package/src/js/pdf417/compaction.js +315 -222
  71. package/src/js/pdf417/decoder.js +71 -36
  72. package/src/js/pdf417/detector.js +377 -361
  73. package/src/js/pdf417/encoder.js +57 -46
  74. package/src/js/pdf417/error-correction.js +5 -8
  75. package/src/js/pdf417/tables.js +248 -250
  76. package/src/js/qr/decoder.js +311 -347
  77. package/src/js/qr/detector.js +378 -395
  78. package/src/js/qr/encoder.js +514 -599
  79. package/src/js/qr/index.js +0 -2
  80. package/src/js/qr/tables.js +375 -422
  81. package/src/js/render/image-data.js +57 -62
  82. package/src/js/render/index.js +21 -23
  83. package/src/js/render/options.js +82 -90
  84. package/src/js/render/png.js +144 -169
  85. package/src/js/render/svg.js +41 -44
  86. package/src/js/render/webgl.js +103 -109
  87. package/src/js/render/webgpu.js +205 -217
  88. package/src/js/rmqr/decoder.js +161 -53
  89. package/src/js/rmqr/detector.js +73 -41
  90. package/src/js/rmqr/encoder.js +253 -104
  91. package/src/js/rmqr/index.js +1 -5
  92. package/src/js/rmqr/tables.js +108 -85
  93. package/src/ts/aztec/decoder.ts +317 -0
  94. package/src/ts/aztec/detector.ts +224 -0
  95. package/src/ts/aztec/encoder.ts +257 -0
  96. package/src/ts/aztec/high-level.ts +211 -0
  97. package/src/ts/aztec/index.ts +45 -0
  98. package/src/ts/aztec/tables.ts +210 -0
  99. package/src/ts/aztecrune/decoder.ts +155 -0
  100. package/src/ts/aztecrune/detector.ts +166 -0
  101. package/src/ts/aztecrune/encoder.ts +121 -0
  102. package/src/ts/aztecrune/index.ts +49 -0
  103. package/src/ts/aztecrune/tables.ts +137 -0
  104. package/src/ts/compactpdf417/decoder.ts +128 -0
  105. package/src/ts/compactpdf417/detector.ts +139 -0
  106. package/src/ts/compactpdf417/encoder.ts +140 -0
  107. package/src/ts/compactpdf417/index.ts +17 -0
  108. package/src/ts/compactpdf417/tables.ts +173 -0
  109. package/src/ts/core/bit-buffer.ts +174 -0
  110. package/src/ts/core/bit-matrix.ts +241 -0
  111. package/src/ts/core/errors.ts +61 -0
  112. package/src/ts/core/galois-field.ts +207 -0
  113. package/src/ts/core/index.ts +56 -0
  114. package/src/ts/core/reed-solomon.ts +327 -0
  115. package/src/ts/databar/codec.ts +181 -0
  116. package/src/ts/databar/decoder.ts +215 -0
  117. package/src/ts/databar/encoder.ts +96 -0
  118. package/src/ts/databar/gs1.ts +177 -0
  119. package/src/ts/databar/index.ts +68 -0
  120. package/src/ts/databar/patterns.ts +143 -0
  121. package/src/ts/databar/tables.ts +124 -0
  122. package/src/ts/datamatrix/decoder.ts +262 -0
  123. package/src/ts/datamatrix/detector.ts +225 -0
  124. package/src/ts/datamatrix/encoder.ts +191 -0
  125. package/src/ts/datamatrix/index.ts +42 -0
  126. package/src/ts/datamatrix/tables.ts +123 -0
  127. package/src/ts/frameqr/decoder.ts +239 -0
  128. package/src/ts/frameqr/detector.ts +192 -0
  129. package/src/ts/frameqr/encoder.ts +156 -0
  130. package/src/ts/frameqr/index.ts +42 -0
  131. package/src/ts/frameqr/tables.ts +270 -0
  132. package/src/ts/image/binarizer.ts +270 -0
  133. package/src/ts/image/grid-sampler.ts +164 -0
  134. package/src/ts/image/index.ts +40 -0
  135. package/src/ts/image/luminance.ts +196 -0
  136. package/src/ts/image/perspective.ts +195 -0
  137. package/src/ts/index.d.ts +1 -1
  138. package/src/ts/index.ts +790 -0
  139. package/src/ts/micropdf417/compaction.ts +116 -0
  140. package/src/ts/micropdf417/decoder.ts +183 -0
  141. package/src/ts/micropdf417/detector.ts +149 -0
  142. package/src/ts/micropdf417/encoder.ts +209 -0
  143. package/src/ts/micropdf417/error-correction.ts +55 -0
  144. package/src/ts/micropdf417/index.ts +49 -0
  145. package/src/ts/micropdf417/tables.ts +184 -0
  146. package/src/ts/microqr/decoder.ts +245 -0
  147. package/src/ts/microqr/detector.ts +355 -0
  148. package/src/ts/microqr/encoder.ts +269 -0
  149. package/src/ts/microqr/index.ts +36 -0
  150. package/src/ts/microqr/tables.ts +316 -0
  151. package/src/ts/oned/addons.ts +420 -0
  152. package/src/ts/oned/index.ts +106 -0
  153. package/src/ts/oned/patterns.ts +384 -0
  154. package/src/ts/oned/reader.ts +1364 -0
  155. package/src/ts/oned/writers.ts +746 -0
  156. package/src/ts/pdf417/compaction.ts +298 -0
  157. package/src/ts/pdf417/decoder.ts +75 -0
  158. package/src/ts/pdf417/detector.ts +468 -0
  159. package/src/ts/pdf417/encoder.ts +91 -0
  160. package/src/ts/pdf417/error-correction.ts +47 -0
  161. package/src/ts/pdf417/index.ts +6 -0
  162. package/src/ts/pdf417/tables.ts +317 -0
  163. package/src/ts/qr/decoder.ts +575 -0
  164. package/src/ts/qr/detector.ts +630 -0
  165. package/src/ts/qr/encoder.ts +958 -0
  166. package/src/ts/qr/index.ts +44 -0
  167. package/src/ts/qr/tables.ts +737 -0
  168. package/src/ts/render/image-data.ts +125 -0
  169. package/src/ts/render/index.ts +130 -0
  170. package/src/ts/render/options.ts +160 -0
  171. package/src/ts/render/png.ts +295 -0
  172. package/src/ts/render/svg.ts +120 -0
  173. package/src/ts/render/webgl.ts +206 -0
  174. package/src/ts/render/webgpu.ts +369 -0
  175. package/src/ts/rmqr/decoder.ts +101 -0
  176. package/src/ts/rmqr/detector.ts +90 -0
  177. package/src/ts/rmqr/encoder.ts +172 -0
  178. package/src/ts/rmqr/index.ts +37 -0
  179. package/src/ts/rmqr/tables.ts +154 -0
@@ -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
- return ch >= '0' && ch <= '9';
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
- return ALPHANUMERIC_CHARS.indexOf(ch);
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
- if (cp < 0x80) return 1;
132
- if (cp < 0x800) return 2;
133
- if (cp < 0x10000) return 3;
134
- return 4;
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
- // The trail byte must be a real Shift_JIS one. This is not defensive
153
- // decoration: the packing is only injective while the rebased low byte stays
154
- // below 0xC0, which holds for trail bytes 0x40-0xFC and fails for anything
155
- // below 0x40. Accepting those would silently map two inputs to one value.
156
- const trail = sjis & 0xff;
157
- if (trail < 0x40 || trail === 0x7f || trail > 0xfc) return -1;
158
-
159
- let v;
160
- if (sjis >= 0x8140 && sjis <= 0x9ffc) v = sjis - 0x8140;
161
- else if (sjis >= 0xe040 && sjis <= 0xebbf) v = sjis - 0xc140;
162
- else return -1;
163
-
164
- const packed = ((v >> 8) * 0xc0) + (v & 0xff);
165
- return packed > 0x1fff ? -1 : packed;
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
- if (sjisReverseTried) return sjisReverseMap;
186
- sjisReverseTried = true;
187
-
188
- let decoder;
189
- try {
190
- decoder = new TextDecoder('shift_jis', { fatal: true });
191
- // Some runtimes accept the label and then decode everything to U+FFFD.
192
- if (decoder.decode(new Uint8Array([0x82, 0xa0])) !== 'あ') return null;
193
- } catch (e) {
194
- return null;
195
- }
196
-
197
- const map = new Map();
198
- const buf = new Uint8Array(2);
199
- const ranges = [[0x8140, 0x9ffc], [0xe040, 0xebbf]];
200
-
201
- for (let r = 0; r < ranges.length; r++) {
202
- for (let sjis = ranges[r][0]; sjis <= ranges[r][1]; sjis++) {
203
- const lo = sjis & 0xff;
204
- // Shift_JIS trail bytes never take these values.
205
- if (lo < 0x40 || lo === 0x7f || lo > 0xfc) continue;
206
- if (sjisToThirteenBits(sjis) < 0) continue;
207
- buf[0] = sjis >> 8;
208
- buf[1] = lo;
209
- let text;
210
- try {
211
- text = decoder.decode(buf);
212
- } catch (e) {
213
- continue;
214
- }
215
- // Reject anything that did not decode to exactly one code point, and
216
- // keep the first (lowest) encoding when a character has several.
217
- if (text.length === 0 || Array.from(text).length !== 1) continue;
218
- if (!map.has(text)) map.set(text, sjis);
219
- }
220
- }
221
-
222
- sjisReverseMap = map;
223
- return map;
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; // 10/3 bits
213
+ const COST_NUMERIC = 20; // 10/3 bits
239
214
  const COST_ALPHANUMERIC = 33; // 11/2 bits
240
- const COST_KANJI = 78; // 13 bits
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
- const points = Array.from(text);
261
- const n = points.length;
262
- const numeric = new Uint8Array(n);
263
- const alnum = new Int32Array(n);
264
- const byteLen = new Int32Array(n);
265
- const kanji = new Int32Array(n);
266
-
267
- const reverse = allowKanji ? getSjisReverseMap() : null;
268
-
269
- for (let i = 0; i < n; i++) {
270
- const ch = points[i];
271
- const cp = ch.codePointAt(0);
272
- numeric[i] = isNumeric(ch) ? 1 : 0;
273
- alnum[i] = alphanumericValue(ch);
274
- byteLen[i] = utf8 ? utf8Length(cp) : 1;
275
-
276
- kanji[i] = -1;
277
- if (reverse) {
278
- const sjis = reverse.get(ch);
279
- if (sjis !== undefined) kanji[i] = sjisToThirteenBits(sjis);
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
- switch (mode) {
294
- case MODE.NUMERIC: return info.numeric[i] ? COST_NUMERIC : INFEASIBLE;
295
- case MODE.ALPHANUMERIC: return info.alnum[i] >= 0 ? COST_ALPHANUMERIC : INFEASIBLE;
296
- case MODE.BYTE: return info.byteLen[i] * 8 * UNIT;
297
- case MODE.KANJI: return info.kanji[i] >= 0 ? COST_KANJI : INFEASIBLE;
298
- default: return INFEASIBLE;
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
- const n = info.points.length;
322
- if (n === 0) return [{ mode: MODE.BYTE, start: 0, end: 0 }];
323
-
324
- const M = SEARCH_MODES.length;
325
- const header = new Array(M);
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
- const cc = charCost(info, i, SEARCH_MODES[m]);
342
- if (cc >= INFEASIBLE) {
343
- next[m] = INFEASIBLE;
344
- from[(i + 1) * M + m] = -1;
345
- continue;
346
- }
347
-
348
- let bestCost = cost[m]; // stay in this mode
349
- let bestPrev = m;
350
- for (let p = 0; p < M; p++) {
351
- if (p === m) continue;
352
- const candidate = cost[p] + header[m];
353
- if (candidate < bestCost) {
354
- bestCost = candidate;
355
- bestPrev = p;
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
- const n = seg.end - seg.start;
404
- let payload;
405
- switch (seg.mode) {
406
- case MODE.NUMERIC:
407
- payload = 10 * Math.floor(n / 3) + [0, 4, 7][n % 3];
408
- break;
409
- case MODE.ALPHANUMERIC:
410
- payload = 11 * Math.floor(n / 2) + (n % 2) * 6;
411
- break;
412
- case MODE.KANJI:
413
- payload = 13 * n;
414
- break;
415
- default: {
416
- let bytes = 0;
417
- for (let i = seg.start; i < seg.end; i++) bytes += info.byteLen[i];
418
- payload = bytes * 8;
419
- break;
420
- }
421
- }
422
- return 4 + countBits(seg.mode, version) + payload;
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
- if (seg.mode !== MODE.BYTE) return seg.end - seg.start;
433
- let bytes = 0;
434
- for (let i = seg.start; i < seg.end; i++) bytes += info.byteLen[i];
435
- return bytes;
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
- const writer = new BitWriter();
451
- const capacity = dataBitCapacity(version, ecc);
452
- const encoder = info.utf8 ? new TextEncoder() : null;
453
-
454
- if (withEci) {
455
- writer.put(MODE.ECI, 4);
456
- writer.put(ECI_UTF8, 8); // single-byte designator form, values 0-127
457
- }
458
-
459
- for (let s = 0; s < segments.length; s++) {
460
- const seg = segments[s];
461
- const count = segmentCount(seg, info);
462
- const width = countBits(seg.mode, version);
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
- if (i < seg.end) writer.put(info.alnum[i], 6);
495
- break;
496
- }
497
-
498
- case MODE.KANJI: {
499
- for (let i = seg.start; i < seg.end; i++) writer.put(info.kanji[i], 13);
500
- break;
501
- }
502
-
503
- default: {
504
- for (let i = seg.start; i < seg.end; i++) {
505
- const ch = info.points[i];
506
- if (encoder) {
507
- writer.putBytes(encoder.encode(ch));
508
- } else {
509
- writer.put(ch.codePointAt(0) & 0xff, 8);
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
- break;
513
- }
514
- }
515
- }
516
-
517
- if (writer.length > capacity) {
518
- throw new EncodeError(
519
- `QR: ${writer.length} bits exceed the ${capacity}-bit capacity of version ${version}-${ecc}`
520
- );
521
- }
522
-
523
- // Terminator: up to four zero bits, truncated if the symbol is nearly full.
524
- const terminator = Math.min(4, capacity - writer.length);
525
- if (terminator > 0) writer.put(0, terminator);
526
- writer.padToByte();
527
-
528
- const bytes = writer.toBytes();
529
- const target = capacity / 8;
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
- const blocks = [];
553
- let offset = 0;
554
- for (let b = 0; b < layout.blockCount; b++) {
555
- const count = b < layout.group1Blocks ? layout.group1DataCount : layout.group2DataCount;
556
- const chunk = data.subarray(offset, offset + count);
557
- offset += count;
558
- blocks.push({ data: chunk, ecc: rsEncode(chunk, layout.eccPerBlock, GF256_QR, 0) });
559
- }
560
-
561
- const out = new Uint8Array(layout.totalCodewords);
562
- let n = 0;
563
-
564
- const maxData = layout.group2Blocks > 0 ? layout.group2DataCount : layout.group1DataCount;
565
- for (let i = 0; i < maxData; i++) {
566
- for (let b = 0; b < blocks.length; b++) {
567
- if (i < blocks[b].data.length) out[n++] = blocks[b].data[i];
568
- }
569
- }
570
- for (let i = 0; i < layout.eccPerBlock; i++) {
571
- for (let b = 0; b < blocks.length; b++) out[n++] = blocks[b].ecc[i];
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
- let n = 0;
587
- while (v !== 0) {
588
- n++;
589
- v >>>= 1;
590
- }
591
- return n;
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
- const degree = bitLength(generator) - 1;
602
- let v = value;
603
- while (bitLength(v) > degree) {
604
- v ^= generator << (bitLength(v) - degree - 1);
605
- }
606
- return v;
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
- const level = ECC_LEVEL_BITS[ecc];
617
- if (level === undefined) throw new EncodeError(`QR: unknown error correction level "${ecc}"`);
618
- const data = (level << 3) | mask;
619
- return ((data << 10) | bchRemainder(data << 10, FORMAT_GENERATOR)) ^ FORMAT_MASK;
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
- return (version << 12) | bchRemainder(version << 12, VERSION_GENERATOR);
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
- for (let j = y; j < y + h; j++) {
645
- for (let i = x; i < x + w; i++) m.unset(i, j);
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
- const size = versionSize(version);
656
-
657
- // Finder patterns: 7x7 dark ring, light ring, 3x3 dark core. Separators are
658
- // simply left light, which they already are.
659
- const corners = [[0, 0], [size - 7, 0], [0, size - 7]];
660
- for (let c = 0; c < corners.length; c++) {
661
- const [x, y] = corners[c];
662
- m.setRegion(x, y, 7, 7);
663
- clearRegion(m, x + 1, y + 1, 5, 5);
664
- m.setRegion(x + 2, y + 2, 3, 3);
665
- }
666
-
667
- // Timing patterns: dark on even coordinates, which keeps them in phase with
668
- // the finder edges they run between.
669
- for (let i = 8; i < size - 8; i++) {
670
- if ((i & 1) === 0) {
671
- m.set(i, 6);
672
- m.set(6, i);
673
- }
674
- }
675
-
676
- // Alignment patterns: 5x5 dark ring, light ring, single dark centre.
677
- const centres = alignmentCentres(version);
678
- for (let c = 0; c < centres.length; c++) {
679
- const [cx, cy] = centres[c];
680
- m.setRegion(cx - 2, cy - 2, 5, 5);
681
- clearRegion(m, cx - 1, cy - 1, 3, 3);
682
- m.set(cx, cy);
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
- const size = versionSize(version);
694
- const bits = formatInfoBits(ecc, mask);
695
- const [a, b] = formatInfoPositions(size);
696
- for (let i = 0; i < 15; i++) {
697
- const on = ((bits >> i) & 1) === 1;
698
- m.setValue(a[i][0], a[i][1], on);
699
- m.setValue(b[i][0], b[i][1], on);
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
- if (version < VERSION_INFO_MIN) return;
708
- const size = versionSize(version);
709
- const bits = versionInfoBits(version);
710
- for (let i = 0; i < 18; i++) {
711
- const on = ((bits >> i) & 1) === 1;
712
- const major = Math.floor(i / 3);
713
- const minor = i % 3;
714
- m.setValue(major, size - 11 + minor, on); // bottom-left block
715
- m.setValue(size - 11 + minor, major, on); // top-right block, transposed
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
- const order = dataModuleOrder(version);
727
- const available = codewords.length * 8;
728
-
729
- for (let p = 0, bit = 0; p < order.length; p += 2, bit++) {
730
- const x = order[p];
731
- const y = order[p + 1];
732
- // Past the last codeword are the remainder bits, which are always zero.
733
- let dark = bit < available &&
734
- ((codewords[bit >> 3] >> (7 - (bit & 7))) & 1) === 1;
735
- if (maskBit(mask, x, y)) dark = !dark;
736
- m.setValue(x, y, dark);
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
- const size = m.width;
762
- let penalty = 0;
763
-
764
- // Rule 1: runs of five or more identical modules, per row and per column.
765
- for (let axis = 0; axis < 2; axis++) {
766
- for (let a = 0; a < size; a++) {
767
- let run = 1;
768
- let prev = axis === 0 ? m.get(0, a) : m.get(a, 0);
769
- for (let b = 1; b < size; b++) {
770
- const cur = axis === 0 ? m.get(b, a) : m.get(a, b);
771
- if (cur === prev) {
772
- run++;
773
- } else {
774
- if (run >= 5) penalty += 3 + (run - 5);
775
- prev = cur;
776
- run = 1;
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
- if (run >= 5) penalty += 3 + (run - 5);
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
- const m = new BitMatrix(versionSize(version));
832
- drawFunctionPatterns(m, version);
833
- placeData(m, version, codewords, mask);
834
- drawFormatInfo(m, version, ecc, mask);
835
- drawVersionInfo(m, version);
836
- return m;
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
- if (typeof text !== 'string') {
854
- throw new EncodeError('QR: text must be a string');
855
- }
856
-
857
- const ecc = options.ecc ?? 'M';
858
- if (ECC_LEVELS.indexOf(ecc) === -1) {
859
- throw new EncodeError(`QR: error correction level must be L, M, Q or H, got "${ecc}"`);
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
- throw new EncodeError(
918
- `QR: text does not fit version ${forcedVersion}-${ecc} ` +
919
- `(capacity ${dataBitCapacity(forcedVersion, ecc)} bits)`
920
- );
921
- }
922
- throw new EncodeError(
923
- `QR: text is too long for any version at error correction level ${ecc}`
924
- );
925
- }
926
-
927
- const { version, segments, needsEci } = chosen;
928
- const layout = blockLayout(version, ecc);
929
- const data = writeBitstream(segments, info, version, ecc, needsEci);
930
- const codewords = interleave(data, layout);
931
-
932
- if (forcedMask !== undefined) {
933
- return buildMatrix(version, ecc, forcedMask, codewords);
934
- }
935
-
936
- let best = null;
937
- let bestScore = Infinity;
938
- for (let mask = 0; mask < 8; mask++) {
939
- const candidate = buildMatrix(version, ecc, mask, codewords);
940
- const score = maskPenalty(candidate);
941
- if (score < bestScore) {
942
- bestScore = score;
943
- best = candidate;
944
- }
945
- }
946
- return best;
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
- for (let i = 0; i < text.length; i++) {
955
- if (text.charCodeAt(i) > 0xff) return false;
956
- }
957
- return true;
868
+ for (let i = 0; i < text.length; i++) {
869
+ if (text.charCodeAt(i) > 0xff)
870
+ return false;
871
+ }
872
+ return true;
958
873
  }