`
salever
  • 浏览: 255725 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

【Java】利用HTML生成PDF之问题整理

阅读更多

首先,技术为apache 的FOP,初级的问题我就不写了,下面记录一下遇到的问题的解决方案:

 

中文乱码问题

这个问题网上的解决方案非常之多,也不详细描述了,每个使用FOP的都会遇见,还是记录一下。

在fop.xconf文件中,修改pdf相关的render,添加中文字体配置

 

  <renderer mime="application/pdf">
      <filterList>
        <!-- provides compression using zlib flate (default is on) -->
        <value>flate</value>
      </filterList>
      <fonts>
       <font metrics-url="[%FOP_ROOT%]/simsun.xml" kerning="yes" embed-url="[%FOP_ROOT%]/fonts/simsun.ttc"> 
 			 <font-triplet name="SimSun" style="normal" weight="normal"/> 
 			 <font-triplet name="宋体" style="normal" weight="normal"/> 
        </font>
      </fonts>
    </renderer>

 当然你还使用其他的方式来添加字体配置,见http://xmlgraphics.apache.org/fop/0.95/configuration.html

 其中的.xml文件是根据fop的字体读取类生成的,例如:

java -cp build\fop.jar;lib\avalon-framework.jar;lib\commons-logging.jar;lib\commons-io.jar
org.apache.fop.fonts.apps.TTFReader [options]
C:\myfonts\cmr10.ttf ttfcm.xm

通过 TTFReader类来直接通过代码生成也是一个选择,它提供的main方法中有详细的参数介绍说明。

 

更多的fop字体相关问题见http://xmlgraphics.apache.org/fop/trunk/fonts.html

很多问题,通过自己读一读官方的介绍问题就明白了。

 

 

中文粗、斜字体问题

这是最新的fop还没有解决的问题之一。

对于英文字体,都有normal、bold、italic三种对应的字体,例如上面的arial.ttf(normal)、arialb.ttf(bold),所以英文的粗体、斜体都没有问题的。

中文就有问题了,常用的宋体,一般使用都是windows的simsun(宋体)字体,这种字体没有对应的bold、italic版本,MS word实现中文粗体、斜体是自己计算的,而很可惜fop处理的pdf没有这种功能,因此,宋体的粗体、斜体就没有办法显示了。

如果想实现宋体的粗体,有两种方式实现:

1,下载宋体的粗体字体文件,然后按照处理宋体的一般字体文件一样,生成xml文件,然后在fop.xconf中配置字体,比如

        <font metrics-url="[%FOP_ROOT%]/simsunb.xml" kerning="yes" embed-url="[%FOP_ROOT%]/fonts/simsunb.ttf"> 
 			 <font-triplet name="SimSun" style="normal" weight="bold"/>
 			 <font-triplet name="宋体" style="normal" weight="bold"/>
 			 <font-triplet name="SimSun" style="italic" weight="bold"/>
 			 <font-triplet name="宋体" style="italic" weight="bold"/>
        </font>

  这里的sinsunb.xml就是根据网上下载的粗宋体文件(simsunb.ttf)生成的,附件为一个可用的粗宋体文件,以及对应的xml文件。使用这些字体,生成的pdf就会有你下载的粗宋体的样式了,不过好不好看就再说了。

 

2,另一种方法,也就是我采用的办法,宋体的粗体其实可以用黑体代替,而fop没有自动进行这种关联,需要我们自己配置了,很简单

 <font metrics-url="[%FOP_ROOT%]/simhei.xml" kerning="yes" embed-url="[%FOP_ROOT%]/fonts/simhei.ttf"> 
 			 <font-triplet name="SimSun" style="normal" weight="bold"/>
 			 <font-triplet name="宋体" style="normal" weight="bold"/>
 			 <font-triplet name="SimSun" style="italic" weight="bold"/>
 			 <font-triplet name="宋体" style="italic" weight="bold"/>
        </font>

 将粗体都交给黑体处理,效果比上一种方式好一点。

 附件上传了一些处理过的宋体(simsun.xml.rar)、黑体(simhei.xml.rar)、隶书(simli.xml.rar)、楷体(simkai.xml.rar),对应的字体(ttf或ttc)文件太大,自己直接去C:/windows/fonts/目录下寻找即可。

 

背景图片、水印问题

有时候报告需要添加背景图片、或者水印,这时候需要修改fo文件,由于我们是直接根据html生成的,所以我们修改xsl-fo文件就行了。

我是用的xsl文件没有处理<html:body background=""/>属性,所以直接在xsl的页面配置中添加背景图片。

 <fo:layout-master-set>
      <fo:simple-page-master master-name="first"
                             xsl:use-attribute-sets="page">
        <fo:region-body margin-top="{$page-margin-top}"
                        margin-right="{$page-margin-right}"
                        margin-bottom="{$page-margin-bottom}"
                        margin-left="{$page-margin-left}"
                        column-count="{$column-count}"
                        column-gap="{$column-gap}"
                        background-image="url('work/report/bg_header.jpg')"
                        />
        <xsl:choose>
          <xsl:when test="$writing-mode = 'tb-rl'">
            <fo:region-before extent="{$page-margin-right}"
                              precedence="true"/>
            <fo:region-after  extent="{$page-margin-left}"
                              precedence="true"/>
            <fo:region-start  region-name="page-header"
                              extent="{$page-margin-top}"
                              writing-mode="lr-tb"
                              display-align="before"/>
            <fo:region-end    region-name="page-footer"
                              extent="{$page-margin-bottom}"
                              writing-mode="lr-tb"
                              display-align="after"/>
          </xsl:when>
          <xsl:when test="$writing-mode = 'rl-tb'">
            <fo:region-before region-name="page-header"
                              extent="{$page-margin-top}"
                              display-align="before"/>
            <fo:region-after  region-name="page-footer"
                              extent="{$page-margin-bottom}"
                              display-align="after"/>
            <fo:region-start  extent="{$page-margin-right}"/>
            <fo:region-end    extent="{$page-margin-left}"/>
          </xsl:when>
          <xsl:otherwise><!-- $writing-mode = 'lr-tb' -->
            <fo:region-before region-name="page-header"
                              extent="{$page-margin-top}"
                              display-align="before"/>
            <fo:region-after  region-name="page-footer"
                              extent="{$page-margin-bottom}"
                              display-align="after"/>
            <fo:region-start  extent="{$page-margin-left}"/>
            <fo:region-end    extent="{$page-margin-bottom}"/>
          </xsl:otherwise>
        </xsl:choose>
      </fo:simple-page-master>
       <fo:simple-page-master master-name="normal"
                             xsl:use-attribute-sets="page">
        <fo:region-body margin-top="{$page-margin-top}"
                        margin-right="{$page-margin-right}"
                        margin-bottom="{$page-margin-bottom}"
                        margin-left="{$page-margin-left}"
                        column-count="{$column-count}"
                        column-gap="{$column-gap}"
                        background-image="url('work/report/bg_content.jpg')"
                        />
        <xsl:choose>
          <xsl:when test="$writing-mode = 'tb-rl'">
            <fo:region-before extent="{$page-margin-right}"
                              precedence="true"/>
            <fo:region-after  extent="{$page-margin-left}"
                              precedence="true"/>
            <fo:region-start  region-name="page-header"
                              extent="{$page-margin-top}"
                              writing-mode="lr-tb"
                              display-align="before"/>
            <fo:region-end    region-name="page-footer"
                              extent="{$page-margin-bottom}"
                              writing-mode="lr-tb"
                              display-align="after"/>
          </xsl:when>
          <xsl:when test="$writing-mode = 'rl-tb'">
            <fo:region-before region-name="page-header"
                              extent="{$page-margin-top}"
                              display-align="before"/>
            <fo:region-after  region-name="page-footer"
                              extent="{$page-margin-bottom}"
                              display-align="after"/>
            <fo:region-start  extent="{$page-margin-right}"/>
            <fo:region-end    extent="{$page-margin-left}"/>
          </xsl:when>
          <xsl:otherwise><!-- $writing-mode = 'lr-tb' -->
            <fo:region-before region-name="page-header"
                              extent="{$page-margin-top}"
                              display-align="before"/>
            <fo:region-after  region-name="page-footer"
                              extent="{$page-margin-bottom}"
                              display-align="after"/>
            <fo:region-start  extent="{$page-margin-left}"/>
            <fo:region-end    extent="{$page-margin-bottom}"/>
          </xsl:otherwise>
        </xsl:choose>
      </fo:simple-page-master>
      <fo:page-sequence-master master-name="standard">
		<fo:repeatable-page-master-alternatives>
			<fo:conditional-page-master-reference
				master-reference="first" page-position="first" />
			<fo:conditional-page-master-reference
				master-reference="normal"/>
			</fo:repeatable-page-master-alternatives>
		</fo:page-sequence-master>
    </fo:layout-master-set>

 上面的代码中,在<fo:region-body>添加了background-image="url('work/report/bg_header.jpg')"属性,因此它生成的pdf就具有背景图片,当然如果你把bg_header.jpg透明度降低,就会获得水印效果了。

 上面的代码还区别实现了首页和其他页的背景图片分别设置的功能,在

		<fo:repeatable-page-master-alternatives>
			<fo:conditional-page-master-reference
				master-reference="first" page-position="first" />
			<fo:conditional-page-master-reference
				master-reference="normal"/>
			</fo:repeatable-page-master-alternatives>
		</fo:page-sequence-master>

 中指定page-position为first时使用first的master,而其他页使用normal的master。

 

页眉图片、页脚图片问题

熟悉fo的应该知道fo的页面布局,也就是layout,在定义好了layout时候,页面将分为上、下、左、右和中五个区域,上对应的就是页眉,所谓的page-header,下对应的就是页脚,所谓的page-footer,上下的距离和间距都可以在xsl文件设置。这里我们对页眉进行图片设置,

<fo:page-sequence master-reference="standard">
      <fo:title>
        <xsl:value-of select="/html:html/html:head/html:title"/>
      </fo:title>
	<fo:static-content flow-name="page-header">

		<fo:block line-height="10pt" font-size="9pt" font-family="宋体"
			text-align="start">
			<fo:external-graphic width="6.26in" height="1in"
				content-height="scale-to-fit" content-width="scale-to-fit"
				src="work/report/header.jpg" />
		</fo:block>
	</fo:static-content>
      <fo:static-content flow-name="page-footer">
        <fo:block space-after.conditionality="retain"
                  space-after="{$page-footer-margin}"
                  xsl:use-attribute-sets="page-footer">
          <xsl:if test="$page-number-print-in-footer = 'true'">
            <xsl:text>- </xsl:text>
            <fo:page-number/>
            <xsl:text> -</xsl:text>
          </xsl:if>
        </fo:block>
    </fo:static-content>
      <fo:flow flow-name="xsl-region-body">
        <fo:block xsl:use-attribute-sets="body">
          <xsl:call-template name="process-common-attributes"/>
          <xsl:apply-templates/>
        </fo:block>
      </fo:flow>
    </fo:page-sequence>

 使用刚刚定义的standard page-squence,在

<fo:static-content flow-name="page-header">

中添加

<fo:block line-height="10pt" font-size="9pt" font-family="宋体"
			text-align="start">
			<fo:external-graphic width="6.26in" height="1in"
				content-height="scale-to-fit" content-width="scale-to-fit"
				src="work/report/header.jpg" />
		</fo:block>

以获得页眉效果,这里也可以同时再添加文字,具体效果自己摸索了。

 

 

复制、打印、编辑权限问题

这个就比较容易了,通过fop user agent进行设置就行了。

FOUserAgent userAgent = fopFactory.newFOUserAgent();
useragent.getRendererOptions().put("encryption-params", new PDFEncryptionParams(
    null, "password", false, false, true, true));
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent);

 具体效果为:

写道
The parameters for the constructor of PDFEncryptionParams are:

1. userPassword: String, may be null
2. ownerPassword: String, may be null
3. allowPrint: true if printing is allowed
4. allowCopyContent: true if copying content is allowed
5. allowEditContent: true if editing content is allowed
6. allowEditAnnotations: true if editing annotations is allowed

 更多参考http://xmlgraphics.apache.org/fop/trunk/pdfencryption.html

 PS:加密以后,<a href=""></a>中定义的链接将不可用,转换时会自动生成一个与本地路径关联的地址,不知道是不是fop的bug,注释了加密代码后又都完全正常。

 

pdf生成失败检测

官方给出的例子中,无法得知pdf是否生成成功,失败了仅仅打出一些错误日志,这对于程序员来说是不够的,如果检测失败也是一个问题。下面的代码提供了这种功能,但是不太好

			Result res = new SAXResult(fop.getDefaultHandler());

			// Start XSLT transformation and FOP processing
			transformer.transform(src, res);

			FormattingResults result = fop.getResults();
			if (result.getPageSequences() == null) {
				log.error("Convert to pdf failed");
				out.close();
				result = null;
				fop = null;
				return false;
			}
			result = null;
			fop = null;
			out.close();

 

 

   htmld到pdf的过程比较坎坷,还会遇到很多不可预见的问题,比如html中的char set设置,可能需要很多类似

  <!ENTITY tilde  "&#126;">
  <!ENTITY florin "&#131;">
  <!ENTITY elip   "&#133;">
  <!ENTITY dag    "&#134;">

  声明,还会遇到pdf中莫名其妙的乱码,比如黑体中的空格,这些都需要自己慢慢调试程序和标准化html。

 

  附件还包含一个可用的fop.xconf文件、html测试文件以及xhtml2fo.xsl 转换文件。

 

  PS:xhtml2fo.xsl文件为根据网上的原始文件修改和完善的,可能功能还不太完整,但是处理基本的html已经足够了。

 

  20110126 更新

    xhtml2fo.xsl无法显示<span></span>中的内容,原因为xsl中处理相关元素的代码有问题,更新一下:(替换相关内容)

  <xsl:template match="html:span">
    <fo:inline>
     	 <xsl:attribute name="role">
     		 <xsl:value-of select="concat('html:', local-name())"/>
   		</xsl:attribute>
   		<!-- 处理span 内的文字 -->
   	   <xsl:apply-templates/>
    </fo:inline>
  </xsl:template>
 

 

1
0
分享到:
评论
5 楼 salever 2011-07-11  
donghustone 写道
请问它支不支持css样式表呢?

这个不太清楚,我没有试过呢,你去fop的官网看看
4 楼 donghustone 2011-07-08  
请问它支不支持css样式表呢?
3 楼 enet_java 2011-01-14  
图片过大是一个原因;后来自己看了看配置文件。是因为配置了
引用
<!-- encodes binary data with hex representation (default off)
This filter is not recommended as it doubles the data size -->
<value>ascii-hex</value>


导致体积增大了很多。

楼主的文章写得很好,多谢了。
2 楼 salever 2011-01-14  
enet_java 写道
请教一个问题,因为添加了字体的样式,导致生成pdf的文件占用空间较大,LZ是怎么解决的呀?
PDF有没有API对内容进行压缩呀?

多谢了!!!


我觉得不是字体的原因吧,是不是你添加了很多图片导致pdf太大?

FOP好像没有专门进行pdf压缩的api,不过你可以看看其他的pdf java api,比如pdf box
1 楼 enet_java 2011-01-13  
请教一个问题,因为添加了字体的样式,导致生成pdf的文件占用空间较大,LZ是怎么解决的呀?
PDF有没有API对内容进行压缩呀?

多谢了!!!

相关推荐

    java后台实现pdf下载导出

    在Java后台生成PDF之前,我们往往需要先设计好数据页面的布局。这时,Freemarker模板引擎就派上了用场。Freemarker是一个基于模板的通用模板语言,它与Java紧密集成,主要用于生成HTML、XML或其他文本格式的文档。...

    java代码生成器整理.pdf

    在Java编程中,我们可以利用IO流和字符串处理来实现简单的代码生成器,就像这个`CodeUtil`类所示。 `CodeUtil`类是一个静态工具类,用于生成HTML文件。它的主要功能是创建一个包含基本HTML结构、脚本和表格的文件。...

    javaPDF教程

    在Java中,开发人员可以利用多种库来读取、写入、编辑和转换PDF文档。 1. **Freetype-2.4.6**: Freetype是一个开源的字体渲染库,它允许程序动态地渲染字体。在处理PDF时,可能需要Freetype来正确显示和操作文档中...

    iText操作Pdf简单整理

    iText不仅支持生成PDF和rtf格式的文档,还具备将XML和HTML文件转化为PDF的功能,大大扩展了其在各种应用场景中的实用性。 在使用iText时,首先需要引入相应的库文件,如压缩包中的`itextpdf-5.5.1.jar`,这是iText...

    基于PDF表单的调查问卷系统的设计与实现(pdf)

    传统的纸质问卷调查存在诸多不便,如分发和回收困难、数据整理复杂等问题。而基于PDF表单的调查问卷系统能够有效解决这些问题,提升调研的效率和安全性。 #### 二、系统概述 该系统主要利用PDF表单的强大功能和JSP...

    js脚本元素调用java代码[整理].pdf

    总结来说,JSP脚本元素调用Java代码是实现动态网页的关键,但为了提高开发效率和代码质量,应当尽量将业务逻辑移出JSP,采用MVC架构,利用独立的Java类和测试,同时善用EL和自定义标签以降低代码复杂度。这样的做法...

    java开发常见漏洞及处理说明整理.pdf

    综上所述,Java Web开发人员应时刻关注这些安全问题,采取有效的预防措施,确保系统在提供服务的同时,能抵御各种潜在的攻击。通过过滤器和其他安全库,可以显著提高应用程序的安全性,防止数据泄露或系统受损。在...

    freemaker导出word的doc_docx_带动态图片及word转pdf工具类_jar_万能整理汇总rengm.zip

    在Freemarker中,我们可以利用Java代码将图片数据嵌入到模板中,比如从数据库获取图片的Base64编码,然后在Word文档中展示。这在生成报告时非常有用,例如根据用户数据生成个性化的图表或照片。 接下来,我们讨论...

    Wikipdf:尝试生成PDF格式的Wikipedia文章的集合

    为了解决这一问题,我们可以利用编程语言,如Java,来生成PDF格式的维基百科文章集合。本文将深入探讨如何利用Java技术实现这一目标。 首先,我们需要理解Java中的PDF处理库。iText是Java中广泛使用的PDF库,它提供...

    第17章报告生成器.pdf

    报告生成器是ANSYS软件中的一个实用工具,用于在分析过程中收集图像和数据,并将它们组织成HTML格式的报告。...通过这些详细步骤和功能,报告生成器成为了一个强大的工具,帮助用户有效地整理和展示ANSYS分析结果。

    java导出报表

    Java提供了多种库来支持这种功能,例如Apache POI用于处理Microsoft Office格式(如Excel),iText或Flying Saucer用于生成PDF,以及JasperReports和BIRT这样的报表工具。 在给定的文件名中,`...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    本书是第II卷,以开发人员在项目开发中经常遇到的问题和必须掌握的技术为中心,介绍了应用Java进行桌面程序开发各个方面的知识和技巧,主要包括Java语法与面向对象技术、Java高级应用、窗体与控件应用、文件操作...

    office to pdf

    本项目利用Java技术栈,结合Apache POI、iText和Jsoup库,实现了在Windows和Linux系统上将Word、Excel文档转换为PDF的功能。 首先,Apache POI是Java平台上的一个开源库,主要用于处理Microsoft Office格式的文件,...

    visualc++开发文档生成工具Doxygen教程参考.pdf

    它可以根据程序本身的结构,将批注经过处理重新整理成为一个纯粹的参考手册,对于后面利用您的程序代码的人而言将会减少许多的负担。 Doxygen 的使用可分为两大部分。首先是特定格式的批注撰写,第二便是利用 ...

    利用JSF应对应用程序开发的未来[整理].pdf

    JavaServer Faces(JSF)是一种基于Java的Web应用程序开发框架,它简化了用户界面组件的创建和管理,提高了开发效率。JSF的核心特性包括: 1. **标准和可扩展的用户界面组件**:JSF提供了一系列预定义的UI组件,如...

    Fusioncharts31教程整理.pdf

    FusionCharts 是一款强大的图表组件,它利用Flash技术展示数据驱动的动态图表,适用于各种网页脚本语言环境,如HTML、JSP等。该组件的核心特点是通过XML作为数据接口,将数据转化为直观、交互式的图表。下面将详细...

    tomcat学习资料收集整理.pdf

    1. Servlet可以与各种资源(如数据库、文件系统等)交互,生成动态内容并返回给客户端。 2. Servlet有权限控制,可以限制外部用户的访问。 3. 它可以从本地或远程存储激活。 4. 通过Servlet Tag技术,Servlet能在...

Global site tag (gtag.js) - Google Analytics