上一篇中介绍了通过类库ThoughtWorks.QRCode.dll生成二维码的简单应用,并对类库ThoughtWorks.QRCode.dll做了个简单的封装,这次在上次的基础上做修改,增加了可以在二维码中间增加自定义头像或图标。
下面这篇文章详细介绍了二维码的原理,因为二维码中间大部分区域都是一些无用码,并不影响我们对二维码正常信息的读取,所以我们可以把一张自己的头像,或是公司商标等放在二维码的中间。
要在二维码中间放置商标,而不影响二维码的读取,我们必须设置二维码的纠错级别 为高:
1 _QRCode.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H;
若不需要中间放图片,正常情况下,我们设置为M即可:
1 _QRCode.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
要注意:当 _QRCode.QRCodeErrorCorrect级别不同时,二维码中可以包含的内容长度是不同的:
当 _QRCode.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M时,最多可包含2332个字节数据,最少可包含15个字节数据;
当 _QRCode.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H时,最多只可包含1274个字节数据,最少可包含8个字节数据;
具体支持多少个字节数据,与属性数值有关。
我们可以通过我简单类中的以下几个方法,来设置二维码的中间图片:
1 ///2 /// 设置二维码中间图片 3 /// 4 /// 图片文件名,包含路径 5 public void SetFillImage(string fileName) 6 { 7 SetFillImage(fileName, 100, 100); 8 } 9 ///10 /// 设置二维码中间图片11 /// 12 /// 图片文件名,包含路径13 /// 图片宽度14 /// 图片高度15 public void SetFillImage(string fileName, int width, int height)16 {17 if (!string.IsNullOrEmpty(fileName)) {18 if (File.Exists(fileName)) {19 SetFillImage(Image.FromFile(fileName), width, height);20 }21 }22 }23 ///24 /// 设置二维码中间图片25 /// 26 /// 图上片IMAGE对象27 public void SetFillImage(Image bitmap)28 {29 SetFillImage(bitmap, 100, 100);30 }31 ///32 /// 设置二维码中间图片33 /// 34 /// 图上片IMAGE对象35 /// 图片宽度36 /// 图片高度37 public void SetFillImage(Image bitmap, int width, int height)38 {39 fileImage = bitmap;40 _QRCode.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H;41 this._maskWidth = width;42 this._maskHeight = height;43 }
设置图片时,会自动设置 属性为H。
为了保证二维码能正确识别,中间图片的大小最好为二维码大小的1/3到3/7之间,我设置为1/3大小的样子:
1 int maskWidth = Math.Min((int)(this.Width / 3), _maskWidth); //最终图片宽度2 int maskHeight = Math.Min((int)(this.Height / 3), _maskHeight); //最终图片高度3 4 int maskLeft = (this.Width - maskWidth) / 2; //图片位置5 int maskTop = (this.Height - maskHeight) / 2; //图片位置
这样在调用Encode方法生成二维码时,会自动将设置好的图片放到二维码的中间。
具体调用方法:
1 ErWeiMa.ErWeiMa er = new ErWeiMa.ErWeiMa();2 er.SetFillImage(Server.MapPath("~/t.jpg"));3 this.img1.Src = er.Encode("中华人民共和国");
生成的二维码如下所示:
源代码:
1 public class ErWeiMa 2 { 3 private QRCodeEncoder _QRCode; 4 private Encoding encoding; 5 6 public ErWeiMa() 7 { 8 _QRCode = new QRCodeEncoder(); 9 _QRCode.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE; 10 _QRCode.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M; 11 _QRCode.QRCodeScale = 4; 12 13 encoding = Encoding.UTF8; 14 } 15 16 public static ErWeiMa GetInstance() 17 { 18 return new ErWeiMa(); 19 } 20 21 ///22 /// 获取二维码图像base64字符串,可直接赋值给img对象的src显示 23 /// 默认编码UTF8,QRCodeEncoder默认值为Unicode 24 /// 25 /// 编码内容 26 ///二维码图像base64字符串 27 public string Encode(string content) 28 { 29 return this.Encode(content, encoding); 30 } 31 ///32 /// 获取二维码图像base64字符串,可直接赋值给img对象的src显示 33 /// 34 /// 编码内容 35 /// 编码类型 36 ///二维码图像base64字符串 37 public string Encode(string content,Encoding encoding) 38 { 39 this.encoding = encoding; 40 this.InitVersion(content); 41 using (var ms = new MemoryStream()) 42 using (Bitmap image = _QRCode.Encode(content, encoding)) 43 { 44 this.Width = image.Width; 45 this.Height = image.Height; 46 FileImage(image); 47 image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); 48 return "data:image/jpeg;base64," + Convert.ToBase64String(ms.ToArray()); 49 } 50 } 51 52 private void FileImage(Bitmap source) 53 { 54 int maskWidth = Math.Min((int)(this.Width / 3), _maskWidth); 55 int maskHeight = Math.Min((int)(this.Height / 3), _maskHeight); 56 57 int maskLeft = (this.Width - maskWidth) / 2; 58 int maskTop = (this.Height - maskHeight) / 2; 59 60 using (Graphics g = Graphics.FromImage(source)) 61 { 62 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; 63 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 64 g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; 65 //g.FillRectangle(new SolidBrush(Color.Red), maskLeft, maskTop, maskWidth, maskHeight); 66 ////g.DrawImage( 67 if (fileImage != null) { 68 g.DrawImage(fileImage, maskLeft, maskTop, maskWidth, maskHeight); 69 } 70 } 71 } 72 73 ///74 /// 根据编码内容自动获取QRCodeVersion值 75 /// 76 /// 77 private void InitVersion(string content) 78 { 79 int length = encoding.GetByteCount(content); 80 81 if (length > GetArray()[40]) { 82 throw new Exception("The content too length;Must be shorter than " + GetArray()[40].ToString() + "byte"); ; 83 } 84 85 _QRCode.QRCodeVersion = GetIndex(length); 86 } 87 88 ///89 /// 每个QRCodeVersion值对应支持最长字节数 90 /// 91 private int[] array = new int[41] { 15,15,27,43,63,85,107,123,153,181,214,252,288,332,363,413,451,505,561,625,667,712,780,858,912,998,1060,1126,1191,1265,1371,1453,1539,1629,1723,1810,1912,1990,2100,2214,2332 }; 92 ///93 /// QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H时 每个QRCodeVersion值对应支持最长字节数 94 /// 95 private int[] array2 = new int[41] { 8, 8, 15, 25, 35, 45, 59, 65, 85, 99, 120, 138, 156, 178, 195, 221, 251, 281, 311, 339, 383, 404, 440, 462, 512, 536, 594, 626, 659, 699, 743, 791, 843, 899, 959, 984, 1052, 1094, 1140, 1220, 1274 }; 96 97 private int[] GetArray() 98 { 99 switch (_QRCode.QRCodeErrorCorrect)100 {101 case QRCodeEncoder.ERROR_CORRECTION.H:102 return array2;103 case QRCodeEncoder.ERROR_CORRECTION.M:104 return array;105 default:106 return array;107 }108 }109 110 private int GetIndex(int length)111 {112 if (length <= GetArray()[0]) {113 return 0;114 }115 if (length == GetArray()[40]) {116 return 40;117 }118 119 return FindIndex(GetArray(), length, 0, 40);120 }121 122 private int FindIndex(int[] sourceArray, int findValue, int startIndex, int endIndex)123 {124 int mid = (startIndex + endIndex) % 2 == 0 ? (startIndex + endIndex) / 2 : (startIndex + endIndex) / 2 + 1;125 126 if (startIndex >= endIndex) {127 return startIndex;128 }129 130 if (findValue == sourceArray[mid])131 {132 return mid;133 }134 else if (findValue < sourceArray[mid])135 {136 if (findValue > sourceArray[mid - 1])137 {138 return mid;139 }140 return FindIndex(sourceArray,findValue, startIndex, mid - 1);141 }142 else {143 if (findValue < sourceArray[mid + 1])144 {145 return mid + 1;146 }147 return FindIndex(sourceArray,findValue, mid + 1, endIndex);148 }149 }150 151 private Image fileImage = null;152 153 ///154 /// 设置二维码中间图片155 /// 156 /// 图片文件名,包含路径157 public void SetFillImage(string fileName)158 {159 SetFillImage(fileName, 100, 100);160 }161 ///162 /// 设置二维码中间图片163 /// 164 /// 图片文件名,包含路径165 /// 图片宽度166 /// 图片高度167 public void SetFillImage(string fileName, int width, int height)168 {169 if (!string.IsNullOrEmpty(fileName)) {170 if (File.Exists(fileName)) {171 SetFillImage(Image.FromFile(fileName), width, height);172 }173 }174 }175 ///176 /// 设置二维码中间图片177 /// 178 /// 图上片IMAGE对象179 public void SetFillImage(Image bitmap)180 {181 SetFillImage(bitmap, 100, 100);182 }183 ///184 /// 设置二维码中间图片185 /// 186 /// 图上片IMAGE对象187 /// 图片宽度188 /// 图片高度189 public void SetFillImage(Image bitmap, int width, int height)190 {191 fileImage = bitmap;192 _QRCode.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H;193 this._maskWidth = width;194 this._maskHeight = height;195 }196 ///197 /// 生成的二维码宽度198 /// 199 public int Width { get; set; }200 ///201 /// 生成的二维码高度202 /// 203 public int Height { get; set; }204 205 private int _maskWidth = 0;206 private int _maskHeight = 0;207 }
ThoughtWorks.QRCode.dll类库地址,请参见我上一篇笔记。