一、39条码简介
39码是1974年发展出来的条码系统,是一种可供使用者双向扫瞄的分散式条码,也就是说相临两资料码之间,必须包含一个不具任何意义的空白(或细白,其逻辑值为0),且其具有支援文字的能力,故较一般一维条码应用广泛,由于CODE 39支持0~9、A~Z等,目前较主要用于,通常运用于资产管理、会员卡、店内码管理、产品卷标等。
标准的39码是由起始安全空间、起始码、资料码、可忽略不计的检查码、终止安全空间及终止码所构成。
二、39码编码
39码的每一个字元编码方式,都是由九条不同排列的线条编码而得。可区分成如表之四种类型:
表1 39码的字元编码方式
(1)英文字母部分
26个英文字母所对应的39码逻辑值如表所示。
字元 |
逻辑型态 |
字元 |
逻辑型态 |
A |
110101001011 |
N |
101011010011 |
B |
101101001011 |
O |
110101101001 |
C |
110110100101 |
P |
101101101001 |
D |
101011001011 |
Q |
101010110011 |
E |
110101100101 |
R |
110101011001 |
F |
101101100101 |
S |
101101011001 |
G |
101010011011 |
T |
101011011001 |
H |
110101001101 |
U |
110010101011 |
I |
101101001101 |
V |
100110101011 |
J |
101011001101 |
W |
110011010101 |
K |
110101010011 |
X |
100101101011 |
L |
101101010011 |
Y |
110010110101 |
M |
110110101001 |
Z |
100110110101 |
(2)数字与特殊符号部分
39码也可表示数字0~9以及特殊符号,其对应的逻辑值如表所示。
字元 |
逻辑型态 |
字元 |
逻辑型态 |
0 |
101001101101 |
+ |
100101001001 |
1 |
110100101011 |
- |
100101011011 |
2 |
101100101011 |
* |
100101101101 |
3 |
110110010101 |
/ |
100100101001 |
4 |
101001101011 |
% |
101001001001 |
5 |
110100110101 |
$ |
100100100101 |
6 |
101100110101 |
. |
110010101101 |
7 |
101001011011 |
空白 |
100110101101 |
8 |
110100101101 |
|
|
9 |
101100101101 |
|
(3)39码的检查码查询表
三、39码编码算法
(1)移除输入待编码内容Value to Code中的*,得到Value;
(2)将Value逐位按“二”中的的检查码查询表计算检查码的和sum,计算检查码 checkcode=sum%43,再以checkcode在检查码中查询对应的编码字元checkChar
(3)将Value与checkChar拼接后,前后加上'*'再逐位按“二”中的对应的编码+‘0’进行编码,将最后一个0去掉后得到Code;
(4)按表1对Code进行绘图。
四、基于C#的算法实现
(1)UI设计
(2)算法代码
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using System.Drawing; 8 using System.Drawing.Drawing2D; 9 10 namespace BarCode 11 { 12 /// <summary> 13 /// 39 barcode 14 /// </summary> 15 public class BarCode 16 { 17 private string _rawData; 18 private bool _enableChecksum; 19 private int _height; 20 private int _width; 21 private bool _includeLabel; 22 private string _code; 23 private enum LabelPositions : int { TOPLEFT, TOPCENTER, TOPRIGHT, BOTTOMLEFT, BOTTOMCENTER, BOTTOMRIGHT }; 24 25 private LabelPositions _labelPosition; 26 private Color _ForeColor = Color.Black; 27 private Color _BackColor = Color.White; 28 private string Code 29 { 30 get 31 { 32 if (string.IsNullOrEmpty(this._code)) 33 { 34 this._code = Encode(); 35 } 36 return this._code; 37 } 38 } 39 private Image _encodedImage; 40 public Image EncodedImage 41 { 42 get{return _encodedImage;} 43 set{this._encodedImage=value;} 44 } 45 private Hashtable C39_Code = new Hashtable(); //is initialized by init_Code39() 46 private Hashtable ExtC39_Translation = new Hashtable(); 47 /// <summary> 48 /// Encodes with Code39. 49 /// </summary> 50 /// <param name="input">Data to encode.</param> 51 /// <param name="AllowExtended">Allow Extended Code 39 (Full Ascii mode).</param> 52 /// <param name="EnableChecksum">Whether to calculate the Mod 43 checksum and encode it into the barcode</param> 53 public BarCode(string input, bool enableChecksum, int height, int width,bool includeLabel) 54 { 55 _rawData = input; 56 _enableChecksum = enableChecksum; 57 _height = height; 58 _width = width; 59 _includeLabel=includeLabel; 60 _labelPosition=LabelPositions.BOTTOMCENTER; 61 } 62 63 /// <summary> 64 /// Encode the raw data using the Code 39 algorithm. 65 /// </summary> 66 private string Encode() 67 { 68 this.init_Code39(); 69 this.init_ExtendedCode39(); 70 71 string strNoAstr = this._rawData.Replace("*", ""); 72 string strFormattedData = "*" + strNoAstr + (this._enableChecksum ? getChecksumChar(strNoAstr).ToString() : String.Empty) + "*"; 73 74 string result = ""; 75 //foreach (char c in this.FormattedData) 76 foreach (char c in strFormattedData) 77 { 78 try 79 { 80 result += C39_Code[c].ToString(); 81 result += "0";//whitespace 82 }//try 83 catch 84 { 85 throw new Exception("EC39-1: Invalid data. (Try using Extended Code39)"); 86 }//catch 87 }//foreach 88 89 result = result.Substring(0, result.Length-1); 90 91 //clear the hashtable so it no longer takes up memory 92 this.C39_Code.Clear(); 93 94 return result; 95 }//Encode_Code39 96 private void init_Code39() 97 { 98 C39_Code.Clear(); 99 C39_Code.Add('0', "101001101101"); 100 C39_Code.Add('1', "110100101011"); 101 C39_Code.Add('2', "101100101011"); 102 C39_Code.Add('3', "110110010101"); 103 C39_Code.Add('4', "101001101011"); 104 C39_Code.Add('5', "110100110101"); 105 C39_Code.Add('6', "101100110101"); 106 C39_Code.Add('7', "101001011011"); 107 C39_Code.Add('8', "110100101101"); 108 C39_Code.Add('9', "101100101101"); 109 C39_Code.Add('A', "110101001011"); 110 C39_Code.Add('B', "101101001011"); 111 C39_Code.Add('C', "110110100101"); 112 C39_Code.Add('D', "101011001011"); 113 C39_Code.Add('E', "110101100101"); 114 C39_Code.Add('F', "101101100101"); 115 C39_Code.Add('G', "101010011011"); 116 C39_Code.Add('H', "110101001101"); 117 C39_Code.Add('I', "101101001101"); 118 C39_Code.Add('J', "101011001101"); 119 C39_Code.Add('K', "110101010011"); 120 C39_Code.Add('L', "101101010011"); 121 C39_Code.Add('M', "110110101001"); 122 C39_Code.Add('N', "101011010011"); 123 C39_Code.Add('O', "110101101001"); 124 C39_Code.Add('P', "101101101001"); 125 C39_Code.Add('Q', "101010110011"); 126 C39_Code.Add('R', "110101011001"); 127 C39_Code.Add('S', "101101011001"); 128 C39_Code.Add('T', "101011011001"); 129 C39_Code.Add('U', "110010101011"); 130 C39_Code.Add('V', "100110101011"); 131 C39_Code.Add('W', "110011010101"); 132 C39_Code.Add('X', "100101101011"); 133 C39_Code.Add('Y', "110010110101"); 134 C39_Code.Add('Z', "100110110101"); 135 C39_Code.Add('-', "100101011011"); 136 C39_Code.Add('.', "110010101101"); 137 C39_Code.Add(' ', "100110101101"); 138 C39_Code.Add('$', "100100100101"); 139 C39_Code.Add('/', "100100101001"); 140 C39_Code.Add('+', "100101001001"); 141 C39_Code.Add('%', "101001001001"); 142 C39_Code.Add('*', "100101101101"); 143 }//init_Code39 144 private void init_ExtendedCode39() 145 { 146 ExtC39_Translation.Clear(); 147 ExtC39_Translation.Add(Convert.ToChar(0).ToString(), "%U"); 148 ExtC39_Translation.Add(Convert.ToChar(1).ToString(), "$A"); 149 ExtC39_Translation.Add(Convert.ToChar(2).ToString(), "$B"); 150 ExtC39_Translation.Add(Convert.ToChar(3).ToString(), "$C"); 151 ExtC39_Translation.Add(Convert.ToChar(4).ToString(), "$D"); 152 ExtC39_Translation.Add(Convert.ToChar(5).ToString(), "$E"); 153 ExtC39_Translation.Add(Convert.ToChar(6).ToString(), "$F"); 154 ExtC39_Translation.Add(Convert.ToChar(7).ToString(), "$G"); 155 ExtC39_Translation.Add(Convert.ToChar(8).ToString(), "$H"); 156 ExtC39_Translation.Add(Convert.ToChar(9).ToString(), "$I"); 157 ExtC39_Translation.Add(Convert.ToChar(10).ToString(), "$J"); 158 ExtC39_Translation.Add(Convert.ToChar(11).ToString(), "$K"); 159 ExtC39_Translation.Add(Convert.ToChar(12).ToString(), "$L"); 160 ExtC39_Translation.Add(Convert.ToChar(13).ToString(), "$M"); 161 ExtC39_Translation.Add(Convert.ToChar(14).ToString(), "$N"); 162 ExtC39_Translation.Add(Convert.ToChar(15).ToString(), "$O"); 163 ExtC39_Translation.Add(Convert.ToChar(16).ToString(), "$P"); 164 ExtC39_Translation.Add(Convert.ToChar(17).ToString(), "$Q"); 165 ExtC39_Translation.Add(Convert.ToChar(18).ToString(), "$R"); 166 ExtC39_Translation.Add(Convert.ToChar(19).ToString(), "$S"); 167 ExtC39_Translation.Add(Convert.ToChar(20).ToString(), "$T"); 168 ExtC39_Translation.Add(Convert.ToChar(21).ToString(), "$U"); 169 ExtC39_Translation.Add(Convert.ToChar(22).ToString(), "$V"); 170 ExtC39_Translation.Add(Convert.ToChar(23).ToString(), "$W"); 171 ExtC39_Translation.Add(Convert.ToChar(24).ToString(), "$X"); 172 ExtC39_Translation.Add(Convert.ToChar(25).ToString(), "$Y"); 173 ExtC39_Translation.Add(Convert.ToChar(26).ToString(), "$Z"); 174 ExtC39_Translation.Add(Convert.ToChar(27).ToString(), "%A"); 175 ExtC39_Translation.Add(Convert.ToChar(28).ToString(), "%B"); 176 ExtC39_Translation.Add(Convert.ToChar(29).ToString(), "%C"); 177 ExtC39_Translation.Add(Convert.ToChar(30).ToString(), "%D"); 178 ExtC39_Translation.Add(Convert.ToChar(31).ToString(), "%E"); 179 ExtC39_Translation.Add("!", "/A"); 180 ExtC39_Translation.Add(""", "/B"); 181 ExtC39_Translation.Add("#", "/C"); 182 ExtC39_Translation.Add("$", "/D"); 183 ExtC39_Translation.Add("%", "/E"); 184 ExtC39_Translation.Add("&", "/F"); 185 ExtC39_Translation.Add("'", "/G"); 186 ExtC39_Translation.Add("(", "/H"); 187 ExtC39_Translation.Add(")", "/I"); 188 ExtC39_Translation.Add("*", "/J"); 189 ExtC39_Translation.Add("+", "/K"); 190 ExtC39_Translation.Add(",", "/L"); 191 ExtC39_Translation.Add("/", "/O"); 192 ExtC39_Translation.Add(":", "/Z"); 193 ExtC39_Translation.Add(";", "%F"); 194 ExtC39_Translation.Add("<", "%G"); 195 ExtC39_Translation.Add("=", "%H"); 196 ExtC39_Translation.Add(">", "%I"); 197 ExtC39_Translation.Add("?", "%J"); 198 ExtC39_Translation.Add("[", "%K"); 199 ExtC39_Translation.Add("\", "%L"); 200 ExtC39_Translation.Add("]", "%M"); 201 ExtC39_Translation.Add("^", "%N"); 202 ExtC39_Translation.Add("_", "%O"); 203 ExtC39_Translation.Add("{", "%P"); 204 ExtC39_Translation.Add("|", "%Q"); 205 ExtC39_Translation.Add("}", "%R"); 206 ExtC39_Translation.Add("~", "%S"); 207 ExtC39_Translation.Add("`", "%W"); 208 ExtC39_Translation.Add("@", "%V"); 209 ExtC39_Translation.Add("a", "+A"); 210 ExtC39_Translation.Add("b", "+B"); 211 ExtC39_Translation.Add("c", "+C"); 212 ExtC39_Translation.Add("d", "+D"); 213 ExtC39_Translation.Add("e", "+E"); 214 ExtC39_Translation.Add("f", "+F"); 215 ExtC39_Translation.Add("g", "+G"); 216 ExtC39_Translation.Add("h", "+H"); 217 ExtC39_Translation.Add("i", "+I"); 218 ExtC39_Translation.Add("j", "+J"); 219 ExtC39_Translation.Add("k", "+K"); 220 ExtC39_Translation.Add("l", "+L"); 221 ExtC39_Translation.Add("m", "+M"); 222 ExtC39_Translation.Add("n", "+N"); 223 ExtC39_Translation.Add("o", "+O"); 224 ExtC39_Translation.Add("p", "+P"); 225 ExtC39_Translation.Add("q", "+Q"); 226 ExtC39_Translation.Add("r", "+R"); 227 ExtC39_Translation.Add("s", "+S"); 228 ExtC39_Translation.Add("t", "+T"); 229 ExtC39_Translation.Add("u", "+U"); 230 ExtC39_Translation.Add("v", "+V"); 231 ExtC39_Translation.Add("w", "+W"); 232 ExtC39_Translation.Add("x", "+X"); 233 ExtC39_Translation.Add("y", "+Y"); 234 ExtC39_Translation.Add("z", "+Z"); 235 ExtC39_Translation.Add(Convert.ToChar(127).ToString(), "%T"); //also %X, %Y, %Z 236 } 237 private void InsertExtendedCharsIfNeeded(ref string FormattedData) 238 { 239 string output = ""; 240 foreach (char c in FormattedData) 241 { 242 try 243 { 244 string s = C39_Code[c].ToString(); 245 output += c; 246 }//try 247 catch 248 { 249 //insert extended substitution 250 object oTrans = ExtC39_Translation[c.ToString()]; 251 output += oTrans.ToString(); 252 }//catch 253 }//foreach 254 255 FormattedData = output; 256 } 257 private char getChecksumChar(string strNoAstr) 258 { 259 //checksum 260 string Code39_Charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%"; 261 InsertExtendedCharsIfNeeded(ref strNoAstr); 262 int sum = 0; 263 264 //Calculate the checksum 265 for (int i = 0; i < strNoAstr.Length; ++i) 266 { 267 sum = sum + Code39_Charset.IndexOf(strNoAstr[i].ToString()); 268 } 269 270 //return the checksum char 271 return Code39_Charset[sum % 43]; 272 } 273 274 /// <summary> 275 /// Gets a bitmap representation of the encoded data. 276 /// </summary> 277 /// <returns>Bitmap of encoded value.</returns> 278 public Bitmap GenerateCodeImage(ref string codeValue) 279 { 280 if (Code == "") throw new Exception("EGENERATE_IMAGE-1: Encode Error."); 281 Bitmap b = null; 282 283 DateTime dtStartTime = DateTime.Now; 284 285 286 b = new Bitmap(this._width, this._height); 287 int iBarWidth = this._width / Code.Length; 288 int shiftAdjustment = (this._width % Code.Length) / 2; 289 int iBarWidthModifier = 1; 290 291 if (iBarWidth <= 0) 292 { 293 throw new Exception("EGENERATE_IMAGE-2: Image size specified not large enough to draw image. (Bar size determined to be less than 1 pixel)"); 294 } 295 296 //draw image 297 int pos = 0; 298 int halfBarWidth = (int)(iBarWidth * 0.5); 299 300 using (Graphics g = Graphics.FromImage(b)) 301 { 302 //clears the image and colors the entire background 303 g.Clear(this._BackColor); 304 305 //lines are fBarWidth wide so draw the appropriate color line vertically 306 using (Pen backpen = new Pen(_BackColor, iBarWidth / iBarWidthModifier)) 307 { 308 using (Pen pen = new Pen(_ForeColor, iBarWidth / iBarWidthModifier)) 309 { 310 while (pos < Code.Length) 311 { 312 if (Code[pos] == '1') 313 { 314 g.DrawLine(pen, new Point(pos * iBarWidth + shiftAdjustment + halfBarWidth, 0), new Point(pos * iBarWidth + shiftAdjustment + halfBarWidth, this._height)); 315 } 316 pos++; 317 }//while 318 }//using 319 }//using 320 321 if (this._includeLabel) 322 { 323 GenerateCodeLabel((Image)b); 324 }//if 325 326 EncodedImage = (Image)b; 327 codeValue=Code; 328 return b; 329 } 330 } 331 332 /// <summary> 333 /// Saves an encoded image to a specified file and type. 334 /// </summary> 335 /// <param name="Filename">Filename to save to.</param> 336 /// <param name="FileType">Format to use.</param> 337 public void SaveImage(string Filename) 338 { 339 try 340 { 341 if (this.Code != null) 342 { 343 string FileType = Filename.Substring(Filename.LastIndexOf('.') + 1).ToUpper(); 344 System.Drawing.Imaging.ImageFormat imageformat; 345 switch (FileType) 346 { 347 case "BMP": imageformat = System.Drawing.Imaging.ImageFormat.Bmp; break; 348 case "GIF": imageformat = System.Drawing.Imaging.ImageFormat.Gif; break; 349 case "JPG": imageformat = System.Drawing.Imaging.ImageFormat.Jpeg; break; 350 case "PNG": imageformat = System.Drawing.Imaging.ImageFormat.Png; break; 351 case "TIFF": imageformat = System.Drawing.Imaging.ImageFormat.Tiff; break; 352 default: imageformat = System.Drawing.Imaging.ImageFormat.Png; break; 353 }//switch 354 ((Bitmap)this.EncodedImage).Save(Filename, imageformat); 355 }//if 356 }//try 357 catch (Exception ex) 358 { 359 throw new Exception("ESAVEIMAGE-1: Could not save image. ======================= " + ex.Message); 360 }//catch 361 }//SaveImage(string, SaveTypes) 362 363 private Image GenerateCodeLabel(Image img) 364 { 365 try 366 { 367 System.Drawing.Font font =new Font("Microsoft Sans Serif", 10, FontStyle.Bold); 368 369 using (Graphics g = Graphics.FromImage(img)) 370 { 371 g.DrawImage(img, (float)0, (float)0); 372 373 g.SmoothingMode = SmoothingMode.HighQuality; 374 g.InterpolationMode = InterpolationMode.HighQualityBicubic; 375 g.PixelOffsetMode = PixelOffsetMode.HighQuality; 376 g.CompositingQuality = CompositingQuality.HighQuality; 377 378 StringFormat f = new StringFormat(); 379 f.Alignment = StringAlignment.Near; 380 f.LineAlignment = StringAlignment.Near; 381 int LabelX = 0; 382 int LabelY = 0; 383 384 switch (this._labelPosition) 385 { 386 case LabelPositions.BOTTOMCENTER: 387 LabelX = img.Width / 2; 388 LabelY = img.Height - (font.Height); 389 f.Alignment = StringAlignment.Center; 390 break; 391 case LabelPositions.BOTTOMLEFT: 392 LabelX = 0; 393 LabelY = img.Height - (font.Height); 394 f.Alignment = StringAlignment.Near; 395 break; 396 case LabelPositions.BOTTOMRIGHT: 397 LabelX = img.Width; 398 LabelY = img.Height - (font.Height); 399 f.Alignment = StringAlignment.Far; 400 break; 401 case LabelPositions.TOPCENTER: 402 LabelX = img.Width / 2; 403 LabelY = 0; 404 f.Alignment = StringAlignment.Center; 405 break; 406 case LabelPositions.TOPLEFT: 407 LabelX = img.Width; 408 LabelY = 0; 409 f.Alignment = StringAlignment.Near; 410 break; 411 case LabelPositions.TOPRIGHT: 412 LabelX = img.Width; 413 LabelY = 0; 414 f.Alignment = StringAlignment.Far; 415 break; 416 }//switch 417 418 //color a background color box at the bottom of the barcode to hold the string of data 419 g.FillRectangle(new SolidBrush(this._BackColor), new RectangleF((float)0, (float)LabelY, (float)img.Width, (float)font.Height)); 420 421 //draw datastring under the barcode image 422 g.DrawString(this._rawData, font, new SolidBrush(this._ForeColor), new RectangleF((float)0, (float)LabelY, (float)img.Width, (float)font.Height), f); 423 424 g.Save(); 425 }//using 426 return img; 427 }//try 428 catch (Exception ex) 429 { 430 throw new Exception("ELABEL_GENERIC-1: " + ex.Message); 431 }//catch 432 }//Label_Generic 433 434 } 435 }
1 private void btnEncode_Click(object sender, EventArgs e) 2 { 3 string input = this.txtInput.Text.Trim(); 4 string txtwidth = this.txtWidth.Text.Trim(); 5 string txtheight = this.txtHeight.Text.Trim(); 6 if (!string.IsNullOrEmpty(input) && !string.IsNullOrEmpty(txtwidth) && !string.IsNullOrEmpty(txtheight)) 7 { 8 this._width = Int32.Parse(txtwidth); 9 this._height = Int32.Parse(txtheight); 10 barCode = new BarCode(input, true, this._height, this._width, true); 11 string codeValue = string.Empty; 12 pb.Image = barCode.GenerateCodeImage(ref codeValue); 13 //reposition the barcode image to the middle 14 pb.Location = new Point((this.pb.Location.X + this.pb.Width / 2) - this.pb.Width / 2, (this.pb.Location.Y + this.pb.Height / 2) - pb.Height / 2); 15 16 //txtEncoded.Text = codeValue; 17 } 18 else 19 { 20 MessageBox.Show("请输入完整信息"); 21 } 22 } 23 /// <summary> 24 /// 打印生成的条形码 25 /// </summary> 26 /// <param name="sender"></param> 27 /// <param name="e"></param> 28 private void btnPrint_Click(object sender, EventArgs e) 29 { 30 PrintDialog printDialog = new PrintDialog(); 31 printDialog.Document = printDocument; 32 if (printDialog.ShowDialog(this) == DialogResult.OK) //到这里会出现选择打印项的窗口 33 { 34 printDocument.Print(); //到这里会出现给文件命名的窗口,点击确定后进行打印并完成打印 35 } 36 } 37 /// <summary> 38 /// 使用PrintDocument,并且设置相应的事件监听 39 /// </summary> 40 /// <param name="sender"></param> 41 /// <param name="e"></param> 42 private void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) 43 { 44 try 45 { 46 if (pb.Image != null) 47 { 48 Rectangle rect = new Rectangle(50, 50, this._width, this._height);//设置打印起始位置和大小 49 e.Graphics.DrawImage(pb.Image,rect);//确定要打印的图片及其打印起始位置和大小 50 e.HasMorePages = false; 51 } 52 } 53 catch (Exception ex) 54 { 55 MessageBox.Show("请先生成条形码!"); 56 } 57 } 58 59 private void btnSaveAsImg_Click(object sender, EventArgs e) 60 { 61 SaveFileDialog saveFdlg = new SaveFileDialog(); 62 saveFdlg.Filter = "*.png|*.jpg"; 63 if (saveFdlg.ShowDialog() == DialogResult.OK) 64 { 65 string fileName = saveFdlg.FileName; 66 barCode.SaveImage(fileName); 67 } 68 }
(3)效果图