GDI繪圖幫助類 GDIHelper
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
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; } } }