我们是做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 一维码的宽度,如果宽度不够,那就绘制的线条不全,就会出现无法识别的问题