iT邦幫忙

8

[C#][iTextSharp] PDF 套版

2022/08 更新

我根據文章內容製作了 Nuget 套件。

PM> Install-Package PdfTemplate.iTextSharp.LGPLv2

這個套件內部使用 iTextSharp.LGPLv2 生成 pdf 文件,因此它的授權較 iTextSharp 和 iText 7 更寬鬆。

用法可以參考 Github 內容。
https://github.com/fysh711426/PdfTemplate.iTextSharp.LGPLv2


前言

做報表常會用到 PDF 套版,套版的類型很多,文字、顏色、圖片、Radio、CheckBox、等等...,有時候還要分頁,所以想把這些需求整理起來。

範本製作

範本一般會用 Word 畫出表格後 匯出成 PDF,再用 Adobe Acrobat 將需要填入資料的地方加入表格元件。

下圖為 Word 製作的基本資料表格。

https://ithelp.ithome.com.tw/upload/images/20171221/20106865Yk61Swu64U.jpg

將其匯出成 PDF,再用 Adobe Acrobat 開啟,點選右上角的 工具 開啟選單,然後選擇 表格 內的 編輯

https://ithelp.ithome.com.tw/upload/images/20171221/20106865E9ITqxgxpZ.jpg

第一次編輯會詢問 是否自動偵測表格,這裡選 可以讓 Acrobat 自動偵測並建立表格,剩下的再手動調整。

https://ithelp.ithome.com.tw/upload/images/20171221/201068650Hx9pxNz5p.jpg

點擊欄位兩下開啟 內容視窗,將 名稱 改成給程式對應的欄位名稱。

https://ithelp.ithome.com.tw/upload/images/20171221/20106865089ltqzrkB.jpg

RadioButton

性別欄位只有男或女且單選,所以用 RadioButton,點選右上角新增欄位,選擇選項按鈕

https://ithelp.ithome.com.tw/upload/images/20171221/201068659W0AdxK3hG.jpg

RadioButton 為同群組單選的元件,其 名稱 為群組,選項按鈕選擇 為被選取的值,這裡新增男女兩個 RadioButton,將名稱改為 SexGroup,選項按鈕選擇分別改為 MaleFemale

https://ithelp.ithome.com.tw/upload/images/20171221/20106865IZ24MVzILf.jpg

CheckBox

興趣欄位可多選,所以用 CheckBox,點選右上角 新增欄位,選擇 核取框

https://ithelp.ithome.com.tw/upload/images/20171221/20106865eiW5NP5TGC.jpg

CheckBox 不像 RadioButton 有 群組 概念,每個 CheckBox 都有自己的名稱,判斷是否被勾選,要看內容視窗裡的 轉存值,預設為 ,只要程式丟過來的值等於 轉存值,CheckBox 就會被勾選。

https://ithelp.ithome.com.tw/upload/images/20171221/20106865EXCczhGJIg.jpg

Image

圖片套版會使用 按鈕 元件,將背景色設為 白色,利用按鈕的 icon 來替換圖片。

https://ithelp.ithome.com.tw/upload/images/20171221/201068650SCkAsRivY.jpg

開啟內容視窗改變背景色。

https://ithelp.ithome.com.tw/upload/images/20171221/20106865EoMItEdUq8.jpg

Barcode

Barcode 部分不用更改,使用 文字欄位 即可。

結果

完整程式太長就不貼上來,下方分享常用功能。

第一頁
https://ithelp.ithome.com.tw/upload/images/20171221/20106865PFE6eusNaP.jpg

第二頁
https://ithelp.ithome.com.tw/upload/images/20171221/20106865Mrnv9FL8Of.jpg


常用功能程式碼

會用到 iTextSharp 可用 Nuget 安裝。

  • iTextSharp 主結構
//可以是任何 Stream 類型 (MemoryStream、FileStream、OutputStream)
var stream = new MemoryStream();

//建立文件
using (var doc = new Document())
{
    using (var writer = PdfWriter.GetInstance(doc, stream))
    {
        doc.Open();

        //操作 PDF ...

        doc.Close();
    }
}
  • 文字
using (var ms = new MemoryStream())
{
    using (var reader = new PdfReader("範本檔路徑"))
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //取得表單
            var form = stamper.AcroFields;
            //套入文字
            form.SetField("Name", "王小明");
        }
    }
}
  • 文字顏色
