namespace ReportEngine { using iTextSharp.text; using iTextSharp.text.pdf; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; public class ReportPainter { public enum ImageScale { IS_KeepOrigin = 0, IS_Width = 1, IS_FitWidth = 2 } public class ColumnMeta { public string Title { get; set; } public float Width { get; set; } public string DisplayFormat { get; set; } } public class TableMeta { public DataTable Table { get; set; } public List Columns { get; set; } } public class DataCell { public enum EDC_TYPE { EDC_CONTENT = 0, EDC_EMPTY = 1 } public int ColumnSpan { get; set; } = 1; public int RowSpan { get; set; } = 1; public int Align { get; set; } = Element.ALIGN_LEFT; public int VAlign { get; set; } = Element.ALIGN_MIDDLE; public string Content { get; set; } public EDC_TYPE CellType { get; set; } = EDC_TYPE.EDC_CONTENT; public float Height { get; set; } = -1; } public class CellTable { public int ColumnNum { get; set; } = 10; public float ColumnWidth { get; set; } = 75; public List Cells { get; set; } } readonly Document _dDocument; readonly ReportCanvasOptions _rcoOption; readonly PdfWriter _pfWriter; readonly double dFontHeight; public ReportPainter(Document i_dDocument, ReportCanvasOptions i_rcoOption, PdfWriter i_pfWriter) { _dDocument = i_dDocument; _rcoOption = i_rcoOption; _pfWriter = i_pfWriter; float ascent = _rcoOption.BaseFont.GetAscentPoint("Some String", _rcoOption.FontSize); float descent = _rcoOption.BaseFont.GetDescentPoint("Some String", _rcoOption.FontSize); dFontHeight = ascent - descent; } public void NewPage() => _dDocument.NewPage(); public void WriteText(string text = "\r\n", int i_nAlign = PdfContentByte.ALIGN_LEFT, Font font = null) { Paragraph p = new Paragraph(text, font ?? _rcoOption.NormalFont) { Alignment = i_nAlign }; _dDocument.Add(p); } public string WriteImage(string i_sFilePath, int i_nAlign = PdfContentByte.ALIGN_LEFT, ImageScale i_isScale = ImageScale.IS_KeepOrigin) { string sMsg = null; if (System.IO.File.Exists(i_sFilePath)) { Image image = Image.GetInstance(filename: i_sFilePath); switch (i_isScale) { case ImageScale.IS_KeepOrigin: { _dDocument.Add(image); } break; case ImageScale.IS_Width: { float fNewWidth = _rcoOption.PageSize.Width - _rcoOption.PageMarginRight - _rcoOption.PageMarginLeft; float fHeight = fNewWidth * image.Height / image.Width; image.ScaleToFit(fNewWidth, fHeight); _dDocument.Add(image); } break; case ImageScale.IS_FitWidth: { float fNewWidth = _rcoOption.PageSize.Width; float fHeight = fNewWidth * image.Height / image.Width; float fY = _pfWriter.GetVerticalPosition(true); image.ScaleToFit(fNewWidth, fHeight); image.SetAbsolutePosition(0, fY - fHeight); Paragraph p = new Paragraph { image }; _dDocument.Add(p); int nLine = (int)Math.Ceiling(fHeight / dFontHeight / 2) + 2; for (int n = 0; n < nLine; n++) // Workaround for vertical shift { WriteText(); } } break; default: break; } } else { sMsg = $"Image not exist! Path = {i_sFilePath}"; } return sMsg; } public void WriteTable(CellTable i_ctTableInfo, int i_nAlign = PdfContentByte.ALIGN_CENTER) { List lf = new List(); for (int nIdx = 0; nIdx < i_ctTableInfo.ColumnNum; nIdx++) { lf.Add(i_ctTableInfo.ColumnWidth); } PdfPTable pptTable = GeneratePTable(lf.ToArray(), 0); foreach (DataCell dc in i_ctTableInfo.Cells) { if (dc.CellType == DataCell.EDC_TYPE.EDC_CONTENT) { pptTable.AddCell(GeneratePCell(dc.Content, dc.RowSpan, dc.ColumnSpan, dc.Align, dc.VAlign)); } else if (dc.CellType == DataCell.EDC_TYPE.EDC_EMPTY) { StringBuilder sb = new StringBuilder(); for (int nIdx = 0; nIdx < dc.RowSpan; nIdx++) { sb.Append(Environment.NewLine); } PdfPCell pc = new PdfPCell(new Phrase(sb.ToString())) { Colspan = dc.ColumnSpan }; if(dc.Height != -1) { pc.FixedHeight = dc.Height; } pptTable.AddCell(pc); } } Paragraph para = new Paragraph { Alignment = i_nAlign }; para.Add(pptTable); _dDocument.Add(para); } public void WriteTable(TableMeta i_tmMeta, int i_nAlign = PdfContentByte.ALIGN_CENTER) { PdfPTable pptTable = GeneratePTable(i_tmMeta.Columns.Select(f => f.Width).ToArray()); string[] asTitles = i_tmMeta.Columns.Select(f => f.Title).ToArray(); foreach (string sTitle in asTitles) { pptTable.AddCell(GeneratePCell(sTitle, align: Element.ALIGN_CENTER)); } DataTable dtData = i_tmMeta.Table; // 表格內容 foreach (DataRow row in dtData.Rows) { foreach (ColumnMeta cm in i_tmMeta.Columns) { var cellValue = GetValue(row, cm); pptTable.AddCell(GeneratePCell(cellValue, align: Element.ALIGN_CENTER)); } } var cell = GeneratePCell(string.Format("Total records: {0}", dtData.Rows.Count), 1, i_tmMeta.Columns.Count, Element.ALIGN_LEFT); pptTable.AddCell(cell); Paragraph para = new Paragraph { Alignment = i_nAlign }; para.Add(pptTable); _dDocument.Add(para); } public void WriteTable(PdfPTable i_pptTable, int i_nAlign = PdfContentByte.ALIGN_CENTER) { Paragraph para = new Paragraph { Alignment = i_nAlign }; para.Add(i_pptTable); _dDocument.Add(para); } public void WriteTable(DataTable i_dtTable, int i_nAlign = PdfContentByte.ALIGN_CENTER) { Paragraph para = new Paragraph(); PdfPTable pdfTable = GenerateTable(i_dtTable); para.Alignment = i_nAlign; para.Add(pdfTable); _dDocument.Add(para); } protected PdfPTable GenerateTable(DataTable dataTable) { // 欄位設定 DataColumn[] columns = dataTable.Columns.Cast().OrderBy(x => x.Ordinal).ToArray(); float[] columnWidths = GetColumnWidths(columns); PdfPTable pdfTable = GeneratePTable(columnWidths, 1); // 表頭 var captions = columns.Select(x => x.Caption ?? x.ColumnName); foreach (var caption in captions) { var cell = GeneratePCell(caption); pdfTable.AddCell(cell); } // 表格內容 foreach (DataRow row in dataTable.Rows) { foreach (var column in columns) { var cellValue = GetValue(row, column); var cell = GeneratePCell(cellValue, align: Element.ALIGN_CENTER); pdfTable.AddCell(cell); } } // 表尾 // 如果不存在指定變數,或變數設定false就不顯示,反之顯示 if (dataTable.ExtendedProperties["RowCount"] as bool? != false) { var cell = GeneratePCell(string.Format("總計: {0} 筆紀錄", dataTable.Rows.Count), 1, columns.Length, Element.ALIGN_LEFT); pdfTable.AddCell(cell); } return pdfTable; } public string GetValue(DataRow row, ColumnMeta column) { if (!row.Table.Columns.Contains(column.Title)) { return ""; } DataColumn dc = row.Table.Columns[column.Title]; var value = row[dc]; // 當有設定欄位時,以欄位為主 if (column.DisplayFormat != null) { return string.Format(column.DisplayFormat, value); } var type = dc.DataType; if (type == typeof(int)) { return string.Format("{0:N0}", value); } else if (type == typeof(double) || type == typeof(decimal)) { return string.Format("{0:0,0.##}", value); } else if (type == typeof(DateTime)) { return string.Format("{0:yyyy/MM/dd HH\\:mm\\:ss}", value); } else if (type == typeof(TimeSpan)) { return string.Format("{0:hh\\:mm}", value); } return value.ToString(); } /// /// 新增表格 /// /// 最多幾個儲存格 /// 固定標題列 /// public string GetValue(DataRow row, DataColumn column) { var value = row[column]; if (value == null) { return ""; } // 當有設定欄位時,以欄位為主 var format = column.ExtendedProperties["Format"] as string; if (!string.IsNullOrEmpty(format)) { return string.Format(format, value); } var type = column.DataType; if (type == typeof(int)) { return string.Format("{0:N0}", value); } else if (type == typeof(double) || type == typeof(decimal)) { return string.Format("{0:0,0.##}", value); } else if (type == typeof(DateTime)) { return string.Format("{0:yyyy/MM/dd HH\\:mm}", value); } else if (type == typeof(TimeSpan)) { return string.Format("{0:hh\\:mm}", value); } return value.ToString(); } protected float[] GetColumnWidths(DataColumn[] columns) { return columns.Select(x => x.ExtendedProperties.ContainsKey("Width") ? Convert.ToSingle(x.ExtendedProperties["Width"]) : 80f).ToArray(); } public PdfPTable GeneratePTable(float[] columnWidths, int headerRows = 1) { var table = new PdfPTable(columnWidths.Length) { HeaderRows = headerRows }; table.SetTotalWidth(columnWidths); table.SetWidths(columnWidths); table.LockedWidth = true; return table; } public PdfPCell GeneratePCell(string text, int rowspan = 1, int colspan = 1, int align = Element.ALIGN_LEFT, int verticalAlign = Element.ALIGN_MIDDLE) { return new PdfPCell(new Phrase(text, _rcoOption.NormalFont)) { PaddingBottom = 5, HorizontalAlignment = align, VerticalAlignment = verticalAlign, Rowspan = rowspan, Colspan = colspan }; } } }