動態產生pdf檔案--從itextsharp到pdfsharp
手邊的某個專案原來使用itextsharp繪製表格。但因為 itextsharp新版本AGPL的要求,為免後續爭議,必須轉換改用其他解決方案,例如Pdfsharp。但在繪製表格時,itextsharp與pdfsharp有明顯的不同,iTextSharp 有內建的 PdfPTable 類別,支援多樣化的表格操作,例如跨列、跨欄、邊框樣式、對齊、顏色、甚至可以在表格中插入圖片或其他元素。對於需要高度客製化的表格,iTextSharp 很強大。PDFsharp則沒有原生表格 API。你必須透過手動計算格線位置,使用 gfx.DrawRectangle 和 gfx.DrawString 等來「畫出」一張表格。代表需要更多低階的控制與程式碼。
當然,使用 PDFsharp 時,可以完全控制繪圖行為,包括只畫出每一列的「底線」。只要在畫表格的時候略過左右與上方的線段,只保留底部的那條線即可。
不過,有個壞消息是,PDFsharp 本身不具備內建解析 HTML 的功能,也就是說,單靠 PDFsharp 無法自動將含有 HTML tag 的文本轉換成對應格式的 PDF 內容。開發人員必須仰賴第三方的擴充套件,例如HtmlRenderer.PdfSharp ,以實現這類功能,將 HTML(包含 CSS 樣式)渲染成 PDF 的內容。基本上,它的做法是通過先將 HTML 渲染到一個圖形物件,再將該圖形物件繪製到 PDF 中,這樣就能夠模擬出 HTML 內容的顯示效果。
另外,PDFsharp 是一個基於繪圖 API 的低階工具,它不提供像文檔排版引擎那樣的自動「流式佈局」功能,也就是說,它不會自動維護目前文檔中已經繪製到哪個位置的座標。這需要你自己來管理目前的「游標位置」(例如目前的 X 與 Y 值)。當你處理動態生成 PDF 並需要換頁時,通常會建立一個頁面佈局管理器,用來計算每個元素的擺放位置以及是否需要新增頁面。基本的邏輯如下:
- 設定頁面邊界與有效區域
通常你會定義固定的頁面邊界(例如上、下、左、右邊距),有效區域就是頁面的實際可用繪製空間。
舉例來說,若頁面高度為 800,而上下邊距分別都是 50,有效高度就是 800 - 50 - 50 = 700。 - 追蹤當前游標(Coordinate Cursor)
你需要保存一個currentY(以及橫向的currentX,但表格一般固定左側起點),這代表目前內容繪製到了頁面上的哪個位置。
以下是chatgpt提供的第一個範例。
using PdfSharp.Drawing;
using PdfSharp.Pdf;
public class ComplexTableGenerator
{
public void GeneratePdf(string filePath)
{
PdfDocument document = new PdfDocument();
document.Info.Title = "複雜表格範例";
PdfPage page = document.AddPage();
XGraphics gfx = XGraphics.FromPdfPage(page);
XFont headerFont = new XFont("Arial", 12, XFontStyle.Bold);
XFont cellFont = new XFont("Arial", 10, XFontStyle.Regular);
// 表格設定
int startX = 50;
int startY = 100;
int rowHeight = 30;
int[] colWidths = { 80, 150, 100, 120 };
string[] headers = { "編號", "名稱", "數量", "備註" };
string[,] data = {
{ "A001", "紅色物件", "12", "特別需求" },
{ "A002", "藍色物件", "30", "標準品" },
{ "A003", "綠色物件", "5", "需要測試" }
};
// 繪製表頭
int x = startX;
for (int i = 0; i < headers.Length; i++)
{
gfx.DrawRectangle(XPens.Black, x, startY, colWidths[i], rowHeight);
gfx.DrawString(headers[i], headerFont, XBrushes.Black,
new XRect(x, startY, colWidths[i], rowHeight),
XStringFormats.Center);
x += colWidths[i];
}
// 繪製資料列
for (int row = 0; row < data.GetLength(0); row++)
{
x = startX;
int y = startY + rowHeight * (row + 1);
for (int col = 0; col < data.GetLength(1); col++)
{
gfx.DrawRectangle(XPens.Black, x, y, colWidths[col], rowHeight);
gfx.DrawString(data[row, col], cellFont, XBrushes.Black,
new XRect(x + 5, y + 5, colWidths[col] - 10, rowHeight - 10),
XStringFormats.TopLeft);
x += colWidths[col];
}
}
document.Save(filePath);
}
}
以下是範例程式碼的調整重點(只畫每列底線):
// 繪製資料列底線(不畫整個矩形)
for (int row = 0; row < data.GetLength(0); row++)
{
int y = startY + rowHeight * (row + 1);
int x = startX;
for (int col = 0; col < data.GetLength(1); col++)
{
int colWidth = colWidths[col];
// 畫儲存格文字
gfx.DrawString(data[row, col], cellFont, XBrushes.Black,
new XRect(x + 5, y - rowHeight + 5, colWidth - 10, rowHeight - 10),
XStringFormats.TopLeft);
// 畫底線
gfx.DrawLine(XPens.Black, x, y, x + colWidth, y);
x += colWidth;
}
}
pdfsharp建立段落與排版的指令,還有繪製表格的指令摘要:
using PdfSharp.Drawing;
using PdfSharp.Pdf;
// 建立文件與頁面
PdfDocument document = new PdfDocument();
document.Info.Title = "PDFsharp 段落與表格示範";
PdfPage page = document.AddPage();
XGraphics gfx = XGraphics.FromPdfPage(page);
//排版
XFont paragraphFont = new XFont("Arial", 12, XFontStyle.Regular);XRect textRect = new XRect(50, 50, 500, 100); // (左上角 x, y, 寬, 高)gfx.DrawString("這是一段使用 PDFsharp 排版的文字,內容自動換行(如果文字超出寬度)。",paragraphFont, XBrushes.Black, textRect, XStringFormats.TopLeft);
//複雜排版
using PdfSharp.Drawing.Layout;XTextFormatter tf = new XTextFormatter(gfx);XFont formatterFont = new XFont("Times New Roman", 12, XFontStyle.Regular);XRect formatterRect = new XRect(50, 150, 500, 200);tf.DrawString("這是一段由 XTextFormatter 處理的文字,可有效處理內容換行與排版。",formatterFont, XBrushes.Black, formatterRect, XStringFormats.TopLeft);
//繪製表格
int startX = 50;
int startY = 400;
int rowHeight = 30;
int[] colWidths = { 80, 150, 100, 120 };
string[] headers = { "編號", "名稱", "數量", "備註" };
XFont headerFont = new XFont("Arial", 12, XFontStyle.Bold);
int x = startX;
for (int i = 0; i < headers.Length; i++)
{
// 畫矩形框(可選擇只畫部分線條)
gfx.DrawRectangle(XPens.Black, x, startY, colWidths[i], rowHeight);
// 畫表頭文字,置中對齊
gfx.DrawString(headers[i], headerFont, XBrushes.Black,
new XRect(x, startY, colWidths[i], rowHeight),
XStringFormats.Center);
x += colWidths[i];
}
留言
張貼留言