@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
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
*
|
|
28
28
|
* Original work. No code from any other barcode implementation.
|
|
29
29
|
*/
|
|
30
|
-
|
|
31
30
|
/**
|
|
32
31
|
* Sythos Canvas QR profile encoder.
|
|
33
32
|
*
|
|
@@ -38,16 +37,9 @@
|
|
|
38
37
|
*
|
|
39
38
|
* @module frameqr/encoder
|
|
40
39
|
*/
|
|
41
|
-
|
|
42
40
|
import { EncodeError } from '../core/errors.js';
|
|
43
41
|
import { encodeQR } from '../qr/encoder.js';
|
|
44
|
-
import {
|
|
45
|
-
FRAMEQR_PROFILE,
|
|
46
|
-
canvasModules,
|
|
47
|
-
normalizeCanvasSpec,
|
|
48
|
-
validateCanvasSpec,
|
|
49
|
-
} from './tables.js';
|
|
50
|
-
|
|
42
|
+
import { FRAMEQR_PROFILE, canvasModules, normalizeCanvasSpec, validateCanvasSpec, } from './tables.js';
|
|
51
43
|
/**
|
|
52
44
|
* @typedef {object} FrameQrEncodeOptions
|
|
53
45
|
* @property {'H'} [ecc] The profile always uses QR error correction H.
|
|
@@ -57,15 +49,13 @@ import {
|
|
|
57
49
|
* @property {boolean} [kanji] Allow QR kanji mode.
|
|
58
50
|
* @property {object} [canvas] Profile artwork reservation.
|
|
59
51
|
*/
|
|
60
|
-
|
|
61
52
|
function versionFor(matrix) {
|
|
62
|
-
|
|
53
|
+
return (matrix.width - 17) / 4;
|
|
63
54
|
}
|
|
64
|
-
|
|
65
55
|
function clearCanvas(matrix, modules) {
|
|
66
|
-
|
|
56
|
+
for (const [x, y] of modules)
|
|
57
|
+
matrix.unset(x, y);
|
|
67
58
|
}
|
|
68
|
-
|
|
69
59
|
/**
|
|
70
60
|
* Encode a QR Code with a conservative artwork canvas according to the
|
|
71
61
|
* non-certified Sythos Canvas QR profile.
|
|
@@ -83,74 +73,71 @@ function clearCanvas(matrix, modules) {
|
|
|
83
73
|
* cannot safely fit the selected QR version.
|
|
84
74
|
*/
|
|
85
75
|
export function encodeFrameQR(text, options = {}) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
if (options.ecc !== undefined && options.ecc !== 'H') {
|
|
90
|
-
throw new EncodeError('Sythos Canvas QR: ecc is fixed to H for this profile');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const qrOptions = {
|
|
94
|
-
mask: options.mask,
|
|
95
|
-
charset: options.charset,
|
|
96
|
-
kanji: options.kanji,
|
|
97
|
-
ecc: 'H',
|
|
98
|
-
};
|
|
99
|
-
for (const key of Object.keys(qrOptions)) {
|
|
100
|
-
if (qrOptions[key] === undefined) delete qrOptions[key];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const versions = options.version === undefined
|
|
104
|
-
? Array.from({ length: 40 }, (_, index) => index + 1)
|
|
105
|
-
: [options.version];
|
|
106
|
-
let capacityError = null;
|
|
107
|
-
let unsafeAnalysis = null;
|
|
108
|
-
let selected = null;
|
|
109
|
-
|
|
110
|
-
for (const version of versions) {
|
|
111
|
-
let matrix;
|
|
112
|
-
try {
|
|
113
|
-
matrix = encodeQR(text, { ...qrOptions, version });
|
|
114
|
-
} catch (error) {
|
|
115
|
-
capacityError = error;
|
|
116
|
-
continue;
|
|
76
|
+
if (typeof text !== 'string') {
|
|
77
|
+
throw new EncodeError('Sythos Canvas QR: text must be a string');
|
|
117
78
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
let analysis;
|
|
121
|
-
try {
|
|
122
|
-
canvas = normalizeCanvasSpec(matrix.width, options.canvas);
|
|
123
|
-
analysis = validateCanvasSpec(versionFor(matrix), canvas);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
if (error instanceof EncodeError) throw error;
|
|
126
|
-
throw new EncodeError(`Sythos Canvas QR: invalid canvas: ${error.message}`);
|
|
79
|
+
if (options.ecc !== undefined && options.ecc !== 'H') {
|
|
80
|
+
throw new EncodeError('Sythos Canvas QR: ecc is fixed to H for this profile');
|
|
127
81
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
82
|
+
const qrOptions = {
|
|
83
|
+
mask: options.mask,
|
|
84
|
+
charset: options.charset,
|
|
85
|
+
kanji: options.kanji,
|
|
86
|
+
ecc: 'H',
|
|
87
|
+
};
|
|
88
|
+
for (const key of Object.keys(qrOptions)) {
|
|
89
|
+
if (qrOptions[key] === undefined)
|
|
90
|
+
delete qrOptions[key];
|
|
131
91
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
92
|
+
const versions = options.version === undefined
|
|
93
|
+
? Array.from({ length: 40 }, (_, index) => index + 1)
|
|
94
|
+
: [options.version];
|
|
95
|
+
let capacityError = null;
|
|
96
|
+
let unsafeAnalysis = null;
|
|
97
|
+
let selected = null;
|
|
98
|
+
for (const version of versions) {
|
|
99
|
+
let matrix;
|
|
100
|
+
try {
|
|
101
|
+
matrix = encodeQR(text, { ...qrOptions, version });
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
capacityError = error;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let canvas;
|
|
108
|
+
let analysis;
|
|
109
|
+
try {
|
|
110
|
+
canvas = normalizeCanvasSpec(matrix.width, options.canvas);
|
|
111
|
+
analysis = validateCanvasSpec(versionFor(matrix), canvas);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error instanceof EncodeError)
|
|
115
|
+
throw error;
|
|
116
|
+
throw new EncodeError(`Sythos Canvas QR: invalid canvas: ${error.message}`);
|
|
117
|
+
}
|
|
118
|
+
if (!analysis.safe) {
|
|
119
|
+
unsafeAnalysis = analysis;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
selected = { matrix, canvas };
|
|
123
|
+
break;
|
|
143
124
|
}
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
125
|
+
if (!selected) {
|
|
126
|
+
if (unsafeAnalysis) {
|
|
127
|
+
throw new EncodeError('Sythos Canvas QR: canvas is not safe for the selected QR version; ' +
|
|
128
|
+
`it touches ${unsafeAnalysis.touchedCodewordCount} codewords and has ` +
|
|
129
|
+
`a per-block correction budget of ${unsafeAnalysis.correctionBudgetPerBlock}`);
|
|
130
|
+
}
|
|
131
|
+
if (capacityError)
|
|
132
|
+
throw capacityError;
|
|
133
|
+
throw new EncodeError('Sythos Canvas QR: unable to select a QR version');
|
|
134
|
+
}
|
|
135
|
+
const { matrix, canvas } = selected;
|
|
136
|
+
clearCanvas(matrix, canvasModules(matrix.width, canvas));
|
|
137
|
+
matrix.frameqr = {
|
|
138
|
+
profile: FRAMEQR_PROFILE.id,
|
|
139
|
+
certified: false,
|
|
140
|
+
canvas,
|
|
141
|
+
};
|
|
142
|
+
return matrix;
|
|
156
143
|
}
|
package/src/js/frameqr/index.js
CHANGED
|
@@ -27,16 +27,7 @@
|
|
|
27
27
|
*
|
|
28
28
|
* Original work. No code from any other barcode implementation.
|
|
29
29
|
*/
|
|
30
|
-
|
|
31
30
|
export { encodeFrameQR } from './encoder.js';
|
|
32
31
|
export { decodeFrameQR } from './decoder.js';
|
|
33
32
|
export { detectFrameQR, detectAndDecodeFrameQR } from './detector.js';
|
|
34
|
-
export {
|
|
35
|
-
FRAMEQR_PROFILE,
|
|
36
|
-
FRAMEQR_CANVAS_SHAPES,
|
|
37
|
-
canvasModules,
|
|
38
|
-
normalizeCanvasSpec,
|
|
39
|
-
analyzeCanvasDamage,
|
|
40
|
-
validateCanvasSpec,
|
|
41
|
-
validateFrameQrTables,
|
|
42
|
-
} from './tables.js';
|
|
33
|
+
export { FRAMEQR_PROFILE, FRAMEQR_CANVAS_SHAPES, canvasModules, normalizeCanvasSpec, analyzeCanvasDamage, validateCanvasSpec, validateFrameQrTables, } from './tables.js';
|
package/src/js/frameqr/tables.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
|
* Structural contract for the Sythos Canvas QR profile.
|
|
33
32
|
*
|
|
@@ -47,40 +46,30 @@
|
|
|
47
46
|
*
|
|
48
47
|
* @module frameqr/tables
|
|
49
48
|
*/
|
|
50
|
-
|
|
51
|
-
import {
|
|
52
|
-
blockLayout,
|
|
53
|
-
dataModuleOrder,
|
|
54
|
-
reservedModules,
|
|
55
|
-
versionSize,
|
|
56
|
-
} from '../qr/tables.js';
|
|
57
|
-
|
|
49
|
+
import { blockLayout, dataModuleOrder, reservedModules, versionSize, } from '../qr/tables.js';
|
|
58
50
|
/** Public identity and compatibility boundary of the implementable profile. */
|
|
59
51
|
export const FRAMEQR_PROFILE = Object.freeze({
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
52
|
+
id: 'sythos-canvas-qr/1',
|
|
53
|
+
name: 'Sythos Canvas QR profile',
|
|
54
|
+
certified: false,
|
|
55
|
+
densoFrameQrCompatible: false,
|
|
56
|
+
baseSymbology: 'QR Code Model 2',
|
|
57
|
+
requiredEcc: 'H',
|
|
58
|
+
standard: 'ISO/IEC 18004 QR Code baseline',
|
|
67
59
|
});
|
|
68
|
-
|
|
69
60
|
/** Canvas shapes whose module membership is fully deterministic. */
|
|
70
61
|
export const FRAMEQR_CANVAS_SHAPES = Object.freeze(['square', 'circle', 'diamond']);
|
|
71
|
-
|
|
72
62
|
function oddAtMost(value, maximum) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
let n = Math.max(1, Math.min(maximum, Math.floor(value)));
|
|
64
|
+
if ((n & 1) === 0)
|
|
65
|
+
n--;
|
|
66
|
+
return Math.max(1, n);
|
|
76
67
|
}
|
|
77
|
-
|
|
78
68
|
function assertSymbolSize(symbolSize) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
if (!Number.isInteger(symbolSize) || symbolSize < 21 || symbolSize > 177 || (symbolSize - 17) % 4 !== 0) {
|
|
70
|
+
throw new RangeError(`Frame QR profile: ${symbolSize} is not a QR Model 2 symbol size`);
|
|
71
|
+
}
|
|
82
72
|
}
|
|
83
|
-
|
|
84
73
|
/**
|
|
85
74
|
* Canonicalise a canvas request.
|
|
86
75
|
*
|
|
@@ -93,44 +82,37 @@ function assertSymbolSize(symbolSize) {
|
|
|
93
82
|
* @returns {{shape:string,centerX:number,centerY:number,width:number,height:number,angle:number}}
|
|
94
83
|
*/
|
|
95
84
|
export function normalizeCanvasSpec(symbolSize, canvas = {}) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
throw new RangeError(
|
|
114
|
-
`Frame QR profile: canvas ${name} must be between 1 and ${maximumDimension} modules`
|
|
115
|
-
);
|
|
85
|
+
assertSymbolSize(symbolSize);
|
|
86
|
+
if (canvas === null || typeof canvas !== 'object' || Array.isArray(canvas)) {
|
|
87
|
+
throw new TypeError('Frame QR profile: canvas must be an object');
|
|
88
|
+
}
|
|
89
|
+
const shape = String(canvas.shape ?? 'square').toLowerCase();
|
|
90
|
+
if (!FRAMEQR_CANVAS_SHAPES.includes(shape)) {
|
|
91
|
+
throw new RangeError(`Frame QR profile: unsupported canvas shape "${shape}"`);
|
|
92
|
+
}
|
|
93
|
+
const defaultSize = oddAtMost(Math.max(3, Math.round(symbolSize * 0.17)), symbolSize - 16);
|
|
94
|
+
const requestedSize = canvas.size;
|
|
95
|
+
const requestedWidth = canvas.width ?? requestedSize ?? defaultSize;
|
|
96
|
+
const requestedHeight = canvas.height ?? requestedSize ?? defaultSize;
|
|
97
|
+
const maximumDimension = symbolSize - 16;
|
|
98
|
+
for (const [name, value] of [['width', requestedWidth], ['height', requestedHeight]]) {
|
|
99
|
+
if (!Number.isFinite(Number(value)) || Number(value) < 1 || Number(value) > maximumDimension) {
|
|
100
|
+
throw new RangeError(`Frame QR profile: canvas ${name} must be between 1 and ${maximumDimension} modules`);
|
|
101
|
+
}
|
|
116
102
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return { shape, centerX, centerY, width, height, angle };
|
|
103
|
+
const width = oddAtMost(Number(requestedWidth), maximumDimension);
|
|
104
|
+
const height = oddAtMost(Number(requestedHeight), maximumDimension);
|
|
105
|
+
const centerX = Math.round(canvas.centerX ?? (symbolSize - 1) / 2);
|
|
106
|
+
const centerY = Math.round(canvas.centerY ?? (symbolSize - 1) / 2);
|
|
107
|
+
const angle = ((Number(canvas.angle ?? 0) % 360) + 360) % 360;
|
|
108
|
+
if (![0, 90, 180, 270].includes(angle)) {
|
|
109
|
+
throw new RangeError('Frame QR profile: angle must be 0, 90, 180 or 270 degrees');
|
|
110
|
+
}
|
|
111
|
+
if (centerX < 8 || centerY < 8 || centerX >= symbolSize - 8 || centerY >= symbolSize - 8) {
|
|
112
|
+
throw new RangeError('Frame QR profile: canvas centre must remain inside the finder-pattern boundary');
|
|
113
|
+
}
|
|
114
|
+
return { shape, centerX, centerY, width, height, angle };
|
|
132
115
|
}
|
|
133
|
-
|
|
134
116
|
/**
|
|
135
117
|
* Enumerate canvas modules, including any overlaps with QR function modules.
|
|
136
118
|
* A conforming encoder rejects such overlaps rather than damaging function
|
|
@@ -141,50 +123,52 @@ export function normalizeCanvasSpec(symbolSize, canvas = {}) {
|
|
|
141
123
|
* @returns {Array<[number, number]>}
|
|
142
124
|
*/
|
|
143
125
|
export function canvasModules(symbolSize, canvas = {}) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
126
|
+
const spec = normalizeCanvasSpec(symbolSize, canvas);
|
|
127
|
+
const rotate = spec.angle === 90 || spec.angle === 270;
|
|
128
|
+
const width = rotate ? spec.height : spec.width;
|
|
129
|
+
const height = rotate ? spec.width : spec.height;
|
|
130
|
+
const halfWidth = (width - 1) / 2;
|
|
131
|
+
const halfHeight = (height - 1) / 2;
|
|
132
|
+
const modules = [];
|
|
133
|
+
for (let dy = -halfHeight; dy <= halfHeight; dy++) {
|
|
134
|
+
for (let dx = -halfWidth; dx <= halfWidth; dx++) {
|
|
135
|
+
const nx = halfWidth === 0 ? 0 : dx / halfWidth;
|
|
136
|
+
const ny = halfHeight === 0 ? 0 : dy / halfHeight;
|
|
137
|
+
let inside;
|
|
138
|
+
if (spec.shape === 'circle')
|
|
139
|
+
inside = nx * nx + ny * ny <= 1 + Number.EPSILON;
|
|
140
|
+
else if (spec.shape === 'diamond')
|
|
141
|
+
inside = Math.abs(nx) + Math.abs(ny) <= 1 + Number.EPSILON;
|
|
142
|
+
else
|
|
143
|
+
inside = true;
|
|
144
|
+
if (inside)
|
|
145
|
+
modules.push([spec.centerX + dx, spec.centerY + dy]);
|
|
146
|
+
}
|
|
161
147
|
}
|
|
162
|
-
|
|
163
|
-
return modules;
|
|
148
|
+
return modules;
|
|
164
149
|
}
|
|
165
|
-
|
|
166
150
|
/** Build the interleaved-codeword to RS-block map used by QR Model 2. */
|
|
167
151
|
function codewordBlockMap(layout) {
|
|
168
|
-
|
|
169
|
-
for (let block = 0; block < layout.blockCount; block++) {
|
|
170
|
-
dataCounts[block] = block < layout.group1Blocks
|
|
171
|
-
? layout.group1DataCount
|
|
172
|
-
: layout.group2DataCount;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const map = [];
|
|
176
|
-
const maxData = Math.max(...dataCounts);
|
|
177
|
-
for (let i = 0; i < maxData; i++) {
|
|
152
|
+
const dataCounts = new Array(layout.blockCount);
|
|
178
153
|
for (let block = 0; block < layout.blockCount; block++) {
|
|
179
|
-
|
|
154
|
+
dataCounts[block] = block < layout.group1Blocks
|
|
155
|
+
? layout.group1DataCount
|
|
156
|
+
: layout.group2DataCount;
|
|
157
|
+
}
|
|
158
|
+
const map = [];
|
|
159
|
+
const maxData = Math.max(...dataCounts);
|
|
160
|
+
for (let i = 0; i < maxData; i++) {
|
|
161
|
+
for (let block = 0; block < layout.blockCount; block++) {
|
|
162
|
+
if (i < dataCounts[block])
|
|
163
|
+
map.push(block);
|
|
164
|
+
}
|
|
180
165
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
166
|
+
for (let i = 0; i < layout.eccPerBlock; i++) {
|
|
167
|
+
for (let block = 0; block < layout.blockCount; block++)
|
|
168
|
+
map.push(block);
|
|
169
|
+
}
|
|
170
|
+
return map;
|
|
186
171
|
}
|
|
187
|
-
|
|
188
172
|
/**
|
|
189
173
|
* Calculate worst-case QR codeword damage caused by a canvas.
|
|
190
174
|
* A codeword is counted if any of its modules is touched. This is conservative:
|
|
@@ -195,76 +179,74 @@ function codewordBlockMap(layout) {
|
|
|
195
179
|
* @param {object} [canvas]
|
|
196
180
|
*/
|
|
197
181
|
export function analyzeCanvasDamage(version, canvas = {}) {
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
};
|
|
182
|
+
if (!Number.isInteger(version) || version < 1 || version > 40) {
|
|
183
|
+
throw new RangeError(`Frame QR profile: version must be an integer 1-40, got ${version}`);
|
|
184
|
+
}
|
|
185
|
+
const symbolSize = versionSize(version);
|
|
186
|
+
const spec = normalizeCanvasSpec(symbolSize, canvas);
|
|
187
|
+
const modules = canvasModules(symbolSize, spec);
|
|
188
|
+
const reserved = reservedModules(version);
|
|
189
|
+
const reservedOverlaps = modules.filter(([x, y]) => reserved.get(x, y));
|
|
190
|
+
const moduleKeys = new Set(modules.map(([x, y]) => `${x},${y}`));
|
|
191
|
+
const order = dataModuleOrder(version);
|
|
192
|
+
const touchedCodewords = new Set();
|
|
193
|
+
for (let bit = 0; bit < order.length / 2; bit++) {
|
|
194
|
+
if (moduleKeys.has(`${order[bit * 2]},${order[bit * 2 + 1]}`))
|
|
195
|
+
touchedCodewords.add(bit >> 3);
|
|
196
|
+
}
|
|
197
|
+
const layout = blockLayout(version, 'H');
|
|
198
|
+
const blockMap = codewordBlockMap(layout);
|
|
199
|
+
const touchedByBlockSets = Array.from({ length: layout.blockCount }, () => new Set());
|
|
200
|
+
for (const codeword of touchedCodewords) {
|
|
201
|
+
const block = blockMap[codeword];
|
|
202
|
+
if (block !== undefined)
|
|
203
|
+
touchedByBlockSets[block].add(codeword);
|
|
204
|
+
}
|
|
205
|
+
const touchedCodewordsByBlock = touchedByBlockSets.map((set) => set.size);
|
|
206
|
+
const correctionBudgetPerBlock = Math.floor(layout.eccPerBlock / 2);
|
|
207
|
+
const safe = reservedOverlaps.length === 0 &&
|
|
208
|
+
touchedCodewordsByBlock.every((count) => count <= correctionBudgetPerBlock);
|
|
209
|
+
return {
|
|
210
|
+
profile: FRAMEQR_PROFILE.id,
|
|
211
|
+
certified: false,
|
|
212
|
+
version,
|
|
213
|
+
symbolSize,
|
|
214
|
+
canvas: spec,
|
|
215
|
+
canvasModuleCount: modules.length,
|
|
216
|
+
reservedOverlaps,
|
|
217
|
+
touchedCodewordCount: touchedCodewords.size,
|
|
218
|
+
touchedCodewordsByBlock,
|
|
219
|
+
correctionBudgetPerBlock,
|
|
220
|
+
safe,
|
|
221
|
+
};
|
|
239
222
|
}
|
|
240
|
-
|
|
241
223
|
/** Validate a canvas and return the non-certifying structural analysis. */
|
|
242
224
|
export function validateCanvasSpec(version, canvas = {}) {
|
|
243
|
-
|
|
225
|
+
return analyzeCanvasDamage(version, canvas);
|
|
244
226
|
}
|
|
245
|
-
|
|
246
227
|
/** Self-check the fixed profile contract and representative QR geometries. */
|
|
247
228
|
export function validateFrameQrTables() {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
for (const version of [1, 2, 3, 4, 7, 10, 20, 30, 40]) {
|
|
256
|
-
const size = versionSize(version);
|
|
257
|
-
const spec = normalizeCanvasSpec(size);
|
|
258
|
-
const modules = canvasModules(size, spec);
|
|
259
|
-
const unique = new Set(modules.map(([x, y]) => `${x},${y}`));
|
|
260
|
-
if (unique.size !== modules.length) problems.push(`v${version}: duplicate canvas modules`);
|
|
261
|
-
if (modules.some(([x, y]) => x < 0 || y < 0 || x >= size || y >= size)) {
|
|
262
|
-
problems.push(`v${version}: canvas escapes the symbol`);
|
|
229
|
+
const problems = [];
|
|
230
|
+
if (FRAMEQR_PROFILE.certified !== false || FRAMEQR_PROFILE.densoFrameQrCompatible !== false) {
|
|
231
|
+
problems.push('profile compatibility boundary must remain explicitly non-certified');
|
|
232
|
+
}
|
|
233
|
+
if (new Set(FRAMEQR_CANVAS_SHAPES).size !== FRAMEQR_CANVAS_SHAPES.length) {
|
|
234
|
+
problems.push('canvas shape identifiers must be unique');
|
|
263
235
|
}
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
236
|
+
for (const version of [1, 2, 3, 4, 7, 10, 20, 30, 40]) {
|
|
237
|
+
const size = versionSize(version);
|
|
238
|
+
const spec = normalizeCanvasSpec(size);
|
|
239
|
+
const modules = canvasModules(size, spec);
|
|
240
|
+
const unique = new Set(modules.map(([x, y]) => `${x},${y}`));
|
|
241
|
+
if (unique.size !== modules.length)
|
|
242
|
+
problems.push(`v${version}: duplicate canvas modules`);
|
|
243
|
+
if (modules.some(([x, y]) => x < 0 || y < 0 || x >= size || y >= size)) {
|
|
244
|
+
problems.push(`v${version}: canvas escapes the symbol`);
|
|
245
|
+
}
|
|
246
|
+
const analysis = analyzeCanvasDamage(version, spec);
|
|
247
|
+
if (analysis.touchedCodewordsByBlock.length !== blockLayout(version, 'H').blockCount) {
|
|
248
|
+
problems.push(`v${version}: damage analysis block count mismatch`);
|
|
249
|
+
}
|
|
267
250
|
}
|
|
268
|
-
|
|
269
|
-
return problems;
|
|
251
|
+
return problems;
|
|
270
252
|
}
|