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






4.71/5 (5投票s)
iTextSharp API 是一个强大的开源工具,可用于动态创建 PDF 文档;它具有生成多语言 PDF 的能力,但默认设置中不包含。为了显示像简体中文这样的 Unicode 字符,有一些简单但文档不完善的技巧。
引言
我使用 iTextSharp 已有一段时间,但一直未能成功创建能够同时显示英文和中文字符的动态 PDF,直到今天。网上关于如何做到这一点的资料并不多,所以我认为如果我能将所有重要步骤总结在一篇文章中,供仍在像我一样挣扎的人参考,将会有所帮助,以便更容易地处理这个问题。尽管我的情况只涉及简体中文字符,但我相信同样的技巧也适用于任何双字节 Unicode 字符集。
关于如何使用 iTextSharp API 创建动态 PDF 的文档有很多,所以在这里我将只关注那些网络帖子中通常缺失的部分——即如何让中文或亚洲字符与英文正确地一起显示。
获取 API
从 这里 下载最新版本的 iTextSharp.dll。下载并解压后,只有一个文件 itextsharp.dll;我将它添加到了我的 C# 项目中作为引用,仅此而已。我还下载了 iTextAsian.dll 和 iTextAsianCmaps.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 错误。
- 从 http://sourceforge.net/projects/itextsharp/files/itextsharp/ 下载 iTextSharp 源代码文件(版本 5.xx)。
- 启动 iTextSharp 解决方案,打开 AssemblyInfo.cs 文件,并在文件末尾添加以下行。
- 编译 DLL 并将其复制到托管站点的 bin 文件夹,完成。
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]
//added this atribute to allow the dll to be callable on a hosting website
[assembly: AllowPartiallyTrustedCallers()]
摘要
总而言之,这四个关键步骤是:
- 下载正确的字体文件,并将其放在 Web 项目内的某个文件夹中。
- 创建一个
BaseFont
,加载字体文件,并将编码设置为BaseFont.IDENTITY_H
;同时将字体嵌入到 PDF 文档中设置为 true (BaseFont.EMBEDDED
)。 - 基于
BaseFont
创建新字体,并在Paragraph
或Phrase
构造函数中使用它们。 - 确保当 iTextSharp.dll 部署到远程托管服务器时,该 DLL 必须重新编译并设置“
AllowPartiallyTrustedCallers
”属性。
历史
- 首次提交于 2011 年 5 月 12 日。