@sythos/js_barcode_universal 1.5.13 → 1.5.15
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 +22 -4
- package/NOTICE.md +10 -1
- package/README.md +416 -22
- package/bundle/sythos-barcode.esm.js +22176 -11991
- package/bundle/sythos-barcode.js +22059 -11991
- package/licenses/README.md +12 -1
- package/licenses/codablockf.license +51 -0
- package/licenses/code16k.license +52 -0
- package/licenses/code25.license +58 -0
- package/licenses/code32.license +51 -0
- package/licenses/dotcode.license +66 -0
- package/licenses/gs1-composite.license +106 -0
- package/licenses/gs1-databar.license +21 -14
- package/licenses/hanxin.license +95 -0
- package/licenses/maxicode.license +62 -0
- package/licenses/postal.license +61 -0
- package/licenses/pzn.license +53 -0
- package/licenses/telepen.license +73 -0
- package/llms.txt +62 -6
- package/package.json +47 -3
- package/src/index.d.ts +111 -6
- package/src/index.js +362 -13
- package/src/js/codablockf/decoder.js +198 -0
- package/src/js/codablockf/encoder.js +97 -0
- package/src/js/codablockf/index.js +3 -0
- package/src/js/code16k/decoder.js +303 -0
- package/src/js/code16k/detector.js +187 -0
- package/src/js/code16k/encoder.js +153 -0
- package/src/js/code16k/index.js +14 -0
- package/src/js/code16k/tables.js +152 -0
- package/src/js/composite/index.js +570 -0
- package/src/js/core/detection-contract.js +185 -0
- package/src/js/core/symbol-layout.js +155 -0
- package/src/js/databar/expanded.js +968 -0
- package/src/js/databar/index.js +4 -0
- package/src/js/databar/layout.js +192 -0
- package/src/js/databar/limited.js +533 -0
- package/src/js/databar/stacked-omnidirectional.js +548 -0
- package/src/js/databar/stacked.js +528 -0
- package/src/js/dotcode/decoder.js +548 -0
- package/src/js/dotcode/detector.js +251 -0
- package/src/js/dotcode/encoder.js +453 -0
- package/src/js/dotcode/index.js +34 -0
- package/src/js/dotcode/tables.js +168 -0
- package/src/js/hanxin/decoder.js +299 -0
- package/src/js/hanxin/detector.js +122 -0
- package/src/js/hanxin/encoder.js +259 -0
- package/src/js/hanxin/index.js +16 -0
- package/src/js/hanxin/tables.js +316 -0
- package/src/js/image/height-coded.js +164 -0
- package/src/js/maxicode/decoder.js +275 -0
- package/src/js/maxicode/detector.js +118 -0
- package/src/js/maxicode/encoder.js +438 -0
- package/src/js/maxicode/index.js +34 -0
- package/src/js/maxicode/tables.js +132 -0
- package/src/js/oned/code25.js +148 -0
- package/src/js/oned/index.js +21 -3
- package/src/js/oned/postal.js +889 -0
- package/src/js/oned/reader.js +272 -7
- package/src/js/oned/telepen.js +361 -0
- package/src/js/oned/writers.js +186 -12
- package/src/js/stacked128/common.js +208 -0
- package/src/ts/codablockf/decoder.ts +212 -0
- package/src/ts/codablockf/encoder.ts +128 -0
- package/src/ts/codablockf/index.d.ts +31 -0
- package/src/ts/codablockf/index.ts +6 -0
- package/src/ts/code16k/decoder.d.ts +25 -0
- package/src/ts/code16k/decoder.ts +313 -0
- package/src/ts/code16k/detector.d.ts +21 -0
- package/src/ts/code16k/detector.ts +203 -0
- package/src/ts/code16k/encoder.d.ts +26 -0
- package/src/ts/code16k/encoder.ts +198 -0
- package/src/ts/code16k/index.d.ts +10 -0
- package/src/ts/code16k/index.ts +47 -0
- package/src/ts/code16k/tables.d.ts +54 -0
- package/src/ts/code16k/tables.ts +195 -0
- package/src/ts/composite/index.d.ts +74 -0
- package/src/ts/composite/index.ts +547 -0
- package/src/ts/core/detection-contract.d.ts +119 -0
- package/src/ts/core/detection-contract.ts +267 -0
- package/src/ts/core/symbol-layout.d.ts +108 -0
- package/src/ts/core/symbol-layout.ts +236 -0
- package/src/ts/databar/expanded.d.ts +40 -0
- package/src/ts/databar/expanded.ts +948 -0
- package/src/ts/databar/index.d.ts +48 -0
- package/src/ts/databar/index.ts +48 -0
- package/src/ts/databar/layout.d.ts +122 -0
- package/src/ts/databar/layout.ts +275 -0
- package/src/ts/databar/limited.d.ts +85 -0
- package/src/ts/databar/limited.ts +553 -0
- package/src/ts/databar/stacked-omnidirectional.d.ts +128 -0
- package/src/ts/databar/stacked-omnidirectional.ts +559 -0
- package/src/ts/databar/stacked.d.ts +96 -0
- package/src/ts/databar/stacked.ts +549 -0
- package/src/ts/dotcode/decoder.d.ts +58 -0
- package/src/ts/dotcode/decoder.ts +467 -0
- package/src/ts/dotcode/detector.d.ts +63 -0
- package/src/ts/dotcode/detector.ts +263 -0
- package/src/ts/dotcode/encoder.d.ts +69 -0
- package/src/ts/dotcode/encoder.ts +427 -0
- package/src/ts/dotcode/index.d.ts +37 -0
- package/src/ts/dotcode/index.ts +72 -0
- package/src/ts/dotcode/tables.d.ts +74 -0
- package/src/ts/dotcode/tables.ts +174 -0
- package/src/ts/hanxin/decoder.d.ts +48 -0
- package/src/ts/hanxin/decoder.ts +314 -0
- package/src/ts/hanxin/detector.d.ts +45 -0
- package/src/ts/hanxin/detector.ts +124 -0
- package/src/ts/hanxin/encoder.d.ts +41 -0
- package/src/ts/hanxin/encoder.ts +290 -0
- package/src/ts/hanxin/index.d.ts +16 -0
- package/src/ts/hanxin/index.ts +18 -0
- package/src/ts/hanxin/tables.d.ts +80 -0
- package/src/ts/hanxin/tables.ts +348 -0
- package/src/ts/image/height-coded.d.ts +101 -0
- package/src/ts/image/height-coded.ts +227 -0
- package/src/ts/index.d.ts +46 -2
- package/src/ts/index.ts +380 -13
- package/src/ts/maxicode/decoder.ts +265 -0
- package/src/ts/maxicode/detector.ts +109 -0
- package/src/ts/maxicode/encoder.ts +414 -0
- package/src/ts/maxicode/index.d.ts +96 -0
- package/src/ts/maxicode/index.ts +44 -0
- package/src/ts/maxicode/tables.ts +136 -0
- package/src/ts/oned/code25.d.ts +42 -0
- package/src/ts/oned/code25.ts +170 -0
- package/src/ts/oned/index.d.ts +7 -2
- package/src/ts/oned/index.ts +45 -3
- package/src/ts/oned/postal.d.ts +44 -0
- package/src/ts/oned/postal.ts +819 -0
- package/src/ts/oned/reader.d.ts +35 -0
- package/src/ts/oned/reader.ts +261 -7
- package/src/ts/oned/telepen.d.ts +65 -0
- package/src/ts/oned/telepen.ts +368 -0
- package/src/ts/oned/writers.d.ts +32 -0
- package/src/ts/oned/writers.ts +184 -13
- package/src/ts/stacked128/common.ts +225 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Sythos Barcode Suite
|
|
3
|
+
*
|
|
4
|
+
* MIT License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Sythos
|
|
7
|
+
*
|
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
* furnished to do so, subject to the following conditions:
|
|
14
|
+
*
|
|
15
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
* copies or substantial portions of the Software.
|
|
17
|
+
*
|
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
* SOFTWARE.
|
|
25
|
+
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
27
|
+
* SPDX-License-Identifier: MIT
|
|
28
|
+
*
|
|
29
|
+
* Original work. No code from any other barcode implementation.
|
|
30
|
+
*/
|
|
31
|
+
function hasOwn(value, property) {
|
|
32
|
+
return Object.prototype.hasOwnProperty.call(value, property);
|
|
33
|
+
}
|
|
34
|
+
function isRecord(value) {
|
|
35
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
36
|
+
}
|
|
37
|
+
function isFiniteNumber(value) {
|
|
38
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
39
|
+
}
|
|
40
|
+
function cross(a, b, c) {
|
|
41
|
+
return (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Normalize a detector rotation to one of the supported 45-degree turns.
|
|
45
|
+
*
|
|
46
|
+
* This function is intentionally strict: arbitrary angles are not silently
|
|
47
|
+
* snapped to a nearby orientation. The accepted domain is the integer range
|
|
48
|
+
* `0..359`, and only exact multiples of 45 degrees are canonical.
|
|
49
|
+
*
|
|
50
|
+
* @throws {TypeError} If `rotation` is not a number.
|
|
51
|
+
* @throws {RangeError} If `rotation` is outside the canonical domain.
|
|
52
|
+
*/
|
|
53
|
+
export function normalizeRotation(rotation) {
|
|
54
|
+
if (typeof rotation !== 'number') {
|
|
55
|
+
throw new TypeError('Rotation must be a number');
|
|
56
|
+
}
|
|
57
|
+
if (!Number.isFinite(rotation) || !Number.isInteger(rotation)
|
|
58
|
+
|| rotation < 0 || rotation >= 360 || rotation % 45 !== 0) {
|
|
59
|
+
throw new RangeError('Rotation must be an integer multiple of 45 degrees in 0..359');
|
|
60
|
+
}
|
|
61
|
+
return rotation;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Return whether four finite points form an ordered, non-degenerate quad.
|
|
65
|
+
*
|
|
66
|
+
* The required order is top-left, top-right, bottom-right, bottom-left. Both
|
|
67
|
+
* clockwise and counter-clockwise winding are accepted; repeated points,
|
|
68
|
+
* zero-length edges, collinear turns, concave quads, and self-intersections
|
|
69
|
+
* are rejected.
|
|
70
|
+
*/
|
|
71
|
+
export function isValidCorners(corners) {
|
|
72
|
+
if (!Array.isArray(corners) || corners.length !== 4)
|
|
73
|
+
return false;
|
|
74
|
+
const points = [];
|
|
75
|
+
for (const value of corners) {
|
|
76
|
+
if (!isRecord(value) || !isFiniteNumber(value.x) || !isFiniteNumber(value.y)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
points.push({ x: value.x, y: value.y });
|
|
80
|
+
}
|
|
81
|
+
for (let first = 0; first < points.length; first++) {
|
|
82
|
+
for (let second = first + 1; second < points.length; second++) {
|
|
83
|
+
if (points[first].x === points[second].x && points[first].y === points[second].y) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let winding = 0;
|
|
89
|
+
for (let index = 0; index < points.length; index++) {
|
|
90
|
+
const turn = cross(points[index], points[(index + 1) % points.length], points[(index + 2) % points.length]);
|
|
91
|
+
if (!Number.isFinite(turn) || turn === 0)
|
|
92
|
+
return false;
|
|
93
|
+
const sign = Math.sign(turn);
|
|
94
|
+
if (winding === 0)
|
|
95
|
+
winding = sign;
|
|
96
|
+
else if (sign !== winding)
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
function isValidQuality(value) {
|
|
102
|
+
if (!isRecord(value))
|
|
103
|
+
return false;
|
|
104
|
+
if (hasOwn(value, 'quietZone') && typeof value.quietZone !== 'boolean')
|
|
105
|
+
return false;
|
|
106
|
+
if (hasOwn(value, 'checksum')
|
|
107
|
+
&& value.checksum !== null && typeof value.checksum !== 'boolean')
|
|
108
|
+
return false;
|
|
109
|
+
if (hasOwn(value, 'rows') && value.rows !== null
|
|
110
|
+
&& (!isFiniteNumber(value.rows) || !Number.isInteger(value.rows) || value.rows < 0)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (hasOwn(value, 'consistency') && value.consistency !== null
|
|
114
|
+
&& (!isFiniteNumber(value.consistency) || value.consistency < 0 || value.consistency > 1)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
function isValidConfidence(value) {
|
|
120
|
+
return value === undefined
|
|
121
|
+
|| (isFiniteNumber(value) && value >= 0 && value <= 1);
|
|
122
|
+
}
|
|
123
|
+
function isValidScore(value) {
|
|
124
|
+
return value === undefined || isFiniteNumber(value);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a validated detector candidate.
|
|
128
|
+
*
|
|
129
|
+
* All optional decoded-result and ranking metadata must be supplied in the
|
|
130
|
+
* options object: `createDetectionCandidate(geometry, { result, quality, score })`.
|
|
131
|
+
* Keeping the decoded result under the explicit `result` key avoids confusing
|
|
132
|
+
* a result object with candidate metadata. The returned value owns a fresh
|
|
133
|
+
* corner array and canonical rotation, and the input objects are never
|
|
134
|
+
* mutated.
|
|
135
|
+
*
|
|
136
|
+
* @throws {TypeError} If the geometry, matrix, or options have an invalid type.
|
|
137
|
+
* @throws {RangeError} If a numeric geometry or metadata value is invalid.
|
|
138
|
+
*/
|
|
139
|
+
export function createDetectionCandidate(geometry, options) {
|
|
140
|
+
if (!isRecord(geometry)) {
|
|
141
|
+
throw new TypeError('Detection geometry must be an object');
|
|
142
|
+
}
|
|
143
|
+
const geometryValue = geometry;
|
|
144
|
+
const corners = geometryValue.corners;
|
|
145
|
+
if (!isValidCorners(corners)) {
|
|
146
|
+
throw new RangeError('Detection geometry must contain four non-degenerate corners');
|
|
147
|
+
}
|
|
148
|
+
const moduleSize = geometryValue.moduleSize;
|
|
149
|
+
if (!isFiniteNumber(moduleSize) || moduleSize <= 0) {
|
|
150
|
+
throw new RangeError('Detection geometry moduleSize must be a positive finite number');
|
|
151
|
+
}
|
|
152
|
+
const rotation = normalizeRotation(geometryValue.rotation);
|
|
153
|
+
if (!hasOwn(geometryValue, 'matrix') || geometryValue.matrix === null
|
|
154
|
+
|| geometryValue.matrix === undefined) {
|
|
155
|
+
throw new TypeError('Detection geometry must contain a matrix');
|
|
156
|
+
}
|
|
157
|
+
if (!isValidConfidence(geometryValue.confidence)) {
|
|
158
|
+
throw new RangeError('Detection geometry confidence must be a finite number in 0..1');
|
|
159
|
+
}
|
|
160
|
+
const candidateOptions = options === undefined ? {} : options;
|
|
161
|
+
if (!isRecord(candidateOptions)) {
|
|
162
|
+
throw new TypeError('Detection candidate options must be an object');
|
|
163
|
+
}
|
|
164
|
+
const validatedOptions = candidateOptions;
|
|
165
|
+
const quality = validatedOptions.quality;
|
|
166
|
+
if (quality !== undefined && !isValidQuality(quality)) {
|
|
167
|
+
throw new TypeError('Detection candidate quality must be an object');
|
|
168
|
+
}
|
|
169
|
+
if (!isValidScore(validatedOptions.score)) {
|
|
170
|
+
throw new RangeError('Detection candidate score must be a finite number');
|
|
171
|
+
}
|
|
172
|
+
const candidate = {
|
|
173
|
+
...geometry,
|
|
174
|
+
corners: corners.map((point) => ({ x: point.x, y: point.y })),
|
|
175
|
+
moduleSize,
|
|
176
|
+
rotation,
|
|
177
|
+
};
|
|
178
|
+
if (hasOwn(validatedOptions, 'result') && validatedOptions.result !== undefined)
|
|
179
|
+
candidate.result = validatedOptions.result;
|
|
180
|
+
if (hasOwn(validatedOptions, 'quality') && quality !== undefined)
|
|
181
|
+
candidate.quality = quality;
|
|
182
|
+
if (hasOwn(validatedOptions, 'score') && validatedOptions.score !== undefined)
|
|
183
|
+
candidate.score = validatedOptions.score;
|
|
184
|
+
return candidate;
|
|
185
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Sythos Barcode Suite
|
|
3
|
+
*
|
|
4
|
+
* MIT License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Sythos
|
|
7
|
+
*
|
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
* furnished to do so, subject to the following conditions:
|
|
14
|
+
*
|
|
15
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
* copies or substantial portions of the Software.
|
|
17
|
+
*
|
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
* SOFTWARE.
|
|
25
|
+
*
|
|
26
|
+
* SPDX-License-Identifier: MIT
|
|
27
|
+
*
|
|
28
|
+
* Original work. No code from any other barcode implementation.
|
|
29
|
+
*/
|
|
30
|
+
/*
|
|
31
|
+
* These limits are intentionally conservative. They keep this foundation safe
|
|
32
|
+
* for future matrix allocators while allowing the largest practical barcode
|
|
33
|
+
* geometries. The implementation never allocates based on a descriptor.
|
|
34
|
+
*/
|
|
35
|
+
const MAX_LAYOUT_DIMENSION = 32768;
|
|
36
|
+
const MAX_LAYOUT_MODULES = 16777216;
|
|
37
|
+
const hasOwn = (value, property) => Object.prototype.hasOwnProperty.call(value, property);
|
|
38
|
+
function isBoundedPositiveInteger(value, maximum) {
|
|
39
|
+
return typeof value === 'number'
|
|
40
|
+
&& Number.isFinite(value)
|
|
41
|
+
&& Number.isInteger(value)
|
|
42
|
+
&& value > 0
|
|
43
|
+
&& value <= maximum;
|
|
44
|
+
}
|
|
45
|
+
function isRecord(value) {
|
|
46
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check that the descriptor's full panel footprint stays within the allocation
|
|
50
|
+
* budget. The quiet zone is included in the footprint because a later renderer
|
|
51
|
+
* may materialize it alongside the active modules.
|
|
52
|
+
*/
|
|
53
|
+
function isWithinModuleBudget(width, height, quietZone, rows, columns) {
|
|
54
|
+
const footprintWidth = width + quietZone * 2;
|
|
55
|
+
const footprintHeight = height + quietZone * 2;
|
|
56
|
+
if (footprintWidth > MAX_LAYOUT_DIMENSION || footprintHeight > MAX_LAYOUT_DIMENSION) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
let total = footprintWidth;
|
|
60
|
+
if (total > MAX_LAYOUT_MODULES / footprintHeight)
|
|
61
|
+
return false;
|
|
62
|
+
total *= footprintHeight;
|
|
63
|
+
if (total > MAX_LAYOUT_MODULES / rows)
|
|
64
|
+
return false;
|
|
65
|
+
total *= rows;
|
|
66
|
+
if (total > MAX_LAYOUT_MODULES / columns)
|
|
67
|
+
return false;
|
|
68
|
+
return total * columns <= MAX_LAYOUT_MODULES;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Return whether an unknown value is a valid symbol layout descriptor.
|
|
72
|
+
*
|
|
73
|
+
* Validation is side-effect free and performs no matrix allocation. A valid
|
|
74
|
+
* descriptor may be mutable when supplied by a caller; use
|
|
75
|
+
* {@link createSymbolLayout} when an immutable value is required.
|
|
76
|
+
*/
|
|
77
|
+
export function validateSymbolLayout(value) {
|
|
78
|
+
if (!isRecord(value))
|
|
79
|
+
return false;
|
|
80
|
+
if (!hasOwn(value, 'moduleShape') || (value.moduleShape !== 'square' && value.moduleShape !== 'dot')) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (!hasOwn(value, 'lattice') || (value.lattice !== 'square' && value.lattice !== 'hex')) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (!hasOwn(value, 'width') || !isBoundedPositiveInteger(value.width, MAX_LAYOUT_DIMENSION)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (!hasOwn(value, 'height') || !isBoundedPositiveInteger(value.height, MAX_LAYOUT_DIMENSION)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (!hasOwn(value, 'quietZone') || !isBoundedPositiveInteger(value.quietZone, MAX_LAYOUT_DIMENSION)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const rows = value.rows;
|
|
96
|
+
if (rows !== undefined
|
|
97
|
+
&& (!hasOwn(value, 'rows') || !isBoundedPositiveInteger(rows, MAX_LAYOUT_DIMENSION))) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const columns = value.columns;
|
|
101
|
+
if (columns !== undefined
|
|
102
|
+
&& (!hasOwn(value, 'columns') || !isBoundedPositiveInteger(columns, MAX_LAYOUT_DIMENSION))) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const rowCount = rows === undefined ? 1 : rows;
|
|
106
|
+
const columnCount = columns === undefined ? 1 : columns;
|
|
107
|
+
return isWithinModuleBudget(value.width, value.height, value.quietZone, rowCount, columnCount);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create a validated, shallowly immutable symbol layout descriptor.
|
|
111
|
+
*
|
|
112
|
+
* Only the documented fields are copied. Unknown input properties are ignored,
|
|
113
|
+
* so a future caller can pass a richer format-specific object without leaking
|
|
114
|
+
* mutable implementation state into the shared descriptor.
|
|
115
|
+
*
|
|
116
|
+
* @throws {TypeError} If `options` is not an object.
|
|
117
|
+
* @throws {RangeError} If any field is invalid or exceeds the safe budget.
|
|
118
|
+
*/
|
|
119
|
+
export function createSymbolLayout(options) {
|
|
120
|
+
if (!isRecord(options)) {
|
|
121
|
+
throw new TypeError('Symbol layout options must be an object');
|
|
122
|
+
}
|
|
123
|
+
const descriptor = {
|
|
124
|
+
moduleShape: options.moduleShape,
|
|
125
|
+
lattice: options.lattice,
|
|
126
|
+
width: options.width,
|
|
127
|
+
height: options.height,
|
|
128
|
+
quietZone: options.quietZone,
|
|
129
|
+
};
|
|
130
|
+
if (options.rows !== undefined)
|
|
131
|
+
descriptor.rows = options.rows;
|
|
132
|
+
if (options.columns !== undefined)
|
|
133
|
+
descriptor.columns = options.columns;
|
|
134
|
+
if (!validateSymbolLayout(descriptor)) {
|
|
135
|
+
throw new RangeError('Invalid symbol layout descriptor');
|
|
136
|
+
}
|
|
137
|
+
return Object.freeze(descriptor);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Count logical modules in a complete layout without allocating a matrix.
|
|
141
|
+
*
|
|
142
|
+
* Optional rows and columns multiply the panel dimensions. Quiet-zone modules
|
|
143
|
+
* are intentionally excluded: this helper reports symbol modules, while the
|
|
144
|
+
* validation budget also protects the future rendered footprint.
|
|
145
|
+
*
|
|
146
|
+
* @throws {TypeError} If `layout` is not a valid descriptor.
|
|
147
|
+
*/
|
|
148
|
+
export function layoutModuleCount(layout) {
|
|
149
|
+
if (!validateSymbolLayout(layout)) {
|
|
150
|
+
throw new TypeError('Cannot count modules for an invalid symbol layout');
|
|
151
|
+
}
|
|
152
|
+
const rows = layout.rows === undefined ? 1 : layout.rows;
|
|
153
|
+
const columns = layout.columns === undefined ? 1 : layout.columns;
|
|
154
|
+
return layout.width * layout.height * rows * columns;
|
|
155
|
+
}
|