using (var ms = new MemoryStream())
{
    using (var reader = new PdfReader("範本檔路徑"))
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //取得表單
            var form = stamper.AcroFields;
            //套入文字
            form.SetField("Name", "王小明");
            //套入顏色
            var color = "#FF0000";   //紅色
            var baseColor = new BaseColor(System.Drawing.ColorTranslator.FromHtml(color));
            form.SetFieldProperty("Name", "textcolor", baseColor, null);
        }
    }
}
  • RadioButton 和 CheckBox
using (var ms = new MemoryStream())
{
    using (var reader = new PdfReader("範本檔路徑"))
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //取得表單
            var form = stamper.AcroFields;
            //RadioButton
            form.SetField("Group", "男", true);
            //CheckBox
            form.SetField("Name", "是", true);
        }
    }
}
  • 圖片
using (var ms = new MemoryStream())
{
    using (var reader = new PdfReader("範本檔路徑"))
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //取得表單
            var form = stamper.AcroFields;
            //取得圖片
            var image = iTextSharp.text.Image.GetInstance("圖片檔路徑");
            //取得按鈕元件
            var pushbuttonField = form.GetNewPushbuttonFromField("Name");
            //指定為 ICON
            pushbuttonField.Layout = PushbuttonField.LAYOUT_ICON_ONLY;
            //圖片等比縮放
            pushbuttonField.ProportionalIcon = true;
            pushbuttonField.Image = image;
            //取代原按鈕
            form.ReplacePushbuttonField("Name", pushbuttonField.Field);
        }
    }
}
  • Barcode39
using (var ms = new MemoryStream())
{
    using (var reader = new PdfReader("範本檔路徑"))
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //取得表單
            var form = stamper.AcroFields;
            //取得欄位位置資訊
            var fieldPosition = form.GetFieldPositions("Name")[0];
            var pdfContentByte = stamper.GetOverContent(fieldPosition.page);
            var barcode = new Barcode39();
            //barcode 內容
            barcode.Code = "123456789";
            //顯示文字是否前後加上星號
            barcode.StartStopText = false;
            //條碼線最小寬度
            barcode.X = 0.8f;
            //設置墨水擴散量,從每個條碼線中減去該值
            barcode.InkSpreading = 0f;
            //為某些類型保留條碼線的乘數
            barcode.N = 2f;
            //字體大小
            barcode.Size = 10f;
            //文本和條形碼之間的距離
            barcode.Baseline = 10f;
            //barcode 高度
            barcode.BarHeight = 20f;
            barcode.GenerateChecksum = false;
            barcode.ChecksumText = false;
            //產生 barcode 圖片
            var image = barcode.CreateImageWithBarcode(pdfContentByte, BaseColor.BLACK, BaseColor.BLACK);
            var rect = fieldPosition.position;
            //設定圖片座標,以欄位的左下角為原點
            image.SetAbsolutePosition(rect.Left, rect.Bottom);
            pdfContentByte.AddImage(image);
        }
    }
}
  • 插入段落
using (var ms = new MemoryStream())
{
    using (var reader = new PdfReader("範本檔路徑"))
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //取得表單
            var form = stamper.AcroFields;
            //新增段落
            var paragraph = new Paragraph("abc");
            //取得欄位位置資訊
            var fieldPositions = form.GetFieldPositions("Name");
            //設定行距
            paragraph.Leading = 30;
            //設定第一列縮排
            paragraph.FirstLineIndent = 24;
            //設定對齊方式
            paragraph.Alignment = iTextSharp.text.Element.ALIGN_JUSTIFIED;

            foreach (var position in fieldPositions)
            {
                var pdfContentByte = stamper.GetOverContent(position.page);
                var columnText = new ColumnText(pdfContentByte);

                //ColumnText 呼叫 AddElement 後,自動由 Text 模式轉為 Composite 模式
                columnText.AddElement(paragraph);
                //設定 ColumnText 邊界
                columnText.SetSimpleColumn(position.position.Left, position.position.Bottom, position.position.Right, position.position.Top);
                columnText.Go();
            }
        }
    }
}
  • 插入浮水印
using (var ms = new MemoryStream())
{
    using (var reader = new PdfReader("範本檔路徑"))
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //取得表單
            var form = stamper.AcroFields;
            //取得欄位位置資訊
            var fieldPositions = form.GetFieldPositions("");
            foreach (var position in fieldPositions)
            {
                var pdfContentByte = stamper.GetUnderContent(position.page);
                pdfContentByte.BeginText();
                var baseFont = BaseFont.CreateFont("字型檔路徑", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
                //設定字型和字體大小
                pdfContentByte.SetFontAndSize(baseFont, 120);
                //設定字體顏色
                pdfContentByte.SetRGBColorFill(210, 210, 210);
                //設定浮水印透明度
                pdfContentByte.SetGState(new PdfGState
                {
                    FillOpacity = 0.6f,    //背景
                    StrokeOpacity = 0.6f   //文字
                });
                //取得當前頁面的位置資訊
                var rect = reader.GetPageSizeWithRotation(position.page);
                var x = (rect.Right + rect.Left) / 2;   //取得頁面 x 軸中心座標
                var y = (rect.Bottom + rect.Top) / 2;   //取得頁面 y 軸中心座標
                //設定浮水印
                pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "浮水印", x, y, 45);
                pdfContentByte.EndText();
            }
        }
    }
}
  • 防止表單編輯
