在WEB系統中,打印的確是比較煩人的問題,如果我們能制作一個屬於自己的自定義的打印插件,那麼我們在後續自定義打印的時候能隨心所欲的控制打印,這樣的效果對於程序員來說是非常開心的一件事件,本文將自己開發編寫的C# 制作的HTML打印插件分享出來,讓有同樣需求的朋友提供一個參考;此插件是基於Microsoft .NET Framework 2.0 開發的,缺點是每台客戶端在安裝插件時,必須要安裝Microsoft .NET Framework 2.0 ;本插件能實現 頁眉、頁腳、表頭、標題、表尾的分頁打印;支持紙張類型、自動補充空行等功能;由於技術有限,肯定有很多不足的地方,請批評指正!
由於本打印插件是基於我們開發平台的報表基礎來開發設計的,所以打印控件的原理:通過JS將頁面表格數據生成固定格式的XML字符串(圖片通過64base圖片格式)傳送給打印插件,有打印插件自主繪圖生成打印頁面。E_Print插件可以在WEB或WinForm中使用:
打印插件完整源碼:E_Print.rar (包含插件源碼、打包程序、winform調試DEMO)
下面貼出源碼:(在源碼中有詳細的注釋說明)
1、PrintControl 打印插件類
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Drawing.Printing; using System.Xml; using System.Security; using System.Drawing.DrawingD; using System.Drawing.Text; using System.Text.RegularExpressions; namespace E_Print { /// <summary> /// 打印控件 /// 實現IObjectSafety接口 /// 網頁上所有所使用到的GUID 通過Guid工具生成的唯一編碼 /// DEDD-BA--A-FFBEEC 編碼以後不允許修改 /// </summary> [Guid("DEDD-BA--A-FFBEEC"), ProgId("EReportPrint"), ComVisible(true)] public partial class PrintControl : UserControl, IObjectSafety { #region 私有變量 #region 通用參數 /// <summary> /// 縮放比例 /// </summary> private float Zoom = ; /// <summary> /// 網頁高度 像素 px /// </summary> private float HtmlHeight = ; /// <summary> /// 網頁寬度 像素 px /// </summary> private float HtmlWidth = ; /// <summary> /// 報表區域矩形 /// </summary> private RectangleF TableRect = new RectangleF(); /// <summary> /// 報表繪制實例 /// </summary> private ReportDraw RptDraw = new ReportDraw(); #endregion #region 頁邊距 /// <summary> /// 左邊距 /// 毫米 mm(一位小數) /// </summary> private float _marginLeft = .f; /// <summary> /// 右邊距 /// </summary> private float _marginRight = .f; /// <summary> /// 上邊距 /// </summary> private float _marginTop = .f; /// <summary> /// 下邊距 /// </summary> private float _marginBottom = .f; #endregion #region 版型方向 /// <summary> /// 版型方向 Landscape: true 橫向;false 縱向 /// </summary> private bool _landscape = false; #endregion #region 紙型大小 /// <summary> /// 紙張類型 /// </summary> private string _paperName = "A"; /// <summary> /// 紙張寬度 /// </summary> private int _paperWidth = ; // 毫米 /// <summary> /// 紙張高度 /// </summary> private int _paperHeight = ; // 毫米 #endregion #region 打印參數 /// <summary> /// 自適應紙張大小方法 /// null: 無 /// row: 橫向 /// col: 縱向 /// </summary> private string _zoomType = "null"; /// <summary> /// 是否每頁打印標題 /// </summary> private bool _isTblTitleAllPage = false; /// <summary> /// 是否每頁打印表頭 /// </summary> private bool _isTblHeadAllPage = false; /// <summary> /// 是否每頁打印表尾 /// </summary> private bool _isTblFootAllPage = false; /// <summary> /// 最後一頁自動補行 /// </summary> private bool _isAutoFillRow = false; /// <summary> /// 字符溢出是否換行縮小處理方式 /// </summary> private bool _isOverFlow = false; /// <summary> /// 打印數據 /// </summary> private string _dataXml = ""; #endregion #region 頁眉參數 /// <summary> /// 頁眉--繪制頁眉 /// </summary> private bool _headDraw = false; /// <summary> /// 頁眉--高度 毫米 /// 默認 剛好 /// </summary> private float _headHeight = .f; /// <summary> /// 頁眉--左側文字 /// </summary> private string _headLeft = ""; /// <summary> /// 頁眉--中間文字 /// </summary> private string _headCenter = ""; /// <summary> /// 頁眉--右側文字 /// </summary> private string _headRight = ""; /// <summary> /// 頁眉--字體名稱 /// </summary> private string _headFontName = "宋體"; /// <summary> /// 頁眉--字體大小 /// </summary> private string _headFontSize = "pt"; /// <summary> /// 頁眉--字體顏色 /// </summary> private string _headFontColor = "Black"; /// <summary> /// 頁眉--字體--粗體 /// </summary> private bool _headFontBold = false; /// <summary> /// 頁眉--字體--斜體 /// </summary> private bool _headFontItalic = false; /// <summary> /// 頁眉--字體--刪除線 /// </summary> private bool _headFontStrikeout = false; /// <summary> /// 頁眉--字體--下劃線 /// </summary> private bool _headFontUnderline = false; /// <summary> /// 頁眉--繪制分隔線 /// </summary> private bool _headLineDraw = false; /// <summary> /// 頁眉--分隔線寬度 /// </summary> private float _headLineWidth = .f; /// <summary> /// 頁眉--分隔線線型 /// </summary> private string _headLineDash = "solid"; /// <summary> /// 頁眉--分隔線顏色 /// </summary> private string _headLineColor = "Black"; #endregion #region 頁腳參數 /// <summary> /// 頁腳--繪制頁腳 /// </summary> private bool _footDraw = false; /// <summary> /// 頁腳--高度 毫米 /// </summary> private float _footHeight = .f; /// <summary> /// 頁腳--左側文字 /// </summary> private string _footLeft = ""; /// <summary> /// 頁腳--中間文字 /// </summary> private string _footCenter = ""; /// <summary> /// 頁腳--右側文字 /// </summary> private string _footRight = ""; /// <summary> /// 頁腳--字體名稱 /// </summary> private string _footFontName = "宋體"; /// <summary> /// 頁腳--字體大小 /// </summary> private string _footFontSize = "pt"; /// <summary> /// 頁腳--字體顏色 /// </summary> private string _footFontColor = "Black"; /// <summary> /// 頁腳--字體--粗體 /// </summary> private bool _footFontBold = false; /// <summary> /// 頁腳--字體--斜體 /// </summary> private bool _footFontItalic = false; /// <summary> /// 頁腳--字體--刪除線 /// </summary> private bool _footFontStrikeout = false; /// <summary> /// 頁腳--字體--下劃線 /// </summary> private bool _footFontUnderline = false; /// <summary> /// 頁腳--繪制分隔線 /// </summary> private bool _footLineDraw = false; /// <summary> /// 頁腳--分隔線寬度 /// </summary> private float _footLineWidth = .f; /// <summary> /// 頁腳--分隔線線型 /// </summary> private string _footLineDash = "solid"; /// <summary> /// 頁腳--分隔線顏色 /// </summary> private string _footLineColor = "Black"; #endregion #endregion #region 構造方法 /// <summary> /// 打印控件構造函數 /// </summary> public PrintControl() { InitializeComponent(); Init_PageSetting(); } #endregion #region 接口實現 private const string _IID_IDispatch = "{---C-}"; private const string _IID_IDispatchEx = "{aef-c-d--acdcaa}"; private const string _IID_IPersistStorage = "{A---C-}"; private const string _IID_IPersistStream = "{---C-}"; private const string _IID_IPersistPropertyBag = "{DF-CB-CE--AABB}"; private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = x; private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = x; private const int S_OK = ; private const int E_FAIL = unchecked((int)x); private const int E_NOINTERFACE = unchecked((int)x); private bool _fSafeForScripting = true; private bool _fSafeForInitializing = true; public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions) { int Rslt = E_FAIL; string strGUID = riid.ToString("B"); pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA; switch (strGUID) { case _IID_IDispatch: case _IID_IDispatchEx: Rslt = S_OK; pdwEnabledOptions = ; if (_fSafeForScripting == true) pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER; break; case _IID_IPersistStorage: case _IID_IPersistStream: case _IID_IPersistPropertyBag: Rslt = S_OK; pdwEnabledOptions = ; if (_fSafeForInitializing == true) pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA; break; default: Rslt = E_NOINTERFACE; break; } return Rslt; } public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions) { int Rslt = E_FAIL; string strGUID = riid.ToString("B"); switch (strGUID) { case _IID_IDispatch: case _IID_IDispatchEx: if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) && (_fSafeForScripting == true)) Rslt = S_OK; break; case _IID_IPersistStorage: case _IID_IPersistStream: case _IID_IPersistPropertyBag: if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) && (_fSafeForInitializing == true)) Rslt = S_OK; break; default: Rslt = E_NOINTERFACE; break; } return Rslt; } #endregion #region 屬性方法 #region 頁邊距 /// <summary> /// 獲取--設置--左邊距 /// 計量單位 毫米(mm) /// </summary> public float MARGINLEFT { get { return _marginLeft; } set { _marginLeft = value; } } /// <summary> /// 獲取--設置--右邊距 /// 計量單位 毫米(mm) /// </summary> public float MARGINRIGHT { get { return _marginRight; } set { _marginRight = value; } } /// <summary> /// 獲取--設置--上邊距 /// 計量單位 毫米(mm) /// </summary> public float MARGINTOP { get { return _marginTop; } set { _marginTop = value; } } /// <summary> /// 獲取--設置--下邊距 /// 計量單位 毫米(mm) /// </summary> public float MARGINBOTTOM { get { return _marginBottom; } set { _marginBottom = value; } } #endregion #region 版型方向 /// <summary> /// 獲取--設置--版型方向 /// Landscape: true 橫向; false 縱向 /// </summary> public bool LANDSCAPE { get { return _landscape; } set { _landscape = value; } } #endregion #region 紙張屬性 /// <summary> /// 獲取--設置--紙張類型 /// </summary> public string PAPERNAME { get { return _paperName; } set { _paperName = value; } } /// <summary> /// 獲取--設置--紙張高度 /// 計量單位 毫米(mm) /// </summary> public int PAPERHEIGHT { get { return _paperHeight; } set { _paperHeight = value; } } /// <summary> /// 獲取--設置--紙張寬度 /// 計量單位 毫米(mm) /// </summary> public int PAPERWIDTH { get { return _paperWidth; } set { _paperWidth = value; } } #endregion #region 頁眉參數 /// <summary> /// 獲取--設置--頁眉是否繪制 /// </summary> public bool HEADDRAW { get { return _headDraw; } set { _headDraw = value; } } /// <summary> /// 獲取--設置--頁眉高度 /// 單位:毫米整數類型 /// </summary> public float HEADHEIGHT { get { return _headHeight; } set { _headHeight = value; } } /// <summary> /// 獲取--設置--頁眉左側文字 /// </summary> public string HEADLEFT { get { return _headLeft; } set { _headLeft = value; } } /// <summary> /// 獲取--設置--頁眉中間文字 /// </summary> public string HEADCENTER { get { return _headCenter; } set { _headCenter = value; } } /// <summary> /// 獲取--設置--頁眉右側文字 /// </summary> public string HEADRIGHT { get { return _headRight; } set { _headRight = value; } } /// <summary> /// 獲取--設置--頁眉字體名稱 /// </summary> public string HEADFONTNAME { get { return _headFontName; } set { _headFontName = value; } } /// <summary> /// 獲取--設置--頁眉字體大小 /// </summary> public string HEADFONTSIZE { get { return _headFontSize; } set { _headFontSize = value; } } /// <summary> /// 獲取--設置--頁眉字體顏色 /// </summary> public string HEADFONTCOLOR { get { return _headFontColor; } set { _headFontColor = value; } } /// <summary> /// 獲取--設置--頁眉字體--粗體 /// </summary> public bool HEADFONTBOLD { get { return _headFontBold; } set { _headFontBold = value; } } /// <summary> /// 獲取--設置--頁眉字體--斜體 /// </summary> public bool HEADFONTITALIC { get { return _headFontItalic; } set { _headFontItalic = value; } } /// <summary> /// 獲取--設置--頁眉字體--刪除線 /// </summary> public bool HEADFONTSTRIKEOUT { get { return _headFontStrikeout; } set { _headFontStrikeout = value; } } /// <summary> /// 獲取--設置--頁眉字體--下劃線 /// </summary> public bool HEADFONTUNDERLINE { get { return _headFontUnderline; } set { _headFontUnderline = value; } } /// <summary> /// 獲取--設置--是否繪制分割線 /// </summary> public bool HEADLINEDRAW { get { return _headLineDraw; } set { _headLineDraw = value; } } /// <summary> /// 獲取--設置--頁眉分隔線寬度 /// </summary> public float HEADLINEWIDTH { get { return _headLineWidth; } set { _headLineWidth = value; } } /// <summary> /// 獲取--設置--頁眉分隔線線型 /// </summary> public string HEADLINEDASH { get { return _headLineDash; } set { _headLineDash = value; } } /// <summary> /// 獲取--設置--頁眉分隔線顏色 /// </summary> public string HEADLINECOLOR { get { return _headLineColor; } set { _headLineColor = value; } } #endregion #region 頁腳參數 /// <summary> /// 獲取--設置--頁腳是否繪制 /// </summary> public bool FOOTDRAW { get { return _footDraw; } set { _footDraw = value; } } /// <summary> /// 獲取--設置--頁腳高度 /// 單位:毫米整數類型 /// </summary> public float FOOTHEIGHT { get { return _footHeight; } set { _footHeight = value; } } /// <summary> /// 獲取--設置--頁腳左側文字 /// </summary> public string FOOTLEFT { get { return _footLeft; } set { _footLeft = value; } } /// <summary> /// 獲取--設置--頁腳中間文字 /// </summary> public string FOOTCENTER { get { return _footCenter; } set { _footCenter = value; } } /// <summary> /// 獲取--設置--頁腳右側文字 /// </summary> public string FOOTRIGHT { get { return _footRight; } set { _footRight = value; } } /// <summary> /// 獲取--設置--頁腳字體名稱 /// </summary> public string FOOTFONTNAME { get { return _footFontName; } set { _footFontName = value; } } /// <summary> /// 獲取--設置--頁腳字體大小 /// </summary> public string FOOTFONTSIZE { get { return _footFontSize; } set { _footFontSize = value; } } /// <summary> /// 獲取--設置--頁腳字體顏色 /// </summary> public string FOOTFONTCOLOR { get { return _footFontColor; } set { _footFontColor = value; } } /// <summary> /// 獲取--設置--頁腳字體--粗體 /// </summary> public bool FOOTFONTBOLD { get { return _footFontBold; } set { _footFontBold = value; } } /// <summary> /// 獲取--設置--頁腳字體--斜體 /// </summary> public bool FOOTFONTITALIC { get { return _footFontItalic; } set { _footFontItalic = value; } } /// <summary> /// 獲取--設置--頁腳字體--刪除線 /// </summary> public bool FOOTFONTSTRIKEOUT { get { return _footFontStrikeout; } set { _footFontStrikeout = value; } } /// <summary> /// 獲取--設置--頁腳字體--下劃線 /// </summary> public bool FOOTFONTUNDERLINE { get { return _footFontUnderline; } set { _footFontUnderline = value; } } /// <summary> /// 獲取--設置--是否繪制分割線 /// </summary> public bool FOOTLINEDRAW { get { return _footLineDraw; } set { _footLineDraw = value; } } /// <summary> /// 獲取--設置--頁腳分隔線寬度 /// </summary> public float FOOTLINEWIDTH { get { return _footLineWidth; } set { _footLineWidth = value; } } /// <summary> /// 獲取--設置--頁腳分隔線線型 /// </summary> public string FOOTLINEDASH { get { return _footLineDash; } set { _footLineDash = value; } } /// <summary> /// 獲取--設置--頁腳分隔線顏色 /// </summary> public string FOOTLINECOLOR { get { return _footLineColor; } set { _footLineColor = value; } } #endregion #region 打印參數 /// <summary> /// 獲取--設置--打印數據 /// 前台傳入的XML格式的打印數據 /// </summary> public string DATAXML { get { return _dataXml; } set { _dataXml = value; } } /// <summary> /// 獲取--設置--是否每頁打印標題 /// </summary> public bool ISTBLTITLEALLPAGE { get { return _isTblTitleAllPage; } set { _isTblTitleAllPage = value; } } /// <summary> /// 獲取--設置--是否每頁打印表頭 /// </summary> public bool ISTBLHEADALLPAGE { get { return _isTblHeadAllPage; } set { _isTblHeadAllPage = value; } } /// <summary> /// 獲取--設置--是否每頁打印表尾 /// </summary> public bool ISTBLFOOTALLPAGE { get { return _isTblFootAllPage; } set { _isTblFootAllPage = value; } } /// <summary> /// 獲取--設置--末頁自動補行 /// </summary> public bool ISAUTOFILLROW { get { return _isAutoFillRow; } set { _isAutoFillRow = value; } } /// <summary> /// 獲取--設置--縮放方向 /// 參數:以下三種;默認null /// null: 無 /// row: 橫向 /// col: 縱向 /// </summary> public string ZOOMTYPE { get { return _zoomType; } set { _zoomType = value; } } /// <summary> /// 獲取--設置--字符溢出是否縮小換行處理方式 /// </summary> public bool ISOVERFLOW { get { return _isOverFlow; } set { _isOverFlow = value; } } #endregion #region 加載參數 /// <summary> /// 加載打印參數 /// </summary> public void INITPRINTPARAM() { Init_PageSetting(); } #endregion #endregion #region 加載事件 /// <summary> /// 初始化--頁面設置參數 /// </summary> private void Init_PageSetting() { this.E_PrintDocument.DefaultPageSettings.Margins.Left = (int)Math.Round(MARGINLEFT * ); // 左邊距 this.E_PrintDocument.DefaultPageSettings.Margins.Right = (int)Math.Round(MARGINRIGHT * ); // 右邊距 this.E_PrintDocument.DefaultPageSettings.Margins.Top = (int)Math.Round(MARGINTOP * ); // 上邊距 this.E_PrintDocument.DefaultPageSettings.Margins.Bottom = (int)Math.Round(MARGINBOTTOM * ); // 下邊距 this.E_PrintDocument.PrinterSettings.Copies = ; // 打印份數 this.E_PrintDocument.DefaultPageSettings.Landscape = this.LANDSCAPE; // 版型方向 PaperSize size = GetPaperSize(PAPERNAME); // 紙張類型 if (size != null) this.E_PrintDocument.DefaultPageSettings.PaperSize = size; else this.E_PrintDocument.DefaultPageSettings.PaperSize = new PaperSize(this.PAPERNAME, (int)Math.Round(this.PAPERWIDTH / . * ), (int)Math.Round(this.PAPERHEIGHT / . * )); } /// <summary> /// 獲取--紙張類型 /// </summary> /// <param name="paperName">紙張類型名稱</param> /// <returns></returns> private PaperSize GetPaperSize(string paperName) { PaperSize paper = null; foreach (PaperSize ps in this.E_PrintDocument.PrinterSettings.PaperSizes) { if (ps.PaperName.ToLower() == paperName.ToLower()) // 檢查打印機是否有指定的紙張類型 { paper = ps; break; } } return paper; } #endregion #region 打印事件 /// <summary> /// 直接打印 /// 此處加入了再次調用打印設置界面,因為用戶可能需要選擇那種打印機 /// </summary> /// <returns></returns> public string PRINT() { // 直接打印時,直接調用printDocument的Print()方法 // 因為用戶可能在打印之前還要再更改打印設置所以需再次顯示打印設置對話框 if (this.E_PrintDialog.ShowDialog() == DialogResult.OK) { try { this.Init_Printer(); this.E_PrintDocument.Print(); } catch (Exception ex) { this.E_PrintDocument.PrintController.OnEndPrint(this.E_PrintDocument, new PrintEventArgs()); return ex.Message.ToString(); } } return ""; } /// <summary> /// 打印預覽 /// 將打印的數據進行預覽 /// </summary> public string PREVIEW() { try { this.Init_Printer(); this.E_PrintPreviewDialog.ShowDialog(); } catch (Exception ex) { return ex.Message.ToString(); } return ""; } /// <summary> /// 頁面設置 /// 設置打印的頁面的紙張大小、紙型、頁面邊距 /// </summary> public void PAGESTE() { // 頁面設置對話框中使用的是公制長度計量單位 (厘米) // 在.net中采用的是英制的計量單位 (英寸) // 英寸約等於.厘米,厘米=毫米 // 所以在下面中需要加入轉換信息 將對話框中設置的頁邊距進行轉換保存 // 設置傳入的紙張信息 if (this.E_PageSetupDialog.ShowDialog() == DialogResult.OK) // 彈出頁面設置對話框 { if (System.Globalization.RegionInfo.CurrentRegion.IsMetric) // 轉換頁邊距計量單位 this.E_PageSetupDialog.PageSettings.Margins = PrinterUnitConvert.Convert(this.E_PageSetupDialog.PageSettings.Margins, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); this.E_PrintDocument.DefaultPageSettings = this.E_PageSetupDialog.PageSettings; // 更新頁面設置參數值 // 更新參數 this.LANDSCAPE = this.E_PrintDocument.DefaultPageSettings.Landscape; // 版型方向 this.PAPERNAME = this.E_PrintDocument.DefaultPageSettings.PaperSize.PaperName; // 紙張類型 PaperSize tmPSize = this.E_PrintDocument.DefaultPageSettings.PaperSize; // 紙張尺寸 this.PAPERWIDTH = (int)Math.Round(tmPSize.Width * . / ); // 紙張寬度 this.PAPERHEIGHT = (int)Math.Round(tmPSize.Height * . / ); // 紙張高度 this.MARGINLEFT = (float)Math.Round(this.E_PrintDocument.DefaultPageSettings.Margins.Left / f, ); // 左邊距 this.MARGINRIGHT = (float)Math.Round(this.E_PrintDocument.DefaultPageSettings.Margins.Right / f, ); // 右邊距 this.MARGINTOP = (float)Math.Round(this.E_PrintDocument.DefaultPageSettings.Margins.Top / f, ); // 上邊距 this.MARGINBOTTOM = (float)Math.Round(this.E_PrintDocument.DefaultPageSettings.Margins.Bottom / f, ); // 下邊距 } } /// <summary> /// 打印設置 /// 設置打印機的信息(選擇打印機、設置打印份數等信息) /// </summary> public void PRINTSET() { this.E_PrintDialog.ShowDialog(); } #endregion #region 繪制對象 /// <summary> /// 打印及打印前初始化數據 /// </summary> private void Init_Printer() { HtmlHeight = ; // 網頁報表高度 HtmlWidth = ; // 網頁報表寬度 CalcTableRect(); // 計算區域矩形 RptDraw = new ReportDraw(); // 報表繪制實例 RptDraw.IsAllPrintTitle = this._isTblTitleAllPage; // 每頁打印標題 RptDraw.IsAllPrintHead = this._isTblHeadAllPage; // 每頁打印表頭 RptDraw.IsAllPrintFoot = this._isTblFootAllPage; // 每頁打印表尾 RptDraw.IsAutoFillRow = this._isAutoFillRow; // 末頁自動補行 RptDraw.IsOverFlow = this._isOverFlow; // 字符溢出縮小 RptDraw.ReptRect = TableRect; // 賦值報表矩形 if (!ParseXML()) return; // 解析報表數據 CalcReportZoom(); // 計算縮小比例 CalcZoomAllSize(); // 按比計算尺寸 RptDraw.Zoom = this.Zoom; // 賦值縮小比例 RptDraw.CalcPaging(); // 計算打印分頁 } /// <summary> /// PrintDocument 對象打印繪制事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void E_PrintDoc_PrintPage(object sender, PrintPageEventArgs e) { Graphics g = e.Graphics; g.Clear(Color.White); DrawHeader(g); DrawFooter(g); if (RptDraw.DrawReport(g)) e.HasMorePages = true; else e.HasMorePages = false; } /// <summary> /// 繪制頁眉 /// </summary> /// <param name="g">繪圖對象</param> private void DrawHeader(Graphics g) { // 是否繪制 if (_headDraw) { // 頁眉實例 PageHeader pgHeader = new PageHeader(); // 頁眉矩形 RectangleF pgHeaderRect = new RectangleF(TableRect.X, // X 坐標 TableRect.Y - mmToPixel(_headHeight), // Y 坐標 TableRect.Width, // W 寬度 mmToPixel(_headHeight) // H 高度 ); // 頁眉賦值 pgHeader.HeadRect = pgHeaderRect; pgHeader.StrLeft = ReplacePageNum(_headLeft); // 左側文本 pgHeader.StrCenter = ReplacePageNum(_headCenter); // 中間文本 pgHeader.StrRight = ReplacePageNum(_headRight); // 右側文本 FontStyle fontStyle = FontStyle.Regular; // 字體樣式 if (_headFontBold) fontStyle |= FontStyle.Bold; if (_headFontItalic) fontStyle |= FontStyle.Italic; if (_headFontStrikeout) fontStyle |= FontStyle.Strikeout; if (_headFontUnderline) fontStyle |= FontStyle.Underline; pgHeader.StrFont = new Font(_headFontName, (float)Convert.ToDouble(_headFontSize.ToLower().Replace("px", "").Replace("pt", "")), fontStyle, GraphicsUnit.Point); pgHeader.StrColor = (Color)PrintTool.StrToColor(_headFontColor); if (_headLineDraw) // 繪制分割線 { pgHeader.LineDraw = _headLineDraw; pgHeader.LineWidth = _headLineWidth; pgHeader.LineColor = (Color)PrintTool.StrToColor(_headLineColor); pgHeader.LineDash = PrintTool.GetDashStyle(_headLineDash); } // 頁眉繪制 pgHeader.Draw(g); } } /// <summary> /// 繪制頁腳 /// </summary> /// <param name="g">繪圖對象</param> private void DrawFooter(Graphics g) { // 是否繪制 if (_footDraw) { // 頁腳實例 PageFooter pgFooter = new PageFooter(); // 頁腳矩形 RectangleF pgFooterRect = new RectangleF(TableRect.X, // X 坐標 TableRect.Y + TableRect.Height, // Y 坐標 TableRect.Width, // W 寬度 mmToPixel(_footHeight) // H 高度 ); // 頁腳賦值 pgFooter.FootRect = pgFooterRect; pgFooter.StrLeft = ReplacePageNum(_footLeft); // 左側文本 pgFooter.StrCenter = ReplacePageNum(_footCenter); // 中間文本 pgFooter.StrRight = ReplacePageNum(_footRight); // 右側文本 FontStyle fontStyle = FontStyle.Regular; // 字體樣式 if (_footFontBold) fontStyle |= FontStyle.Bold; if (_footFontItalic) fontStyle |= FontStyle.Italic; if (_footFontStrikeout) fontStyle |= FontStyle.Strikeout; if (_footFontUnderline) fontStyle |= FontStyle.Underline; pgFooter.StrFont = new Font(_footFontName, (float)Convert.ToDouble(_footFontSize.ToLower().Replace("px", "").Replace("pt", "")), fontStyle, GraphicsUnit.Point); pgFooter.StrColor = (Color)PrintTool.StrToColor(_footFontColor); if (_footLineDraw) // 繪制分割線 { pgFooter.LineDraw = _footLineDraw; pgFooter.LineWidth = _footLineWidth; pgFooter.LineColor = (Color)PrintTool.StrToColor(_footLineColor); pgFooter.LineDash = PrintTool.GetDashStyle(_footLineDash); } // 頁腳繪制 pgFooter.Draw(g); } } #endregion #region 輔助方法 /// <summary> /// 毫米 TO 像素 /// </summary> /// <param name="mmValue">毫米值</param> /// <returns></returns> public static float mmToPixel(float mmValue) //mmValue是毫米,厘米=毫米 { return (mmValue / .f * f); } /// <summary> /// 替換 當前頁碼、總共頁數兩個變量 /// </summary> /// <param name="str"></param> /// <returns></returns> private string ReplacePageNum(string str) { string retStr = ""; if (str == null || str.Trim() == "") return retStr; retStr = str; int t = ; while (t >= ) { t = retStr.IndexOf("[curpage]", StringComparison.OrdinalIgnoreCase); if (t >= ) { retStr = retStr.Substring(, t) + RptDraw.CurPageNum.ToString() + retStr.Substring(t + "[curpage]".Length); } } t = ; while (t >= ) { t = retStr.IndexOf("[allpage]", StringComparison.OrdinalIgnoreCase); if (t >= ) { retStr = retStr.Substring(, t) + RptDraw.AllPageNum.ToString() + retStr.Substring(t + "[allpage]".Length); } } return retStr; } /// <summary> /// 解析XML文件 /// </summary> /// <returns>返回成功與否</returns> private bool ParseXML() { if (this.DATAXML == null || this.DATAXML.Trim() == "") return false; XmlDataDocument xmlDoc = new XmlDataDocument(); try { xmlDoc.LoadXml(this.DATAXML); XmlNode rootNode = xmlDoc.DocumentElement; if (rootNode.ChildNodes.Count == ) return false; if (rootNode.ChildNodes[].Name.ToLower() != "table") return false; XmlNode tableNode = rootNode.ChildNodes[]; // 表格節點 HtmlHeight = float.Parse(tableNode.Attributes["height"].Value); HtmlWidth = float.Parse(tableNode.Attributes["width"].Value); int tmRowIndex = ; foreach (XmlNode trNode in tableNode.ChildNodes) { if (trNode.Name.ToLower() != "tr") continue; // 解析表格行 Row tmRow = new Row(); tmRow.RowIndex = tmRowIndex; tmRow.RowHeight = float.Parse(trNode.Attributes["height"].Value); tmRow.RowType = trNode.Attributes["rowtype"].Value.ToLower(); // 解析單元格 foreach (XmlNode tdNode in trNode.ChildNodes) { Cell tmCell = new Cell(); #region 合並\坐標\矩形 tmCell.RowSpan = Convert.ToInt(tdNode.Attributes["rowspan"].Value); tmCell.ColSpan = Convert.ToInt(tdNode.Attributes["colspan"].Value); tmCell.RowIndex = Convert.ToInt(tdNode.Attributes["r"].Value); tmCell.ColIndex = Convert.ToInt(tdNode.Attributes["c"].Value); tmCell.RectX = float.Parse(tdNode.Attributes["x"].Value); tmCell.RectY = float.Parse(tdNode.Attributes["y"].Value); tmCell.RectW = float.Parse(tdNode.Attributes["w"].Value); tmCell.RectH = float.Parse(tdNode.Attributes["h"].Value); #endregion #region 設置單元格字體 FontStyle tmStyle = new FontStyle(); tmStyle = FontStyle.Regular; if (tdNode.Attributes["italic"].Value.ToString() == "") tmStyle |= FontStyle.Italic; if (tdNode.Attributes["bold"].Value.ToString() == "") tmStyle |= FontStyle.Bold; if (tdNode.Attributes["strikeout"].Value.ToString() == "") tmStyle |= FontStyle.Strikeout; if (tdNode.Attributes["underline"].Value.ToString() == "") tmStyle |= FontStyle.Underline; tmCell.CellFont = new Font(tdNode.Attributes["fontname"].Value, float.Parse(tdNode.Attributes["fontsize"].Value.Replace("pt", "").Replace("px", "")), tmStyle, GraphicsUnit.Point); tmCell.FontColor = (Color)PrintTool.StrToColor(tdNode.Attributes["fontcolor"].Value); tmCell.BackColor = (Color)PrintTool.StrToColor(tdNode.Attributes["backcolor"].Value); StringFormat tmFormat = new StringFormat(); switch (tdNode.Attributes["align"].Value.ToLower()) // 水平對齊方式 { case "center": tmFormat.Alignment = StringAlignment.Center; break; case "right": tmFormat.Alignment = StringAlignment.Far; break; default: tmFormat.Alignment = StringAlignment.Near; break; } switch (tdNode.Attributes["valign"].Value.ToLower()) // 垂直對齊方式 { case "middle": tmFormat.LineAlignment = StringAlignment.Center; break; case "bottom": tmFormat.LineAlignment = StringAlignment.Far; break; default: tmFormat.LineAlignment = StringAlignment.Near; break; } tmCell.strFormat = tmFormat; #endregion #region 內嵌圖片-屬性 tmCell.IsImage = tdNode.Attributes["isimage"].Value.ToString() == "" ? true : false; if (tmCell.IsImage) tmCell.ImageUrl = tdNode.Attributes["imageurl"].Value; #endregion #region 單元格邊框屬性 // 左邊框線 string tmVal = tdNode.Attributes["leftwidth"].Value; if (tmVal.IndexOf("px") >= ) { tmCell.LeftBorder = new BorderLine( float.Parse(tmVal.Replace("px", "")), (Color)PrintTool.StrToColor(tdNode.Attributes["leftcolor"].Value), PrintTool.GetDashStyle(tdNode.Attributes["leftdash"].Value) ); } // 上邊框線 tmVal = tdNode.Attributes["topwidth"].Value; if (tmVal.IndexOf("px") >= ) { tmCell.TopBorder = new BorderLine( float.Parse(tmVal.Replace("px", "")), (Color)PrintTool.StrToColor(tdNode.Attributes["topcolor"].Value), PrintTool.GetDashStyle(tdNode.Attributes["topdash"].Value) ); } // 右邊框線 tmVal = tdNode.Attributes["rightwidth"].Value; if (tmVal.IndexOf("px") >= ) { tmCell.RightBorder = new BorderLine( float.Parse(tmVal.Replace("px", "")), (Color)PrintTool.StrToColor(tdNode.Attributes["rightcolor"].Value), PrintTool.GetDashStyle(tdNode.Attributes["rightdash"].Value) ); } // 下邊框線 tmVal = tdNode.Attributes["bottomwidth"].Value; if (tmVal.IndexOf("px") >= ) { tmCell.BottomBorder = new BorderLine( float.Parse(tmVal.Replace("px", "")), (Color)PrintTool.StrToColor(tdNode.Attributes["bottomcolor"].Value), PrintTool.GetDashStyle(tdNode.Attributes["bottomdash"].Value) ); } #endregion #region 單據格數據數值 tmCell.Value = tdNode.InnerText; #endregion // 加入對應的行內 tmRow.RowCells.Add(tmCell); } RptDraw.RowsList.Add(tmRow); tmRowIndex++; } } catch { return false; } finally { xmlDoc = null; } return true; } /// <summary> /// 計算報表區域矩形 /// 真實的繪制報表的區域 /// </summary> private void CalcTableRect() { // 重新初始化實例 TableRect = new RectangleF(); // 左頂點 X坐標 TableRect.X = mmToPixel(_marginLeft); // 左頂點 Y坐標 TableRect.Y = mmToPixel(_marginTop); if (_headDraw) // 需要繪制頁眉 TableRect.Y += mmToPixel(_headHeight); // 報表矩形寬度高度 if (this.LANDSCAPE) // 版型方向 橫向 { // 顛倒 寬高 TableRect.Width = mmToPixel((float)_paperHeight - _marginLeft - _marginRight); TableRect.Height = mmToPixel((float)_paperWidth - _marginTop - _marginBottom); } else // 版型方向 縱向 { TableRect.Width = mmToPixel((float)_paperWidth - _marginLeft - _marginRight); TableRect.Height = mmToPixel((float)_paperHeight - _marginTop - _marginBottom); } // 報表矩形高度縮減 if (_headDraw) // 需要繪制頁眉 TableRect.Height -= mmToPixel(_headHeight); if (_footDraw) // 需要繪制頁腳 TableRect.Height -= mmToPixel(_footHeight); } /// <summary> /// 計算縮放比例 /// </summary> private void CalcReportZoom() { if (this.ZOOMTYPE.ToLower() == "row") { if (Convert.ToInt(TableRect.Width) >= HtmlWidth) this.Zoom = ; else this.Zoom = TableRect.Width / HtmlWidth; } else if (this.ZOOMTYPE.ToLower() == "col") { if (Convert.ToInt(TableRect.Height) >= HtmlHeight) this.Zoom = ; else this.Zoom = TableRect.Height / HtmlHeight; } else { this.Zoom = ; } } /// <summary> /// 轉換所有的尺寸 /// 根據縮放比例 /// </summary> private void CalcZoomAllSize() { if (this.Zoom != ) { // 轉換HTML 高度寬度 HtmlWidth = HtmlWidth * Zoom; HtmlHeight = HtmlHeight * Zoom; // 轉換所有行號 foreach (Row zRow in this.RptDraw.RowsList) { // 行高縮小 zRow.RowHeight = zRow.RowHeight * Zoom; // 轉換所有單元格 foreach (Cell zCell in zRow.RowCells) { zCell.RectX = zCell.RectX * Zoom; zCell.RectY = zCell.RectY * Zoom; zCell.RectW = zCell.RectW * Zoom; zCell.RectH = zCell.RectH * Zoom; zCell.TopBorder.LineWidth = zCell.TopBorder.LineWidth * Zoom; zCell.BottomBorder.LineWidth = zCell.BottomBorder.LineWidth * Zoom; zCell.LeftBorder.LineWidth = zCell.LeftBorder.LineWidth * Zoom; zCell.RightBorder.LineWidth = zCell.RightBorder.LineWidth * Zoom; // 字體相應縮小 zCell.CellFont = new Font(zCell.CellFont.Name, zCell.CellFont.Size * Zoom, zCell.CellFont.Style, GraphicsUnit.Point); } } } } #endregion } }
2、ReportDraw 打印繪制類
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.DrawingD; using System.IO; namespace E_Print { /// <summary> /// 報表繪制 /// </summary> public class ReportDraw { #region 私有成員 /// <summary> /// 當前頁碼 /// </summary> private int _curPageNum; /// <summary> /// 總共頁數 /// </summary> private int _allPageNum; /// <summary> /// 表格矩形 /// </summary> private RectangleF _reptRect; /// <summary> /// 報表全部行集 /// </summary> private List<Row> _rowsList; /// <summary> /// 分頁頁面數組 /// </summary> private List<PagingItem> _pageList; /// <summary> /// 是否每頁打印標題 /// </summary> private bool _isAllPrintTitle; /// <summary> /// 是否每頁打印表頭 /// </summary> private bool _isAllPrintHead; /// <summary> /// 是否每頁打印表尾 /// </summary> private bool _isAllPrintFoot; /// <summary> /// 是否末頁自動補行 /// </summary> private bool _isAutoFillRow; /// <summary> /// 縮小比例 /// </summary> private float _zoom; /// <summary> /// 字符溢出處理方式 /// </summary> private bool _isOverFlow; /// <summary> /// 每頁打印的標題+表頭的高度 /// </summary> private float _headPix; /// <summary> /// 每頁打印的表尾高度 /// </summary> private float _footPix; #endregion #region 構造方法 /// <summary> /// 構造函數 /// </summary> public ReportDraw() { _curPageNum = ; _allPageNum = ; _reptRect = new RectangleF(); _rowsList = new List<Row>(); _pageList = new List<PagingItem>(); _isAllPrintTitle = false; _isAllPrintHead = false; _isAllPrintFoot = false; _isAutoFillRow = false; _zoom = ; _isOverFlow = false; _headPix = ; _footPix = ; } /// <summary> /// 構造函數 /// </summary> /// <param name="printTitle">每頁打印標題</param> /// <param name="printHead">每頁打印表頭</param> /// <param name="printFoot">每頁打印表位</param> /// <param name="fillRows">自動補全空行</param> /// <param name="tableRect">報表尺寸矩形</param> /// <param name="overFlow">字符溢出處理</param> public ReportDraw(bool printTitle, bool printHead, bool printFoot, bool fillRows, RectangleF tableRect, bool overFlow) { _reptRect = tableRect; _isAllPrintTitle = printTitle; _isAllPrintHead = printHead; _isAllPrintFoot = printFoot; _isAutoFillRow = fillRows; _isOverFlow = overFlow; _curPageNum = ; _allPageNum = ; _zoom = ; _rowsList = new List<Row>(); _pageList = new List<PagingItem>(); _headPix = ; _footPix = ; } #endregion #region 屬性方法 /// <summary> /// 獲取--設置--當前頁碼 /// </summary> public int CurPageNum { get { return _curPageNum; } set { _curPageNum = value; } } /// <summary> /// 獲取--設置--總共頁數 /// </summary> public int AllPageNum { get { return _allPageNum; } set { _allPageNum = value; } } /// <summary> /// 獲取--設置--表格矩形 /// </summary> public RectangleF ReptRect { get { return _reptRect; } set { _reptRect = value; } } /// <summary> /// 獲取--設置--報表全部行集 /// </summary> public List<Row> RowsList { get { return _rowsList; } set { _rowsList = value; } } /// <summary> /// 獲取--設置--分頁頁面數組 /// </summary> public List<PagingItem> PageList { get { return _pageList; } set { _pageList = value; } } /// <summary> /// 獲取--設置--是否每頁打印標題 /// </summary> public bool IsAllPrintTitle { get { return _isAllPrintTitle; } set { _isAllPrintTitle = value; } } /// <summary> /// 獲取--設置--是否每頁打印表頭 /// </summary> public bool IsAllPrintHead { get { return _isAllPrintHead; } set { _isAllPrintHead = value; } } /// <summary> /// 獲取--設置--是否每頁打印表尾 /// </summary> public bool IsAllPrintFoot { get { return _isAllPrintFoot; } set { _isAllPrintFoot = value; } } /// <summary> /// 獲取--設置--末頁是否自動補行 /// </summary> public bool IsAutoFillRow { get { return _isAutoFillRow; } set { _isAutoFillRow = value; } } /// <summary> /// 獲取--設置--縮小比例 /// </summary> public float Zoom { get { return _zoom; } set { _zoom = value; } } /// <summary> /// 獲取--設置--字符溢出處理方式 /// </summary> public bool IsOverFlow { get { return _isOverFlow; } set { _isOverFlow = value; } } /// <summary> /// 獲取--設置--每頁打印的標題+表頭高度 /// </summary> public float HeadPix { get { return _headPix; } set { _headPix = value; } } /// <summary> /// 獲取--設置--每頁打印的表尾高度 /// </summary> public float FootPix { get { return _footPix; } set { _footPix = value; } } #endregion #region 計算分頁 /// <summary> /// 計算分頁 /// </summary> public void CalcPaging() { // 分頁實例 PagingCalc insCalc = new PagingCalc(); insCalc.TableRect = this.ReptRect; insCalc.RowsList = this.RowsList; insCalc.IsAllPrintTitle = this.IsAllPrintTitle; insCalc.IsAllPrintHead = this.IsAllPrintHead; insCalc.IsAllPrintFoot = this.IsAllPrintFoot; // 分頁計算 _pageList = insCalc.CalcPages(); this._allPageNum = _pageList.Count; this._curPageNum = ; this._headPix = insCalc.MyHeadPix; this._footPix = insCalc.MyFootPix; } #endregion #region 繪制方法 /// <summary> /// 繪制報表 /// </summary> /// <param name="g"></param> /// <returns>返回是否結束</returns> public bool DrawReport(Graphics g) { bool isMorePage = false; float offsetX = _reptRect.X; // X 偏移量 float offsetY = _reptRect.Y; // Y 偏移量 bool isCanFillRow = false; // 是否需要補行 bool isFillFlag = false; // 是否已經補過 int isFillRowNum = ; // 需要補充幾行 PagingItem nowPage = GetPageItem(CurPageNum); // 當前頁 if (nowPage != null) { #region 判定高度不足是否自動補行 // 判定補行條件 報表設置了末頁不足自動補行、同時 為最後一頁 if (_isAutoFillRow == true && CurPageNum == AllPageNum) { // 判定頁面高度 與 數據高度 float N_PageHeith = ReptRect.Height; // 當前頁面高度 float N_DataHeith = GetThisPageDataRowsHeight(); // 當前數據高度 // 補行行高 while ((N_DataHeith + (isFillRowNum + ) * * Zoom) < N_PageHeith) { isFillRowNum++; } if (isFillRowNum > ) isCanFillRow = true; } #endregion #region 首先繪制上一頁補充單元格 if (CurPageNum > ) { PagingItem prePage = GetPageItem(CurPageNum - ); // 上一頁 if (prePage != null) { foreach (PagingMakeUp tmMk in prePage.MakeupList) { // 繪制補充單元格(上頁中未繪制完成的單元格) DrawTD(g, tmMk.MakeupCell, offsetX, offsetY, true, tmMk.MakeupHeight, false); } } } #endregion #region 其次繪制當前頁面的單元格 // 其次繪制當前頁的單元格 for (int ii = ; ii < nowPage.IndexList.Count; ii++) { // 先繪制 TD CELL 單元格 Row rowTR = GetRow(nowPage.IndexList[ii]); #region 執行補行 if (isCanFillRow == true && rowTR.RowType.ToLower().Trim() == "f") // 需要補行 行標志為F 表尾前進行補充空行 { Row fillRow = new Row(); if (ii == ) fillRow = rowTR; else fillRow = GetRow(nowPage.IndexList[ii - ]); if (fillRow != null) // 繪制自動補充的空行單元格 { // 開始補充空行 for (int fi = ; fi <= isFillRowNum; fi++) { bool bcFlag = true; // 繪制單元格 foreach (Cell fillTdCell in fillRow.RowCells) { if (bcFlag) { // 繪制邊框線(合並單元格的情況才用到) if (fillTdCell.RectX > ) { DrawLine(g, offsetX, offsetY, offsetX, offsetY + .F * Zoom); // 最左邊豎線 DrawLine(g, offsetX, offsetY + .F * Zoom, offsetX + fillTdCell.RectX, offsetY + .F * Zoom); } bcFlag = false; } DrawTD(g, fillTdCell, offsetX, offsetY, false, , true); } // 再將偏移量+行號 補充的行高全部為px offsetY += * Zoom; } } isFillFlag = true; } #endregion #region 正常繪制 foreach (Cell td in rowTR.RowCells) { DrawTD(g, td, offsetX, offsetY, false, , false); } // 再將偏移量+行號 offsetY += rowTR.RowHeight; #endregion } // 判定是否補過;沒有補充過,則在最後進行補充空行 if ((isCanFillRow == true) && (isFillFlag == false) && (nowPage.IndexList.Count > )) { Row fillRow = GetRow(nowPage.IndexList[nowPage.IndexList.Count - ]); if (fillRow != null) // 繪制自動補充的空行單元格 { // 開始補充空行 for (int fi = ; fi <= isFillRowNum; fi++) { bool newFlag = true; foreach (Cell fillTdCell in fillRow.RowCells) { if (newFlag) { // 繪制邊框線(合並單元格的情況才用到) if (fillTdCell.RectX > ) { DrawLine(g, offsetX, offsetY, offsetX, offsetY + .F * Zoom); // 最左邊豎線 DrawLine(g, offsetX, offsetY + .F * Zoom, offsetX + fillTdCell.RectX, offsetY + .F * Zoom); } newFlag = false; } DrawTD(g, fillTdCell, offsetX, offsetY, false, , true); } offsetY += * Zoom; // 再將偏移量+行號 補充的行高全部為px } } } #endregion if (CurPageNum < AllPageNum) { isMorePage = true; // 還有下頁 CurPageNum++; // 頁碼增加 } } return isMorePage; } /// <summary> /// 繪制單元格 /// </summary> /// <param name="g">繪圖對象</param> /// <param name="tdCell">單元格</param> /// <param name="setX">X偏移量</param> /// <param name="setY">Y坐標值</param> /// <param name="isMakeup">是否補充單元格</param> /// <param name="mkH">補充單元格高度</param> /// <param name="fillCell">自動補行的單元格</param> private void DrawTD(Graphics g, Cell tdCell, float setX, float setY, bool isMakeup, float mkH, bool fillCell) { #region 參數變量 Pen pen; Brush brush; // 獲取單元格繪制坐標矩形信息 float tdX = tdCell.RectX + setX; float tdY = setY; float tdW = tdCell.RectW; float tdH = ; if (fillCell) { tdH = * Zoom; // 自動補行的單元格的高度固定為px } else { if (isMakeup) // 補充單元格 { tdH = mkH; tdY = tdY + HeadPix; // 如果是補充單元格,則此單元格的Y坐標:如果每頁打印標題或表頭,則Y坐標 需要下移 HeadPix } else // 實際單元格 { tdH = tdCell.RectH; } if (tdCell.RowSpan > ) // 判定單元格高度是否超過底線 { tdH = Calc_CellOverHeight(tdCell, tdY, tdH); } } #endregion #region 繪制背景 // 填充顏色 brush = new SolidBrush(tdCell.BackColor); g.FillRectangle(brush, tdX + .f * Zoom, tdY + .f * Zoom, tdW - .f * Zoom, tdH - .f * Zoom); #endregion #region 繪制邊框 // 左邊框線 if (tdCell.LeftBorder.LineWidth > ) { pen = new Pen(tdCell.LeftBorder.LineColor); pen.DashStyle = tdCell.LeftBorder.LineDash; pen.Width = tdCell.LeftBorder.LineWidth; g.DrawLine(pen, tdX, tdY, tdX, tdY + tdH); } // 上邊框線 if (tdCell.TopBorder.LineWidth > ) { pen = new Pen(tdCell.TopBorder.LineColor); pen.DashStyle = tdCell.TopBorder.LineDash; pen.Width = tdCell.TopBorder.LineWidth; g.DrawLine(pen, tdX, tdY, tdX + tdW, tdY); } // 右邊框線 if (tdCell.RightBorder.LineWidth > ) { pen = new Pen(tdCell.RightBorder.LineColor); pen.DashStyle = tdCell.RightBorder.LineDash; pen.Width = tdCell.RightBorder.LineWidth; g.DrawLine(pen, tdX + tdW, tdY, tdX + tdW, tdY + tdH); } // 下邊框線 if (tdCell.BottomBorder.LineWidth > ) { pen = new Pen(tdCell.BottomBorder.LineColor); pen.DashStyle = tdCell.BottomBorder.LineDash; pen.Width = tdCell.BottomBorder.LineWidth; g.DrawLine(pen, tdX, tdY + tdH, tdX + tdW, tdY + tdH); } #endregion #region 繪制文字 if (!fillCell) { RectangleF rect = new RectangleF(tdX, tdY, tdW, tdH); if (tdCell.IsImage) { this.DrawImg(g, rect, tdCell.ImageUrl); } else { brush = new SolidBrush(tdCell.FontColor); this.DrawStr(g, rect, brush, tdCell.CellFont, tdCell.strFormat, tdCell.Value); } } #endregion } /// <summary> /// 繪制字符串 /// 溢出時,換行縮小字符 /// 字體縮小到的最小值不得小於 /// </summary> /// <param name="g">繪圖對象</param> /// <param name="strRect">文本區域</param> /// <param name="strBrush">文本筆畫</param> /// <param name="strFont">文本字體</param> /// <param name="strFormat">文本格式</param> /// <param name="strValue">文本字符</param> /// <returns></returns> private void DrawStr(Graphics g, RectangleF strRect, Brush strBrush, Font strFont, StringFormat strFormat, string strValue) { // 報表設置:字符溢出不做處理 if (!this.IsOverFlow) { g.DrawString(strValue, strFont, strBrush, strRect, strFormat); } else // 需要處理 { // 測量字體的寬度和高度 會發現誤差很大,如果一個一個的測量,誤差就實在太大,所以這裡就用簡單的方式來進行處理 SizeF sf = g.MeasureString(strValue, strFont); // 此種方式測量誤差很大,如果 if (strRect.Width > sf.Width) { g.DrawString(strValue, strFont, strBrush, strRect, strFormat); } else { // 計算換行後字符的全部高度是否滿足 int maxLines = ; // 計算當前字符當前字體最大打印的行數 maxLines = (int)Math.Ceiling((double)sf.Width / (double)strRect.Width); if (strRect.Height >= maxLines * sf.Height) { g.DrawString(strValue, strFont, strBrush, strRect, strFormat); } else { float tmScale = strRect.Height / (maxLines * sf.Height); Font tmNewFont = new Font(strFont.Name, strFont.Size * tmScale, strFont.Style, GraphicsUnit.Point); g.DrawString(strValue, tmNewFont, strBrush, strRect, strFormat); } } } } /// <summary> /// 繪制圖片 /// 將Base圖片流字符串轉換成圖片並進行繪制 /// </summary> /// <param name="g"></param> /// <param name="strRect"></param> /// <param name="baseImg"></param> private void DrawImg(Graphics g, RectangleF strRect, string baseImg) { if (baseImg.Trim() == "") return; string imgStr = baseImg.Replace("data:image/gif;base,", "").Trim(); if (imgStr == "") return; // 生成圖片 try { MemoryStream stream = new MemoryStream(Convert.FromBaseString(imgStr)); Bitmap picImg = new Bitmap(stream); RectangleF imgRectF = new RectangleF(f, f, (float)picImg.Width, (float)picImg.Height); // 原始圖片矩形 RectangleF newRectF = new RectangleF(strRect.X + f, strRect.Y + f, (float)strRect.Width - f, (float)strRect.Height - f); // 繪制圖片矩形 g.DrawImage(picImg, newRectF, imgRectF, GraphicsUnit.Pixel); // 繪制縮放圖片 stream.Close(); } catch { return; } } /// <summary> /// 繪制線條 /// </summary> /// <param name="g">繪圖對象</param> /// <param name="start_X">開始X</param> /// <param name="start_Y">開始Y</param> /// <param name="end_X">結束X</param> /// <param name="end_Y">結束Y</param> private void DrawLine(Graphics g, float start_X, float start_Y, float end_X, float end_Y) { Pen linePen = new Pen(Color.Black, .f); linePen.DashStyle = DashStyle.Solid; g.DrawLine(linePen, start_X, start_Y, end_X, end_Y); } private float ChangeUnit(float vSize) { return (vSize * f / f * f / f); } /// <summary> /// 獲取行對象 /// </summary> /// <param name="rowIndex"></param> /// <returns></returns> private Row GetRow(int rowIndex) { foreach (Row retRow in _rowsList) { if (retRow.RowIndex == rowIndex) return retRow; } return null; } /// <summary> /// 獲取分頁頁面 /// </summary> /// <param name="pNo">頁碼</param> /// <returns></returns> private PagingItem GetPageItem(int pNo) { foreach (PagingItem retPItem in PageList) { if (retPItem.PageNum == pNo) return retPItem; } return null; } /// <summary> /// 計算繪制高度 /// 判定並且計算單元格高度是否超過當前頁面所有行高度的底線 /// </summary> /// <param name="mCell">單元格</param> /// <param name="mY">Y 軸坐標值</param> /// <param name="mH">H 當前高度</param> /// <returns></returns> private float Calc_CellOverHeight(Cell mCell, float mY, float mH) { float returnHeight = ; // 返回高度 float tm_AllTrHeight = GetThisPageDataRowsHeight(); // 當前頁面內所有數據行的高度 float tm_RealY = ; // 相對最大Y值 float tm_AbsY = ; // 實際最大Y值 float tm_OverPlus = ; // 單元格剩余高度 tm_RealY = mY + mH; // 實際最大Y值 if (IsAllPrintFoot) // 每頁打印表尾 tm_AbsY = ReptRect.Y + (tm_AllTrHeight - FootPix); // 需要減去表尾高度 else tm_AbsY = tm_AllTrHeight + ReptRect.Y; if (tm_RealY > tm_AbsY) { returnHeight = tm_AbsY - mY; // 當前頁面實際最大高度-單元格的當前Y坐標值 = 返回單元格在本頁內需要繪制的高度 tm_OverPlus = mH - returnHeight; // 當前高度-單元格當前頁面需要繪制的高度=下頁需要繪制的補充高度 // 將當前單元格添加到後頁需要補充繪制數組中去 PagingItem nPageItem = GetPageItem(CurPageNum); PagingMakeUp nMakeUp = new PagingMakeUp(); nMakeUp.MakeupCell = mCell; nMakeUp.MakeupHeight = tm_OverPlus; nPageItem.MakeupList.Add(nMakeUp); } else { returnHeight = mH; } return returnHeight; } /// <summary> /// 獲取本頁內所有數據行的高度 /// </summary> /// <returns></returns> private float GetThisPageDataRowsHeight() { float retHeight = ; PagingItem oThisPage = GetPageItem(CurPageNum); // 當前頁 foreach (int oRowIndex in oThisPage.IndexList) { Row oThisRow = GetRow(oRowIndex); retHeight += oThisRow.RowHeight; } return retHeight; } /// <summary> /// 獲取頁內某一項所屬行的高度 /// </summary> /// <param name="itemPage">頁面對象</param> /// <param name="itemIndex">本頁行數組中的某一項的序號</param> /// <returns></returns> private float GetThisPageOneRowHeight(PagingItem itemPage, int itemIndex) { float retHeight = ; if (itemIndex < itemPage.IndexList.Count && itemIndex >= ) { Row oThisRow = GetRow(itemPage.IndexList[itemIndex]); retHeight = oThisRow.RowHeight; } return retHeight; } #endregion } } 3、PagingCalc 分頁計算類 using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace E_Print { /// <summary> /// 分頁計算 /// </summary> public class PagingCalc { #region 私有變量 /// <summary> /// 表格區域 /// </summary> private RectangleF _tableRect; /// <summary> /// 報表行集 /// </summary> private List<Row> _rowsList; /// <summary> /// 是否每頁打印標題 /// </summary> private bool _isAllPrintTitle; /// <summary> /// 是否每頁打印表頭 /// </summary> private bool _isAllPrintHead; /// <summary> /// 是否每頁打印表尾 /// </summary> private bool _isAllPrintFoot; /// <summary> /// 標題行集 /// </summary> private List<Row> TitleList; /// <summary> /// 表頭前行集 /// </summary> private List<Row> HForeList; /// <summary> /// 表頭行集 /// </summary> private List<Row> HeadList; /// <summary> /// 數據行集 /// </summary> private List<Row> DataList; /// <summary> /// 表尾行集 /// </summary> private List<Row> FootList; /// <summary> /// 每頁打印標題+表頭高度 /// </summary> private float _myHeadPix; /// <summary> /// 每頁打印表尾高度 /// </summary> private float _myFootPix; #endregion #region 構造方法 /// <summary> /// 構造函數 /// </summary> public PagingCalc() { _tableRect = new RectangleF(); _rowsList = new List<Row>(); _isAllPrintTitle = false; _isAllPrintHead = false; _isAllPrintFoot = false; TitleList = new List<Row>(); HForeList = new List<Row>(); HeadList = new List<Row>(); DataList = new List<Row>(); FootList = new List<Row>(); _myHeadPix = ; _myFootPix = ; } #endregion #region 屬性方法 /// <summary> /// 獲取--設置--表格區域 /// </summary> public RectangleF TableRect { get { return _tableRect; } set { _tableRect = value; } } /// <summary> /// 獲取--設置--表格行集 /// </summary> public List<Row> RowsList { get { return _rowsList; } set { _rowsList = value; } } /// <summary> /// 獲取--設置--是否每頁打印標題 /// </summary> public bool IsAllPrintTitle { get { return _isAllPrintTitle; } set { _isAllPrintTitle = value; } } /// <summary> /// 獲取--設置--是否每頁打印表頭 /// </summary> public bool IsAllPrintHead { get { return _isAllPrintHead; } set { _isAllPrintHead = value; } } /// <summary> /// 獲取--設置--是否每頁打印表尾 /// </summary> public bool IsAllPrintFoot { get { return _isAllPrintFoot; } set { _isAllPrintFoot = value; } } /// <summary> /// 獲取--設置--每頁打印標題+表頭高度 /// </summary> public float MyHeadPix { get { return _myHeadPix; } set { _myHeadPix = value; } } /// <summary> /// 獲取--設置--每頁打印表尾巴高度 /// </summary> public float MyFootPix { get { return _myFootPix; } set { _myFootPix = value; } } #endregion #region 計算方法 /// <summary> /// 分頁計算 /// </summary> /// <returns></returns> public List<PagingItem> CalcPages() { List<PagingItem> retPages = new List<PagingItem>(); // 無需分頁 if (Get_TableAllHeight() <= TableRect.Height) { PagingItem tmItem = new PagingItem(); tmItem.PageNum = ; for (int y = ; y < RowsList.Count; y++) { tmItem.IndexList.Add(y); } retPages.Add(tmItem); } else // 需要分頁 { // 有設置了 每頁打印標題、表頭、表位 其中的任意一個 if (Get_IsCusSet_THDF()) // 則執行每頁相對分頁 { Paging_Relative(, ref retPages); // 計算每頁打印頭尾高度 MyHeadPix = ; if (IsAllPrintTitle) { MyHeadPix += Get_TableTileHeight(); } if (IsAllPrintHead) { MyHeadPix += Get_TableHeadHeight(); } if (IsAllPrintFoot) { MyFootPix = Get_TableFootHeight(); } } else // 執行直接數據分頁 { Paging_Direct(, ref retPages); } } return retPages; } /// <summary> /// 直接分頁 /// </summary> /// <param name="startR">開始行號</param> /// <param name="pages">頁面數組</param> private void Paging_Direct(int startR, ref List<PagingItem> pages) { float p_Height = TableRect.Height; PagingItem p_Item = new PagingItem(); p_Item.PageNum = pages.Count + ; for (int t = startR; t < RowsList.Count; t++) { // 檢查行內單元格是否不允許分頁兩種情況:條形碼,圖片 if (Paging_CheckCell(RowsList[t], p_Height)) { startR = t; pages.Add(p_Item); Paging_Direct(startR, ref pages); break; } else { p_Height -= RowsList[t].RowHeight; if (p_Height <= ) { startR = t; pages.Add(p_Item); Paging_Direct(startR, ref pages); break; } else { p_Item.IndexList.Add(t); if (t == RowsList.Count - ) { pages.Add(p_Item); } } } } } /// <summary> /// 相對分頁 /// </summary> /// <param name="startR">開始序號</param> /// <param name="pages">頁面數組</param> private void Paging_Relative(int startR, ref List<PagingItem> pages) { SplitReportArea(); // 拆分表行 float p_Height = TableRect.Height; // 頁面總高 PagingItem p_Item = new PagingItem(); // 分頁頁面 p_Item.PageNum = pages.Count + ; // 分頁頁碼 bool runNext = false; // 繼續分頁 #region 每頁打印標題 // 每頁打印標題 if (IsAllPrintTitle) { p_Height -= Get_TableTileHeight(); foreach (Row p_Row in TitleList) p_Item.IndexList.Add(p_Row.RowIndex); } else { if (p_Item.PageNum == ) // 第一頁特殊處理 { p_Height -= Get_TableTileHeight(); foreach (Row p_Row in TitleList) p_Item.IndexList.Add(p_Row.RowIndex); } } #endregion #region 每頁打印表頭 // 每頁打印表頭 if (IsAllPrintHead) { if (p_Item.PageNum == ) // 第一頁特殊處理 { // 計算表頭前的行高 p_Height -= Get_TableHForHeight(); foreach (Row p_Row in HForeList) p_Item.IndexList.Add(p_Row.RowIndex); } // 計算表頭行的高度 p_Height -= Get_TableHeadHeight(); foreach (Row p_Row in HeadList) p_Item.IndexList.Add(p_Row.RowIndex); } else { if (p_Item.PageNum == ) // 第一頁特殊處理 { // 計算表頭前的行高 p_Height -= Get_TableHForHeight(); foreach (Row p_Row in HForeList) p_Item.IndexList.Add(p_Row.RowIndex); // 計算表頭行的高度 p_Height -= Get_TableHeadHeight(); foreach (Row p_Row in HeadList) p_Item.IndexList.Add(p_Row.RowIndex); } } #endregion #region 每頁數據區域 // 每頁數據劃分 if (IsAllPrintFoot) { p_Height -= Get_TableFootHeight(); // 表格高度 先減去表尾的高度 } for (int t = startR; t < DataList.Count; t++) { // 檢查行內單元格是否不允許分頁兩種情況:條形碼,圖片 if (Paging_CheckCell(DataList[t], p_Height)) // 此情況下,單元格不能分割,並且高度超過頁面剩余高度,所以要啟動新的一頁 { startR = t; runNext = true; break; } else { p_Height -= DataList[t].RowHeight; if (p_Height <= ) { startR = t; runNext = true; break; } else { p_Item.IndexList.Add(DataList[t].RowIndex); } } } #endregion #region 每頁打印表尾 // 每頁打印表尾 if (IsAllPrintFoot) { foreach (Row p_Row in FootList) p_Item.IndexList.Add(p_Row.RowIndex); } #endregion #region 添加分頁頁面 pages.Add(p_Item); if (runNext) { Paging_Relative(startR, ref pages); } #endregion } /// <summary> /// 檢查行內單元格如果是圖片 /// 並且合並行數大於 /// </summary> /// <param name="cRow"></param> /// <param name="cHeight"></param> /// <returns></returns> private bool Paging_CheckCell(Row cRow, float cHeight) { foreach (Cell cCell in cRow.RowCells) { if (cCell.IsImage == true) { if (cCell.RectH > cHeight) return true; } } return false; } #endregion #region 輔助方法 /// <summary> /// 獲取--報表全部高度 /// </summary> /// <returns></returns> private float Get_TableAllHeight() { float retHight = ; for (int k = ; k < RowsList.Count; k++) { Row t_Row = RowsList[k]; retHight += t_Row.RowHeight; } return retHight; } /// <summary> /// 獲取是否設置了標題、表頭、表尾 中的任意一個 /// </summary> /// <returns></returns> private bool Get_IsCusSet_THDF() { string tmType = ""; foreach (Row cusRow in this.RowsList) { tmType = cusRow.RowType.ToLower().Trim(); if (tmType == "t" || tmType == "h" || tmType == "f") return true; } return false; } /// <summary> /// 獲取--報表標題高度 /// </summary> /// <returns></returns> private float Get_TableTileHeight() { float retHight = ; for (int k = ; k < TitleList.Count; k++) retHight += TitleList[k].RowHeight; return retHight; } /// <summary> /// 獲取--報表表頭前高度 /// </summary> /// <returns></returns> private float Get_TableHForHeight() { float retHight = ; for (int k = ; k < HForeList.Count; k++) retHight += HForeList[k].RowHeight; return retHight; } /// <summary> /// 獲取--報表表頭高度 /// </summary> /// <returns></returns> private float Get_TableHeadHeight() { float retHight = ; for (int k = ; k < HeadList.Count; k++) retHight += HeadList[k].RowHeight; return retHight; } /// <summary> /// 獲取--報表表尾高度 /// </summary> /// <returns></returns> private float Get_TableFootHeight() { float retHight = ; for (int k = ; k < FootList.Count; k++) retHight += FootList[k].RowHeight; return retHight; } /// <summary> /// 拆分報表區域 /// </summary> public void SplitReportArea() { TitleList = new List<Row>(); HForeList = new List<Row>(); HeadList = new List<Row>(); DataList = new List<Row>(); FootList = new List<Row>(); for (int m = ; m < RowsList.Count; m++) { Row mmRow = RowsList[m]; switch (mmRow.RowType.ToLower()) { case "t": // 標題 TitleList.Add(mmRow); break; case "h": // 表頭 HeadList.Add(mmRow); break; case "f": // 表尾 FootList.Add(mmRow); break; case "d": // 數據 default: DataList.Add(mmRow); break; } } // 設置表頭前行集 if (TitleList.Count == && HeadList.Count > ) { List<Row> tmpList = new List<Row>(); for (int n = ; n < DataList.Count; n++) { if (DataList[n].RowIndex < HeadList[].RowIndex) { HForeList.Add(DataList[n]); tmpList.Add(DataList[n]); } } for (int n = ; n < tmpList.Count; n++) { DataList.Remove(tmpList[n]); } tmpList.Clear(); } // 重設表尾 不是每頁打印表尾情況下,那麼表位就去掉 if (!IsAllPrintFoot) { foreach (Row tRow in FootList) DataList.Add(tRow); FootList.Clear(); } } #endregion } }
4、PagingMakeUp 分頁補充繪制類
using System; using System.Collections.Generic; using System.Text; namespace E_Print { /// <summary> /// 下頁需要補充繪制 /// </summary> public class PagingMakeUp { /// <summary> /// 補充單元格 /// </summary> private Cell _makeupCell; /// <summary> /// 補充高度 /// </summary> private float _makeupHeight; /// <summary> /// 構造函數 /// </summary> public PagingMakeUp() { _makeupCell = new Cell(); _makeupHeight = ; } /// <summary> /// 獲取--設置--補充單元格 /// </summary> public Cell MakeupCell { get { return _makeupCell; } set { _makeupCell = value; } } /// <summary> /// 獲取--設置--補充高度 /// </summary> public float MakeupHeight { get { return _makeupHeight; } set { _makeupHeight = value; } } } }
5、IObjectSafety 抽象接口類
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace E_Print { /// <summary> /// 接口抽象基類 /// </summary> [ComImport, GuidAttribute("CBBDC-C-CF-F-FCD")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] public interface IObjectSafety { [PreserveSig] int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U)] ref int pdwEnabledOptions); [PreserveSig()] int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U)] int dwEnabledOptions); } }
6、預覽效果
7、我們可以通過源碼中的 EPrintTest.exe 打印Winform 實例 ,來執行示范,
通過WEB前端js 生成打印的TABLE的 RptData.xml格式 數據,在WINForm中直接使用
如果在網頁中,通過JS 直接傳給打印控件就OK