nv-img-barcode-bw 1.0.1
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/DEPS/html2canvas.min.js +20 -0
- package/DEPS/jsbarcode.min.js +2 -0
- package/DEPS/zxing.js +1 -0
- package/DIST/nv-img-barcode-bw.full.js +3173 -0
- package/DIST/nv-img-barcode-bw.js +265 -0
- package/TEST/api.html +1132 -0
- package/TEST/code128-VS-ean128.html +501 -0
- package/TEST/gs1-VS-nongs1.html +208 -0
- package/TEST/index.html +746 -0
- package/TEST/tst.html +950 -0
- package/TOOL/api.tmpl.html +867 -0
- package/TOOL/cli-creat-dist.js +5 -0
- package/TOOL/cli-creat-html.js +13 -0
- package/TOOL/index.tmpl.html +481 -0
- package/cfg.js +56 -0
- package/com.sh +5 -0
- package/css.js +170 -0
- package/decd.js +270 -0
- package/eng.js +589 -0
- package/index.js +366 -0
- package/package.json +17 -0
- package/ui.js +36 -0
package/eng.js
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
const {gs1} = require("nv-img-barcode-cmmn");
|
|
2
|
+
|
|
3
|
+
async function _load(src,impt_name) {
|
|
4
|
+
if (window[impt_name]) return true;
|
|
5
|
+
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const script = document.createElement('script');
|
|
8
|
+
script.src = src;
|
|
9
|
+
script.onload = () => resolve(true);
|
|
10
|
+
script.onerror = () => reject(new Error('Failed to load JsBarcode'));
|
|
11
|
+
script.async = true;
|
|
12
|
+
document.head.appendChild(script);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// barcode-utils.js
|
|
18
|
+
// 辅助函数:加载 JsBarcode
|
|
19
|
+
async function loadJsBarcode(src = 'https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js') {return await _load(src,"JsBarcode");}
|
|
20
|
+
// 辅助函数:加载 html2canvas
|
|
21
|
+
async function loadHtml2Canvas(src = 'https://html2canvas.hertzen.com/dist/html2canvas.min.js') { return await _load(src,"html2canvas");}
|
|
22
|
+
async function loadZXing(src = 'https://cdn.jsdelivr.net/npm/quagga@0.12.1/dist/quagga.min.js') { return await _load(src,"ZXing");}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// 独立的条形码工具函数库
|
|
26
|
+
|
|
27
|
+
const FNC1_IF_USING_CODE128_AND_EAN128 = 102;
|
|
28
|
+
|
|
29
|
+
// 工具函数:str2svg - 字符串转SVG条形码
|
|
30
|
+
async function str2svg(text, options = {}) {
|
|
31
|
+
const defaultOptions = {
|
|
32
|
+
format: 'CODE128',
|
|
33
|
+
ean128: true,
|
|
34
|
+
width: 10,
|
|
35
|
+
height: 100,
|
|
36
|
+
lineColor: '#000000',
|
|
37
|
+
background: '#ffffff',
|
|
38
|
+
displayValue: false,
|
|
39
|
+
fontSize: 20,
|
|
40
|
+
margin: 30 //-------------------------------如果不留足够宽 解不了 。。。。。。
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const opts = { ...defaultOptions, ...options };
|
|
44
|
+
|
|
45
|
+
console.log({opts})
|
|
46
|
+
|
|
47
|
+
if(opts.format.toLowerCase()==="code128") {
|
|
48
|
+
if(opts.ean128 !== false) {
|
|
49
|
+
opts.ean128 = true;
|
|
50
|
+
}
|
|
51
|
+
} else {}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
// 加载 JsBarcode
|
|
55
|
+
if (!window.JsBarcode) {
|
|
56
|
+
await loadJsBarcode();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
try {
|
|
61
|
+
const tempSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
62
|
+
const id = 'temp-str2svg-' + Date.now();
|
|
63
|
+
tempSvg.id = id;
|
|
64
|
+
tempSvg.style.position = 'absolute';
|
|
65
|
+
tempSvg.style.left = '-9999px';
|
|
66
|
+
document.body.appendChild(tempSvg);
|
|
67
|
+
|
|
68
|
+
JsBarcode(`#${id}`, text, opts);
|
|
69
|
+
|
|
70
|
+
const svgString = tempSvg.outerHTML;
|
|
71
|
+
tempSvg.remove();
|
|
72
|
+
|
|
73
|
+
resolve(svgString);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
reject(error);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 工具函数:str2png - 字符串转PNG条形码
|
|
81
|
+
async function str2png(text, opts = {}) {
|
|
82
|
+
try {
|
|
83
|
+
const svgString = await str2svg(text, opts);
|
|
84
|
+
return await svg2png(svgString, opts.background);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
////
|
|
91
|
+
function getZXingFormatsByType(barcodeType) {
|
|
92
|
+
const F = ZXing.BarcodeFormat;
|
|
93
|
+
|
|
94
|
+
// 默认:支持所有一维 + 二维
|
|
95
|
+
if (!barcodeType) {
|
|
96
|
+
return [
|
|
97
|
+
F.CODE_128,
|
|
98
|
+
F.EAN_13,
|
|
99
|
+
F.EAN_8,
|
|
100
|
+
F.CODE_39,
|
|
101
|
+
F.CODE_93,
|
|
102
|
+
F.CODABAR,
|
|
103
|
+
F.UPC_A,
|
|
104
|
+
F.UPC_E,
|
|
105
|
+
F.ITF,
|
|
106
|
+
F.QR_CODE,
|
|
107
|
+
F.DATA_MATRIX,
|
|
108
|
+
F.PDF_417,
|
|
109
|
+
F.AZTEC
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
const type = barcodeType.toLowerCase().trim();
|
|
113
|
+
|
|
114
|
+
if (type.includes('ean') && type.includes('128') || type === 'ean128') {
|
|
115
|
+
return [F.CODE_128]; // GS1-128 / EAN-128
|
|
116
|
+
}
|
|
117
|
+
if (type.includes('code') && type.includes('128') || type === 'code128') {
|
|
118
|
+
return [F.CODE_128];
|
|
119
|
+
}
|
|
120
|
+
if (type.includes('ean') && type.includes('13') || type === 'ean13') {
|
|
121
|
+
return [F.EAN_13];
|
|
122
|
+
}
|
|
123
|
+
if (type.includes('ean') && type.includes('8') || type === 'ean8') {
|
|
124
|
+
return [F.EAN_8];
|
|
125
|
+
}
|
|
126
|
+
if (type.includes('upc')) {
|
|
127
|
+
return [F.UPC_A, F.UPC_E];
|
|
128
|
+
}
|
|
129
|
+
if (type.includes('code') && type.includes('39') || type === 'code39') {
|
|
130
|
+
return [F.CODE_39];
|
|
131
|
+
}
|
|
132
|
+
if (type.includes('codabar') || type.includes('nw-7')) {
|
|
133
|
+
return [F.CODABAR];
|
|
134
|
+
}
|
|
135
|
+
if (type.includes('2of5') || type.includes('2/5') || type.includes('25')) {
|
|
136
|
+
return [F.ITF];
|
|
137
|
+
}
|
|
138
|
+
if (type.includes('code') && type.includes('93') || type === 'code93') {
|
|
139
|
+
return [F.CODE_93];
|
|
140
|
+
}
|
|
141
|
+
if (type.includes('qr') || type.includes('qrcode') || type.includes('二维码')) {
|
|
142
|
+
return [F.QR_CODE];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 未知类型:全部支持
|
|
146
|
+
return [
|
|
147
|
+
F.CODE_128,
|
|
148
|
+
F.EAN_13,
|
|
149
|
+
F.EAN_8,
|
|
150
|
+
F.CODE_39,
|
|
151
|
+
F.CODE_93,
|
|
152
|
+
F.CODABAR,
|
|
153
|
+
F.UPC_A,
|
|
154
|
+
F.UPC_E,
|
|
155
|
+
F.ITF,
|
|
156
|
+
F.QR_CODE
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* svg2barcode - 将 SVG 条码字符串解码为文本(支持 Code128 / GS1-128)
|
|
162
|
+
* @param {string} svgString - SVG 条码内容
|
|
163
|
+
* @param {string} [barcodeType] - 条码类型,例如 "CODE128"
|
|
164
|
+
* @returns {Promise<string>} - 解码后的条码文本
|
|
165
|
+
*/
|
|
166
|
+
async function svg2str(svgString, barcodeType = 'CODE128') {
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
try {
|
|
169
|
+
const svgUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgString);
|
|
170
|
+
const img = new Image();
|
|
171
|
+
|
|
172
|
+
img.onload = () => {
|
|
173
|
+
try {
|
|
174
|
+
const SCALE = 4; // 放大倍数,提高解码成功率
|
|
175
|
+
const canvas = document.createElement('canvas');
|
|
176
|
+
const ctx = canvas.getContext('2d');
|
|
177
|
+
|
|
178
|
+
canvas.width = img.width * SCALE;
|
|
179
|
+
canvas.height = img.height * SCALE;
|
|
180
|
+
|
|
181
|
+
ctx.setTransform(SCALE, 0, 0, SCALE, 0, 0);
|
|
182
|
+
|
|
183
|
+
// 白底
|
|
184
|
+
ctx.fillStyle = '#fff';
|
|
185
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
186
|
+
|
|
187
|
+
// 关闭抗锯齿----否则有概率解不了
|
|
188
|
+
ctx.imageSmoothingEnabled = false;
|
|
189
|
+
|
|
190
|
+
// 绘制 SVG 图片到 Canvas
|
|
191
|
+
ctx.drawImage(img, 0, 0);
|
|
192
|
+
|
|
193
|
+
// 灰度化
|
|
194
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
195
|
+
const luminance = new Uint8ClampedArray(canvas.width * canvas.height);
|
|
196
|
+
|
|
197
|
+
for (let i = 0, j = 0; i < imageData.data.length; i += 4, j++) {
|
|
198
|
+
const r = imageData.data[i];
|
|
199
|
+
const g = imageData.data[i + 1];
|
|
200
|
+
const b = imageData.data[i + 2];
|
|
201
|
+
luminance[j] = (r * 299 + g * 587 + b * 114) / 1000;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ZXing JS 解码
|
|
205
|
+
const source = new ZXing.RGBLuminanceSource(luminance, canvas.width, canvas.height);
|
|
206
|
+
const bitmap = new ZXing.BinaryBitmap(new ZXing.HybridBinarizer(source));
|
|
207
|
+
|
|
208
|
+
const reader = new ZXing.MultiFormatReader();
|
|
209
|
+
|
|
210
|
+
const hints = new Map();
|
|
211
|
+
hints.set(
|
|
212
|
+
ZXing.DecodeHintType.POSSIBLE_FORMATS,
|
|
213
|
+
getZXingFormatsByType(barcodeType)
|
|
214
|
+
);
|
|
215
|
+
hints.set(ZXing.DecodeHintType.TRY_HARDER, true);
|
|
216
|
+
|
|
217
|
+
reader.setHints(hints);
|
|
218
|
+
|
|
219
|
+
const result = reader.decode(bitmap);
|
|
220
|
+
|
|
221
|
+
// ✅ GS1-128 FNC1 控制字符处理
|
|
222
|
+
let text = result.getText();
|
|
223
|
+
if (barcodeType.toUpperCase() === 'CODE128' && text.includes('\u00f1')) {
|
|
224
|
+
// ZXing 会把 FNC1 解码成 \u00f1
|
|
225
|
+
text = text.replace(/\u00f1/g, '');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
resolve(text);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
console.error(e);
|
|
231
|
+
reject(new Error('无法解码条码'));
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
img.onerror = (err) => {
|
|
236
|
+
console.error(err);
|
|
237
|
+
reject(new Error('SVG 图片加载失败'));
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
img.src = svgUrl;
|
|
241
|
+
} catch (e) {
|
|
242
|
+
console.error(e);
|
|
243
|
+
reject(new Error('处理 SVG 异常'));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
////
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
// 工具函数:png2str - PNG条形码转字符串
|
|
254
|
+
async function png2str(pngData) {
|
|
255
|
+
return new Promise((resolve, reject) => {
|
|
256
|
+
const img = new Image();
|
|
257
|
+
img.onload = async () => {
|
|
258
|
+
try {
|
|
259
|
+
// 加载 html2canvas
|
|
260
|
+
if (!window.html2canvas) {
|
|
261
|
+
await loadHtml2Canvas();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 使用 html2canvas 从图片中读取信息
|
|
265
|
+
const canvas = document.createElement('canvas');
|
|
266
|
+
canvas.width = img.width;
|
|
267
|
+
canvas.height = img.height;
|
|
268
|
+
const ctx = canvas.getContext('2d');
|
|
269
|
+
ctx.drawImage(img, 0, 0);
|
|
270
|
+
|
|
271
|
+
// 转换为SVG然后解析
|
|
272
|
+
const svg = await png2svg(pngData);
|
|
273
|
+
const text = await svg2str(svg);
|
|
274
|
+
resolve(text);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
reject(error);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
img.onerror = () => reject(new Error('Failed to load PNG image'));
|
|
280
|
+
img.src = pngData;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 工具函数:png2svg - PNG条形码转SVG
|
|
285
|
+
async function png2svg(pngData) {
|
|
286
|
+
return new Promise((resolve, reject) => {
|
|
287
|
+
const img = new Image();
|
|
288
|
+
img.onload = async () => {
|
|
289
|
+
try {
|
|
290
|
+
// 加载 html2canvas
|
|
291
|
+
if (!window.html2canvas) {
|
|
292
|
+
await loadHtml2Canvas();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 使用 html2canvas 获取图片数据
|
|
296
|
+
const canvas = document.createElement('canvas');
|
|
297
|
+
canvas.width = img.width;
|
|
298
|
+
canvas.height = img.height;
|
|
299
|
+
const ctx = canvas.getContext('2d');
|
|
300
|
+
ctx.drawImage(img, 0, 0);
|
|
301
|
+
|
|
302
|
+
// 获取图片数据
|
|
303
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
304
|
+
|
|
305
|
+
// 尝试从图片中提取条形码信息
|
|
306
|
+
const text = await png2str(pngData);
|
|
307
|
+
|
|
308
|
+
// 如果提取到了文本,重新生成SVG
|
|
309
|
+
if (text && !text.startsWith('[') && !text.endsWith(']')) {
|
|
310
|
+
const svg = await str2svg(text, {
|
|
311
|
+
width: 2,
|
|
312
|
+
height: img.height * 0.7,
|
|
313
|
+
displayValue: false
|
|
314
|
+
});
|
|
315
|
+
resolve(svg);
|
|
316
|
+
} else {
|
|
317
|
+
// 如果提取失败,创建一个简单的SVG
|
|
318
|
+
const svg = `
|
|
319
|
+
<svg width="${canvas.width}" height="${canvas.height}"
|
|
320
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
321
|
+
<rect width="100%" height="100%" fill="white"/>
|
|
322
|
+
<text x="50%" y="50%"
|
|
323
|
+
text-anchor="middle"
|
|
324
|
+
dominant-baseline="middle"
|
|
325
|
+
font-family="Arial"
|
|
326
|
+
font-size="20"
|
|
327
|
+
fill="black">
|
|
328
|
+
无法解码条形码
|
|
329
|
+
</text>
|
|
330
|
+
</svg>
|
|
331
|
+
`;
|
|
332
|
+
resolve(svg);
|
|
333
|
+
}
|
|
334
|
+
} catch (error) {
|
|
335
|
+
reject(error);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
img.onerror = () => reject(new Error('Failed to load PNG image'));
|
|
339
|
+
img.src = pngData;
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 工具函数:svg2png - SVG条形码转PNG
|
|
344
|
+
function svg2png(svgString, options = {}) {
|
|
345
|
+
return new Promise((resolve, reject) => {
|
|
346
|
+
try {
|
|
347
|
+
const {
|
|
348
|
+
width = null, // 输出宽度
|
|
349
|
+
height = null, // 输出高度
|
|
350
|
+
scale = 1, // 缩放比例
|
|
351
|
+
backgroundColor = 'white', // 背景色
|
|
352
|
+
quality = 1, // PNG质量 (0-1)
|
|
353
|
+
mimeType = 'image/png' // 输出格式
|
|
354
|
+
} = options;
|
|
355
|
+
|
|
356
|
+
const img = new Image();
|
|
357
|
+
const svgData = encodeURIComponent(svgString);
|
|
358
|
+
const svgUrl = `data:image/svg+xml;charset=utf-8,${svgData}`;
|
|
359
|
+
|
|
360
|
+
img.onload = function() {
|
|
361
|
+
const canvas = document.createElement('canvas');
|
|
362
|
+
const ctx = canvas.getContext('2d');
|
|
363
|
+
|
|
364
|
+
// 计算输出尺寸
|
|
365
|
+
let outputWidth = img.width;
|
|
366
|
+
let outputHeight = img.height;
|
|
367
|
+
|
|
368
|
+
if (width && height) {
|
|
369
|
+
outputWidth = width;
|
|
370
|
+
outputHeight = height;
|
|
371
|
+
} else if (width) {
|
|
372
|
+
outputHeight = (img.height * width) / img.width;
|
|
373
|
+
outputWidth = width;
|
|
374
|
+
} else if (height) {
|
|
375
|
+
outputWidth = (img.width * height) / img.height;
|
|
376
|
+
outputHeight = height;
|
|
377
|
+
} else if (scale !== 1) {
|
|
378
|
+
outputWidth = img.width * scale;
|
|
379
|
+
outputHeight = img.height * scale;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
canvas.width = outputWidth;
|
|
383
|
+
canvas.height = outputHeight;
|
|
384
|
+
|
|
385
|
+
// 绘制背景
|
|
386
|
+
ctx.fillStyle = backgroundColor;
|
|
387
|
+
ctx.fillRect(0, 0, outputWidth, outputHeight);
|
|
388
|
+
|
|
389
|
+
// 绘制SVG(支持缩放)
|
|
390
|
+
ctx.drawImage(img, 0, 0, outputWidth, outputHeight);
|
|
391
|
+
|
|
392
|
+
// 转换为Data URL
|
|
393
|
+
const dataUrl = canvas.toDataURL(mimeType, quality);
|
|
394
|
+
|
|
395
|
+
// 转换为Blob
|
|
396
|
+
canvas.toBlob(function(blob) {
|
|
397
|
+
resolve({
|
|
398
|
+
dataUrl: dataUrl,
|
|
399
|
+
blob: blob,
|
|
400
|
+
width: outputWidth,
|
|
401
|
+
height: outputHeight,
|
|
402
|
+
imageData: ctx.getImageData(0, 0, outputWidth, outputHeight),
|
|
403
|
+
originalSize: { width: img.width, height: img.height },
|
|
404
|
+
canvas: canvas
|
|
405
|
+
});
|
|
406
|
+
}, mimeType, quality);
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
img.onerror = () => reject(new Error('SVG加载失败'));
|
|
410
|
+
img.src = svgUrl;
|
|
411
|
+
|
|
412
|
+
} catch (error) {
|
|
413
|
+
reject(error);
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
function calcEAN13CheckDigit(codeStr) {
|
|
420
|
+
/**
|
|
421
|
+
* 计算EAN-13条码的校验码
|
|
422
|
+
*
|
|
423
|
+
* @param {string} codeStr - 12位数字字符串(不含校验码)
|
|
424
|
+
* @returns {number} 校验码
|
|
425
|
+
*
|
|
426
|
+
* 示例:
|
|
427
|
+
* calculateEAN13CheckDigit("695476741387") 返回 7
|
|
428
|
+
*/
|
|
429
|
+
|
|
430
|
+
if (codeStr.length !== 12 || !/^\d+$/.test(codeStr)) {
|
|
431
|
+
throw new Error("请输入12位数字字符串");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
let total = 0;
|
|
435
|
+
|
|
436
|
+
// 从左边第一位开始,奇数位乘1,偶数位乘3
|
|
437
|
+
for (let i = 0; i < codeStr.length; i++) {
|
|
438
|
+
const digit = parseInt(codeStr[i]);
|
|
439
|
+
// 注意:EAN-13是从1开始计数的
|
|
440
|
+
if ((i + 1) % 2 === 1) { // 奇数位
|
|
441
|
+
total += digit * 1;
|
|
442
|
+
} else { // 偶数位
|
|
443
|
+
total += digit * 3;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// 计算校验码
|
|
448
|
+
const checkDigit = (10 - (total % 10)) % 10;
|
|
449
|
+
|
|
450
|
+
return checkDigit;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
// 工具函数:检查条形码内容有效性
|
|
455
|
+
function validateBarcodeContent(text, format = 'CODE128') {
|
|
456
|
+
if (!text || typeof text !== 'string') {
|
|
457
|
+
return { valid: false, error: '内容必须是非空字符串' };
|
|
458
|
+
}
|
|
459
|
+
const formatValidators = {
|
|
460
|
+
'EAN13': (text) => {
|
|
461
|
+
if (text.length !== 13) return false;
|
|
462
|
+
if (!/^\d+$/.test(text)) return false;
|
|
463
|
+
|
|
464
|
+
// EAN13 校验和计算
|
|
465
|
+
let sum = 0;
|
|
466
|
+
for (let i = 0; i < 12; i++) {
|
|
467
|
+
const digit = parseInt(text[i]);
|
|
468
|
+
sum += digit * (i % 2 === 0 ? 1 : 3);
|
|
469
|
+
}
|
|
470
|
+
const checksum = (10 - (sum % 10)) % 10;
|
|
471
|
+
return checksum === parseInt(text[12]);
|
|
472
|
+
},
|
|
473
|
+
'EAN8': (text) => {
|
|
474
|
+
if (text.length !== 8) return false;
|
|
475
|
+
return /^\d+$/.test(text);
|
|
476
|
+
},
|
|
477
|
+
'UPC': (text) => {
|
|
478
|
+
if (text.length !== 12) return false;
|
|
479
|
+
return /^\d+$/.test(text);
|
|
480
|
+
},
|
|
481
|
+
'CODE128': (text) => {
|
|
482
|
+
// CODE128 支持所有ASCII字符
|
|
483
|
+
return text.length > 0 && text.length < 256;
|
|
484
|
+
},
|
|
485
|
+
'CODE39': (text) => {
|
|
486
|
+
// CODE39 支持数字、大写字母、部分符号
|
|
487
|
+
return /^[A-Z0-9\-\. \$/+%]+$/.test(text);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const validator = formatValidators[format];
|
|
492
|
+
if (validator) {
|
|
493
|
+
return {
|
|
494
|
+
valid: validator(text),
|
|
495
|
+
error: validator(text) ? null : `内容不符合 ${format} 格式要求`
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return { valid: true, error: null };
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// 工具函数:获取条形码信息
|
|
503
|
+
async function getBarcodeInfo(data, type = 'auto') {
|
|
504
|
+
try {
|
|
505
|
+
if (type === 'svg' || (type === 'auto' && data.includes('<svg'))) {
|
|
506
|
+
const text = await svg2str(data);
|
|
507
|
+
const svg = await str2svg(text);
|
|
508
|
+
const png = await svg2png(svg);
|
|
509
|
+
return {
|
|
510
|
+
type: 'svg',
|
|
511
|
+
text: text,
|
|
512
|
+
svg: data,
|
|
513
|
+
png: png,
|
|
514
|
+
width: 300,
|
|
515
|
+
height: 100
|
|
516
|
+
};
|
|
517
|
+
} else if (type === 'png' || (type === 'auto' && data.startsWith('data:image/png'))) {
|
|
518
|
+
const text = await png2str(data);
|
|
519
|
+
const svg = await str2svg(text);
|
|
520
|
+
const png = data;
|
|
521
|
+
return {
|
|
522
|
+
type: 'png',
|
|
523
|
+
text: text,
|
|
524
|
+
svg: svg,
|
|
525
|
+
png: png,
|
|
526
|
+
width: 300,
|
|
527
|
+
height: 100
|
|
528
|
+
};
|
|
529
|
+
} else if (type === 'text' || (type === 'auto' && typeof data === 'string')) {
|
|
530
|
+
const validation = validateBarcodeContent(data);
|
|
531
|
+
if (!validation.valid) {
|
|
532
|
+
throw new Error(validation.error);
|
|
533
|
+
}
|
|
534
|
+
const svg = await str2svg(data);
|
|
535
|
+
const png = await svg2png(svg);
|
|
536
|
+
return {
|
|
537
|
+
type: 'text',
|
|
538
|
+
text: data,
|
|
539
|
+
svg: svg,
|
|
540
|
+
png: png,
|
|
541
|
+
width: 300,
|
|
542
|
+
height: 100
|
|
543
|
+
};
|
|
544
|
+
} else {
|
|
545
|
+
throw new Error('不支持的条形码数据类型');
|
|
546
|
+
}
|
|
547
|
+
} catch (error) {
|
|
548
|
+
throw error;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
const defaultGS1Options = {
|
|
554
|
+
format: 'CODE128',
|
|
555
|
+
ean128: true,
|
|
556
|
+
width: 2,
|
|
557
|
+
height: 100,
|
|
558
|
+
lineColor: '#000000',
|
|
559
|
+
background: '#ffffff',
|
|
560
|
+
displayValue: true,
|
|
561
|
+
fontSize: 20,
|
|
562
|
+
margin: 10
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const encd_to_gs1_svg = (...args)=>str2svg(gs1.encd(...args),defaultGS1Options);
|
|
566
|
+
const encd_to_gs1_png = (...args)=>str2png(gs1.encd(...args),defaultGS1Options);
|
|
567
|
+
const decd_fr_gs1_svg = (imgData)=>gs1.decd(svg2str(imgData));
|
|
568
|
+
const decd_fr_gs1_png = (imgData)=>gs1.decd(png2str(imgData));
|
|
569
|
+
|
|
570
|
+
module.exports = {
|
|
571
|
+
loadJsBarcode,
|
|
572
|
+
loadHtml2Canvas,
|
|
573
|
+
loadZXing,
|
|
574
|
+
FNC1_IF_USING_CODE128_AND_EAN128,
|
|
575
|
+
str2svg,
|
|
576
|
+
str2png,
|
|
577
|
+
getZXingFormatsByType,svg2str,
|
|
578
|
+
png2str,
|
|
579
|
+
png2svg,
|
|
580
|
+
svg2png,
|
|
581
|
+
calcEAN13CheckDigit,
|
|
582
|
+
validateBarcodeContent,
|
|
583
|
+
getBarcodeInfo,
|
|
584
|
+
encd_to_gs1_svg,
|
|
585
|
+
encd_to_gs1_png,
|
|
586
|
+
decd_fr_gs1_svg,
|
|
587
|
+
decd_fr_gs1_png,
|
|
588
|
+
gs1,
|
|
589
|
+
};
|