using Microsoft.AspNetCore.Hosting; using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Text; using System.Globalization; using System.IO; using System.Security.Cryptography; namespace Infrastructure.Web.Mvc { public class CaptchaHelper : IDisposable { private readonly PrivateFontCollection _collection; public CaptchaHelper(IWebHostEnvironment env) { if (env is null) { throw new ArgumentNullException(nameof(env)); } _collection = new PrivateFontCollection(); _collection.AddFontFile(Path.Combine(env.WebRootPath, "fonts", "arial.ttf")); } public byte[] GetCaptcha(string code, int width = 80, int height = 30) { return GetImage(code, width, height); } private static Color GetColor() { var colors = new Color[] { Color.DodgerBlue, Color.SteelBlue, Color.LimeGreen, Color.LightSeaGreen, Color.DarkGoldenrod, Color.Chocolate, Color.IndianRed, Color.Cyan }; return colors[GetRandom(0, 7)]; } private byte[] GetImage(string value, int width, int height) { using var image = DrawImage(value, width, height); using var stream = new MemoryStream(); image.Save(stream, ImageFormat.Jpeg); return stream.ToArray(); } private Bitmap DrawImage(string value, int width, int height) { var size = new Size(80, 30); var image = new Bitmap(size.Width, size.Height); var g = Graphics.FromImage(image); g.Clear(Color.FloralWhite); using (var pen = new Pen(Color.Lavender)) { g.DrawRectangle(pen, 0, 0, width, height); } DrawBeziers(g, width, height); var top = 0; var left = 0; var index = 0; DrawChar(value[index++], g, left, top, width, height); left += width / 4; DrawChar(value[index++], g, left, top, width, height); left += width / 4; DrawChar(value[index++], g, left, top, width, height); left += width / 4; DrawChar(value[index++], g, left, top, width, height); return image; } private static void DrawBeziers(Graphics g, int width, int height) { for (int i = 0; i < width * height / 100; i++) { using var pen = new Pen(GetColor(), 0); var x = GetRandom(0, width); var y = GetRandom(0, height); if (GetRandom() > 4) { g.DrawEllipse(pen, x, y, GetRandom(), GetRandom()); } else { g.DrawBezier(pen, x, y, x + 5, y + 5, x + 10, y + 10, x + 20, y + 20); } } } private void DrawChar(char ch, Graphics g, int left, int top, int width, int height) { var fontSize = 16; fontSize += GetRandom(-5, 5); var value = ch.ToString(CultureInfo.CurrentCulture); using var font = new Font(_collection.Families[0], fontSize); var point = new Point(left, top); var size = new Size(width, height); using var brush = new LinearGradientBrush(new Rectangle(point, size), GetColor(), GetColor(), new Random(DateTime.Now.Millisecond).Next(0, 359)); g.DrawString(value, font, brush, point); } private static int GetRandom(int min = 0, int max = 9) { var random = new byte[1]; using (var rg = RandomNumberGenerator.Create()) { rg.GetBytes(random); } return new Random(Convert.ToInt32(random[0])).Next(min, max); } protected virtual void Dispose(bool disposing) { if (disposing) { this._collection.Dispose(); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~CaptchaHelper() { Dispose(false); } } }