我们是做HIS系统开发的,前段时间发现某些处方的一维码出现无法识别的情况。看了一下一维码生成的逻辑,使用到了BarcodeLib库,经过反复确认,我们程序是没有问题的。后面不得不反编译看一下BarcodeLib生成一维码的逻辑。最后调整一维码的宽度解决了问题。而且发现它的编码和通信领域的编码思想有神奇的相似之处。编码格式类型:开始位+数据位+检查校验位+STOP停止位+11。总的来说是解密和编码都是查表法,如要把数字69进行编码,通过查表可以知道编码的结果为:10110010000。 解密就是逆过程,如知道编码为10110010000,通过查表,也可以知道编码前的数字为69
这里简单介绍一下BarcodeLib库Code128一维码的编码过程:
1 先把数字如:6986081进行编码,两个数字作为一组进行编码,这里数字69一组,86一组,08一组,剩下一个1就比较特别,源码看得不是很懂,有特殊的规则进行编码
2 把两位数字通过查表法进行编码,编码为11位的0或1数字。如数字69编码为10110010000
3 通过C#的绘图类Graphics的画线方法.DrawLine进行绘制线,如要绘制10110010000编码,碰到1就纵向绘制一定宽度的直线,碰到0就不绘制直线。
下面我们一起来分析一下这个一维码:
先告知一下这个一维码的值为:6986081
通过上图的分析,上图的编码可以编码为:
1101001110010110010000111101001001000110010011101011110
10011100110100010001101100011101011
按照11位进行分组整理后如下:
11010011100
10110010000
11110100100
10001100100
11101011110
10011100110
10001000110
11000111010
11
通过编码11010011100查找本文后面的编码表得到的数值为START_C
通过编码10110010000查找本文后面的编码表得到的数值为69
通过编码10001100100查找本文后面的编码表得到的数值为08
通过编码11101011110查找本文后面的编码表得到的数值为CODE_A
通过编码10011100110 查找本文后面的编码表得到的数值为17,但前面有一个CODE_A,应该编码为1
通过编码10001000110查找本文后面的编码表得到的数值为C,则为检查校验位
通过编码11000111010查找本文后面的编码表得到的数值为STOP,则为停止位
最后两位11应该也是校验位吧。
最后汇总如下:
11010011100 START_C 开始
10110010000 69
11110100100 86
10001100100 08
11101011110 CODE_A
10011100110 这个是编1而不是17
10001000110 检查校验位
11000111010 STOP停止位
11
最后一维码的解码结果为:6986081
补充说明:
BarcodeLib库Code128编码表如下,偷懒一点,就直接贴源码了
private void init_Code128()
{
this.C128_Code.CaseSensitive = true;
this.C128_Code.Columns.Add("Value", typeof(string));
this.C128_Code.Columns.Add("A", typeof(string));
this.C128_Code.Columns.Add("B", typeof(string));
this.C128_Code.Columns.Add("C", typeof(string));
this.C128_Code.Columns.Add("Encoding", typeof(string));
this.C128_Code.Rows.Add(new object[]
{
"0",
" ",
" ",
"00",
"11011001100"
});
this.C128_Code.Rows.Add(new object[]
{
"1",
"!",
"!",
"01",
"11001101100"
});
this.C128_Code.Rows.Add(new object[]
{
"2",
"\"",
"\"",
"02",
"11001100110"
});
this.C128_Code.Rows.Add(new object[]
{
"3",
"#",
"#",
"03",
"10010011000"
});
this.C128_Code.Rows.Add(new object[]
{
"4",
"$",
"$",
"04",
"10010001100"
});
this.C128_Code.Rows.Add(new object[]
{
"5",
"%",
"%",
"05",
"10001001100"
});
this.C128_Code.Rows.Add(new object[]
{
"6",
"&",
"&",
"06",
"10011001000"
});
this.C128_Code.Rows.Add(new object[]
{
"7",
"'",
"'",
"07",
"10011000100"
});
this.C128_Code.Rows.Add(new object[]
{
"8",
"(",
"(",
"08",
"10001100100"
});
this.C128_Code.Rows.Add(new object[]
{
"9",
")",
")",
"09",
"11001001000"
});
this.C128_Code.Rows.Add(new object[]
{
"10",
"*",
"*",
"10",
"11001000100"
});
this.C128_Code.Rows.Add(new object[]
{
"11",
"+",
"+",
"11",
"11000100100"
});
this.C128_Code.Rows.Add(new object[]
{
"12",
",",
",",
"12",
"10110011100"
});
this.C128_Code.Rows.Add(new object[]
{
"13",
"-",
"-",
"13",
"10011011100"
});
this.C128_Code.Rows.Add(new object[]
{
"14",
".",
".",
"14",
"10011001110"
});
this.C128_Code.Rows.Add(new object[]
{
"15",
"/",
"/",
"15",
"10111001100"
});
this.C128_Code.Rows.Add(new object[]
{
"16",
"0",
"0",
"16",
"10011101100"
});
this.C128_Code.Rows.Add(new object[]
{
"17",
"1",
"1",
"17",
"10011100110"
});
this.C128_Code.Rows.Add(new object[]
{
"18",
"2",
"2",
"18",
"11001110010"
});
this.C128_Code.Rows.Add(new object[]
{
"19",
"3",
"3",
"19",
"11001011100"
});
this.C128_Code.Rows.Add(new object[]
{
"20",
"4",
"4",
"20",
"11001001110"
});
this.C128_Code.Rows.Add(new object[]
{
"21",
"5",
"5",
"21",
"11011100100"
});
this.C128_Code.Rows.Add(new object[]
{
"22",
"6",
"6",
"22",
"11001110100"
});
this.C128_Code.Rows.Add(new object[]
{
"23",
"7",
"7",
"23",
"11101101110"
});
this.C128_Code.Rows.Add(new object[]
{
"24",
"8",
"8",
"24",
"11101001100"
});
this.C128_Code.Rows.Add(new object[]
{
"25",
"9",
"9",
"25",
"11100101100"
});
this.C128_Code.Rows.Add(new object[]
{
"26",
":",
":",
"26",
"11100100110"
});
this.C128_Code.Rows.Add(new object[]
{
"27",
";",
";",
"27",
"11101100100"
});
this.C128_Code.Rows.Add(new object[]
{
"28",
"<",
"<",
"28",
"11100110100"
});
this.C128_Code.Rows.Add(new object[]
{
"29",
"=",
"=",
"29",
"11100110010"
});
this.C128_Code.Rows.Add(new object[]
{
"30",
">",
">",
"30",
"11011011000"
});
this.C128_Code.Rows.Add(new object[]
{
"31",
"?",
"?",
"31",
"11011000110"
});
this.C128_Code.Rows.Add(new object[]
{
"32",
"@",
"@",
"32",
"11000110110"
});
this.C128_Code.Rows.Add(new object[]
{
"33",
"A",
"A",
"33",
"10100011000"
});
this.C128_Code.Rows.Add(new object[]
{
"34",
"B",
"B",
"34",
"10001011000"
});
this.C128_Code.Rows.Add(new object[]
{
"35",
"C",
"C",
"35",
"10001000110"
});
this.C128_Code.Rows.Add(new object[]
{
"36",
"D",
"D",
"36",
"10110001000"
});
this.C128_Code.Rows.Add(new object[]
{
"37",
"E",
"E",
"37",
"10001101000"
});
this.C128_Code.Rows.Add(new object[]
{
"38",
"F",
"F",
"38",
"10001100010"
});
this.C128_Code.Rows.Add(new object[]
{
"39",
"G",
"G",
"39",
"11010001000"
});
this.C128_Code.Rows.Add(new object[]
{
"40",
"H",
"H",
"40",
"11000101000"
});
this.C128_Code.Rows.Add(new object[]
{
"41",
"I",
"I",
"41",
"11000100010"
});
this.C128_Code.Rows.Add(new object[]
{
"42",
"J",
"J",
"42",
"10110111000"
});
this.C128_Code.Rows.Add(new object[]
{
"43",
"K",
"K",
"43",
"10110001110"
});
this.C128_Code.Rows.Add(new object[]
{
"44",
"L",
"L",
"44",
"10001101110"
});
this.C128_Code.Rows.Add(new object[]
{
"45",
"M",
"M",
"45",
"10111011000"
});
this.C128_Code.Rows.Add(new object[]
{
"46",
"N",
"N",
"46",
"10111000110"
});
this.C128_Code.Rows.Add(new object[]
{
"47",
"O",
"O",
"47",
"10001110110"
});
this.C128_Code.Rows.Add(new object[]
{
"48",
"P",
"P",
"48",
"11101110110"
});
this.C128_Code.Rows.Add(new object[]
{
"49",
"Q",
"Q",
"49",
"11010001110"
});
this.C128_Code.Rows.Add(new object[]
{
"50",
"R",
"R",
"50",
"11000101110"
});
this.C128_Code.Rows.Add(new object[]
{
"51",
"S",
"S",
"51",
"11011101000"
});
this.C128_Code.Rows.Add(new object[]
{
"52",
"T",
"T",
"52",
"11011100010"
});
this.C128_Code.Rows.Add(new object[]
{
"53",
"U",
"U",
"53",
"11011101110"
});
this.C128_Code.Rows.Add(new object[]
{
"54",
"V",
"V",
"54",
"11101011000"
});
this.C128_Code.Rows.Add(new object[]
{
"55",
"W",
"W",
"55",
"11101000110"
});
this.C128_Code.Rows.Add(new object[]
{
"56",
"X",
"X",
"56",
"11100010110"
});
this.C128_Code.Rows.Add(new object[]
{
"57",
"Y",
"Y",
"57",
"11101101000"
});
this.C128_Code.Rows.Add(new object[]
{
"58",
"Z",
"Z",
"58",
"11101100010"
});
this.C128_Code.Rows.Add(new object[]
{
"59",
"[",
"[",
"59",
"11100011010"
});
this.C128_Code.Rows.Add(new object[]
{
"60",
"\\",
"\\",
"60",
"11101111010"
});
this.C128_Code.Rows.Add(new object[]
{
"61",
"]",
"]",
"61",
"11001000010"
});
this.C128_Code.Rows.Add(new object[]
{
"62",
"^",
"^",
"62",
"11110001010"
});
this.C128_Code.Rows.Add(new object[]
{
"63",
"_",
"_",
"63",
"10100110000"
});
this.C128_Code.Rows.Add(new object[]
{
"64",
"\0",
"`",
"64",
"10100001100"
});
this.C128_Code.Rows.Add(new object[]
{
"65",
Convert.ToChar(1).ToString(),
"a",
"65",
"10010110000"
});
this.C128_Code.Rows.Add(new object[]
{
"66",
Convert.ToChar(2).ToString(),
"b",
"66",
"10010000110"
});
this.C128_Code.Rows.Add(new object[]
{
"67",
Convert.ToChar(3).ToString(),
"c",
"67",
"10000101100"
});
this.C128_Code.Rows.Add(new object[]
{
"68",
Convert.ToChar(4).ToString(),
"d",
"68",
"10000100110"
});
this.C128_Code.Rows.Add(new object[]
{
"69",
Convert.ToChar(5).ToString(),
"e",
"69",
"10110010000"
});
this.C128_Code.Rows.Add(new object[]
{
"70",
Convert.ToChar(6).ToString(),
"f",
"70",
"10110000100"
});
this.C128_Code.Rows.Add(new object[]
{
"71",
Convert.ToChar(7).ToString(),
"g",
"71",
"10011010000"
});
this.C128_Code.Rows.Add(new object[]
{
"72",
Convert.ToChar(8).ToString(),
"h",
"72",
"10011000010"
});
this.C128_Code.Rows.Add(new object[]
{
"73",
Convert.ToChar(9).ToString(),
"i",
"73",
"10000110100"
});
this.C128_Code.Rows.Add(new object[]
{
"74",
Convert.ToChar(10).ToString(),
"j",
"74",
"10000110010"
});
this.C128_Code.Rows.Add(new object[]
{
"75",
Convert.ToChar(11).ToString(),
"k",
"75",
"11000010010"
});
this.C128_Code.Rows.Add(new object[]
{
"76",
Convert.ToChar(12).ToString(),
"l",
"76",
"11001010000"
});
this.C128_Code.Rows.Add(new object[]
{
"77",
Convert.ToChar(13).ToString(),
"m",
"77",
"11110111010"
});
this.C128_Code.Rows.Add(new object[]
{
"78",
Convert.ToChar(14).ToString(),
"n",
"78",
"11000010100"
});
this.C128_Code.Rows.Add(new object[]
{
"79",
Convert.ToChar(15).ToString(),
"o",
"79",
"10001111010"
});
this.C128_Code.Rows.Add(new object[]
{
"80",
Convert.ToChar(16).ToString(),
"p",
"80",
"10100111100"
});
this.C128_Code.Rows.Add(new object[]
{
"81",
Convert.ToChar(17).ToString(),
"q",
"81",
"10010111100"
});
this.C128_Code.Rows.Add(new object[]
{
"82",
Convert.ToChar(18).ToString(),
"r",
"82",
"10010011110"
});
this.C128_Code.Rows.Add(new object[]
{
"83",
Convert.ToChar(19).ToString(),
"s",
"83",
"10111100100"
});
this.C128_Code.Rows.Add(new object[]
{
"84",
Convert.ToChar(20).ToString(),
"t",
"84",
"10011110100"
});
this.C128_Code.Rows.Add(new object[]
{
"85",
Convert.ToChar(21).ToString(),
"u",
"85",
"10011110010"
});
this.C128_Code.Rows.Add(new object[]
{
"86",
Convert.ToChar(22).ToString(),
"v",
"86",
"11110100100"
});
this.C128_Code.Rows.Add(new object[]
{
"87",
Convert.ToChar(23).ToString(),
"w",
"87",
"11110010100"
});
this.C128_Code.Rows.Add(new object[]
{
"88",
Convert.ToChar(24).ToString(),
"x",
"88",
"11110010010"
});
this.C128_Code.Rows.Add(new object[]
{
"89",
Convert.ToChar(25).ToString(),
"y",
"89",
"11011011110"
});
this.C128_Code.Rows.Add(new object[]
{
"90",
Convert.ToChar(26).ToString(),
"z",
"90",
"11011110110"
});
this.C128_Code.Rows.Add(new object[]
{
"91",
Convert.ToChar(27).ToString(),
"{",
"91",
"11110110110"
});
this.C128_Code.Rows.Add(new object[]
{
"92",
Convert.ToChar(28).ToString(),
"|",
"92",
"10101111000"
});
this.C128_Code.Rows.Add(new object[]
{
"93",
Convert.ToChar(29).ToString(),
"}",
"93",
"10100011110"
});
this.C128_Code.Rows.Add(new object[]
{
"94",
Convert.ToChar(30).ToString(),
"~",
"94",
"10001011110"
});
this.C128_Code.Rows.Add(new object[]
{
"95",
Convert.ToChar(31).ToString(),
Convert.ToChar(127).ToString(),
"95",
"10111101000"
});
this.C128_Code.Rows.Add(new object[]
{
"96",
Convert.ToChar(202).ToString(),
Convert.ToChar(202).ToString(),
"96",
"10111100010"
});
this.C128_Code.Rows.Add(new object[]
{
"97",
Convert.ToChar(201).ToString(),
Convert.ToChar(201).ToString(),
"97",
"11110101000"
});
this.C128_Code.Rows.Add(new object[]
{
"98",
"SHIFT",
"SHIFT",
"98",
"11110100010"
});
this.C128_Code.Rows.Add(new object[]
{
"99",
"CODE_C",
"CODE_C",
"99",
"10111011110"
});
this.C128_Code.Rows.Add(new object[]
{
"100",
"CODE_B",
Convert.ToChar(203).ToString(),
"CODE_B",
"10111101110"
});
this.C128_Code.Rows.Add(new object[]
{
"101",
Convert.ToChar(203).ToString(),
"CODE_A",
"CODE_A",
"11101011110"
});
this.C128_Code.Rows.Add(new object[]
{
"102",
Convert.ToChar(200).ToString(),
Convert.ToChar(200).ToString(),
Convert.ToChar(200).ToString(),
"11110101110"
});
this.C128_Code.Rows.Add(new object[]
{
"103",
"START_A",
"START_A",
"START_A",
"11010000100"
});
this.C128_Code.Rows.Add(new object[]
{
"104",
"START_B",
"START_B",
"START_B",
"11010010000"
});
this.C128_Code.Rows.Add(new object[]
{
"105",
"START_C",
"START_C",
"START_C",
"11010011100"
});
this.C128_Code.Rows.Add(new object[]
{
"",
"STOP",
"STOP",
"STOP",
"11000111010"
});
}
通过阅读BarCode类生成图片的源码,如下:
private Bitmap Generate_Image()
{
if (this.Encoded_Value == "")
{
throw new Exception("EGENERATE_IMAGE-1: Must be encoded first.");
}
Bitmap bitmap = null;
DateTime now = DateTime.Now;
TYPE encoded_Type = this.Encoded_Type;
if (encoded_Type != TYPE.ITF14)
{
bitmap = new Bitmap(this.Width, this.Height);
int num = this.Width / this.Encoded_Value.Length;
int num2 = 1;
if (this.Encoded_Type == TYPE.PostNet)
{
num2 = 2;
}
int num3;
switch (this.Alignment)
{
case AlignmentPositions.CENTER:
num3 = this.Width % this.Encoded_Value.Length / 2;
break;
case AlignmentPositions.LEFT:
num3 = 0;
break;
case AlignmentPositions.RIGHT:
num3 = this.Width % this.Encoded_Value.Length;
break;
default:
num3 = this.Width % this.Encoded_Value.Length / 2;
break;
}
if (num <= 0)
{
throw new Exception("EGENERATE_IMAGE-2: Image size specified not large enough to draw image. (Bar size determined to be less than 1 pixel)");
}
int i = 0;
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(this.BackColor);
using (Pen pen = new Pen(this.BackColor, (float)(num / num2)))
{
using (Pen pen2 = new Pen(this.ForeColor, (float)(num / num2)))
{
while (i < this.Encoded_Value.Length)
{
if (this.Encoded_Type == TYPE.PostNet)
{
if (this.Encoded_Value[i] != '1')
{
graphics.DrawLine(pen2, new Point(i * num + num3 + 1, this.Height), new Point(i * num + num3 + 1, this.Height / 2));
}
graphics.DrawLine(pen, new Point(i * (num * num2) + num3 + num + 1, 0), new Point(i * (num * num2) + num3 + num + 1, this.Height));
}
if (this.Encoded_Value[i] == '1')
{
graphics.DrawLine(pen2, new Point(i * num + num3 + 1, 0), new Point(i * num + num3 + 1, this.Height));
}
i++;
}
}
}
}
if (this.IncludeLabel)
{
this.Label_Generic(bitmap);
}
}
else
{
bitmap = new Bitmap(this.Width, this.Height);
int num4 = (int)((double)bitmap.Width / 12.05);
int num5 = Convert.ToInt32((double)bitmap.Width * 0.05);
int num = (bitmap.Width - num4 * 2 - num5 * 2) / this.Encoded_Value.Length;
int num3 = (bitmap.Width - num4 * 2 - num5 * 2) % this.Encoded_Value.Length / 2;
if (num <= 0 || num5 <= 0)
{
throw new Exception("EGENERATE_IMAGE-3: Image size specified not large enough to draw image. (Bar size determined to be less than 1 pixel or quiet zone determined to be less than 1 pixel)");
}
int i = 0;
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(this.BackColor);
using (Pen pen2 = new Pen(this.ForeColor, (float)num))
{
pen2.Alignment = PenAlignment.Right;
while (i < this.Encoded_Value.Length)
{
if (this.Encoded_Value[i] == '1')
{
graphics.DrawLine(pen2, new Point(i * num + num3 + num4 + num5, 0), new Point(i * num + num3 + num4 + num5, this.Height));
}
i++;
}
pen2.Width = (float)bitmap.Height / 8f;
pen2.Color = this.ForeColor;
pen2.Alignment = PenAlignment.Inset;
graphics.DrawLine(pen2, new Point(0, 0), new Point(bitmap.Width, 0));
graphics.DrawLine(pen2, new Point(0, bitmap.Height), new Point(bitmap.Width, bitmap.Height));
graphics.DrawLine(pen2, new Point(0, 0), new Point(0, bitmap.Height));
graphics.DrawLine(pen2, new Point(bitmap.Width, 0), new Point(bitmap.Width, bitmap.Height));
}
}
if (this.IncludeLabel)
{
this.Label_ITF14(bitmap);
}
}
this._Encoded_Image = bitmap;
this._EncodingTime += (DateTime.Now - now).TotalMilliseconds;
return bitmap;
}
影响一维码生成结果有
1 AlignmentPositions对齐方式:靠左、居中、靠右和默认
2 一维码的宽度,如果宽度不够,那就绘制的线条不全,就会出现无法识别的问题