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.
- package/index.js +217 -0
- package/package.json +11 -0
- 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
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 {}
|