65.9K
CodeProject 正在变化。 阅读更多。
Home

在 iTextSharp 创建的 PDF 中显示中文字符

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (5投票s)

2011 年 5 月 13 日

CPOL

5分钟阅读

viewsIcon

64209

iTextSharp API 是一个强大的开源工具,可用于动态创建 PDF 文档;它具有生成多语言 PDF 的能力,但默认设置中不包含。为了显示像简体中文这样的 Unicode 字符,有一些简单但文档不完善的技巧。

引言

我使用 iTextSharp 已有一段时间,但一直未能成功创建能够同时显示英文和中文字符的动态 PDF,直到今天。网上关于如何做到这一点的资料并不多,所以我认为如果我能将所有重要步骤总结在一篇文章中,供仍在像我一样挣扎的人参考,将会有所帮助,以便更容易地处理这个问题。尽管我的情况只涉及简体中文字符,但我相信同样的技巧也适用于任何双字节 Unicode 字符集。

关于如何使用 iTextSharp API 创建动态 PDF 的文档有很多,所以在这里我将只关注那些网络帖子中通常缺失的部分——即如何让中文或亚洲字符与英文正确地一起显示。

获取 API

这里 下载最新版本的 iTextSharp.dll。下载并解压后,只有一个文件 itextsharp.dll;我将它添加到了我的 C# 项目中作为引用,仅此而已。我还下载了 iTextAsian.dlliTextAsianCmaps.dll(从 iTextSharp.dll 的同一个下载页面的 Extras 链接中),并添加了对它们的引用,但事实证明,为了完成我想要做的事情,我完全不需要它们,所以后来我将它们从项目中移除了。

获取字体文件

这对我来说是一个转折点,在我下载了想要显示的简体中文字符的正确 TTF 文件后,一切都进行得很顺利。简体中文(主要在中国大陆使用)有很多字体文件;我通过广泛的 Google 搜索,眼睛几乎都干了,我使用了一个 Microsoft Word 的技巧来帮助我解决这个问题——我打开 Word,将输入语言切换到简体中文,然后在文档中输入了几个简体中文字符。当我查看菜单栏左上角的“字体”下拉框时,它自动从默认的“Calibri”变成了“宋体”,这就解决了——我需要获取的是“SimSun.ttf”。然后我简单地在 Google 搜索框中输入“simsun.ttf”,然后从搜索结果中选择了第一个:http://www.gamefront.com/files/16488629/simsun.ttf/。不需要安装字体文件。我只是把它放在我的 ASP.NET 项目中的一个“Fonts”文件夹(你可以随意命名)下,该文件夹可由运行时 ASP.NET 代码访问。

生成混合语言 PDF

现在,该展示了。在这个主题下,我有一个 ASP.NETGridView,其中包含从 SQL Server 2008 数据库中提取的英文和中文文本。这是一个显示学校活动和日程的日历;因此,这里显示一个“另存为 PDF”链接是很自然的,允许用户获取学校日历的副本。如果不存在中文字符,实现这一点会相当直接——我将GridView数据包装到一个DataTable对象中,并将其传递给一个预先编写好的名为ExportPdfTable的函数,该函数进而调用适当的iTextSharp API来完成繁重的工作。下面是遍历DataTable对象并动态生成PDF文档的代码片段。

using iTextSharp.text
using iTextSharp.text.pdf; 
..

