使用iTextSharp来填充PDF模板文件

需求简介

遇到了这样一个需求:某公司需要为所有用户的培训生成一个培训记录,过程如下:

  1. 用户在培训完之后会进入到一个填写信息的界面。
  2. 用户填写信息。
  3. 生成PDF格式的培训记录。
  4. 用户下载并打印归档。

思路

因为每次培训后生成的PDF文件内容都不完全一样,但是格式却是完全相同的,所以很容易让人联想到使用模板文件。每次只需要提取用户输入的有效信息,然后复制模板、填充模板、弹出下载即可。

解决方案

(1)制作模板:我先使用Microsoft Office 2010编辑模板文件,在保存的时候将文件保存为pdf文件。然后用Adobe Acrobat X编辑刚刚生成的PDF文件,把Textbox、Checkbox等域拖动到指定的位置,然后通过预览功能调整各个域的位置和其中文字的字体、大小,完成后保存。模板完成!(我用Google搜索的时候也看到过用OpenOffice来完成模板的,不过没点开看)

(2)编程填充PDF文件:本人使用的编程语言是C#,所以使用对应的免费开源类库iTextSharp来完成填充的功能。程序的执行过程是:先读取网页中的有效信息,然后打开模板文件填充到模板文件的域中间,最后另存为一个PDF文件。代码见下文。

(3)服务器端推送文件给浏览器下载。

遇到的问题

(1)首先遇到的问题是Checkbox的传参问题。一开始本人是从一些英文网站查看的相关资料,里面在设置Checkbox使用的语句是:pdfFormFields.SetField(“male”, “Yes”); 试了很多遍,结果就是不行,Checkbox并没有被勾选上。后来无奈了点开Checkbox属性中的选项值一栏,发现它有一栏名为导出值,其中的值为“是”。我猜想我的模板由于使用中文版,其中参数的设置也发生了相应的变化,一试之下果然如此。pdfFormFields.SetField(“male”, “是”); 就是行得通。

(2)显示中文的问题。如果直接把中文字符串设置到PDF模板中去的话中文字符一个都不会显示。Google了一下,应该是亚洲的文字都不能显示,为此还需要两个附加组件:iTextAsianCmaps.dll和iTextAsian.dll。这两个组件可以在sourceforge上面下载。大家不妨参考:http://www.cnblogs.com/haogj/archive/2011/09/05/2167659.html 。

(3)以为这就好了?差远了!在输入第一句BaseFont.AddToResourceSearch(“iTextAsian.dll”);的时候有错误?对了!目前iTextSharp最新的版本是5.4.4,也就是我当时使用的版本。估计版本的变迁使得方法的调用也出了问题,于是果断换成了5.1.2的版本。

(4)改完之后一路顺畅地写完了方法。试验一下,又出错!BaseFont bf = BaseFont.CreateFont(“STSong-Light”, “UniGB-UCS2-H”, BaseFont.EMBEDDED);中的STSong-Light和UniGB-UCS2-H无法被识别!这个问题我直到最后都没能解决(看了好几篇帖子还是没能解决,大家如果有成功的通知小弟一声,谢谢啊!),退而求其次我只好使用TTF 字体。注:使用TTF 字体貌似是在PDF文件中嵌入字体文件,这使得PDF文件体积巨大,成了我的心病。

源代码

FillPdfTemplate.cs

Anyway,代码还是最重要的,以下的静态类用来填充PDF模板:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
using System.IO;
using iTextSharp.text.pdf;

public class FillPdfTemplate
{
    public static void GetEnPdf(string templatePath, string newFilePath, Dictionary<string, string> parameters)
    {
        PdfReader pdfReader = new PdfReader(templatePath);
        PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(newFilePath,
FileMode.Create));
        //获取域的集合;
        AcroFields pdfFormFields = pdfStamper.AcroFields;
        //为需要赋值的域设置值;
        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            pdfFormFields.SetField(parameter.Key, parameter.Value);
        }
        //这句很重要,如果为false那么生成的PDF文件还能编辑,一定要设为true;
        pdfStamper.FormFlattening = true;
        pdfStamper.Close();
        pdfReader.Close();
    }

    public static void GetChPdf(string templatePath, string newFilePath, string iTextAsianCmapsPath, Dictionary<string, string> parameters)
    {
        PdfReader pdfReader = new PdfReader(templatePath);
        PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(newFilePath, FileMode.Create));
        //获取域的集合;
        AcroFields pdfFormFields = pdfStamper.AcroFields;

        BaseFont.AddToResourceSearch(iTextAsianCmapsPath);
        //创建中文字体,第一个参数是中文字体的路径,第二个参数表示文字方向水平,第三个貌似是字体嵌入PDF文件;
        BaseFont baseFT = BaseFont.CreateFont(@"C:WindowsFontssimsun.ttc,1", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            //要输入中文就要设置域的字体;
            pdfFormFields.SetFieldProperty(parameter.Key, "textfont", baseFT, null);
            //为需要赋值的域设置值;
            pdfFormFields.SetField(parameter.Key, parameter.Value);
        }
        //这句很重要,如果为false那么生成的PDF文件还能编辑,一定要设为true;
        pdfStamper.FormFlattening = true;
        pdfStamper.Close();
        pdfReader.Close();
    }
}
事件响应

下面是参数的获取和调用的过程和推送PDF文件下载的方法:

protected void btnDownLoad_Click(object sender, EventArgs e)
{
    string position = Text1.Value;
    string venue = Text2.Value;
    string method = Text3.Value;
    string date = Text4.Value;
    string teacher = Text5.Value;
    string content = TextArea1.Value;
    string examination = Checkbox1.Checked ? "是" : "否";
    string selfassessment = Checkbox2.Checked ? "是" : "否";
    string certificate = Checkbox3.Checked ? "是" : "否";
    string etc = Checkbox4.Checked ? "是" : "否";
    string trainee = Text6.Value;

    Dictionary<string, string> dict = new Dictionary<string, string>();
    dict.Add("TextPosition", position);
    dict.Add("TextVenue", venue);
    dict.Add("TextMethod", method);
    dict.Add("TextDate", date);
    dict.Add("TextTeacher", teacher);
    dict.Add("TextContent", content);
    dict.Add("TextTrainee", trainee);
    dict.Add("CheckBoxExamination", examination);
    dict.Add("CheckBoxSelf-assessment", selfassessment);
    dict.Add("CheckBoxCertificate", certificate);
    dict.Add("CheckBoxEtc", etc);

    string template = Server.MapPath("~/PDFTemplate/ch.pdf");
    string newFile = Server.MapPath("~/PDFTemplate") + "\\" + Session["UserID"].ToString() + ".pdf";
    string iTextAsianCmaps = Server.MapPath("~/Libs/iTextAsianCmaps.dll");
    TrainingRecordToPDF.GetChPdf(template, newFile, iTextAsianCmaps, dict);

    OutFile(newFile);
}

public void OutFile(string  filename)
{
    System.IO.FileInfo file = new System.IO.FileInfo(filename);
    Response.Clear();
    Response.Charset = "GB2312";
    Response.ContentEncoding = System.Text.Encoding.UTF8;
    Response.AddHeader("Content-Disposition", "attachment; filename=" + Server.UrlEncode(file.Name));
    Response.AddHeader("Content-Length", file.Length.ToString());
    Response.ContentType = "application/x-bittorrent";
    Response.WriteFile(file.FullName);
    Response.End();  

}