内容


在 Web 应用程序中动态生成国际化的 PDF

使用开放源代码技术生成双字节的 PDF 文档

Comments

Internet 使得公司能够在国际市场上做生意。因此使用支持 Web 的方式为客户提供国际化的内容变得非常迫切。可移植文档格式(PDF)是一种流行的在 Web 上交付内容的格式,使用任何主流的浏览器都可以方便地下载 PDF 文档,然后使用 Adobe Acrobat Reader 来查看它,或者使用 Adobe 插件在浏览器中查看。为国际化的受众生成 PDF 内容提出了一项挑战,特别是日语、中文和韩语这类语言的双字节特性更需要专门考虑。Unicode 字体通常是一种不错的解决方法,但是这类字体可能是特定于平台的。另一个重要问题是,不能仅仅因为希望在普通的 HTML 之外使用 PDF 格式而改变应用程序逻辑。

我们首先对字体和语言作一般的讨论,然后介绍一种在 Java Web 应用程序中使用开放源代码技术生成 PDF 文档的方法。

字体概述

字体是称为图符的字符图像以及从字符码到图符的映射的集合。字符是在特定书写系统中表示字母和数字这类对象的符号。呈现特定的字符时,表示这个字符的形状称为图符。计算机系统中使用的常见字体是 Unicode TrueType 字体。但是,TrueType 字体自身不足以呈现双字节语言中的数据。TrueType 字体是呈现大量字符的最佳选择,但是它是特定于系统的:

  • 在 Windows 操作系统中,Microsoft 提供了 Arial Unicode MS 字体,支持多数语言中的字符显示,其中包括双字节语言。该字体放在一个单独的文件中,默认情况下,字体不用安装到用户的系统上。为了使用这种字体,可能需要 Windows 的国际化支持特性。
  • 类似地,在 IBM 的 AIX 的操作系统中,如果默认使用英语安装和配置系统,TrueType 字体也不会自动安装。您必须安装多个字体文件,比如用于日语、汉语和中文(简体和繁体)的 AIXwindows Unicode TrueType 字体-CJK。如果操作系统是 Linux,我们建议您仔细看看如何安装 TrueType 字体。

PDF 文档中的字体

在支持国际化的标准 Web 应用程序中,小心处理本地化和编码问题非常重要。但是,对于国际化 PDF 文档,还需要考虑到与字体有关的问题。

在 PDF 中,嵌入字体使得文档能够移植,从而可以在安装了 Adobe Acrobat Reader 的任何系统上查看。如果没有嵌入字体,那么用户系统上 Reader 的本地化版本就会选择本机上的字体来显示这种语言。比方说,如果系统支持英语,则显示和打印的时候就用 Base 14 PostScript 字体来代替。这种字体涵盖了多数单字节语言,但是不包括任何双字节语言。另一种办法是提示用户从 Adobe 下载 Font Pack 来查看国际化文档,但是让客户下载额外的字体工具不是一种好办法。通过在 PDF 中使用嵌入字体,就无需担心远程用户或机器是否有显示文档所需要的字体了。因此,使用嵌入式 Unicode TrueType 字体来呈现国际化的 PDF 数据是一种很好的解决方案。

FOP 和 XSL-FO

我们采用的在 Web 应用程序中生成 PDF 文档的方法是以格式化对象处理程序(Formatting Objects Processor,FOP)为基础。FOP 是由 XSL 格式化对象(XSL-FO)推动的世界上第一个打印格式化程序。它是一个开放源代码项目,是 Apache 下一项使用 Java 技术的 XML 项目。

图 1 说明了使用开放源代码 FOP 生成 PDF 文档的一般流程。输入数据需要采用 XML UTF-8 编码。转换程序接收 XML 数据,使用样式表生成 PDF。为了生成 PDF,FOP 格式化程序需要知道文档中使用的字体,特别是使用的所有图符的宽度。它需要这些细节来计算行的长度、连字符、对齐格式,等等。这些信息称为字体的规格,保存在每种字体中。如果能够得到字体规格信息,FOP 格式化程序就能成功地生成的 PDF。本文的后面部分将讨论如何生成字体规格文件。

图 1. 使用 Apache 开放源代码 FOP 转换 PDF
使用 Apache 开放源代码 FOP 转换 PDF
使用 Apache 开放源代码 FOP 转换 PDF

