nv-img-barcode-cmmn 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.js +217 -0
  2. package/package.json +11 -0
  3. package/ptrn.js +161 -0
package/index.js ADDED
@@ -0,0 +1,217 @@
1
+ const {
2
+ code128_code_to_ptrn,
3
+ code128_ptrn_to_code,
4
+ CODE128_CODE_TO_PTRN
5
+ } = require("./ptrn")
6
+
7
+ const bytes_to_code128 = (bytes) => {
8
+ const codes = [];
9
+ const START_B = 104;
10
+ codes.push(START_B);
11
+
12
+ for (const b of bytes) {
13
+ codes.push(b & 0x7f); // 与 decoder 对称
14
+ }
15
+
16
+ // checksum
17
+ let sum = START_B;
18
+ codes.slice(1).forEach((c, i) => {
19
+ sum += c * (i + 1);
20
+ });
21
+ codes.push(sum % 103);
22
+
23
+ // STOP
24
+ codes.push(106);
25
+
26
+ // codes -> modules
27
+ return codes.flatMap(c => CODE128_CODE_TO_PTRN[c]);
28
+ };
29
+
30
+ const code128_to_bytes = (modules) => {
31
+ const codes = [];
32
+ let i = 0;
33
+
34
+ while (i < modules.length) {
35
+ let code = code128_ptrn_to_code(modules.slice(i, i + 6));
36
+ if (code >= 0) {
37
+ codes.push(code);
38
+ i += 6;
39
+ continue;
40
+ }
41
+
42
+ // 尝试 STOP
43
+ if (i + 7 <= modules.length) {
44
+ code = code128_ptrn_to_code(modules.slice(i, i + 7));
45
+ if (code === 106) {
46
+ codes.push(106);
47
+ i += 7;
48
+ break;
49
+ }
50
+ }
51
+
52
+ // 不匹配也安全跳过:+1 移动
53
+ i += 1;
54
+ }
55
+
56
+ // 安全处理 START / STOP
57
+ if (codes.length === 0 || codes[0] !== 104) {
58
+ // 没有 START_B,用空 payload
59
+ return new Uint8Array(0);
60
+ }
61
+
62
+ // 去掉 START / checksum / STOP
63
+ const payload = codes.slice(1, -2);
64
+
65
+ // checksum 校验可选
66
+ let sum = 104;
67
+ payload.forEach((c, i) => sum += c * (i + 1));
68
+ const checksum = codes[codes.length - 2] ?? 0;
69
+ if (sum % 103 !== checksum) {
70
+ // 可以返回 null 或空数组表示校验失败
71
+ return new Uint8Array(0);
72
+ }
73
+
74
+ return Uint8Array.from(payload);
75
+ };
76
+
77
+ const normalize_runs_to_modules = (runs) => {
78
+ const min = Math.min(...runs);
79
+ return runs.map(r => Math.max(1, Math.round(r / min)));
80
+ };
81
+
82
+
83
+ /**
84
+ * transfer_ab_to_png_ab
85
+ * @param {ArrayBuffer} ab - 输入数据
86
+ * @param {number} moduleWidth - 每个最小 module 的像素宽度,默认 2
87
+ * @param {number} height - 条形码高度,默认 50px
88
+ * @returns {Promise<ArrayBuffer>} - PNG 的 ArrayBuffer
89
+ */
90
+ const transfer_ab_to_png_ab = async (ab, moduleWidth = 2, height = 50) => {
91
+ const bytes = new Uint8Array(ab);
92
+
93
+ // 1️⃣ bytes -> Code128 modules
94
+ const modules = bytes_to_code128(bytes);
95
+
96
+ // 2️⃣ Canvas 尺寸
97
+ const canvasWidth = modules.reduce((acc, m) => acc + m * moduleWidth, 0);
98
+ const canvas = document.createElement("canvas");
99
+ canvas.width = canvasWidth;
100
+ canvas.height = height;
101
+ const ctx = canvas.getContext("2d");
102
+
103
+ // 3️⃣ 绘制条形码
104
+ let x = 0;
105
+ modules.forEach((m, idx) => {
106
+ ctx.fillStyle = idx % 2 === 0 ? "black" : "white"; // 黑白交替
107
+ ctx.fillRect(x, 0, m * moduleWidth, height);
108
+ x += m * moduleWidth;
109
+ });
110
+
111
+ // 4️⃣ Canvas -> Blob -> ArrayBuffer
112
+ return new Promise((resolve, reject) => {
113
+ canvas.toBlob((blob) => {
114
+ if (!blob) return reject(new Error("Canvas to Blob failed"));
115
+ const reader = new FileReader();
116
+ reader.onload = () => resolve(reader.result);
117
+ reader.onerror = reject;
118
+ reader.readAsArrayBuffer(blob);
119
+ }, "image/png");
120
+ });
121
+ };
122
+
123
+ /**
124
+ * png_ab_to_transfer_ab
125
+ * @param {ArrayBuffer} pngAb - PNG 数据
126
+ * @param {number} moduleWidthEstimate - 用于初步估算模块宽度,默认 2
127
+ * @returns {Promise<ArrayBuffer>} - 原始 ArrayBuffer
128
+ */
129
+ const png_ab_to_transfer_ab = async (pngAb, moduleWidthEstimate = 2) => {
130
+ // 1️⃣ ArrayBuffer -> Blob -> Image
131
+ const blob = new Blob([pngAb], { type: "image/png" });
132
+ const url = URL.createObjectURL(blob);
133
+
134
+ const img = new Image();
135
+ img.src = url;
136
+ await img.decode();
137
+
138
+ // 2️⃣ Image -> Canvas
139
+ const canvas = document.createElement("canvas");
140
+ canvas.width = img.width;
141
+ canvas.height = img.height;
142
+ const ctx = canvas.getContext("2d");
143
+ ctx.drawImage(img, 0, 0);
144
+
145
+ // 3️⃣ 读取顶部一行像素
146
+ const imageData = ctx.getImageData(0, 0, canvas.width, 1).data;
147
+ const runs = [];
148
+ let lastBW = null;
149
+ let count = 0;
150
+
151
+ for (let x = 0; x < canvas.width; x++) {
152
+ const i = x * 4;
153
+ const r = imageData[i];
154
+ const g = imageData[i + 1];
155
+ const b = imageData[i + 2];
156
+ const bw = (r + g + b) / 3 < 128 ? 1 : 0; // 黑 = 1, 白 = 0
157
+
158
+ if (lastBW === null || bw === lastBW) {
159
+ count++;
160
+ } else {
161
+ runs.push(count);
162
+ count = 1;
163
+ }
164
+ lastBW = bw;
165
+ }
166
+ runs.push(count);
167
+
168
+ // 4️⃣ run-length -> modules
169
+ const normalize_runs_to_modules = (runs) => {
170
+ const min = Math.min(...runs);
171
+ return runs.map(r => Math.max(1, Math.round(r / min)));
172
+ };
173
+ const modules = normalize_runs_to_modules(runs);
174
+
175
+ // 5️⃣ modules -> bytes -> ArrayBuffer
176
+ const bytes = code128_to_bytes(modules);
177
+
178
+ URL.revokeObjectURL(url); // 清理
179
+
180
+ return bytes.buffer;
181
+ };
182
+
183
+ /**
184
+ * str2png
185
+ * @param {string} s - 可读文本
186
+ * @param {number} moduleWidth - 每个 module 的像素宽度
187
+ * @param {number} height - 条形码高度
188
+ * @returns {Promise<ArrayBuffer>} - PNG 数据
189
+ */
190
+ const str2png = async (s, moduleWidth = 2, height = 50) => {
191
+ // 将字符串编码为 UTF-8 bytes
192
+ const encoder = new TextEncoder(); // 浏览器内置 API
193
+ const ab = encoder.encode(s).buffer; // Uint8Array -> ArrayBuffer
194
+ return transfer_ab_to_png_ab(ab, moduleWidth, height);
195
+ };
196
+
197
+ /**
198
+ * png2str
199
+ * @param {ArrayBuffer} pngAb - PNG 数据
200
+ * @param {number} moduleWidthEstimate - 用于初步估算模块宽度
201
+ * @returns {Promise<string>} - 解码后的文本
202
+ */
203
+ const png2str = async (pngAb, moduleWidthEstimate = 2) => {
204
+ const ab = await png_ab_to_transfer_ab(pngAb, moduleWidthEstimate); // PNG -> ArrayBuffer
205
+ const decoder = new TextDecoder(); // 默认 utf-8
206
+ return decoder.decode(ab);
207
+ };
208
+
209
+ module.exports = {
210
+ bytes_to_code128,
211
+ code128_to_bytes,
212
+ normalize_runs_to_modules,
213
+ transfer_ab_to_png_ab,
214
+ png_ab_to_transfer_ab,
215
+ str2png,
216
+ png2str,
217
+ }
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "nv-img-barcode-cmmn",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "author": "",
9
+ "license": "ISC",
10
+ "description": ""
11
+ }
package/ptrn.js ADDED
@@ -0,0 +1,161 @@
1
+ // Code 128 patterns (ISO/IEC 15417)
2
+ const CODE128_CODE_TO_PTRN = [
3
+ [2,1,2,2,2,2], // 0
4
+ [2,2,2,1,2,2], // 1
5
+ [2,2,2,2,2,1], // 2
6
+ [1,2,1,2,2,3], // 3
7
+ [1,2,1,3,2,2], // 4
8
+ [1,3,1,2,2,2], // 5
9
+ [1,2,2,2,1,3], // 6
10
+ [1,2,2,3,1,2], // 7
11
+ [1,3,2,2,1,2], // 8
12
+ [2,2,1,2,1,3], // 9
13
+ [2,2,1,3,1,2], // 10
14
+ [2,3,1,2,1,2], // 11
15
+ [1,1,2,2,3,2], // 12
16
+ [1,2,2,1,3,2], // 13
17
+ [1,2,2,2,3,1], // 14
18
+ [1,1,3,2,2,2], // 15
19
+ [1,2,3,1,2,2], // 16
20
+ [1,2,3,2,2,1], // 17
21
+ [2,2,3,2,1,1], // 18
22
+ [2,2,1,1,3,2], // 19
23
+ [2,2,1,2,3,1], // 20
24
+ [2,1,3,2,1,2], // 21
25
+ [2,2,3,1,1,2], // 22
26
+ [3,1,2,1,3,1], // 23
27
+ [3,1,1,2,2,2], // 24
28
+ [3,2,1,1,2,2], // 25
29
+ [3,2,1,2,2,1], // 26
30
+ [3,1,2,2,1,2], // 27
31
+ [3,2,2,1,1,2], // 28
32
+ [3,2,2,2,1,1], // 29
33
+ [2,1,2,1,2,3], // 30
34
+ [2,1,2,3,2,1], // 31
35
+ [2,3,2,1,2,1], // 32
36
+ [1,1,1,3,2,3], // 33
37
+ [1,3,1,1,2,3], // 34
38
+ [1,3,1,3,2,1], // 35
39
+ [1,1,2,3,1,3], // 36
40
+ [1,3,2,1,1,3], // 37
41
+ [1,3,2,3,1,1], // 38
42
+ [2,1,1,3,1,3], // 39
43
+ [2,3,1,1,1,3], // 40
44
+ [2,3,1,3,1,1], // 41
45
+ [1,1,2,1,3,3], // 42
46
+ [1,1,2,3,3,1], // 43
47
+ [1,3,2,1,3,1], // 44
48
+ [1,1,3,1,2,3], // 45
49
+ [1,1,3,3,2,1], // 46
50
+ [1,3,3,1,2,1], // 47
51
+ [3,1,3,1,2,1], // 48
52
+ [2,1,1,3,3,1], // 49
53
+ [2,3,1,1,3,1], // 50
54
+ [2,1,3,1,1,3], // 51
55
+ [2,1,3,3,1,1], // 52
56
+ [2,1,3,1,3,1], // 53
57
+ [3,1,1,1,2,3], // 54
58
+ [3,1,1,3,2,1], // 55
59
+ [3,3,1,1,2,1], // 56
60
+ [3,1,2,1,1,3], // 57
61
+ [3,1,2,3,1,1], // 58
62
+ [3,3,2,1,1,1], // 59
63
+ [3,1,4,1,1,1], // 60
64
+ [2,2,1,4,1,1], // 61
65
+ [4,3,1,1,1,1], // 62
66
+ [1,1,1,2,2,4], // 63
67
+ [1,1,1,4,2,2], // 64
68
+ [1,2,1,1,2,4], // 65
69
+ [1,2,1,4,2,1], // 66
70
+ [1,4,1,1,2,2], // 67
71
+ [1,4,1,2,2,1], // 68
72
+ [1,1,2,2,1,4], // 69
73
+ [1,1,2,4,1,2], // 70
74
+ [1,2,2,1,1,4], // 71
75
+ [1,2,2,4,1,1], // 72
76
+ [1,4,2,1,1,2], // 73
77
+ [1,4,2,2,1,1], // 74
78
+ [2,4,1,2,1,1], // 75
79
+ [2,2,1,1,1,4], // 76
80
+ [4,1,3,1,1,1], // 77
81
+ [2,4,1,1,1,2], // 78
82
+ [1,3,4,1,1,1], // 79
83
+ [1,1,1,2,4,2], // 80
84
+ [1,2,1,1,4,2], // 81
85
+ [1,2,1,2,4,1], // 82
86
+ [1,1,4,2,1,2], // 83
87
+ [1,2,4,1,1,2], // 84
88
+ [1,2,4,2,1,1], // 85
89
+ [4,1,1,2,1,2], // 86
90
+ [4,2,1,1,1,2], // 87
91
+ [4,2,1,2,1,1], // 88
92
+ [2,1,2,1,4,1], // 89
93
+ [2,1,4,1,2,1], // 90
94
+ [4,1,2,1,2,1], // 91
95
+ [1,1,1,1,4,3], // 92
96
+ [1,1,1,3,4,1], // 93
97
+ [1,3,1,1,4,1], // 94
98
+ [1,1,4,1,1,3], // 95
99
+ [1,1,4,3,1,1], // 96
100
+ [4,1,1,1,1,3], // 97
101
+ [4,1,1,3,1,1], // 98
102
+ [1,1,3,1,4,1], // 99
103
+ [1,1,4,1,3,1], // 100
104
+ [3,1,1,1,4,1], // 101
105
+ [4,1,1,1,3,1], // 102
106
+ [2,1,1,4,1,2], // 103 START A
107
+ [2,1,1,2,1,4], // 104 START B
108
+ [2,1,1,2,3,2], // 105 START C
109
+ [2,3,3,1,1,1,2], // 106 STOP (7 modules!)
110
+ ];
111
+
112
+ const arr_to_intkey = (arr)=> parseInt(arr.join(""),5);
113
+
114
+ const [MIN_KEY,CODE128_PTRN_TO_CODE] = JSON.parse(JSON.stringify(
115
+ (() => {
116
+ const mp = {};
117
+ for (let i = 0; i < CODE128_CODE_TO_PTRN.length; i++) {
118
+ const arr = CODE128_CODE_TO_PTRN[i];
119
+ mp[arr_to_intkey(arr)]=i;
120
+ }
121
+ let ks = Object.keys(mp);
122
+ ks = ks.map(r=>Number(r));
123
+ ks.sort((a,b)=>a-b);
124
+ let MIN = ks[0];
125
+ let MAX = ks[ks.length-1];
126
+ let arr = [];
127
+ for(let i=0;i<=MAX;++i) {arr[i] = 0;}
128
+ for(let k of ks) { arr[k-MIN] = mp[k];}
129
+ return [MIN,arr];
130
+ })()
131
+ ));
132
+
133
+
134
+ const code128_code_to_ptrn = (cd) => CODE128_CODE_TO_PTRN[cd]; //->Array<>
135
+ const code128_ptrn_to_code = (arr)=> {
136
+ const key = arr_to_intkey(arr);
137
+ const idx = key - MIN_KEY;
138
+ return CODE128_PTRN_TO_CODE[idx];
139
+ }
140
+
141
+
142
+ module.exports = {
143
+ CODE128_CODE_TO_PTRN,
144
+ MIN_KEY,
145
+ CODE128_PTRN_TO_CODE,
146
+ ////
147
+ arr_to_intkey,
148
+ code128_code_to_ptrn,
149
+ code128_ptrn_to_code
150
+ }
151
+
152
+ if(module.id===".") {
153
+ for(let i=0;i<CODE128_CODE_TO_PTRN.length;++i) {
154
+ let expected = CODE128_CODE_TO_PTRN[i];
155
+ let actual = code128_ptrn_to_code(CODE128_CODE_TO_PTRN[i]);
156
+ if(actual!== i) {
157
+ throw({expected,actual})
158
+ } else {
159
+ }
160
+ }
161
+ } else {}