GDI绘图帮助类 GDIHelper
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; namespace CommonUtil.GraphicsDrawString { /// <summary> /// GDI绘图帮助类 /// </summary> public class GDIHelper { private float y;//当前y坐标 private readonly float ratio;//整体全局的缩放 private Graphics graphics;//绘制执行对象 /// <summary> /// GDI将要绘制的对象 /// </summary> private List<GDIDataModel> Models { get; set; } private GDIHelper() { } /// <summary> /// GDI绘图帮助类 /// </summary> /// <param name="startX">起点坐标</param> /// <param name="startY"></param> public GDIHelper(float startY, float ratio, Graphics graphics) { y = startY; this.ratio = ratio; this.graphics = graphics; Models = new List<GDIDataModel>(); } /// <summary> /// 添加待绘制对象,并返回计算占距大小 /// </summary> /// <param name="model"></param> /// <returns></returns> public SizeF Add(GDIDataModel m) { Models.Add(m); return GetSizeF(m); } /// <summary> /// 添加待绘制对象,x坐标计算为当前内容的中点,即x=x-内容宽/2 /// </summary> /// <returns></returns> public SizeF Add_StrXCenter(GDIDataModel m) { var size=GetSizeF(m); m.X -= size.Width / 2; Models.Add(m); return size; } /// <summary> /// 仅获取待添加对象的占距,不添加到列表 /// </summary> /// <param name="m"></param> /// <returns></returns> public SizeF GetSizeF(GDIDataModel m) { if (graphics != null && !string.IsNullOrWhiteSpace(m.S)) { if (m.LimitW > 0) { m.Size_Str = graphics.MeasureString(m.S, m.font, (int)m.LimitW); } else { m.Size_Str = graphics.MeasureString(m.S, m.font); } } return m.Size_Str; } /// <summary> /// 绘制,使用计算的y坐标 /// </summary> /// <param name="graphics"></param> public void DrawString_ComputeY() { if (Models == null || !Models.Any() || graphics == null) { return; } //按行排序 Models = Models.OrderBy(x => x.Row).ToList(); foreach (var row in Models.Select(x => x.Row).Distinct().ToList()) { var thisRow = Models.Where(x => x.Row == row).ToList(); foreach (var m in thisRow) { //全局的缩放 m.SetRatio(ratio); //绘制当前 m.DrawString(graphics, y); //计算高 if (!string.IsNullOrWhiteSpace(m.S)) { float h = m.Size_Str.Height; if (m.DistanceH < m.Size_Str.Height) { m.DistanceH = m.Size_Str.Height + m.RowSpace; } } } float thisMaxH = thisRow.Max(x => x.DistanceH);//当前行高最大值 //y轴偏移 y += thisMaxH; } } } /// <summary> /// GDI将要绘制的对象 /// </summary> public class GDIDataModel { #region 属性 /// <summary> /// 行下标 /// </summary> public int Row; /// <summary> /// 内容限定宽。 /// </summary> public float LimitW; /// <summary> /// 占高,文本可以按字体和限宽计算出来。 /// </summary> public float DistanceH; //参数 public Pen pen; //画笔 public Font font;//字体 public Brush brush;//图形 /// <summary> /// 字符内容 /// </summary> public string S; /// <summary> /// 绘制起点坐标 /// </summary> public float X; /// <summary> /// 坐标y通常是计算的 /// </summary> private float Y; /// <summary> /// 矩形宽 /// </summary> public float Width; /// <summary> /// 矩形高 /// </summary> public float Height; //public RectangleF layoutRectangle; /// <summary> /// 文本布局 /// </summary> public StringFormat Format; /// <summary> /// 行间隔,文本的当行高度变为内容高度加行间隔 /// </summary> public float RowSpace = 0; /// <summary> /// 参数类型,绘制类型,即Graphics绘制调用的方法 /// </summary> public EnumDrawType Type; /// <summary> /// 为文本内容时,计算所占大小保存到此,由外部Graphics计算,所以此类中不使用本字段。 /// </summary> public SizeF Size_Str { get; set; } #endregion private GDIDataModel() { } /// <summary> /// 坐标 /// </summary> /// <param name="row">行下标</param> /// <param name="limitW">内容限定宽,字符且有限定宽时,绘制时自动转矩形框</param> /// <param name="s">字符内容</param> /// <param name="font"></param> /// <param name="brush"></param> /// <param name="x">绘制起点坐标</param> /// <param name="y">坐标y通常是计算的</param> /// <param name="rowSpace">行间隔,当行高度变为内容高度加行间隔</param> public GDIDataModel(int row, float limitW, string s, Font font, Brush brush, float x, float y , float rowSpace = 0) { this.Row = row; this.LimitW = limitW; this.S = s; this.font = font; this.brush = brush; this.X = x; this.Y = y; this.RowSpace = rowSpace; Type = EnumDrawType.坐标; } /// <summary> /// 坐标_文本布局 /// </summary> /// <param name="row"></param> /// <param name="w"></param> /// <param name="s"></param> /// <param name="font"></param> /// <param name="brush"></param> /// <param name="x"></param> /// <param name="y"></param> /// <param name="format"></param> /// <param name="rowSpace"></param> public GDIDataModel(int row, float limitW, string s, Font font, Brush brush, float x, float y, StringFormat format , float rowSpace = 0) { this.Row = row; this.LimitW = limitW; this.S = s; this.font = font; this.brush = brush; this.X = x; this.Y = y; this.Format = format; this.RowSpace = rowSpace; Type = EnumDrawType.坐标_文本布局; } /// <summary> /// 点 /// </summary> /// <param name="row"></param> /// <param name="w"></param> /// <param name="s"></param> /// <param name="font"></param> /// <param name="brush"></param> /// <param name="point"></param> /// <param name="rowSpace"></param> public GDIDataModel(int row, float limitW, string s, Font font, Brush brush, PointF point , float rowSpace = 0) { this.Row = row; this.LimitW = limitW; this.S = s; this.font = font; this.brush = brush; this.X = point.X; this.Y = point.Y; this.RowSpace = rowSpace; Type = EnumDrawType.点; } /// <summary> /// 点_文本布局 /// </summary> /// <param name="row"></param> /// <param name="w"></param> /// <param name="s"></param> /// <param name="font"></param> /// <param name="brush"></param> /// <param name="point"></param> /// <param name="format"></param> /// <param name="rowSpace"></param> public GDIDataModel(int row, float limitW, string s, Font font, Brush brush, PointF point, StringFormat format , float rowSpace = 0) { this.Row = row; this.LimitW = limitW; this.S = s; this.font = font; this.brush = brush; this.X = point.X; this.Y = point.Y; this.Format = format; this.RowSpace = rowSpace; Type = EnumDrawType.点_文本布局; } /// <summary> /// 矩形范围 /// </summary> /// <param name="row"></param> /// <param name="w"></param> /// <param name="s"></param> /// <param name="font"></param> /// <param name="brush"></param> /// <param name="layoutRectangle"></param> /// <param name="rowSpace"></param> public GDIDataModel(int row, float limitW, string s, Font font, Brush brush, RectangleF layoutRectangle , float rowSpace = 0) { this.Row = row; this.LimitW = limitW; this.S = s; this.font = font; this.brush = brush; this.X = layoutRectangle.X; this.Y = layoutRectangle.Y; this.Width = layoutRectangle.Width; this.Height = layoutRectangle.Height; this.RowSpace = rowSpace; Type = EnumDrawType.矩形范围; } /// <summary> /// 矩形范围_文本布局 /// </summary> /// <param name="row"></param> /// <param name="w"></param> /// <param name="s"></param> /// <param name="font"></param> /// <param name="brush"></param> /// <param name="layoutRectangle"></param> /// <param name="format"></param> /// <param name="rowSpace"></param> public GDIDataModel(int row, float limitW, string s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format , float rowSpace = 0) { this.Row = row; this.LimitW = limitW ; this.S = s; this.font = font; this.brush = brush; this.X = layoutRectangle.X; this.Y = layoutRectangle.Y; this.Width = layoutRectangle.Width; this.Height = layoutRectangle.Height; this.Format = format; this.RowSpace = rowSpace; Type = EnumDrawType.矩形范围_文本布局; } /// <summary> /// 画笔_两点,必须是小坐标到大坐标 /// </summary> /// <param name="pen"></param> /// <param name="x1"></param> /// <param name="y1"></param> /// <param name="x2"></param> /// <param name="y2"></param> public GDIDataModel(int row, Pen pen, float x1, float y1, float x2, float y2 , float rowSpace = 0) { this.Row = row; this.pen = pen; this.X = x1; this.Width = Math.Abs(x2 - x1); this.Y = y1; this.Height = Math.Abs(y2 - y1); this.DistanceH = this.Height; Type = EnumDrawType.画笔_两点; } /// <summary> /// 空白占高 /// </summary> /// <param name="row"></param> /// <param name="height"></param> public GDIDataModel(int row, float height) { this.Row = row; this.DistanceH = height; this.Type = EnumDrawType.空白占高; } /// <summary> /// 设置缩放 /// </summary> /// <param name="ratio"></param> public void SetRatio(float ratio) { //修改所有位置参数 X *= ratio; Width *= ratio; Y *= ratio; Height *= ratio; } /// <summary> /// 按位置属性绘制 /// </summary> /// <param name="graphics"></param> /// <param name="newY">计算的y坐标,覆盖属性内的y坐标</param> public void DrawString(Graphics graphics, float? newY = null) { if (newY.HasValue) { this.Y = newY.Value; } //限定宽的,改为矩形 if (LimitW > 0 && !string.IsNullOrWhiteSpace(S)) { Width = LimitW; Height = this.Size_Str.Height; if (this.Format == null) { this.Type = EnumDrawType.矩形范围; } else { this.Type = EnumDrawType.矩形范围_文本布局; } } //绘制当前 switch (this.Type) { case EnumDrawType.坐标: graphics.DrawString(this.S, this.font, this.brush, this.X, this.Y); break; case EnumDrawType.坐标_文本布局: graphics.DrawString(this.S, this.font, this.brush, this.X, this.Y, this.Format); break; case EnumDrawType.点: graphics.DrawString(this.S, this.font, this.brush, new PointF(this.X, this.Y)); break; case EnumDrawType.点_文本布局: graphics.DrawString(this.S, this.font, this.brush, new PointF(this.X, this.Y), this.Format); break; case EnumDrawType.矩形范围: graphics.DrawString(this.S, this.font, this.brush, new RectangleF(this.X, this.Y, this.Width, this.Height)); break; case EnumDrawType.矩形范围_文本布局: graphics.DrawString(this.S, this.font, this.brush, new RectangleF(this.X, this.Y, this.Width, this.Height), this.Format); break; case EnumDrawType.画笔_两点: graphics.DrawLine(this.pen, this.X, this.Y, this.X + this.Width, this.Y + this.Height); break; case EnumDrawType.空白占高: break; default: break; } } } public enum EnumDrawType { 点 = 0, 点_文本布局 = 1, 坐标 = 2, 坐标_文本布局 = 3, 矩形范围 = 4, 矩形范围_文本布局 = 5, 画笔_两点 = 6, 空白占高 = 7, } }
示例 GDIHelperDemo
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using CommonUtil.QRCodeManage; using System.Collections.Generic; using System.Drawing; using System.Drawing.Text; using System.IO; using System.Linq; namespace CommonUtil.GraphicsDrawString { public class GDIHelperDemo { /// <summary> /// 生成报告图片执行 /// </summary> /// <param name="data"></param> private void CreateReportImg_Contagion_Execute(CreateReportImgDataMode_Contagion cn, CreateReportImgDataMode_Contagion en, int languageReport = 1, bool isCover = false) { if (languageReport == 1) { //不覆盖 if (!isCover) { int existsCount = 0; foreach (var item in cn.FilePathList) { if (File.Exists(item)) { existsCount++; } } if (existsCount == cn.FilePathList.Count) { return; } } //目录判断 foreach (var dire in cn.DirectoryList) { if (!Directory.Exists(dire)) { Directory.CreateDirectory(dire); } } //只要到生成这步就是覆盖 foreach (var filePath in cn.FilePathList) { if (File.Exists(filePath)) { File.Delete(filePath); } } //Html解码 //cn.TestDescription = cn.TestDescription.Select(x => System.Web.HttpUtility.HtmlDecode(x)).ToList(); cn.Explain = System.Web.HttpUtility.HtmlDecode(cn.Explain); string baseDirectory = System.AppDomain.CurrentDomain.BaseDirectory; string path = baseDirectory + "fonts/Alibaba-PuHuiTi-Regular.ttf"; //读取字体文件 PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(path); FontFamily fontFamily = pfc.Families[0]; //绘画参数 float ratio = cn.Ratio;//全局缩放 float startX = 96;//初始位置x float startY = 192;//初始位置y int nowRow = 0;//当前行 float rowSpace = 0;//行间距 float rowOneH = 0;//大体文本的行高,用来计算留白高度 Font FontTitle = new Font(fontFamily, 16 * ratio, FontStyle.Bold, GraphicsUnit.Point);//宠物信息列名 Font FontContent = new Font(fontFamily, 16 * ratio, FontStyle.Bold, GraphicsUnit.Point);//宠物信息内容 Image back = Image.FromFile(cn.BackGroundPath, true); try { using (Bitmap bitmap = new Bitmap(back)) { using (Graphics graphics = Graphics.FromImage(bitmap)) { GDIHelper g = new GDIHelper(startY, ratio, graphics); float W = bitmap.Width; float H = bitmap.Height; //画笔 Pen pen = new Pen(Brushes.DimGray, 1); pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid; //实线 //顶部横线 SizeF size = g.Add(new GDIDataModel(nowRow, pen, startX, 0, W - startX, 0)); nowRow++; #region 基本信息,每行格式为 列名:值 第二列:值 //基本信息,每行格式为 列名:值 第二列:值 float cellX = W / 2;//列2的x坐标 size = g.Add(new GDIDataModel(nowRow, 0, "检测项目:", FontTitle, Brushes.Black, startX, 0, rowSpace)); rowOneH = size.Height; size = g.Add(new GDIDataModel(nowRow, 0, cn.ProductName, FontTitle, Brushes.Black, startX + size.Width, 0, rowSpace)); //基本信息_第二列 size = g.Add(new GDIDataModel(nowRow, 0, "收样日期:", FontTitle, Brushes.Black, cellX, 0, rowSpace)); size = g.Add(new GDIDataModel(nowRow, 0, cn.SampleDeliveryTime, FontTitle, Brushes.Black, cellX + size.Width, 0, rowSpace)); nowRow++;//换行 size = g.Add(new GDIDataModel(nowRow, 0, "报告编号:", FontTitle, Brushes.Black, startX, 0, rowSpace)); size = g.Add(new GDIDataModel(nowRow, 0, cn.ReportNumber, FontTitle, Brushes.Black, startX + size.Width, 0, rowSpace)); size = g.Add(new GDIDataModel(nowRow, 0, "报告日期:", FontTitle, Brushes.Black, cellX, 0, rowSpace)); size = g.Add(new GDIDataModel(nowRow, 0, cn.ReportTime, FontTitle, Brushes.Black, cellX + size.Width, 0, rowSpace)); nowRow++;//换行 //... #endregion //留白 size = g.Add(new GDIDataModel(nowRow, rowOneH * 2)); nowRow++; #region 结果内容,格式 列1 列2 列3 float resultCellW = (W - startX * 2) / 3;//列内容每列占总内容的三分之一 float centerX = W / 2; //列2居中 float resultCellX3 = startX + resultCellW * 2;//列3的x在三分之二 size = g.Add(new GDIDataModel(nowRow, 0, "报告内容", FontTitle, Brushes.Black, startX, 0, rowSpace)); nowRow++; //横线 size = g.Add(new GDIDataModel(nowRow, pen, startX, 0, W - startX, 0)); nowRow++; //标题 size = g.Add(new GDIDataModel(nowRow, resultCellW, "项目名称", FontTitle, Brushes.Black, startX, 0, rowSpace)); //列2居中用Add_StrXCenter size = g.Add_StrXCenter(new GDIDataModel(nowRow, resultCellW, "检测结果(Ct值)", FontTitle, Brushes.Black, centerX, 0, rowSpace)); //列3的x在三分之二 size = g.Add(new GDIDataModel(nowRow, resultCellW, "结果判定", FontTitle, Brushes.Black, resultCellX3, 0, rowSpace)); nowRow++; size = g.Add(new GDIDataModel(nowRow, pen, startX, 0, W - startX, 0)); nowRow++; foreach (var item in cn.TestResult) { size = g.Add(new GDIDataModel(nowRow, resultCellW, item[0], FontTitle, Brushes.Black, startX, 0, rowSpace)); size = g.Add_StrXCenter(new GDIDataModel(nowRow, resultCellW, item[1], FontTitle, Brushes.Black, centerX, 0, rowSpace)); size = g.Add(new GDIDataModel(nowRow, resultCellW, item[2], FontTitle, Brushes.Black, resultCellX3, 0, rowSpace)); nowRow++; } #endregion //留白 size = g.Add(new GDIDataModel(nowRow, rowOneH * 2)); nowRow++; #region 结果说明,格式 (缩进2)列1标题:列2详情 size = g.Add(new GDIDataModel(nowRow, 0, "结果说明", FontTitle, Brushes.Black, startX, 0, rowSpace)); nowRow++; float resultDeX = startX + (size.Width / 2);//结果说明标题列缩进两个字符 int resultOldRow = nowRow;//结果说明第一行 float resultMaxW = 0;//结果说明标题列最大宽度 //结果说明标题列 foreach (var item in cn.ResultDescriptionList) { size = g.Add(new GDIDataModel(nowRow, 0, item[0] + ":", FontTitle, Brushes.Black, resultDeX, 0, rowSpace)); if (resultMaxW < size.Width) { resultMaxW = size.Width; } nowRow++; } //结果说明内容列 nowRow = resultOldRow;//回到结果说明第一行 float resultDeX2 = resultDeX + resultMaxW;//结果说明内容列的x float resultDeW = W - startX - resultDeX2;//结果说明内容列的限定宽 foreach (var item in cn.ResultDescriptionList) { size = g.Add(new GDIDataModel(nowRow, resultDeW, item[1], FontTitle, Brushes.Black, resultDeX2, 0, rowSpace)); nowRow++; } #endregion //绘制文本 g.DrawString_ComputeY(); #region 检测与判读说明等等,显示在底部 float remarkY = (1000 * ratio); int remarkW = (int)(W - startX * 2); size = graphics.MeasureString("检测与判读说明", FontTitle); graphics.DrawString("检测与判读说明", FontTitle, Brushes.Black, startX, remarkY); remarkY += size.Height + rowSpace; if (cn.TestDescription != null && cn.TestDescription.Any()) { foreach (var item in cn.TestDescription) { if (item != null && item.Length > 1) { //说明标题 size = graphics.MeasureString(item[0], FontTitle); graphics.DrawString(item[0], FontTitle, Brushes.Black, startX, remarkY); var thisX = startX + size.Width; //说明内容 size = graphics.MeasureString(item[1], FontTitle, remarkW); graphics.DrawString(item[1], FontTitle, Brushes.Black, new RectangleF(thisX, remarkY, remarkW, size.Height)); remarkY += size.Height + rowSpace; } } } #endregion //准备贴上二维码 string url = ConfigurationHelper.AppSetting("qrcodeurl") + "?" + cn.ReportNumber; var qrcodeImage = QRCodeHelper.CreateQRCode(url); Rectangle fromR = new Rectangle(0, 0, qrcodeImage.Width, qrcodeImage.Height); //固定二维码的位置 int qrcodeWidth = (int)(150 * ratio), qrcodeHeight = (int)(150 * ratio); Rectangle toR = new Rectangle((int)(155 * ratio), (int)(1210 * ratio), qrcodeWidth, qrcodeHeight); //画图 graphics.DrawImage(qrcodeImage, toR, fromR, GraphicsUnit.Pixel); qrcodeImage.Dispose(); } //保存文件 bitmap.Save(cn.FilePath); } } finally { back.Dispose(); fontFamily.Dispose(); pfc.Dispose(); } } else if (languageReport == 1) { } } } public class CreateReportImgDataMode_Contagion { /// <summary> /// 背景图 /// </summary> public string BackGroundPath { get; set; } /// <summary> /// 显示比例 /// </summary> public float Ratio { get; set; } /// <summary> /// 报告保存路径 /// </summary> public string FilePath { get; set; } /// <summary> /// 检测项目 /// </summary> public string ProductName { get; set; } /// <summary> /// 是否致病描述 /// </summary> public string IsPathogenic { get; set; } /// <summary> /// 治疗方式描述 /// </summary> public string TreatmentMethod { get; set; } /// <summary> /// 项目说明 /// </summary> public string Explain { get; set; } /// <summary> /// 收样日期 /// </summary> public string SampleDeliveryTime { get; set; } /// <summary> /// 报告日期 /// </summary> public string ReportTime { get; set; } /// <summary> /// 报告编号 /// </summary> public string ReportNumber { get; set; } /// <summary> /// 样本类型 /// </summary> public int SampleType { get; set; } /// <summary> /// 样本类型名称 /// </summary> public string SampleTypeName { get; set; } /// <summary> /// 样本编号 /// </summary> public string SampleNumber { get; set; } /// <summary> /// 检测结果,0项目名,1结果ct值,2结果判定 /// </summary> public List<string[]> TestResult { get; set; } /// <summary> /// 宠物,0名称,1编号 /// </summary> public List<string[]> Pets { get; set; } /// <summary> /// 所有结果说明,0结果名称,1描述 /// </summary> public List<string[]> ResultDescriptionList { get; set; } /// <summary> /// 检测与判读说明 /// </summary> public List<string[]> TestDescription { get; set; } /// <summary> /// 传染病套餐项目,要将一张报告放到多个检测下的文件路径 /// </summary> public List<string> FilePathList { get; set; } /// <summary> /// 报告路径目录 /// </summary> public List<string> DirectoryList { get; set; } } }