此外,样式表中还使用 font-family 属性从 XSL-FO 生成 PDF。根据 W3C 定义,可以使用字体族名(font family name)或者一般族名(generic family name)。font-family 的值可以是 Helvetica、Times、Courier 和 Symbol。而一般族名有 serif、sansserif、cursive、fantasy 和 monospace。 在 XSL-FO 中,font-family 属性是按优先顺序排列的字体族名列表,可以试着按照顺序寻找符合选择条件的可用字体(如 清单 1 所示)。但是,当前的 FOP 不支持 font-family 列表,如果存在这样的列表,也只会使用列表中的第一个字体。比如,如果指定 font-family="A,B,C"(如 清单 1 所示),而 A 不存在,则 B 和 C 都将被忽略不用。有关的更多信息,请参阅 W3C 站点。

清单 1. FOP 不支持在 font-family 中使用字体列表的典型 XSL-FO 语法
	<fo:block text-align="center" 
			  font-family="A,B,C" 
			  font-size="16pt">
		<xsl:text>Welcome! </xsl:text>
	</fo:block>

与 iText 的比较

我们前面说过,FOP 是使用 XSL-FO 和 XML 进行 PDF 转换的开放源代码 Java 程序。另一种开放源代码的 PDF 转换程序称为 iText(请参阅 参考资料),它采用面向对象的方法,并提供 Java 对象来呈现 PDF 文档。这两种方法各有优缺点,但我们认为 XSL-FO 模型在视图层上更适合模型-视图-控制器(MVC)架构。它还得到了 Struts for Transforming XML and XSL(stxx)扩展的支持,后者基于 Struts 应用程序框架。此外,这种方法更适合生成 PDF 报告,报告的模板可使用 XSL 指定,后者得到了 W3C 的支持。

示例 Web 应用程序

我们将使用一个示例 Java Web 应用程序示范建立 Unicode 字体、生成字体规格文件和使用 stxx 生成 PDF 文档的步骤。

该示例应用程序将一些应用程序数据转换成 PDF。我们假设您对 XML、XSL-FO、Struts 和 J2EE 有一些了解。示例应用程序有以下两个版本:

  • 第一个版本说明如果不使用嵌入式 Unicode 字体,为何生成的 PDF 不能正确显示双字节语言。
  • 第二个版本示范了为双字节语言生成 PDF 需要添加的步骤。

本质上,这是同一个应用程序,只不过在 XML 配置文件中的一个标志控制下以两种模式运行。这种方法需要以 Unicode 编码的 XML 格式提供数据。很多 Web 应用程序都在某一点生成 XML,这样就可以将 XML 和适当的 XSL 交给 stxx FOP 来完成转换。但是,如果 XML 不是在应用程序中生成的,那么需要首先将应用程序数据转换成 XML。交给 stxx 的 XML 数据添加了地区信息。可以利用地区挑选出 PDF 文档中显示的适当静态信息和动态数据。比如,在生成 PDF 输出的订单状态应用程序中,可以使用适当的地区信息从资源绑定中挑选静态标签。

为了运行示例应用程序,我们使用了 Tomcat 4.1.30 和 stxx 1.3。Tomcat 是一种开放源代码的 servlet 容器。当然您也可以使用其他的 servlet 容器,无论是开放源代码产品,还是 IBM WebSphere Application Server 这样的商业产品。stxx 是 Struts 框架的扩展,不需要改变 Struts 的运行时行为就可以支持 XML 和 XSL。stxx 当前的版本是 1.3,它使用 FOP 0.20.4。

这个例子是一个简单的基于 Struts 的应用程序。我们使用 IBM WebSphere Studio 作为开发环境。(WebSphere Studio 使用 Eclipse 工具框架,可以与 Tomcat 服务器很好地集成在一起,并提供了 Java 和 XML 开发特性)。呈现为 PDF 格式的数据来自包含多语言内容(比如 图 2)的 XML 文件。其中包含翻译成不同双字节语言的文本“Product Service”。初始化应用程序的时候,嵌入字体被加载到 Struts 系统中。使用 stxx 动作将 XML 文档转化成 stxx XML 格式,其中包括 Web 应用程序的地区信息。我们使用 Apache Jakarta Digester 模式将 XML 配置文件加载到 Struts 插件中。stxx FOP 转换器使用 XSL-FO 样式表进行 PDF 转换。样式表中明确使用了 Arial Unicode MS,它属于 Helvetica 字体家族,最终,字体信息嵌入在了 PDF 中。

