上一篇二维码生成中介绍了通过类库ThoughtWorks.QRCode.dll生成二维码的简单应用,并对类库ThoughtWorks.QRCode.dll做了个简单的封装,这次在上次的基础上做修改,增加了可以在二维码中间增加自定义头像或图标。
下面这篇文章详细介绍了二维码的原理,因为二维码中间大部分区域都是一些无用码,并不影响我们对二维码正常信息的读取,所以我们可以把一张自己的头像,或是公司商标等放在二维码的中间。
http://developer.51cto.com/art/201310/414082_all.htm
要在二维码中间放置商标,而不影响二维码的读取,我们必须设置二维码的纠错级别QRCodeErrorCorrect 为高:
1 _QRCode.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H;
若不需要中间放图片,正常情况下,我们设置QRCodeErrorCorrect为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个字节数据;
具体支持多少个字节数据,与属性QRCodeVersion数值有关。
我们可以通过我简单类中的以下几个方法,来设置二维码的中间图片:
1 /// <summary> 2 /// 设置二维码中间图片 3 /// </summary> 4 /// <param name="fileName">图片文件名,包含路径</param> 5 public void SetFillImage(string fileName) 6 { 7 SetFillImage(fileName, 100, 100); 8 } 9 /// <summary> 10 /// 设置二维码中间图片 11 /// </summary> 12 /// <param name="fileName">图片文件名,包含路径</param> 13 /// <param name="width">图片宽度</param> 14 /// <param name="height">图片高度</param> 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 /// <summary> 24 /// 设置二维码中间图片 25 /// </summary> 26 /// <param name="bitmap">图上片IMAGE对象</param> 27 public void SetFillImage(Image bitmap) 28 { 29 SetFillImage(bitmap, 100, 100); 30 } 31 /// <summary> 32 /// 设置二维码中间图片 33 /// </summary> 34 /// <param name="bitmap">图上片IMAGE对象</param> 35 /// <param name="width">图片宽度</param> 36 /// <param name="height">图片高度</param> 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 }
设置图片时,会自动设置QRCodeErrorCorrect 属性为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 /// <summary> 22 /// 获取二维码图像base64字符串,可直接赋值给img对象的src显示 23 /// 默认编码UTF8,QRCodeEncoder默认值为Unicode 24 /// </summary> 25 /// <param name="content">编码内容</param> 26 /// <returns>二维码图像base64字符串</returns> 27 public string Encode(string content) 28 { 29 return this.Encode(content, encoding); 30 } 31 /// <summary> 32 /// 获取二维码图像base64字符串,可直接赋值给img对象的src显示 33 /// </summary> 34 /// <param name="content">编码内容</param> 35 /// <param name="encoding">编码类型</param> 36 /// <returns>二维码图像base64字符串</returns> 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 /// <summary> 74 /// 根据编码内容自动获取QRCodeVersion值 75 /// </summary> 76 /// <param name="content"></param> 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 /// <summary> 89 /// 每个QRCodeVersion值对应支持最长字节数 90 /// </summary> 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 /// <summary> 93 /// QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H时 每个QRCodeVersion值对应支持最长字节数 94 /// </summary> 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 /// <summary> 154 /// 设置二维码中间图片 155 /// </summary> 156 /// <param name="fileName">图片文件名,包含路径</param> 157 public void SetFillImage(string fileName) 158 { 159 SetFillImage(fileName, 100, 100); 160 } 161 /// <summary> 162 /// 设置二维码中间图片 163 /// </summary> 164 /// <param name="fileName">图片文件名,包含路径</param> 165 /// <param name="width">图片宽度</param> 166 /// <param name="height">图片高度</param> 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 /// <summary> 176 /// 设置二维码中间图片 177 /// </summary> 178 /// <param name="bitmap">图上片IMAGE对象</param> 179 public void SetFillImage(Image bitmap) 180 { 181 SetFillImage(bitmap, 100, 100); 182 } 183 /// <summary> 184 /// 设置二维码中间图片 185 /// </summary> 186 /// <param name="bitmap">图上片IMAGE对象</param> 187 /// <param name="width">图片宽度</param> 188 /// <param name="height">图片高度</param> 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 /// <summary> 197 /// 生成的二维码宽度 198 /// </summary> 199 public int Width { get; set; } 200 /// <summary> 201 /// 生成的二维码高度 202 /// </summary> 203 public int Height { get; set; } 204 205 private int _maskWidth = 0; 206 private int _maskHeight = 0; 207 }
ThoughtWorks.QRCode.dll类库地址,请参见我上一篇笔记。