QRCodeHelper.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. 
  2. using System;
  3. using System.Drawing;
  4. using System.Drawing.Imaging;
  5. using System.Runtime.InteropServices;
  6. //using Curtain.Helpers;
  7. using ZXing;
  8. using ZXing.QrCode;
  9. using ZXing.QrCode.Internal;
  10. namespace Dongke.WinForm.Controls.InvoiceLayout.DrawBarcode.QRCode
  11. {
  12. /// <summary>
  13. /// 二维码生成
  14. /// </summary>
  15. public sealed class QRCodeHelper
  16. {
  17. /// <summary>
  18. /// 原始二维码
  19. /// </summary>
  20. /// <param name="contents"></param>
  21. /// <param name="encodingOptions"></param>
  22. /// <returns></returns>
  23. public static QRCodeData Encode(string contents, QRCodeEncodingOptions encodingOptions = null)
  24. {
  25. if (encodingOptions == null)
  26. {
  27. encodingOptions = new QRCodeEncodingOptions();
  28. }
  29. QrCodeEncodingOptions qroptions = new QrCodeEncodingOptions
  30. {
  31. DisableECI = encodingOptions.DisableECI,
  32. CharacterSet = encodingOptions.CharacterSet,
  33. ErrorCorrection = GetErrorCorrectionLevel(encodingOptions.ECLevel),
  34. QrVersion = encodingOptions.Version
  35. };
  36. ZXing.QrCode.Internal.QRCode qrcode = null;
  37. try
  38. {
  39. qrcode = ZXing.QrCode.Internal.Encoder.encode(contents, qroptions.ErrorCorrection, qroptions.Hints);
  40. }
  41. catch (WriterException wex)
  42. {
  43. if (encodingOptions.Version.HasValue && wex.Message.Contains("Data too big for requested version"))
  44. {
  45. qroptions.QrVersion = null;
  46. try
  47. {
  48. qrcode = ZXing.QrCode.Internal.Encoder.encode(contents, qroptions.ErrorCorrection, qroptions.Hints);
  49. }
  50. catch
  51. {
  52. }
  53. }
  54. }
  55. if (qrcode == null)
  56. {
  57. return null;
  58. }
  59. ByteMatrix bm = qrcode.Matrix;
  60. int size = bm.Width;
  61. QRCodeData data = new QRCodeData(size)
  62. {
  63. Text = contents,
  64. MaskPattern = qrcode.MaskPattern,
  65. ModeName = qrcode.Mode.Name.ToString(),
  66. Version = qrcode.Version.VersionNumber,
  67. Options = encodingOptions,
  68. Width = size,
  69. Height = size,
  70. };
  71. for (int y = 0; y < size; y++)
  72. {
  73. for (int x = 0; x < size; x++)
  74. {
  75. data[x, y] = (bm[x, y] == 1);
  76. }
  77. }
  78. return data;
  79. }
  80. /// <summary>
  81. /// 按比例放大的原始二维码
  82. /// </summary>
  83. /// <param name="contents"></param>
  84. /// <param name="width"></param>
  85. /// <param name="height"></param>
  86. /// <param name="encodingOptions"></param>
  87. /// <returns></returns>
  88. public static QRCodeData Encode(string contents, int width, int height, QRCodeEncodingOptions encodingOptions = null)
  89. {
  90. QRCodeData data = Encode(contents, encodingOptions);
  91. if (data == null)
  92. {
  93. return null;
  94. }
  95. return Encode(data, width, height);
  96. }
  97. /// <summary>
  98. /// 按比例放大的原始二维码
  99. /// </summary>
  100. /// <param name="data"></param>
  101. /// <param name="width"></param>
  102. /// <param name="height"></param>
  103. /// <returns></returns>
  104. public static QRCodeData Encode(QRCodeData data, int width, int height)
  105. {
  106. if (data == null)
  107. {
  108. return null;
  109. }
  110. data.Width = width;
  111. data.Height = height;
  112. bool isHorizontal = (width > height);
  113. int qrSize = (isHorizontal ? height : width);
  114. //if (qrSize < data.Size)
  115. //{
  116. // throw new ArgumentOutOfRangeException((isHorizontal? "barcode height" : "barcode width"), (isHorizontal ? height : width), $"data size is {data.Size}");
  117. //}
  118. if (qrSize <= data.Size)
  119. {
  120. return data;
  121. }
  122. int multiple = (qrSize / data.Size);
  123. if (multiple == 1)
  124. {
  125. return data;
  126. }
  127. int qrDataSize = data.Size * multiple;
  128. QRCodeData qrData = new QRCodeData(qrDataSize)
  129. {
  130. Text = data.Text,
  131. MaskPattern = data.MaskPattern,
  132. ModeName = data.ModeName,
  133. Version = data.Version,
  134. Options = data.Options,
  135. Width = width,
  136. Height = height,
  137. };
  138. for (int y = 0; y < data.Size; y++)
  139. {
  140. int w = y * multiple;
  141. for (int x = 0; x < data.Size; x++)
  142. {
  143. int h = x * multiple;
  144. for (int j = w; j < w + multiple; j++)
  145. {
  146. for (int i = h; i < h + multiple; i++)
  147. {
  148. qrData[i, j] = data[x, y];
  149. }
  150. }
  151. }
  152. }
  153. return qrData;
  154. }
  155. /// <summary>
  156. /// 直接绘制条码
  157. /// </summary>
  158. /// <param name="contents"></param>
  159. /// <param name="options"></param>
  160. /// <param name="encodingOptions"></param>
  161. public static void DrawQRCode(string contents, QRCodeDrawingOptions options,
  162. QRCodeEncodingOptions encodingOptions = null)
  163. {
  164. if (options?.DrawGraphics == null)
  165. {
  166. throw new ArgumentNullException("QRCodeDrawingOptions.DrawGraphics");
  167. }
  168. QRCodeData data = Encode(contents, encodingOptions);
  169. // 条码范围
  170. int width = options.ImageRect.Width - options.Margin.Horizontal;
  171. int height = options.ImageRect.Height - options.Margin.Vertical;
  172. data = Encode(data, width, height);
  173. DrawQRCode(data, options.DrawGraphics, options);
  174. }
  175. /// <summary>
  176. /// 直接绘制条码
  177. /// </summary>
  178. /// <param name="data"></param>
  179. /// <param name="options"></param>
  180. public static void DrawQRCode(QRCodeData data, QRCodeDrawingOptions options)
  181. {
  182. if (options?.DrawGraphics == null)
  183. {
  184. throw new ArgumentNullException("QRCodeDrawingOptions.DrawGraphics");
  185. }
  186. //// 条码范围
  187. //int width = options.ImageRect.Width - options.Margin.Horizontal;
  188. //int height = options.ImageRect.Height - options.Margin.Vertical;
  189. //if (data.Width != width || data.Height != height)
  190. //{
  191. // data = Encode(data, width, height);
  192. //}
  193. DrawQRCode(data, options.DrawGraphics, options);
  194. }
  195. /// <summary>
  196. /// 直接绘制条码
  197. /// </summary>
  198. /// <param name="imageData"></param>
  199. /// <param name="graphics"></param>
  200. /// <param name="options"></param>
  201. private static void DrawQRCode(QRCodeData imageData, Graphics graphics, QRCodeDrawingOptions options)
  202. {
  203. // 条码范围
  204. using (Image image = GetQRCodeSource(imageData, options))
  205. {
  206. Rectangle rect = new Rectangle(
  207. options.ImageRect.Left + options.Margin.Left,
  208. options.ImageRect.Top + options.Margin.Top,
  209. //imageData.Width,
  210. //imageData.Height);
  211. options.ImageRect.Width - options.Margin.Horizontal,
  212. options.ImageRect.Height - options.Margin.Vertical);
  213. if (options.ShowType == BarcodeShowType.Zoom)
  214. {
  215. graphics.DrawImage(image, rect);
  216. }
  217. else if (options.ShowType == BarcodeShowType.Show)
  218. {
  219. ImageHelper.Show(image, graphics, rect, ContentAlignment.MiddleCenter, false);
  220. }
  221. else
  222. {
  223. ImageHelper.Show(image, graphics, rect, ContentAlignment.TopLeft, false);
  224. }
  225. // TODO LOGO
  226. // TODO TEXT
  227. }
  228. }
  229. /// <summary>
  230. /// 条码图片
  231. /// </summary>
  232. /// <param name="contents"></param>
  233. /// <param name="options"></param>
  234. /// <param name="encodingOptions"></param>
  235. /// <returns></returns>
  236. public static Image GetQRCodeImage(string contents, QRCodeDrawingOptions options, QRCodeEncodingOptions encodingOptions = null)
  237. {
  238. if (options == null)
  239. {
  240. throw new ArgumentNullException("QRCodeDrawingOptions");
  241. }
  242. QRCodeData data = Encode(contents, encodingOptions);
  243. // 条码范围
  244. int width = options.ImageRect.Width - options.Margin.Horizontal;
  245. int height = options.ImageRect.Height - options.Margin.Vertical;
  246. QRCodeData imageData = Encode(data, width, height);
  247. return GetQRCodeImage(imageData, options);
  248. }
  249. /// <summary>
  250. /// 条码图片
  251. /// </summary>
  252. /// <param name="data"></param>
  253. /// <param name="options"></param>
  254. /// <returns></returns>
  255. public static Image GetQRCodeImage(QRCodeData data, QRCodeDrawingOptions options)
  256. {
  257. if (options == null)
  258. {
  259. throw new ArgumentNullException("QRCodeDrawingOptions");
  260. }
  261. if (options.ShowType == BarcodeShowType.Original)
  262. {
  263. return GetQRCodeSource(data, options);
  264. }
  265. Bitmap dataImage = new Bitmap(options.ImageRect.Width, options.ImageRect.Height);
  266. if (options.DpiX.HasValue)
  267. {
  268. dataImage.SetResolution(options.DpiX.Value, (options.DpiY) ?? options.DpiX.Value);
  269. }
  270. using (Graphics graphics = Graphics.FromImage(dataImage))
  271. {
  272. graphics.Clear(options.BackColor);
  273. DrawQRCode(data, graphics, options);
  274. }
  275. return dataImage;
  276. }
  277. /// <summary>
  278. /// 原始条码图片
  279. /// </summary>
  280. /// <param name="data"></param>
  281. /// <param name="options"></param>
  282. /// <returns></returns>
  283. public static Image GetQRCodeSource(QRCodeData data, QRCodeDrawingOptions options)
  284. {
  285. Bitmap dataImage = new Bitmap(data.Size, data.Size);
  286. if (options != null && options.DpiX.HasValue)
  287. {
  288. dataImage.SetResolution(options.DpiX.Value, (options.DpiY) ?? options.DpiX.Value);
  289. }
  290. Rectangle rectImage = new Rectangle(0, 0, data.Size, data.Size);
  291. BitmapData bmpData = dataImage.LockBits(rectImage, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
  292. try
  293. {
  294. byte[] pixels = new byte[bmpData.Stride * bmpData.Height];
  295. int index = 0;
  296. for (int y = 0; y < data.Size; y++)
  297. {
  298. for (int x = 0; x < data.Size; x++)
  299. {
  300. Color color = (data[x, y] ? options.ForeColor : options.BackColor);
  301. pixels[index++] = color.B;
  302. pixels[index++] = color.G;
  303. pixels[index++] = color.R;
  304. pixels[index++] = color.A;
  305. }
  306. }
  307. Marshal.Copy(pixels, 0, bmpData.Scan0, pixels.Length);
  308. }
  309. finally
  310. {
  311. dataImage.UnlockBits(bmpData);
  312. }
  313. return dataImage;
  314. }
  315. /// <summary>
  316. /// 转换为zxing Level
  317. /// </summary>
  318. /// <param name="level"></param>
  319. /// <returns></returns>
  320. private static ErrorCorrectionLevel GetErrorCorrectionLevel(QRECLevel level)
  321. {
  322. switch (level)
  323. {
  324. case QRECLevel.H:
  325. return ErrorCorrectionLevel.H;
  326. case QRECLevel.L:
  327. return ErrorCorrectionLevel.L;
  328. case QRECLevel.Q:
  329. return ErrorCorrectionLevel.Q;
  330. case QRECLevel.M:
  331. return ErrorCorrectionLevel.M;
  332. default:
  333. return ErrorCorrectionLevel.H;
  334. }
  335. }
  336. /// <summary>
  337. /// 二维码版本对应像素
  338. /// </summary>
  339. /// <param name="version"></param>
  340. /// <returns></returns>
  341. public static int VersionToPixel(int version)
  342. {
  343. return (version - 1) * 4 + 21;
  344. }
  345. }
  346. }