使用JavaScript创建Code128编码

起因

本来设计使用20开头的EAN13 作为店内码的, 实际查询发现. 一些20开头的也是商品条码. 这就有点尴尬了, 为了不和商品条码冲突, 只好改变编码规则, 因此需要实现输出SVG格式的Code128条码.

设计

Code128是支持ABC混合编码的, 在生成条码过程中可以切换码表. 考虑字符密度,优先使用 Code128-C 作为数字编码, 如果有字母,则切换到Code128-B.

这里附上 Code-128码表

ID Code128A Code128B Code128C BandCode 编码值
0 SP SP 0 212222 bbsbbssbbss
1 ! ! 1 222122 bbssbbsbbss
2 2 222221 bbssbbssbbs
3 # # 3 121223 bssbssbbsss
4 $ $ 4 121322 bssbsssbbss
5 % % 5 131222 bsssbssbbss
6 & & 6 122213 bssbbssbsss
7 ' ' 7 122312 bssbbsssbss
8 ( ( 8 132212 bsssbbssbss
9 ) ) 9 221213 bbssbssbsss
10 * * 10 221312 bbssbsssbss
11 + + 11 231212 bbsssbssbss
12 , , 12 112232 bsbbssbbbss
13 - - 13 122132 bssbbsbbbss
14 . . 14 122231 bssbbssbbbs
15 / / 15 113222 bsbbbssbbss
16 0 0 16 123122 bssbbbsbbss
17 1 1 17 123221 bssbbbssbbs
18 2 2 18 223211 bbssbbbssbs
19 3 3 19 221132 bbssbsbbbss
20 4 4 20 221231 bbssbssbbbs
21 5 5 21 213212 bbsbbbssbss
22 6 6 22 223112 bbssbbbsbss
23 7 7 23 312131 bbbsbbsbbbs
24 8 8 24 311222 bbbsbssbbss
25 9 9 25 321122 bbbssbsbbss
26 : : 26 321221 bbbssbssbbs
27 ; ; 27 312212 bbbsbbssbss
28 < < 28 322112 bbbssbbsbss
29 = = 29 322211 bbbssbbssbs
30 > > 30 212123 bbsbbsbbsss
31 ? ? 31 212321 bbsbbsssbbs
32 @ @ 32 232121 bbsssbbsbbs
33 A A 33 111323 bsbsssbbsss
34 B B 34 131123 bsssbsbbsss
35 C C 35 131321 bsssbsssbbs
36 D D 36 112313 bsbbsssbsss
37 E E 37 132113 bsssbbsbsss
38 F F 38 132311 bsssbbsssbs
39 G G 39 211313 bbsbsssbsss
40 H H 40 231113 bbsssbsbsss
41 I I 41 231311 bbsssbsssbs
42 J J 42 112133 bsbbsbbbsss
43 K K 43 112331 bsbbsssbbbs
44 L L 44 132131 bsssbbsbbbs
45 M M 45 113123 bsbbbsbbsss
46 N N 46 113321 bsbbbsssbbs
47 O O 47 133121 bsssbbbsbbs
48 P P 48 313121 bbbsbbbsbbs
49 Q Q 49 211331 bbsbsssbbbs
50 R R 50 231131 bbsssbsbbbs
51 S S 51 213113 bbsbbbsbsss
52 T T 52 213311 bbsbbbsssbs
53 U U 53 213131 bbsbbbsbbbs
54 V V 54 311123 bbbsbsbbsss
55 W W 55 311321 bbbsbsssbbs
56 X X 56 331121 bbbsssbsbbs
57 Y Y 57 312113 bbbsbbsbsss
58 Z Z 58 312311 bbbsbbsssbs
59 [ [ 59 332111 bbbsssbbsbs
60 \ \ 60 314111 bbbsbbbbsbs
61 ] ] 61 221411 bbssbssssbs
62 ^ ^ 62 431111 bbbbsssbsbs
63 _ _ 63 111224 bsbssbbssss
64 NUL ` 64 111422 bsbssssbbss
65 SOH a 65 121124 bssbsbbssss
66 STX b 66 121421 bssbssssbbs
67 ETX c 67 141122 bssssbsbbss
68 EOT d 68 141221 bssssbssbbs
69 ENQ e 69 112214 bsbbssbssss
70 ACK f 70 112412 bsbbssssbss
71 BEL g 71 122114 bssbbsbssss
72 BS h 72 122411 bssbbssssbs
73 HT i 73 142112 bssssbbsbss
74 LF j 74 142211 bssssbbssbs
75 VT k 75 241211 bbssssbssbs
76 FF I 76 221114 bbssbsbssss
77 CR m 77 413111 bbbbsbbbsbs
78 SO n 78 241112 bbssssbsbss
79 SI o 79 134111 bsssbbbbsbs
80 DLE p 80 111242 bsbssbbbbss
81 DC1 q 81 121142 bssbsbbbbss
82 DC2 r 82 121241 bssbssbbbbs
83 DC3 s 83 114212 bsbbbbssbss
84 DC4 t 84 124112 bssbbbbsbss
85 NAK u 85 124211 bssbbbbssbs
86 SYN v 86 411212 bbbbsbssbss
87 ETB w 87 421112 bbbbssbsbss
88 CAN x 88 421211 bbbbssbssbs
89 EM y 89 212141 bbsbbsbbbbs
90 SUB z 90 214121 bbsbbbbsbbs
91 ESC { 91 412121 bbbbsbbsbbs
92 FS | 92 111143 bsbsbbbbsss
93 GS } 93 111341 bsbsssbbbbs
94 RS ~ 94 131141 bsssbsbbbbs
95 US DEL 95 114113 bsbbbbsbsss
96 FNC3 FNC3 96 114311 bsbbbbsssbs
97 FNC2 FNC2 97 411113 bbbbsbsbsss
98 SHIFT SHIFT 98 411311 bbbbsbsssbs
99 CODEC CODEC 99 113141 bsbbbsbbbbs
100 CODEB FNC4 CODEB 114131 bsbbbbsbbbs
101 FNC4 CODEA CODEA 311141 bbbsbsbbbbs
102 FNC1 FNC1 FNC1 411131 bbbbsbsbbbs
103 StartA StartA StartA 211412 bbsbssssbss
104 StartB StartB StartB 211214 bbsbssbssss
105 StartC StartC StartC 211232 bbsbssbbbss
106 Stop Stop Stop 2331112 bbsssbbbsbsbb

编码构成

开始位 + 后面所有的数据按顺序拼接 + 校验位 + 结束位

编码索引的103-106为起始位于结束位,只会在开头或结尾出现

我们首先使用一个简单的例子来解释如何使用三种编码方式进行条形码的编码:

需要编码成条形码的数据:1346

对于Code128A编码:

位类型 码表 线 校验值
起始位 StartA bbsbssssbss 103
数据位 Code128A中的1 bssbbbssbbs 103 + 17 * 1
数据位 Code128A中的3 bbssbsbbbss 103 + 19 * 2
数据位 Code128A中的4 bbssbssbbbs 103 + 20 * 3
数据位 Code128A中的6 bbssbbbsbss 103 + 22 * 4
校验位 校验值 % 103 —- 前面的求和 % 103后查表.
停止位 Stop bbsssbbbsbsbb

对于Code128B编码:

位类型 码表 线 校验值
起始位 StartB bbsbssssbss 104
数据位 Code128B中的1 bssbbbssbbs 17 * 1
数据位 Code128B中的3 bbssbsbbbss 19 * 2
数据位 Code128B中的4 bbssbssbbbs 20 * 3
数据位 Code128B中的6 bbssbbbsbss 22 * 4
校验位 校验值 % 103 —- 前面的求和 % 103后查表.
停止位 Stop bbsssbbbsbsbb

对于Code128C编码:

Code128C编码时,只能编码数字内容,并且在编码前会将偶数个的数字两个两个分为一组,进行编码:

位类型 码表 线 校验值
起始位 StartC bbsbssbbbss 105
数据位 Code128C中的13 bssbbsbbbss 13 * 1
数据位 Code128C中的46 bsbbbsssbbs 46 * 2
校验位 校验值 % 103 —- 前面的求和 % 103 后查表
停止位 Stop bbsssbbbsbsbb

混合编码

中间要切换编码只需加入切换编码查表线值表即可,索引需要计入校验,计算方法一致。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
const codeC = [
1740,1644,1638,1176,1164,1100,1224,1220,1124,1608,1604,1572,1436,1244,1230,1484,1260,1254,1650,1628,
1614,1764,1652,1902,1868,1836,1830,1892,1844,1842,1752,1734,1590,1304,1112,1094,1416,1128,1122,1672,
1576,1570,1464,1422,1134,1496,1478,1142,1910,1678,1582,1768,1762,1774,1880,1862,1814,1896,1890,1818,
1914,1602,1930,1328,1292,1200,1158,1068,1062,1424,1412,1232,1218,1076,1074,1554,1616,1978,1556,1146,
1340,1212,1182,1508,1268,1266,1956,1940,1938,1758,1782,1974,1400,1310,1118,1512,1506,1960,1954,1502,
1518,1886,1966,1668,1680,1692,6379
],codeB=[
null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,
null,null,null,null,null,null,null,null,null,null,null,null,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,76,42,43,44,45,46,47,48,49,50,51,52,
53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,null,77,78,79,80,81,82,83,84,85,86,87,
88,89,90,91,92,93,94,95,102,97,96,100
];

function Code128B(code, conf){
let chars = code.split(/(\d{2}|[^\d]{1})/).filter(v=>v!=''), sum = 105, n=[], bits = [], pos = 1;
var enc;
const modeB=0,modeC=1;
chars.forEach((ch, i)=>{
let len = ch.length, v;
if(i == 0){
if(len == 1){
sum = 104;
enc = modeB;
v = codeB[ch.charCodeAt(0)];
n.push(codeC[104]);
}else{
sum = 105;
enc = modeC;
v = parseInt(ch);
n.push(codeC[105]);
//console.log('start C', ch, v);
}
n.push(codeC[v]);
}else{
if(enc == modeC){
if(len == 1){
enc = modeB;
sum += 100 * pos++;
n.push(codeC[100]);
v = codeB[ch.charCodeAt(0)];
}else{
v = parseInt(ch);
}
}else{
if(len == 1){
v = codeB[ch.charCodeAt(0)];
}else{
enc = modeC;
sum += 99 * pos++;
n.push(codeC[99]); //codeB => codeC
v = parseInt(ch);
}
}
n.push(codeC[v]);
}
sum += v * pos++;
});

n.push(codeC[sum % 103], 6379);
n.forEach(v=>{
var b = []
do{
b.push(v & 1 ? 1:0);
v >>=1;
}while(v > 0)
bits.push(...b.reverse());
})
//console.log(bits);
var x=-1, w = 0, nCount = bits.length+6, y = conf.name ? (2+conf.fsize) : 0,s = [],
bar = [`<svg viewBox="0 0 ${nCount} ${conf.h}" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">`];