using (var ms = new MemoryStream())
{
    using (var reader = new PdfReader("範本檔路徑"))
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //表單扁平化,防止表單編輯
            stamper.FormFlattening = true;
        }
    }
}
  • 設定字型
var baseFont1 = BaseFont.CreateFont("字型檔1路徑", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
var baseFont2 = BaseFont.CreateFont("字型檔2路徑", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

//表單設定字型
stamper.AcroFields.AddSubstitutionFont(baseFont1);
stamper.AcroFields.AddSubstitutionFont(baseFont2);

//段落設定字型
var font1 = new Font(baseFont1, 12f, Font.NORMAL);
var font2 = new Font(baseFont2, 12f, Font.NORMAL);

var selector = new iTextSharp.text.pdf.FontSelector();
selector.AddFont(font1);
selector.AddFont(font2);

var paragraph = new iTextSharp.text.Paragraph();
paragraph.Add(selector.Process("abc一二三"));
  • 合併 PDF 產生分頁
//要合併的多個 PDF 文件
var files = new List<byte[]>();

using (var doc = new Document())
{
    using (var writer = PdfWriter.GetInstance(doc, stream))
    {
        doc.Open();

        var contentByte = writer.DirectContent;

        foreach(var file in files)
        {
            using (var reader = new PdfReader(file))
            {
                for (var i = 1; i <= reader.NumberOfPages; i++)
                {
                    //設定頁面大小為當前範本的頁面的大小
                    doc.SetPageSize(reader.GetPageSize(i));
                    //產生新頁面
                    doc.NewPage();
                    //將 Reader 轉為 PdfImportedPage
                    var newPage = writer.GetImportedPage(reader, i);
                    //插入新頁面
                    contentByte.AddTemplate(newPage, 0, 0);
                }
                //釋放 reader
                writer.FreeReader(reader);
            }
        }
        
        doc.Close();
    }
}

參考文章

使用ASP .NET (C#) 產生PDF檔的好幫手—iTextSharp library (上)
c#使用itextsharp输出pdf(动态填充表单内容,显示中文)
How to change the text color of an AcroForm field?
使用iTextSharp進行PDF檔案套版
iTextSharp Fill Pdf Form Image Field
Code 39/128 Barcode Image Generator
[iTextSharp学习笔记]在指定的位置上添加文本
CSharp - 在每個頁面上,使用水印創建 C# itextsharp PDF
如何使用itextsharp将表单字段添加到现有的pdf中?
Missing character in custom font
itextsharp - 如何使用iTextSharp組合多個不包括分頁符的PDF文件?


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
2
小魚
iT邦大師 1 級 ‧ 2017-09-30 19:26:33

這套件我也有用過,
還不錯用,
不過我是拿來做表格的。

表格也可以套板

學號 姓名
Id1 Name1
Id2 Name2
... ...
在範本裡面先把整個表格排滿,哈哈。
因為很懶所以都會想用範本做,可以省很多工。
小魚 iT邦大師 1 級 ‧ 2017-09-30 20:04:38 檢舉

前提是你會常用到那些範本,
要不然做範本只在一個地方用到也沒有意義 @@

恩恩,還是要看需求使用。
/images/emoticon/emoticon12.gif

0
curry_sun
iT邦新手 5 級 ‧ 2018-11-14 09:12:18

大大的文章真是幫助極大,目前接手的案子就是需要用到填表格的功能,過去是使用讀取excel表填寫再轉PDF輸出,看到這篇之後終於有其他解法啦/images/emoticon/emoticon02.gif,謝謝分享

表單如果欄位固定很適合用此方法,不怕客戶改樣式、開發快速
缺點是欄位無法動態、產生的檔案較大
不過我覺得優點還是更多些
/images/emoticon/emoticon37.gif

curry_sun iT邦新手 5 級 ‧ 2018-12-04 14:37:08 檢舉

請問如果用這種方式,是不是需要使用Acrobat Pro DC才能規劃表格呢?

只要支援表格編輯的 PDF 軟體都可以

0
wayneli
iT邦新手 5 級 ‧ 2018-11-16 15:03:50

最近專案也有將資料讀入PDF匯出的需求,除了iTextSharp還有搜尋到一款ironpdf,好像也蠻好用的版主有使用過嗎??

這套看起來偏 Html 轉 PDF
以前試過的 Html 轉 PDF 套件一般都會跑版
所以還是喜歡套版或用刻的

0
summer10316
iT邦新手 5 級 ‧ 2022-08-08 15:48:40

不好意思,最近正在學習MVC,正好需要用到此功能,可是一直出現問題,並且上網爬文都沒有找到解決方案,請問版主大大可以幫忙看一下嗎, 謝謝~~
https://ithelp.ithome.com.tw/upload/images/20220808/201511960jAzpAelrY.png

程式碼

 public void  DownloadPdf(int id)
        {
            //然後把值傳進Post_ed_details裡
            var classes = db.ClassDetails.Where(m => m.Class_Id == id).FirstOrDefault();
            //可以是任何 Stream 類型 (MemoryStream、FileStream、OutputStream)
            var stream = new MemoryStream();


            DateTime nowdate = DateTime.Now;
            //下拉式選單把數字轉成文字
            int starttimeid = (int)classes.Class_time_start;
            int endtimeid = (int)classes.Class_time_end;
            int departmentid = (int)classes.Class_department;

            var startTimes = db.ed_Create_time.Where(m => m.ed_Time_id == starttimeid).FirstOrDefault();
            var endTimes = db.ed_Create_time.Where(m => m.ed_Time_id == endtimeid).FirstOrDefault();
            var departments = db.ed_Create_department.Where(m => m.department_id == departmentid).FirstOrDefault();

            classes.ed_Time_namestart = startTimes.ed_Time_value;
            classes.ed_Time_nameend = endTimes.ed_Time_value;
            classes.department_name = departments.department_value;

            //建立文件
            using (var doc = new Document())
            {
                using (var writer = PdfWriter.GetInstance(doc, stream))
                {
                    doc.Open();

                    //操作 PDF ...
                    using (var ms = new MemoryStream())
                    {
                        using (var reader = new PdfReader(@"D:\123.pdf"))
                        {
                            using (var stamper = new PdfStamper(reader, ms))
                            {
                       //取得表單
                       var form = stamper.AcroFields;
                       //套入文字
                       form.SetField("department", classes.department_name);
                       form.SetField("em_id", classes.Em_Id);
                       form.SetField("em_name", classes.Em_Name);
                       form.SetField("class_name", classes.Class_Name);
                       form.SetField("class_place", classes.Class_Place);
                     form.SetField("Class_date_start", classes.Class_date_start);
                       form.SetField("Class_date_end", classes.Class_date_end);
                    form.SetField("Class_time_start", classes.ed_Time_namestart);
                       form.SetField("Class_time_end", classes.ed_Time_nameend);
                       form.SetField("duration", classes.Class_Duration);
                       form.SetField("cost", classes.Class_Cost);
                       form.SetField("organizer", classes.Class_Organizer);
                            }
                        }
                    }

                    doc.Close();
                }
            }
        }

        public ActionResult pdf(int id)
        {
            DownloadPdf(id);
            return View();
        }
看更多先前的回應...收起先前的回應...
using (var ms = new MemoryStream())

要放在 doc 之外

using (var doc = new Document())

謝謝你上面的解答,我已經可以輸出成一個pdf,只是裡面的表格無法正常顯示

開啟pdf看到的畫面↓
https://ithelp.ithome.com.tw/upload/images/20220810/20151196Lo1VncVfHI.png
像是這張圖只有用滑鼠點才會顯示↓
https://ithelp.ithome.com.tw/upload/images/20220810/20151196aGYwl9cNee.png
https://ithelp.ithome.com.tw/upload/images/20220810/20151196efRlvEziUW.png

請問此問題要如何解決,謝謝~

試試在 stamper 的最後加上

stamper.FormFlattening = true;

會全部變成空白

可能是缺少字型檔。

var baseFont = BaseFont.CreateFont("字型路徑.ttf", 
    BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
stamper.AcroFields.AddSubstitutionFont(baseFont);

原來是我沒看清楚 謝謝大大時間幫我解決問題 感謝~

我根據文章內容製作了套件
方便大家使用,可以參考看看
https://github.com/fysh711426/PdfTemplate.iTextSharp.LGPLv2

如果你要做分頁、多範本、插入圖片或浮水印等,推薦使用

我要留言

立即登入留言