You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

414 lines
13 KiB

  1. 
  2. namespace ReportEngine
  3. {
  4. using iTextSharp.text;
  5. using iTextSharp.text.pdf;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Data;
  9. using System.Linq;
  10. using System.Text;
  11. public class ReportPainter
  12. {
  13. public enum ImageScale
  14. {
  15. IS_KeepOrigin = 0,
  16. IS_Width = 1,
  17. IS_FitWidth = 2
  18. }
  19. public class ColumnMeta
  20. {
  21. public string Title { get; set; }
  22. public float Width { get; set; }
  23. public string DisplayFormat { get; set; }
  24. }
  25. public class TableMeta
  26. {
  27. public DataTable Table { get; set; }
  28. public List<ColumnMeta> Columns { get; set; }
  29. }
  30. public class DataCell
  31. {
  32. public enum EDC_TYPE
  33. {
  34. EDC_CONTENT = 0,
  35. EDC_EMPTY = 1
  36. }
  37. public int ColumnSpan { get; set; } = 1;
  38. public int RowSpan { get; set; } = 1;
  39. public int Align { get; set; } = Element.ALIGN_LEFT;
  40. public int VAlign { get; set; } = Element.ALIGN_MIDDLE;
  41. public string Content { get; set; }
  42. public EDC_TYPE CellType { get; set; } = EDC_TYPE.EDC_CONTENT;
  43. public float Height { get; set; } = -1;
  44. }
  45. public class CellTable
  46. {
  47. public int ColumnNum { get; set; } = 10;
  48. public float ColumnWidth { get; set; } = 75;
  49. public List<DataCell> Cells { get; set; }
  50. }
  51. readonly Document _dDocument;
  52. readonly ReportCanvasOptions _rcoOption;
  53. readonly PdfWriter _pfWriter;
  54. readonly double dFontHeight;
  55. public ReportPainter(Document i_dDocument, ReportCanvasOptions i_rcoOption, PdfWriter i_pfWriter)
  56. {
  57. _dDocument = i_dDocument;
  58. _rcoOption = i_rcoOption;
  59. _pfWriter = i_pfWriter;
  60. float ascent = _rcoOption.BaseFont.GetAscentPoint("Some String", _rcoOption.FontSize);
  61. float descent = _rcoOption.BaseFont.GetDescentPoint("Some String", _rcoOption.FontSize);
  62. dFontHeight = ascent - descent;
  63. }
  64. public void NewPage() => _dDocument.NewPage();
  65. public void WriteText(string text = "\r\n", int i_nAlign = PdfContentByte.ALIGN_LEFT, Font font = null)
  66. {
  67. Paragraph p = new Paragraph(text, font ?? _rcoOption.NormalFont)
  68. {
  69. Alignment = i_nAlign
  70. };
  71. _dDocument.Add(p);
  72. }
  73. public string WriteImage(string i_sFilePath, int i_nAlign = PdfContentByte.ALIGN_LEFT, ImageScale i_isScale = ImageScale.IS_KeepOrigin)
  74. {
  75. string sMsg = null;
  76. if (System.IO.File.Exists(i_sFilePath))
  77. {
  78. Image image = Image.GetInstance(filename: i_sFilePath);
  79. switch (i_isScale)
  80. {
  81. case ImageScale.IS_KeepOrigin:
  82. {
  83. _dDocument.Add(image);
  84. }
  85. break;
  86. case ImageScale.IS_Width:
  87. {
  88. float fNewWidth = _rcoOption.PageSize.Width - _rcoOption.PageMarginRight - _rcoOption.PageMarginLeft;
  89. float fHeight = fNewWidth * image.Height / image.Width;
  90. image.ScaleToFit(fNewWidth, fHeight);
  91. _dDocument.Add(image);
  92. }
  93. break;
  94. case ImageScale.IS_FitWidth:
  95. {
  96. float fNewWidth = _rcoOption.PageSize.Width;
  97. float fHeight = fNewWidth * image.Height / image.Width;
  98. float fY = _pfWriter.GetVerticalPosition(true);
  99. image.ScaleToFit(fNewWidth, fHeight);
  100. image.SetAbsolutePosition(0, fY - fHeight);
  101. Paragraph p = new Paragraph
  102. {
  103. image
  104. };
  105. _dDocument.Add(p);
  106. int nLine = (int)Math.Ceiling(fHeight / dFontHeight / 2) + 2;
  107. for (int n = 0; n < nLine; n++) // Workaround for vertical shift
  108. {
  109. WriteText();
  110. }
  111. }
  112. break;
  113. default:
  114. break;
  115. }
  116. }
  117. else
  118. {
  119. sMsg = $"Image not exist! Path = {i_sFilePath}";
  120. }
  121. return sMsg;
  122. }
  123. public void WriteTable(CellTable i_ctTableInfo, int i_nAlign = PdfContentByte.ALIGN_CENTER)
  124. {
  125. List<float> lf = new List<float>();
  126. for (int nIdx = 0; nIdx < i_ctTableInfo.ColumnNum; nIdx++)
  127. {
  128. lf.Add(i_ctTableInfo.ColumnWidth);
  129. }
  130. PdfPTable pptTable = GeneratePTable(lf.ToArray(), 0);
  131. foreach (DataCell dc in i_ctTableInfo.Cells)
  132. {
  133. if (dc.CellType == DataCell.EDC_TYPE.EDC_CONTENT)
  134. {
  135. pptTable.AddCell(GeneratePCell(dc.Content, dc.RowSpan, dc.ColumnSpan, dc.Align, dc.VAlign));
  136. }
  137. else if (dc.CellType == DataCell.EDC_TYPE.EDC_EMPTY)
  138. {
  139. StringBuilder sb = new StringBuilder();
  140. for (int nIdx = 0; nIdx < dc.RowSpan; nIdx++)
  141. {
  142. sb.Append(Environment.NewLine);
  143. }
  144. PdfPCell pc = new PdfPCell(new Phrase(sb.ToString()))
  145. {
  146. Colspan = dc.ColumnSpan
  147. };
  148. if(dc.Height != -1)
  149. {
  150. pc.FixedHeight = dc.Height;
  151. }
  152. pptTable.AddCell(pc);
  153. }
  154. }
  155. Paragraph para = new Paragraph
  156. {
  157. Alignment = i_nAlign
  158. };
  159. para.Add(pptTable);
  160. _dDocument.Add(para);
  161. }
  162. public void WriteTable(TableMeta i_tmMeta, int i_nAlign = PdfContentByte.ALIGN_CENTER)
  163. {
  164. PdfPTable pptTable = GeneratePTable(i_tmMeta.Columns.Select(f => f.Width).ToArray());
  165. string[] asTitles = i_tmMeta.Columns.Select(f => f.Title).ToArray();
  166. foreach (string sTitle in asTitles)
  167. {
  168. pptTable.AddCell(GeneratePCell(sTitle, align: Element.ALIGN_CENTER));
  169. }
  170. DataTable dtData = i_tmMeta.Table;
  171. // 表格內容
  172. foreach (DataRow row in dtData.Rows)
  173. {
  174. foreach (ColumnMeta cm in i_tmMeta.Columns)
  175. {
  176. var cellValue = GetValue(row, cm);
  177. pptTable.AddCell(GeneratePCell(cellValue, align: Element.ALIGN_CENTER));
  178. }
  179. }
  180. var cell = GeneratePCell(string.Format("Total records: {0}", dtData.Rows.Count), 1, i_tmMeta.Columns.Count, Element.ALIGN_LEFT);
  181. pptTable.AddCell(cell);
  182. Paragraph para = new Paragraph
  183. {
  184. Alignment = i_nAlign
  185. };
  186. para.Add(pptTable);
  187. _dDocument.Add(para);
  188. }
  189. public void WriteTable(PdfPTable i_pptTable, int i_nAlign = PdfContentByte.ALIGN_CENTER)
  190. {
  191. Paragraph para = new Paragraph
  192. {
  193. Alignment = i_nAlign
  194. };
  195. para.Add(i_pptTable);
  196. _dDocument.Add(para);
  197. }
  198. public void WriteTable(DataTable i_dtTable, int i_nAlign = PdfContentByte.ALIGN_CENTER)
  199. {
  200. Paragraph para = new Paragraph();
  201. PdfPTable pdfTable = GenerateTable(i_dtTable);
  202. para.Alignment = i_nAlign;
  203. para.Add(pdfTable);
  204. _dDocument.Add(para);
  205. }
  206. protected PdfPTable GenerateTable(DataTable dataTable)
  207. {
  208. // 欄位設定
  209. DataColumn[] columns = dataTable.Columns.Cast<DataColumn>().OrderBy(x => x.Ordinal).ToArray();
  210. float[] columnWidths = GetColumnWidths(columns);
  211. PdfPTable pdfTable = GeneratePTable(columnWidths, 1);
  212. // 表頭
  213. var captions = columns.Select(x => x.Caption ?? x.ColumnName);
  214. foreach (var caption in captions)
  215. {
  216. var cell = GeneratePCell(caption);
  217. pdfTable.AddCell(cell);
  218. }
  219. // 表格內容
  220. foreach (DataRow row in dataTable.Rows)
  221. {
  222. foreach (var column in columns)
  223. {
  224. var cellValue = GetValue(row, column);
  225. var cell = GeneratePCell(cellValue, align: Element.ALIGN_CENTER);
  226. pdfTable.AddCell(cell);
  227. }
  228. }
  229. // 表尾
  230. // 如果不存在指定變數,或變數設定false就不顯示,反之顯示
  231. if (dataTable.ExtendedProperties["RowCount"] as bool? != false)
  232. {
  233. var cell = GeneratePCell(string.Format("總計: {0} 筆紀錄", dataTable.Rows.Count), 1, columns.Length, Element.ALIGN_LEFT);
  234. pdfTable.AddCell(cell);
  235. }
  236. return pdfTable;
  237. }
  238. public string GetValue(DataRow row, ColumnMeta column)
  239. {
  240. if (!row.Table.Columns.Contains(column.Title))
  241. {
  242. return "";
  243. }
  244. DataColumn dc = row.Table.Columns[column.Title];
  245. var value = row[dc];
  246. // 當有設定欄位時,以欄位為主
  247. if (column.DisplayFormat != null)
  248. {
  249. return string.Format(column.DisplayFormat, value);
  250. }
  251. var type = dc.DataType;
  252. if (type == typeof(int))
  253. {
  254. return string.Format("{0:N0}", value);
  255. }
  256. else if (type == typeof(double) || type == typeof(decimal))
  257. {
  258. return string.Format("{0:0,0.##}", value);
  259. }
  260. else if (type == typeof(DateTime))
  261. {
  262. return string.Format("{0:yyyy/MM/dd HH\\:mm\\:ss}", value);
  263. }
  264. else if (type == typeof(TimeSpan))
  265. {
  266. return string.Format("{0:hh\\:mm}", value);
  267. }
  268. return value.ToString();
  269. }
  270. /// <summary>
  271. /// 新增表格
  272. /// </summary>
  273. /// <param name="numColumns">最多幾個儲存格</param>
  274. /// <param name="headerRows">固定標題列</param>
  275. /// <returns></returns>
  276. public string GetValue(DataRow row, DataColumn column)
  277. {
  278. var value = row[column];
  279. if (value == null)
  280. {
  281. return "";
  282. }
  283. // 當有設定欄位時,以欄位為主
  284. var format = column.ExtendedProperties["Format"] as string;
  285. if (!string.IsNullOrEmpty(format))
  286. {
  287. return string.Format(format, value);
  288. }
  289. var type = column.DataType;
  290. if (type == typeof(int))
  291. {
  292. return string.Format("{0:N0}", value);
  293. }
  294. else if (type == typeof(double) || type == typeof(decimal))
  295. {
  296. return string.Format("{0:0,0.##}", value);
  297. }
  298. else if (type == typeof(DateTime))
  299. {
  300. return string.Format("{0:yyyy/MM/dd HH\\:mm}", value);
  301. }
  302. else if (type == typeof(TimeSpan))
  303. {
  304. return string.Format("{0:hh\\:mm}", value);
  305. }
  306. return value.ToString();
  307. }
  308. protected float[] GetColumnWidths(DataColumn[] columns)
  309. {
  310. return columns.Select(x => x.ExtendedProperties.ContainsKey("Width") ? Convert.ToSingle(x.ExtendedProperties["Width"]) : 80f).ToArray();
  311. }
  312. public PdfPTable GeneratePTable(float[] columnWidths, int headerRows = 1)
  313. {
  314. var table = new PdfPTable(columnWidths.Length)
  315. {
  316. HeaderRows = headerRows
  317. };
  318. table.SetTotalWidth(columnWidths);
  319. table.SetWidths(columnWidths);
  320. table.LockedWidth = true;
  321. return table;
  322. }
  323. public PdfPCell GeneratePCell(string text, int rowspan = 1, int colspan = 1, int align = Element.ALIGN_LEFT, int verticalAlign = Element.ALIGN_MIDDLE)
  324. {
  325. return new PdfPCell(new Phrase(text, _rcoOption.NormalFont))
  326. {
  327. PaddingBottom = 5,
  328. HorizontalAlignment = align,
  329. VerticalAlignment = verticalAlign,
  330. Rowspan = rowspan,
  331. Colspan = colspan
  332. };
  333. }
  334. }
  335. }