bits.forEach((v,i)=>{
if(0 == v || i == bits.length-1){
if(w > 0){
var h = (x < 14 || x > bits.length - 12)? conf.h - y : conf.h - y - 6.5;
s.push('M', x, ' ', y,'h', w, 'v', h, 'h', -w, 'Z')
x = -1;w = 0;
}
}else{
if(-1 == x ){x = i+3;}
w++;
}
})

bar.push('<path d="', s.join(''),'"/>')
const unit = (nCount - (6 + 13 + 14)) / code.length;
for(var i = 0; i < code.length;i ++){
bar.push('<text x="', 16 + i * unit ,'" y="', conf.h - 0.4,'" text-anchor="left" font-size="7" font-family="Verdana">', code[i] , '</text>')
}
if(conf.name){
bar.push('<text x="',nCount/2,'" y="',conf.fsize - 0.5,'" font-size="',conf.fsize,'" width="',nCount,'" text-anchor="middle" font-family="Verdana">',conf.name,'</text>')
}
bar.push('</svg>')
return {
svg: bar.join(''),
lines: nCount
}
}

var svg = Code128B('Z65432189120', {h:45, name:'很好', fsize:9});

上面的代码执行效果如下:

Z65432189120很好
作者

bywayboy

发布于

2022-03-31

更新于

2022-03-31

许可协议