解压 sample.war 文件后可以看到所有的配置文件和源代码。

图 2. XML 示例数据
XML 示例数据
XML 示例数据

部署示例应用程序的步骤

本文中的例子使用的是没有安装 Unicode 字体的 English 语言机器。如果您使用的是不同的机器(比如日语机器),步骤仍然一样。

可以在任何兼容 J2EE 的 servlet 容器中部署这个应用程序。下面是在 Tomcat 上安装该应用程序的步骤。(其他详细信息,请参阅 参考资料。)

  1. 在安装 Tomcat 后启动服务器。
  2. 使用 Tomcat Web Application Manager 在某个 URL(如 http://localhost:8080/manager/html/list)中上传 下载 中提供的 sample.war 文件,然后安装这个应用程序。
  3. 将 stxx 中所有必需的 jar 文件(如 stxx-1.3.jar 和 struts.jar)复制到 Tomcat 示例 Web 应用程序的 lib 目录中。jar 文件清单如 图 3 所示。
    图 3. 示例 Web 应用程序 lib 目录中的 jar 文件
    示例 Web 应用程序 lib 目录中的 jar 文件
    示例 Web 应用程序 lib 目录中的 jar 文件
  4. 使用 http://localhost:8080/sample/homepage.html 之类的 URL 打开 homepage.html。可以看到示例应用程序的主页,如 图 4 所示。您可以选择在浏览器中查看 PDF 或者将它们下载到自己的系统中。
    图 4. 示例应用程序的主页
    示例应用程序的主页
    示例应用程序的主页
  5. 单击 View PDF 可以查看 PDF,如 图 5 所示。
    图 5. 用错误字体呈现的 PDF
    用错误字体呈现的 PDF
    用错误字体呈现的 PDF

可以看到,双字节样本数据被显示成“#####”这样的符号。这意味着系统中的默认字体设置不能显示双字节语言的字符。现在已经完成了应用程序第一个版本的运行。

为了保证正确地呈现双字节数据,请按照下面的步骤将使用的 Unicode 字体嵌入到 PDF 中:

  1. 如果您的系统还不支持这种字体的话,请 安装 Unicode TrueType 字体。
  2. 生成 Unicode TrueType 字体规格文件。
  3. 将嵌入字体 注册 到 FOP 中。
  4. 在 servlet 引擎中使用 FOP 进行 转换

第 1 步:安装 Unicode TrueType 字体

这个例子是从 Windows 系统上开发的。要安装 Arial Unicode MS 字体,还请参阅 Microsoft 的国际化支持站点(请参阅 参考资料)。如果使用其他操作系统,请参阅安装字体的标准说明。

第 2 步:生成 Unicode TrueType 字体规格文件

TrueType 字体文件有两种类型:TrueType Font 文件(扩展名为 .ttf)和 TrueType Collection 文件(扩展名为 .ttc)。FOP 允许嵌入这两种文件。

在安装 Unicode 字体之后,看看字体目录中是否能找到字体文件(比如 C:\windows\fonts\ARIALUNI.TTF)。然后使用 清单 2 中的 FOP 命令生成字体规格文件。一定要确保类路径的设置正确。还要保存生成的 XML 文件,以便 Web 应用程序运行时使用该文件。

清单 2. 使用 FOP 生成字体规格文件
$ java org.apache.fop.fonts.apps.TTFReader C:\windows\fonts\Arialuni.ttf arialuni.xml

第 3 步:用 FOP 注册嵌入字体

初始化应用程序时用 FOP 注册嵌入字体是一个好主意。在 Struts 中,可以使用插件加载这个字体规格文件。可以创建一个 XML 文件规定字体规格文件的位置,我们选择了 清单 3 所示的格式。

修改 userconfig.xml 选择 上面生成的字体规格文件

清单 3. 使用 userconfig.xml 注册嵌入式 Unicode TrueType 字体
   <font metrics-file="C:/temp/font/arialuni/arialuni.xml" 
   			embed-file="C:/windows/Fonts/arialuni.ttf" kerning="yes">
    	<font-triplet name="arialuni" style="normal" weight="normal"/>
    	<font-triplet name="arialuni" style="normal" weight="bold"/>
    	<font-triplet name="arialuni" style="italic" weight="normal"/>
    	<font-triplet name="arialuni" style="italic" weight="bold"/>
  </font>

应用程序配置是在单独的配置文件(pdf-userconfig.xml)中指定的,该文件由 Struts 插件选择。它位于示例应用程序的“WEB-INF”文件夹中。文件内容如 清单 4 所示。默认情况下,enabled 属性被设为 false。

清单 4. pdf-userconfig.xml 应用程序配置
 <configuration>
	<pdf-fonts>
		<userconfig name="pdf-unicode" 
					path="font\userconfig.xml" 
					enabled="false" 
					comment="for Unicode Font"/>
	</pdf-fonts>
 </configuration>

要加载字体规格文件,可以使用 Struts 插件和 Java 代码加载 userconfig.xml,如 清单 5 所示。

清单 5. Struts 插件中用于支持嵌入字体的 Java 代码
	...
	try {
		File userConfigFile = new File("userconfig.xml");
		org.apache.fop.apps.Options options 
				= new org.apache.fop.apps.Options(userConfigFile);
	} catch (FOPException fe) {
		fe.printStackTrace();
	}
	...

第 4 步:在 servlet 引擎中使用 FOP 转换

按照 Struts 架构,可以创建一个动作(action),通过三步生成 PDF 文档:

  1. 构造 XML 文档。
  2. 在 Web 上处理文档视图选项。
  3. 呈现 PDF。

代码如 清单 6 所示。

清单 6. 呈现 PDF 的 Struts 动作
 public class SampleXslFoAction extends Action {
 
 	private String xmlUsed = "/xml/sample_transform.xml"; //default
	private String successFwd = "success"; //default
	
	public org.apache.struts.action.ActionForward execute(
					ActionMapping mapping,
					ActionForm form,
					HttpServletRequest request,
			    	HttpServletResponse response)
			throws IOException, ServletException {
				
		//**************************
		// make user selections
		//**************************
		decideSuccessFwd(request);
		decideXmlUsed(request);
		
		//*******************
		// Construct XML 
		//*******************
		Document doc = null;
		try { 
			String fileName = request.getRealPath(xmlUsed);
			FileInputStream fis = new FileInputStream(fileName);
			doc = new SAXBuilder().build(fis);
		} 
		catch (Exception ex) {
			ex.printStackTrace();
		}
		saveDocument(request, doc);
		//****************************
		// process PDF doc view option
		//****************************
		if(request.getParameter("viewformat").equalsIgnoreCase("download")){
			response.setContentType("application/pdf");
			response.setHeader("Content-Disposition",
			"attachment;filename=pdfdoc.pdf");
		}
		//**************************
		//Go forward rendering it
		//**************************
		return mapping.findForward(successFwd);
	}
	...
}

所有的配置和代码都在这个例子中实现了。按照上述步骤生成字体规格文件并修改 userconfig.xml 文件之后,必须将 pdf-userconfig.xml 文件中的 enabled 属性改为 true。然后重新启动 Tomcat 服务器,再次打开 homepage.html。如果单击 View PDF,可以看到这一次正确显示了所有内容,如图 6 所示。

图 6. FOP 转换的多语言内容的 PDF 视图
FOP 转换的多语言内容的 PDF 视图
FOP 转换的多语言内容的 PDF 视图

可以看到,这里正确地呈现了不同语言的示例文本“Product Service”。

结束语

可以使用 XSL-FO 和 FOP 在 Web 应用程序中动态生成国际化的 PDF 文档。在缺乏能够满足不同操作系统字体需求的通用解决方案的情况下,XSL-FO 提供了一种呈现国际化 PDF 文档的独立于系统的解决方案。本文所用的 stxx 特性非常适合 Web 应用程序的 MVC 架构。通过这种方法,PDF 转换对应用程序开发人员变得透明了。一旦确定了 XML DTD,应用程序开发人员就可以将精力集中在业务逻辑上,由 XSL 开发人员定义转换的 XSL。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, Web development
ArticleID=58215
ArticleTitle=在 Web 应用程序中动态生成国际化的 PDF
publish-date=02012005