为网页提供pdf文件支持
概要
在这篇文章里,nick afshartous描述了一种把html的内容转换为pdf格式的方法。这种方法相当有用,比如说,一个web程序可以在它的页面上提供如“下载为pdf”的功能。这种功能方便了打印和储存,以供日后使用。afshartous的转换方法只使用开源的组件。也有一些商业产品可供使用。因此,在这篇文章里描述的这种方法既在价格上可以承担,又能够获得所用组件的源码。
把网页内容以pdf的格式呈献有利于内容的传播。在一些应用中,提供格式便于打印的文档是一个必需的功能,比如员工利益表等。事实上,法律规定summmary plan descriptions(spds)必须能够打印,即使它们是在线提供的也是如此。然而只打印网页本身是不够的,因为打印格式必包含表格内容和页码。
为了提供这样的功能,开发人员可以把html内容转换为pdf格式。在此即做介绍。这里介绍的这种方法只使用开源组件。一些商业产品也支持动态的文档生成,比如说adobe,它有document server产品线。但是,使用商业产品的开销是相当可观的。使用开源方案可以缓解开销的问题,并增加了组件源码的透明度。
转换过程包含以下三步:
1.把html转换为xhtml;
2.把xhtml转换为xsl-fo(extensible stylesheet language formatting objects扩展样式表语言格式化对象)。这里使用xsl样式表和xslt转换器;
3.把xsl-fo文档传递给格式化程序来生成目标pdf文档。
本文先介绍怎样用命令行界面来做这种转换,然后介绍怎样在java中使用dom接口来做同样的工作。
组件版本:
本文中的代码在以下版本中进行了测试:
组件 版本
jdk 1.5_06
jtidy r7-dev
xalan-j 2.7
fop 0.20.5
版权声明:任何获得matrix授权的网站,转载时请务必保留以下作者信息和链接
作者:nick afshartous;rainy14f(作者的blog:http://blog.matrix.org.cn/page/rainy14f)
原文:http://www.javaworld.com/javaworld/jw-04-2006/jw-0410-html.html
matrix:http://www.matrix.org.cn/resource/article/44/44489_html+pdf.html
关键字:html;pdf
使用命令行界面
在转换过程中的每一步都包含了从一个输入文件生成输出文件的过程。这个过程可以用下图来表示:
使用这三个工具的命令行界面开始我们的工作是个好方法,尽管这种方法并不适合产品级的系统,因为它需要往磁盘中写入临时的中间文件。这种额外的i/o会导致性能的降低。稍后,在我们用java来调用这三个工具时,这个问题就会得到解决。
第一步:转换html为xhtml
第一步就是把html转换为一个新的xhtml文件。当然,如果文件本来已经就是xhtml,那就不需要这一步了。
我用jtidy来完成这个转换。jtidy是tidy html解析器的java版本。在转换的过程中,jtidy会自动添加缺少的标签来创建格式良好(well-formed)的xml文档。我用的是在sourceforge上的最新版本r7-dev。
可以用以下的脚本来运行jtidy:
#/bin/sh
java -classpath lib/tidy.jar org.w3c.tidy.tidy -asxml $1 >$2
此脚本设置了classpath并调用了jtidy。运行时,要输入的文件是以命令行参数的形式传给jtidy。默认情况下,生成的xhtml将被输出到标准输出设备。-modify开关可以用来覆写输入文件。-asxml开关把jtidy的输出重定向到格式良好的xml。
调用时像这样:
tidy.sh hello.html hello.xml
hello.html(输入)和hello.xml(输出)的内容如下:
<p>hello world!</p>
是jtidy自动添加的[译注1]。
第二步:转换xhtml为xsl-fo[译注2]
下面,xhtml将被转换为xsl-fo,一种用来为xml文档指定打印格式的语言。我通过用xslt转换器(apache xalan)处理xsl样式表来完成这个转换。我使用的样式表是由antenna house提供的xhtml2fo.xsl。antenna house是一个出售xsl-fo上商用格式程序的公司。
xhtml2fo.xsl样式表指定了如何把每个html标签翻译成相应的xsl-fo格式化命令序列。举例来说,html中的h2标签在翻译中被定义为:
[code] <xsl:template match="html:h2">
<fo:block xsl:use-attribute-sets="h2">
<xsl:call-template name="process-common-attributes-and-children"/>
</fo:block>
</xsl:template>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/xsl/transform"
xmlns:fo="http://www.w3.org/1999/xsl/format"
xmlns:html="http://www.w3.org/1999/xhtml">...
<fo:block xsl:use-attribute-sets="h2">
<xsl:attribute-set name="h2">
<xsl:attribute name="start-indent">10mm
<xsl:attribute name="end-indent">10mm
<xsl:attribute name="space-before">1em
<xsl:attribute name="space-after">0.5em
<xsl:attribute name="font-size">x-large
<xsl:attribute name="font-weight">bold
<xsl:attribute name="color">black
</xsl:attribute-set>
<xsl:call-template name="process-common-attributes-and-children"/>
<xsl:apply-templates/>
<h2> hello <em> there </em> </h2>
<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/xsl/format"
xmlns:html="http://www.w3.org/1999/xhtml"
writing-mode="lr-tb"
hyphenate="false"
text-align="start"
role="html:html">
<fo:layout-master-set>
<fo:simple-page-master page-width="auto" page-height="auto"
master-name="all-pages">
<fo:region-body column-gap="12pt" column-count="1" margin-left="1in"
margin-bottom="1in" margin-right="1in" margin-top="1in"/>
<fo:region-before display-align="before" extent="1in"
region-name="page-header"/>
<fo:region-after display-align="after" extent="1in"
region-name="page-footer"/>
<fo:region-start extent="1in"/>
<fo:region-end extent="1in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="all-pages">
<fo:title>hello world
<fo:static-content flow-name="page-header">
<fo:block font-size="small" text-align="center" space-before="0.5in"
space-before.conditionality=;"retain">
hello world
</fo:block>
</fo:static-content>
<fo:static-content flow-name="page-footer">
<fo:block font-size="small" text-align="center" space-after="0.5in"
space-after.conditionality=quot;retain">
- <fo:page-number/> -
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<fo:block role="html:body">
<fo:block space-before="1em" space-after="1em" role="html:p">
hello world!
</fo:block>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
public static void main(string[] args) {
// 打开文件
if (args.length != 2) {
system.out.println("usage: html2pdf htmlfile stylesheet");
system.exit(1);
}
fileinputstream input = null;
string htmlfilename = args[0];
try {
input = new fileinputstream(htmlfilename);
}
catch (java.io.filenotfoundexception e) {
system.out.println("file not found: " + htmlfilename);
}
tidy tidy = new tidy();
document xmldoc = tidy.parsedom(input, null);<xsl:template match="html:h2">
<fo:block xsl:use-attribute-sets="h2">
<xsl:call-template name="process-common-attributes-and-children"/>
</fo:block>
</xsl:template>
<xsl:template match="h2">
<fo:block xsl:use-attribute-sets="h2">
<xsl:call-template name="process-common-attributes-and-children"/>
</fo:block>
</xsl:template>
document fodoc = xml2fo(xmldoc, args[1]);
private static document xml2fo(document xml, string stylesheet) {
domsource xmldomsource = new domsource(xml);
domresult domresult = new domresult();
transformer transformer = gettransformer(stylesheet);
if (transformer == null) {
system.out.println("error creating transformer for " + stylesheet);
system.exit(1);
}
try {
transformer.transform(xmldomsource, domresult);
}
catch (javax.xml.transform.transformerexception e) {
return null;
}
return (document) domresult.getnode();
}
string pdffilename = htmlfilename.substring(0, htmlfilename.indexof(".")) + ".pdf";
try {
outputstream pdf = new fileoutputstream(new file(pdffilename));
pdf.write(fo2pdf(fodoc));
}
catch (java.io.filenotfoundexception e) {
system.out.println("error creating pdf: " + pdffilename);
}
catch (java.io.ioexception e) {
system.out.println("error writing pdf: " + pdffilename);
}
private static byte[] fo2pdf(document fodocument) {
documentinputsource fopinputsource = new documentinputsource(
fodocument);
try {
bytearrayoutputstream out = new bytearrayoutputstream();
logger log = new consolelogger(consolelogger.level_warn);
driver driver = new driver(fopinputsource, out);
driver.setlogger(log);
driver.setrenderer(driver.render_pdf);
driver.run();
return out.tobytearray();
} catch (exception ex) {
return null;
}
}Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器