/// <summary>
/// Print a table to a Pdf file in tabular format,
/// with header image and text on every page
/// </summary>
/// <param name="PdfFileName">This is pyhsical file name
/// of Pdf document that you wanto write to</param>
/// <param name="dt">Datatable that contain the data to print</param>
/// <param name="DocTitle">This is the title of the
/// document to be printed once on first page</param>
/// <param name="PageHeader">Header text to go
///    with header image on every page</param>
/// <param name="HeaderImgPath">the physical file path of a image
/// that you want to print out on every page header</param>
/// <returns></returns>        
public static bool ExportPdfTable(string PdfFileName, DataTable dt, 
       string DocTitle,string PageHeader,string HeaderImgPath)
{
    Document doc = new Document(); //iTextSharp document

    try
    {
        string physicalFile = PdfFileName;

        PdfWriter pw=PdfWriter.GetInstance(doc, 
                       new FileStream(physicalFile, FileMode.Create));

        //prepare for inserting header on every page, using Pageevent
        //DocumentEevent e = new DocumentEevent(HeaderImgPath, PageHeader);
        //pw.PageEvent = e;

        doc.Open();

        if (PageHeader.Length > 0)
            doc.Add(new Paragraph(new Phrase("")));

        if (DocTitle.Length > 0)
            doc.Add(new Phrase(DocTitle));

        if (dt.Columns.Count == 0) return false;
        int cols = dt.Columns.Count;
        int rows = dt.Rows.Count;

        //prepare the table object
       PdfPTable t = new PdfPTable(cols);
                           
       t.WidthPercentage = 100;

        //cell object
        PdfPCell c;

        //Use BaseFont to load unicode fonts like Simplified Chinese font
        string fontpath = System.Web.HttpContext.Current.Request.PhysicalApplicationPath + 
                          "\\includes\\fonts\\simsun.ttf";

        //"simsun.ttf" file was downloaded from web and placed in the folder
        BaseFont bf = BaseFont.CreateFont(fontpath,BaseFont.IDENTITY_H, 
                                          BaseFont.EMBEDDED);
                 
        //create new font based on BaseFont
        Font fontContent = new Font(bf, 11);
        Font fontHeader = new Font(bf, 12);

        //write header
        for (int j = 0; j < cols; j++)
        {
            Phrase pr=new Phrase((dt.Columns[j].Caption != null && 
              dt.Columns[j].Caption.Length > 0) ? dt.Columns[j].Caption : 
              dt.Columns[j].ColumnName,fontHeader);

            c = new PdfPCell(pr);
            c.PaddingBottom = 5f;
            c.PaddingTop = 5f;
            c.PaddingLeft = 8f;
            c.PaddingRight = 8f;
            t.AddCell(c);
        }
        //write table content
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                //c = new Cell(dt.Rows[i][j].ToString());
                //c.Header = false;
                c = new PdfPCell(new Phrase(dt.Rows[i][j].ToString(),fontContent));
                c.PaddingBottom = 5f;
                c.PaddingTop = 5f;
                c.PaddingLeft = 8f;
                c.PaddingRight = 8f;

                t.AddCell(c);
            }
        }
        return doc.Add(t);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        return false;
    }
    finally
    {
        doc.Close();
    }
}

这里突出了四个关键行,它们对于在动态生成的 PDF 中显示中文字符至关重要。

string fontpath = 
  System.Web.HttpContext.Current.Request.PhysicalApplicationPath + 
  "\\includes\\fonts\\simsun.ttf";
//"simsun.ttf" file was downloaded from web and placed in the folder
BaseFont bf = BaseFont.CreateFont(fontpath,
                 BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        
//create new font based on BaseFont
Font fontContent = new Font(bf, 11);
Font fontHeader = new Font(bf, 12);

如果去掉这四行,该函数仍然会输出相同的美观的 PDF 文件,只是所有中文字符都会完全消失。在这种情况下,创建正确的基础字体(BaseFont)的关键是加载正确的 TTF 文件并将编码设置为 BaseFont.IDENTITY_H。我尝试过“UTF-8”和其他一些值,但似乎都不起作用。我从一篇非常简短但有用的帖子中获得了这个技巧:http://stackoverflow.com/questions/1727765/itextsharp-international-text,我非常感谢它的作者 StewBob 和托管该帖子的 stackoverflow.com

部署

一切都在我的本地开发机器上完美运行……PDF 文件被创建,中文字符也显示了出来……但是在项目部署到外部托管站点后,却抛出了一个“该程序集不允许部分信任的调用者。”的错误。发生了什么?嗯,这实际上不是一个新问题,与中文字体无关。结果是,我不应该仅仅将 itextsharp.dll 上传到托管服务器。我需要下载 iTextSharp 源代码,并添加一些额外的安全属性来编译 DLL。因此,采取了这些额外的步骤来修复这个 DLL 错误。

  1. http://sourceforge.net/projects/itextsharp/files/itextsharp/ 下载 iTextSharp 源代码文件(版本 5.xx)。
  2. 启动 iTextSharp 解决方案,打开 AssemblyInfo.cs 文件,并在文件末尾添加以下行。
  3. [assembly: AssemblyDelaySign(false)]
    [assembly: AssemblyKeyFile("")]
    [assembly: AssemblyKeyName("")]
    //added this atribute to allow the dll to be callable on a hosting website
    [assembly: AllowPartiallyTrustedCallers()]
  4. 编译 DLL 并将其复制到托管站点的 bin 文件夹,完成。

摘要

总而言之,这四个关键步骤是:

  1. 下载正确的字体文件,并将其放在 Web 项目内的某个文件夹中。
  2. 创建一个 BaseFont,加载字体文件,并将编码设置为 BaseFont.IDENTITY_H;同时将字体嵌入到 PDF 文档中设置为 true (BaseFont.EMBEDDED)。
  3. 基于 BaseFont 创建新字体,并在 ParagraphPhrase 构造函数中使用它们。
  4. 确保当 iTextSharp.dll 部署到远程托管服务器时,该 DLL 必须重新编译并设置“AllowPartiallyTrustedCallers”属性。

历史

  • 首次提交于 2011 年 5 月 12 日。
© . All rights reserved.