iT邦幫忙

5

[C#][iTextSharp] PDF 套版

前言

做報表常會用到 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

本來做好了漂亮的 GIF 結果圖,沒想到 IT邦不能上傳 GIF 格式的圖片,所以默默的換成了 2張 JPG。 QQ

常用功能程式碼

會用到 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.FontSelect();
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文件?


1
小魚
iT邦大師 1 級 ‧ 2017-09-30 19:26:33

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

fysh711426 iT邦研究生 2 級‧ 2017-09-30 19:34:00 檢舉

表格也可以套板

學號 姓名
Id1 Name1
Id2 Name2
... ...

在範本裡面先把整個表格排滿,哈哈。
因為很懶所以都會想用範本做,可以省很多工。

小魚 iT邦大師 1 級‧ 2017-09-30 20:04:38 檢舉

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

fysh711426 iT邦研究生 2 級‧ 2017-09-30 21:06:15 檢舉

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

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

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

fysh711426 iT邦研究生 2 級‧ 2018-11-14 09:28:00 檢舉

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

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

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

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

fysh711426 iT邦研究生 2 級‧ 2018-11-17 09:30:14 檢舉

沒有用過
這套看起來功能偏 Html 轉 PDF,不過不知道效果如何
以前試過的 Html 轉 PDF 一般都會跑版,所以還是喜歡套版或用刻的

我要留言

立即登入留言