java - 带有嵌入式 HTML 的 PDF 报告

标签 java html xslt xsl-fo apache-fop

我们有一个基于 Java 的系统,它从数据库中读取数据,将各个数据字段与预设 XSL-FO 合并标记并将结果转换为 PDFApache FOP .

XSL-FO格式如下:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE Html [
<!ENTITY nbsp  "&#160;"> 
    <!-- all other entities -->
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="xml" indent="yes" />
    <xsl:template match="/">

        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg" font-family="..." font-size="...">
            <fo:layout-master-set>          
                <fo:simple-page-master master-name="Letter Page" page-width="8.500in" page-height="11.000in">

                    <!-- appropriate settings -->

                </fo:simple-page-master>
            </fo:layout-master-set>
            <fo:page-sequence master-reference="Letter Page">

                <!-- some static content -->

            <fo:flow flow-name="xsl-region-body">
                    <fo:block>
                        <fo:table ...>
                            <fo:table-column ... />
                            <fo:table-body>
                                <fo:table-row>
                                    <fo:table-cell ...>
                                        <fo:block text-align="...">
                                            <fo:inline font-size="..." font-weight="...">
                                                <!-- Header / Title -->
                                            </fo:inline>
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                            </fo:table-body>
                        </fo:table>
                    </fo:block>

                    <fo:block>

                        <fo:table ...>
                            <fo:table-column ... />
                            <fo:table-body> 
                                <fo:table-row>
                                    <fo:table-cell>
                                        <fo:block ...>
                                            <!-- Field A -->                                
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                            </fo:table-body>
                        </fo:table>

                        <!-- Other fields in a very similar fashion as the above "Field A" -->

                    </fo:block>

                </fo:flow>      

            </fo:page-sequence>

        </fo:root>              

    </xsl:template>

</xsl:stylesheet>

现在我正在寻找一种允许某些字段包含静态HTML 格式 内容的方法。此内容将由我们支持 HTML 的编辑器生成(类似于 CLEditorCKEditor 等)或从外部粘贴。

我的计划是遵循食谱 from this JavaWorld article :

  • 使用JTidy将 HTML 格式的字符串转换为正确的 XHTML
  • 进一步修改xhtml2fo.xsl从 Antenna House 删除所有文档范围和页面范围的转换
  • 将修改后的 XSLT 应用于我的 XHTML 字符串 (javax.xml.transform)
  • 用XPath (javax.xml.xpath) 提取根目录下的所有节点
  • 将结果直接输入现有的 XSL-FO 文档

我有此类代码的基本版本,但出现以下错误:

(Location of error unknown)org.apache.fop1.fo.ValidationException: "{http://www.w3.org/1999/XSL/Format}table-body" is not a valid child of "fo:block"! (No context info available)

我的问题:

  1. 解决此问题的方法是什么?
  2. 可以<fo:block>充当通用容器,其中嵌套了其他对象(包括表格)?
  3. 这是解决任务的总体合理方法吗?

如果有人“在那里做过那件事”,请分享您的经验。

最佳答案

  1. 如果您在 oXygen 或 XML Spy 中使用 XSLT 调试器,则可以逐步完成转换。使用 oXygen——不确定 XML Spy 或其他编辑器——如果您单击调试器输出中的标记,oXygen 会突出显示来自源代码和生成该节点的样式表的标记。

    一旦您拥有 FO,focheck 框架 ( https://github.com/AntennaHouse/focheck ) 就拥有目前可用的最完整的 FO 验证。

  2. fo:block可以包含表格等。在 XSL 1.1 规范中,每个 FO 的定义都包含一个“内容”小节,其中列出了其允许的内容。参见,例如 http://www.w3.org/TR/xsl11/#fo_block .内容模型中“参数实体”的定义位于 http://www.w3.org/TR/xsl11/#d0e6532。 , 但一些 FO 在其定义的文本中有额外的限制。

  3. 您引用的文章似乎没有“使用 XPath 提取根下的所有节点”步骤,我不确定您为什么需要它。除此之外,它看起来是使用 Java 完成这项工作的合理方法。


不是将从 JTidy 编辑的 HTML 转换而来的 FO 插入静态 FO,而是可以替换 <!-- Field A -->使用非 FO 标记提供足够的信息来引用要插入的字段。然后,您可以制作一个 XSLT 样式表,通过对 FO 部分进行恒等变换,将模板+引用文档转换为直接 FO——正如@kevin-brown 的回答——并使用引用标记中的信息构建与 document() 一起使用的 URI函数 ( http://www.w3.org/TR/xslt#document ) 查找要插入的标记。

如果字段内容的 FO 位于磁盘上,则使用 document()很简单。如果不是,那么您必须执行一些操作,例如覆盖 XSLT 处理器使用的 URIResolver,这样它就可以正确地检索内容,而不是在磁盘上查找。您甚至可以让 JTidying 作为 URIResolver 检索 HTML 的一部分发生。您也可以在 URIResolver“内部”对 FO 进行转换,或者也像@kevin-brown 建议的那样,将其作为单独的模式进行。如果转换是在 URIResolver 检索 FO 之前或期间完成的,那么模板+对 FO 的引用的“主要”转换只需要提取 FO 子文档的正确部分,例如document('constructed-URI')/fo:root/fo:page-sequence/* .但是,如果您从 Antenna House 修改样式表,那么您应该能够修改它以不生成外部 fo:root。等等。

几年前我做了类似的事情,为基于 XSLT 的服务器覆盖了 libxslt XSLT 处理器的 URI 解析器:内部 XSLT 处理器连续运行的上下文被保存为特殊 URI 的文档,不一定写入到文件系统。

相反,您可以编写一个扩展函数来查找对字段的引用。例如,Print and Page Layout Community Group @ W3C 已经为多个 XSLT 处理器生成了扩展函数,这些处理器在 XSLT 转换的中间运行一个 FO 处理器,以获取格式化结果的区域树的 XML。参见 http://www.w3.org/community/ppl/wiki/XSLTExtensions

关于java - 带有嵌入式 HTML 的 PDF 报告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32789541/

相关文章:

java:在 H2 中的 executeBatch() 之后检索键

java - Hibernate - 多对一关系,是否可以自动删除最后一个共享引用?

javascript - 将 ul 中的第一项插入到最后

html - 无论屏幕分辨率如何,如何使 JQuery 数据表适合整个屏幕宽度?

XSLT 前同级限制和解决方法

java - 说明 数组列表

java - IncationContext.proceed() 异常?

javascript - 使用 vue.js 和 js 获取数组

xslt - XSL递归调用-xsl :functions vs xsl:template with call template

xml - XSLT 文档 ('' ) 函数不起作用