以前项目中生成海报都是前端生成(html2canvas)但由于前端生成有各种各样的限制如
图片跨域,浏览器兼容等问题。老板就想网站中生成海报用回服务端生成海报的方式。直接通过链接就可以输出海报~~~~ 那就干吧
先看下实现的效果(主要是 背景图+二维码+标题+价格)
实现的效果感觉还是可以,就是折扣这框本来要有圆角的,但是圆角的实现好像有点麻烦最终没有弄
为了方式使用这里使用 IHttpHandler的方式实现,直接使用链接传入参数即可【xxx/Images/poster.ashx?id=d9577af85ce44a2ebccf0a8593ab3332&path=http://jvimg001-10003558.image.myqcloud.com/d8f155f8437c7c3953d365e72e5200c3&title=%E8%85%BE%E8%AE%AF%E8%A7%86%E9%A2%91%E8%B6%85%E7%BA%A7%E5%BD%B1%E8%A7%86VIP%E4%BC%9A%E5%91%98%E5%B9%B4%E5%8D%A1%EF%BC%88%E6%94%AF%E6%8C%81%E7%94%B5%E8%A7%86%E7%AB%AF%EF%BC%89&op=488&p=288】
演示
引用的 dll 如下
ThoughtWorks.QRCode.dll (用于生成二维码) 【下载】
代码实现
QRCodeHelper 二维码生成类,可生成中心带有logo 的二维码
public class QRCodeHelper
{
///
/// 生成二维码,默认边长为250px
///
/// 二维码内容
///
public static Image BuildQRCode(string content)
{
return BuildQRCode(content, 250, Color.White, Color.Black);
}
///
/// 生成二维码,自定义边长
///
/// 二维码内容
/// 二维码边长px
///
public static Image BuildQRCode(string content, int imgSize)
{
return BuildQRCode(content, imgSize, Color.White, Color.Black);
}
///
/// 生成二维码
/// 注:自定义边长以及颜色
///
/// 二维码内容
/// 二维码边长px
/// 二维码底色
/// 二维码前景色
///
public static Image BuildQRCode(string content, int imgSize, Color background, Color foreground)
{
return BuildQRCode_Logo(content, imgSize, background, foreground, null);
}
///
/// 生成二维码并添加Logo
/// 注:默认生成边长为250px的二维码
///
/// 二维码内容
/// logo图片
///
public static Image BuildQRCode_Logo(string content, Bitmap logo)
{
return BuildQRCode_Logo(content, 250, Color.White, Color.Black, logo);
}
///
/// 生成二维码并添加Logo
/// 注:自定义边长
///
/// 二维码内容
/// 二维码边长px
/// logo图片
///
public static Image BuildQRCode_Logo(string content, int imgSize, Bitmap logo)
{
return BuildQRCode_Logo(content, imgSize, Color.White, Color.Black, logo);
}
///
/// 生成二维码并添加Logo
/// 注:自定义边长以及颜色
///
/// 二维码内容
/// 二维码边长px
/// 二维码底色
/// 二维码前景色
/// logo图片
///
public static Image BuildQRCode_Logo(string content, int imgSize, Color? background = null, Color? foreground = null, Bitmap logo = null)
{
QRCodeEncoder encoder = new QRCodeEncoder();
encoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;//编码方式(注意:BYTE能支持中文,ALPHA_NUMERIC扫描出来的都是数字)
encoder.QRCodeScale = 5;//大小(值越大生成的二维码图片像素越高)
encoder.QRCodeVersion = 0;//版本(注意:设置为0主要是防止编码的字符串太长时发生错误)
encoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H;//错误效验、错误更正(有4个等级)
encoder.QRCodeBackgroundColor = background ?? Color.White;
encoder.QRCodeForegroundColor = foreground ?? Color.Black;
System.Drawing.Image image = encoder.Encode(content, System.Text.Encoding.UTF8);
//这里+10 使得二维码留边
int resWidth = imgSize + 10;
Bitmap newBit = new Bitmap(resWidth, resWidth, PixelFormat.Format32bppRgb);
Graphics g = Graphics.FromImage(newBit);
//设置白色背景
g.FillRectangle(new SolidBrush(Color.White), 0, 0, (int)resWidth, (int)resWidth);
g.DrawImage(image, 5, 5, imgSize, imgSize);
if (logo != null)
{
int logoW = 40;
int w = resWidth / 4;
int x = resWidth / 2 - w / 2;
int centerW = resWidth / logoW;
//设置白色背景
g.FillRectangle(new SolidBrush(Color.White), x, x, w, w);
Bitmap copyImage = new Bitmap(logo, w - centerW, w - centerW);
g.DrawImage(copyImage, x + centerW / logoW / 2, x + centerW / 2);
copyImage.Dispose();
}
g.Dispose();
return newBit;
}
}
为了使用颜色丰富,需要将css 常规的进行转换
private Color ColorConvertBy16(string colorString)//16进制颜色值
{
int v = int.Parse(colorString, System.Globalization.NumberStyles.HexNumber);
return Color.FromArgb(255, Convert.ToByte((v >> 16) & 255), Convert.ToByte((v >> 8) & 255), Convert.ToByte((v >> 0) & 255));
}
绘制海报的主要方法 CreatePoster ,FileCacheHelper 是封装的文件缓存类
using Jonvie.xxx 是内部封装的缓存相关的引用 (可去掉)
<%@ WebHandler Language="C#" Class="Enterprise.WebSite.App_Code.Poster" %>
using System;
using System.Web;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Web.SessionState;
using System.Net;
using ThoughtWorks.QRCode.Codec;
using Jonvie.CacheUtil;
using Jonvie.Extensions;
namespace Enterprise.WebSite.App_Code
{
public class Poster : IHttpHandler
{
private readonly string fontName = "微软雅黑";
public void ProcessRequest(HttpContext context)
{
string id = context.Request.QueryString["id"] ?? "";
if (!string.IsNullOrWhiteSpace(id))
{
string path = context.Request.QueryString["path"] ?? "";
string title = context.Request.QueryString["title"] ?? "";
if (title.Length > 40)
{
title = title.Substring(0, 40);
}
string qrcodeUrl = $"https://wx.jonvie.com/yp/plugins/RechargeClient/SureOrder?pid={id}&flag=pc";
decimal op = 0;
if (!string.IsNullOrWhiteSpace(context.Request.QueryString["op"]))
{
op = Convert.ToDecimal(context.Request.QueryString["op"]);
}
decimal p = 0;
if (!string.IsNullOrWhiteSpace(context.Request.QueryString["p"]))
{
p = Convert.ToDecimal(context.Request.QueryString["p"]);
}
if (!string.IsNullOrWhiteSpace(path))
path += "?imageView2/1/w/750/h/750/q/100";
string key = ($"{id}_{p}").MD5();
FileCacheHelper _fileCache = new FileCacheHelper("_cache", "product-poster", new TimeSpan(5, 0, 0, 0, 0));
byte[] poster = _fileCache.GetOrAddCacheItem<byte[]>(key, () =>
{
return CreatePoster(path, title, op, p, qrcodeUrl);
});
context.Response.Clear();
context.Response.ContentType = "image/jpeg";
context.Response.BinaryWrite(poster);
}
}
private byte[] CreatePoster(string path, string title, decimal op = 0, decimal p = 0, string qrcodeUrl = "")
{
decimal discount = 0;
if (op > 0)
{
discount = Math.Floor(p / op * 100M) / 10M;
}
Image image = ReduceNetworkImage(path);
int w = 750;
int h = 1000;
int margin = 20;
//先绘画一个750*1000的背景
Bitmap bitmap = new Bitmap(w, h, PixelFormat.Format32bppPArgb);
//在背景上开始绘画
Graphics g = Graphics.FromImage(bitmap);
//插入的像素位置由偏移量控制。
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
//像素偏移方式,像素在水平和垂直距离上均偏移若干个单位,以进行高速锯齿消除。
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
//填充颜色
g.Clear(ColorConvertBy16("fafafa"));
//绘制头部图片,指定位置为 0, 0, 334, 334
Image imgBack = ReduceNetworkImage(path);
g.DrawImage(imgBack, 0, 0, 750, 750);
AddFontWarp(g, title, 5, 760, new Font(new Font(fontName, 24), FontStyle.Regular), w);
AddFont(g, $"原价{op}", margin, 850, new Font(new Font(fontName, 18), FontStyle.Strikeout), new SolidBrush(ColorConvertBy16("a7a7a7")));
AddFont(g, $"¥{p}", margin, 880, new Font(new Font(fontName, 20), FontStyle.Bold), new SolidBrush(Color.Red));
if (discount > 0)
{
string zkTxt = $"{discount}折";
g.FillRectangle(new SolidBrush(ColorConvertBy16("ffdf00")), margin, 920, zkTxt.Length * 16f + 4, 28);
AddFont(g, zkTxt, margin + 4, 920, new Font(fontName, 16), new SolidBrush(Color.Red));
}
AddFont(g, "微信扫一扫 立即充值>>", margin, 960, new Font(fontName, 18), new SolidBrush(ColorConvertBy16("a7a7a7")));
g.DrawImage(QRCodeHelper.BuildQRCode(qrcodeUrl, 140), new PointF(580, 840));
MemoryStream ms = new MemoryStream();
//保存为Jpg类型
bitmap.Save(ms, ImageFormat.Jpeg);
g.Dispose();
bitmap.Dispose();
image.Dispose();
return ms.ToArray();
}
///
/// 添加矩形
///
///
private void AddRectangle(Graphics g, int x, int y, int w, int h, int radius)
{
int cRadius = radius;
// 要实现 圆角化的 矩形
Rectangle rect = new Rectangle(x, y, w - cRadius, h - cRadius);
// 指定图形路径, 有一系列 直线/曲线 组成
System.Drawing.Drawing2D.GraphicsPath myPath = new System.Drawing.Drawing2D.GraphicsPath();
myPath.StartFigure();
myPath.AddArc(new Rectangle(new Point(rect.X, rect.Y), new Size(2 * cRadius, 2 * cRadius)), 180, 90);
myPath.AddLine(new Point(rect.X + cRadius, rect.Y), new Point(rect.Right - cRadius, rect.Y));
myPath.AddArc(new Rectangle(new Point(rect.Right - 2 * cRadius, rect.Y), new Size(2 * cRadius, 2 * cRadius)), 270, 90);
myPath.AddLine(new Point(rect.Right, rect.Y + cRadius), new Point(rect.Right, rect.Bottom - cRadius));
myPath.AddArc(new Rectangle(new Point(rect.Right - 2 * cRadius, rect.Bottom - 2 * cRadius), new Size(2 * cRadius, 2 * cRadius)), 0, 90);
myPath.AddLine(new Point(rect.Right - cRadius, rect.Bottom), new Point(rect.X + cRadius, rect.Bottom));
myPath.AddArc(new Rectangle(new Point(rect.X, rect.Bottom - 2 * cRadius), new Size(2 * cRadius, 2 * cRadius)), 90, 90);
myPath.AddLine(new Point(rect.X, rect.Bottom - cRadius), new Point(rect.X, rect.Y + cRadius));
myPath.CloseFigure();
g.DrawPath(new Pen(Color.Red, 1), myPath);
}
//添加文字
private void AddFont(Graphics g, string txt, int x, int y, Font font = null, SolidBrush mybrush = null)
{
if (mybrush == null)
mybrush = new SolidBrush(Color.Black);
if (font == null)
font = new Font(fontName, 14);
g.DrawString(txt, font, mybrush, new PointF(x, y));
}
//添加文字
private void AddFont(Graphics g, string txt, int x, int y, float emSize = 14, Font font = null, SolidBrush mybrush = null)
{
if (mybrush == null)
mybrush = new SolidBrush(Color.Black);
if (font == null)
font = new Font(fontName, emSize);
g.DrawString(txt, font, mybrush, new PointF(x, y));
}
//添加文字,可换行
private void AddFontWarp(Graphics g, string txt, int x, int y, int emSize = 14, Font font = null, int maxw = 400)
{
if (font == null)
font = new Font(fontName, emSize);
RectangleF descRect = new RectangleF();
descRect.Location = new Point(x, y);
descRect.Size = new Size(maxw, ((int)g.MeasureString(txt, font, maxw, StringFormat.GenericTypographic).Height));
g.DrawString(txt, font, Brushes.Black, descRect);
}
private void AddFontWarp(Graphics g, string txt, int x, int y, Font font = null, int maxw = 400)
{
if (font == null)
font = new Font(fontName, 14);
RectangleF descRect = new RectangleF();
descRect.Location = new Point(x, y);
descRect.Size = new Size(maxw, ((int)g.MeasureString(txt, font, maxw, StringFormat.GenericTypographic).Height));
g.DrawString(txt, font, Brushes.Black, descRect);
}
//获取图片并处理成指定只存返回,宽高填写0,直接返回原尺寸
private Image ReduceNetworkImage(string url, int toWidth = 0, int toHeight = 0)
{
HttpWebRequest httpwebr = (HttpWebRequest)HttpWebRequest.Create(url);
httpwebr.Method = "GET";
Stream s = httpwebr.GetResponse().GetResponseStream();
Image originalImage = Image.FromStream(s);
if (toWidth <= 0 && toHeight <= 0)
{
return originalImage;
}
else if (toWidth > 0 && toHeight > 0)
{
if (originalImage.Width < toWidth && originalImage.Height < toHeight)
return originalImage;
}
else if (toWidth <= 0 && toHeight > 0)
{
if (originalImage.Height < toHeight)
return originalImage;
toWidth = originalImage.Width * toHeight / originalImage.Height;
}
else if (toHeight <= 0 && toWidth > 0)
{
if (originalImage.Width < toWidth)
return originalImage;
toHeight = originalImage.Height * toWidth / originalImage.Width;
}
Image toBitmap = new Bitmap(toWidth, toHeight);
using (Graphics g = Graphics.FromImage(toBitmap))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.Clear(Color.Transparent);
g.DrawImage(originalImage,
new Rectangle(0, 0, toWidth, toHeight),
new Rectangle(0, 0, originalImage.Width, originalImage.Height),
GraphicsUnit.Pixel);
originalImage.Dispose();
return toBitmap;
}
}
private Color ColorConvertBy16(string colorString)//16进制颜色值
{
int v = int.Parse(colorString, System.Globalization.NumberStyles.HexNumber);
return Color.FromArgb(255, Convert.ToByte((v >> 16) & 255), Convert.ToByte((v >> 8) & 255), Convert.ToByte((v >> 0) & 255));
}
public bool IsReusable
{
get
{
return false;
}
}
private Point getPoint(int i, int w, int h)
{
Random r = new Random(i + DateTime.Now.Millisecond);
int x = r.Next(-3 * w, 3 * w);
int y = r.Next(-3 * h, 3 * h);
return new Point(x, y);
}
private Color getFontColor(int seed)
{
Random r = new Random(seed + DateTime.Now.Millisecond);
int i = r.Next(0, 9);
Color[] color = new Color[]{
Color.Violet,
Color.YellowGreen,
Color.Red,
Color.RoyalBlue,
Color.Purple,
Color.SeaGreen,
Color.SlateGray,
Color.SteelBlue,
Color.Teal,
Color.YellowGreen
};
return color[i];
}
public class QRCodeHelper
{
///
/// 生成二维码,默认边长为250px
///
/// 二维码内容
///
public static Image BuildQRCode(string content)
{
return BuildQRCode(content, 250, Color.White, Color.Black);
}
///
/// 生成二维码,自定义边长
///
/// 二维码内容
/// 二维码边长px
///
public static Image BuildQRCode(string content, int imgSize)
{
return BuildQRCode(content, imgSize, Color.White, Color.Black);
}
///
/// 生成二维码
/// 注:自定义边长以及颜色
///
/// 二维码内容
/// 二维码边长px
/// 二维码底色
/// 二维码前景色
///
public static Image BuildQRCode(string content, int imgSize, Color background, Color foreground)
{
return BuildQRCode_Logo(content, imgSize, background, foreground, null);
}
///
/// 生成二维码并添加Logo
/// 注:默认生成边长为250px的二维码
///
/// 二维码内容
/// logo图片
///
public static Image BuildQRCode_Logo(string content, Bitmap logo)
{
return BuildQRCode_Logo(content, 250, Color.White, Color.Black, logo);
}
///
/// 生成二维码并添加Logo
/// 注:自定义边长
///
/// 二维码内容
/// 二维码边长px
/// logo图片
///
public static Image BuildQRCode_Logo(string content, int imgSize, Bitmap logo)
{
return BuildQRCode_Logo(content, imgSize, Color.White, Color.Black, logo);
}
///
/// 生成二维码并添加Logo
/// 注:自定义边长以及颜色
///
/// 二维码内容
/// 二维码边长px
/// 二维码底色
/// 二维码前景色
/// logo图片
///
public static Image BuildQRCode_Logo(string content, int imgSize, Color? background = null, Color? foreground = null, Bitmap logo = null)
{
QRCodeEncoder encoder = new QRCodeEncoder();
encoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;//编码方式(注意:BYTE能支持中文,ALPHA_NUMERIC扫描出来的都是数字)
encoder.QRCodeScale = 5;//大小(值越大生成的二维码图片像素越高)
encoder.QRCodeVersion = 0;//版本(注意:设置为0主要是防止编码的字符串太长时发生错误)
encoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H;//错误效验、错误更正(有4个等级)
encoder.QRCodeBackgroundColor = background ?? Color.White;
encoder.QRCodeForegroundColor = foreground ?? Color.Black;
System.Drawing.Image image = encoder.Encode(content, System.Text.Encoding.UTF8);
//这里+10 使得二维码留边
int resWidth = imgSize + 10;
Bitmap newBit = new Bitmap(resWidth, resWidth, PixelFormat.Format32bppRgb);
Graphics g = Graphics.FromImage(newBit);
//设置白色背景
g.FillRectangle(new SolidBrush(Color.White), 0, 0, (int)resWidth, (int)resWidth);
g.DrawImage(image, 5, 5, imgSize, imgSize);
if (logo != null)
{
int logoW = 40;
int w = resWidth / 4;
int x = resWidth / 2 - w / 2;
int centerW = resWidth / logoW;
//设置白色背景
g.FillRectangle(new SolidBrush(Color.White), x, x, w, w);
Bitmap copyImage = new Bitmap(logo, w - centerW, w - centerW);
g.DrawImage(copyImage, x + centerW / logoW / 2, x + centerW / 2);
copyImage.Dispose();
}
g.Dispose();
return newBit;
}
}
}
}