包含嵌入式HTML的PDF报告

时间:2015-09-25 19:43:46

标签: java html xslt xsl-fo apache-fop

我们有一个基于Java的系统,它从数据库中读取数据,将单个数据字段与预设的XSL-FO标记合并,并将结果转换为PDF Apache 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
  • 进一步修改Antenna House中的xhtml2fo.xsl以删除所有文档范围和页面范围的转换
  • 将此修改后的XSLT应用于我的XHTML字符串(javax.xml.transform)
  • 使用XPath(javax.xml.xpath)
  • 提取根目录下的所有节点
  • 将结果直接提供给现有的XSL-FO文档

我有这样的代码的裸骨版本,并得到以下错误:

  

(错误的位置未知)org.apache.fop1.fo.ValidationException:   &#34; {http://www.w3.org/1999/XSL/Format}表体&#34;不是一个有效的孩子   &#34; fo:block&#34;! (没有上下文信息)

我的问题:

  1. 解决此问题的方法是什么?
  2. <fo:block>可以作为通用容器,其他对象(包括表格)嵌套在里面吗?
  3. 这是解决任务的整体合理方法吗?
  4. 如果某人已经&#34; 完成了&#34;,请分享您的体验。

2 个答案:

答案 0 :(得分:4)

  1. 如果您使用XXT调试器,例如oXygen或XML Spy,那么您可以逐步完成转换。使用oXygen - 不确定XML Spy或其他编辑器 - 如果单击调试器输出中的标记,oXygen将突出显示源和生成该节点的样式表的标记。

    获得FO后, focheck 框架(https://github.com/AntennaHouse/focheck)对当前可用的FO进行了最完整的验证。

  2. fo:block可以包含表格等。在XSL 1.1规范中,每个FO的定义都包含一个&#39;内容&#39;列出其允许内容的子部分。例如,见http://www.w3.org/TR/xsl11/#fo_block。 &#39;参数实体的定义&#39;内容模型中的http://www.w3.org/TR/xsl11/#d0e6532,但有些FO在其定义文本中有其他限制。
  3. 您引用的文章似乎没有提取根据XPath&#39;提取根目录下的所有节点。一步,我不确定你为什么需要它。除此之外,它看起来像是使用Java完成工作的合理方法。
  4. 您可以将具有非FO标记的<!-- Field A -->替换为提供足够信息以引入要插入的字段的非FO标记,而不是将从您的JTidy编辑的HTML转换为静态FO。然后,您可以创建一个XSLT样式表,通过对FO部分进行标识转换,将模板+引用文档转换为直接FO,如@kevin-brown的答案,并使用引用标记中的信息构建与document()函数(http://www.w3.org/TR/xslt#document)一起使用的URI,用于查找要插入的标记。

    如果字段内容的FO位于磁盘上,则使用document()非常简单。如果不是,那么你必须做一些事情,比如重写XSLT处理器使用的URIResolver,这样,它不是在磁盘上查找,而是检索内容。您甚至可以将JTidy作为URIResolver检索HTML的一部分进行。您也可以转换到FO&#39; inside&#39; URIResolver或者@ kevin-brown建议,将它作为一个单独的模式。如果转换是在URIResolver检索FO之前或期间完成的,那么主要的&#39;模板的转换+对FO的引用只需要提取FO子文档的正确部分,例如, document('constructed-URI')/fo:root/fo:page-sequence/*。但是,如果您要从Antenna House修改样式表,那么无论如何您应该能够修改它以不产生外部fo:root等。

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

    相反,您可以编写一个扩展函数来查找对字段的引用。例如,打印和页面布局社区组@W3C已经为多个XSLT处理器生成了扩展函数,这些处理器在XSLT转换过程中运行FO处理器,以获取格式化结果的区域树的XML。见http://www.w3.org/community/ppl/wiki/XSLTExtensions

答案 1 :(得分:3)

排除故障的最佳方法是使用验证查看器/编辑器来检查XSL FO。许多(例如oXygen)会在您打开它们时向您显示XSL FO结构中的错误,并且它们将描述该问题(就像报告的错误一样)。

在你的情况下,你显然有一个fo:table-body作为fo:block的孩子。它不可能是。 fo:table-body只有一个有效的父元素fo:table。您要么缺少fo:table标签,要么错误地在此位置插入了fo:block。

在我看来,我可能会做一些稍微不同的事情。我会将XHTML内容直接放入您想要的XSL FO中。然后我将创建一个身份转换,复制所有基于fo的内容,但使用XSL转换XHTML部分。通过这种方式,您可以在像XXygen这样的XSL编辑器中实际执行转换,并查看错误发生的位置以及确切原因。像任何其他德国人一样。

注意:您可能也希望查看其他XSL,特别是如果您的HTML可能有任何样式=&#34;&#34; CSS属性。如果是这种情况,它不是简单的HTML,那么你将需要一个更好的方法来处理HTML到FO的HTML。

http://www.cloudformatter.com/css2pdf基于这个完整的转换。这个通用样式表可以在这里找到:http://xep.cloudformatter.com/doc/XSL/xeponline-fo-translate-2.xsl

我是该样式表的作者。它比你提出的要多得多,但是有一个相当复杂的解析递归,用于将CSS样式转换为XSL FO属性。