xml - 如何使用 XSL-FO 样式表和 Apache FOP 将 XML 文件中的不同表转换为 PDF
问题描述
我使用 xsl 样式表ReportXHTML.xsl将带有 2 个表的 XML 文件ReportXHTML.xml转换为 xhtml 。作为下一步,我构建了可比较的 XSL-FO 文件ReportFO.xsl以将其与修改后的 xml 文件ReportFO.xml和 Apache FOP 一起使用以获取 PDF 文件,但我的 Java 应用程序FopReport.java因此ValidationException而失败
“fo:table-row”缺少子元素。所需的内容模型:(table-cell+)
为了寻找解决方案,我阅读了 XSL-Fo 规范并通过删除名为 table-rows 的 xsl-template 中的xsl-statement for-each来修改 XSL-FO 文件。我得到了 PDF文件 ReportFO.pdf。
这表明名为table-head的 xsl 模板中的 xsl 语句 for-each有效。名为table-rows的 xsl-template与之相当,但失败了。那是我的问题。在我的 XSL-FO 文件中要修改什么?谢谢你的帮助。
文件 ReportXHTML.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="ReportXHTML.xsl" ?>
<report>
<table>
<tableRow>
<tableHead>Name</tableHead>
<tableHead>Street</tableHead>
<tableHead>City</tableHead>
</tableRow>
<tableRow>
<entry>Torsten Horn</entry>
<entry>Hauptsr. 44</entry>
<entry>Aachen</entry>
</tableRow>
<tableRow>
<entry>Heinz Hinz</entry>
<entry>Bahnhofstr. 22</entry>
<entry>Hamburg</entry>
</tableRow>
<tableRow>
<entry>Karl Kunz</entry>
<entry>Königstr. 1</entry>
<entry>Köln</entry>
</tableRow>
</table>
<table>
<tableRow>
<tableHead>Name</tableHead>
<tableHead>City</tableHead>
</tableRow>
<tableRow>
<entry>Torsten Horn</entry>
<entry>Aachen</entry>
</tableRow>
<tableRow>
<entry>Heinz Hinz</entry>
<entry>Hamburg</entry>
</tableRow>
<tableRow>
<entry>Karl Kunz</entry>
<entry>Köln</entry>
</tableRow>
</table>
</report>
样式表 ReportXHTML.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<html>
<head>
<title>Report</title>
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
<xsl:template match="table">
<p>
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<xsl:for-each select="tableRow/tableHead">
<th><xsl:apply-templates/></th>
</xsl:for-each>
</tr>
<xsl:for-each select="tableRow">
<tr>
<xsl:for-each select="entry">
<td><xsl:apply-templates/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</p>
</xsl:template>
</xsl:stylesheet>
导致 FireFox: 结果的图像
样式表 ReportFO.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xs:stylesheet version="1.0"
xmlns:xs="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<!-- XLS/ FO Specification see: https://www.w3.org/TR/xsl11/ -->
<!-- Attribute-Sets -->
<xs:attribute-set name="cell-style">
<xs:attribute name="border-width">0.5pt</xs:attribute>
<xs:attribute name="border-style">solid</xs:attribute>
</xs:attribute-set>
<xs:attribute-set name="block-style">
<xs:attribute name="font-size"> 10pt</xs:attribute>
<xs:attribute name="line-height">15pt</xs:attribute>
<xs:attribute name="start-indent">1mm</xs:attribute>
<xs:attribute name="end-indent"> 1mm</xs:attribute>
</xs:attribute-set>
<!-- Page Layout -->
<xs:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="DIN-A4"
page-height="29.7cm" page-width="21cm"
margin-top="2cm" margin-bottom="1.5cm"
margin-left="2.5cm" margin-right="1.0cm">
<fo:region-body
margin-top="1.5cm" margin-bottom="1.0cm"
margin-left="0.0cm" margin-right="0.0cm"/>
<fo:region-before region-name="header" extent="1.3cm"/>
<fo:region-after region-name="footer" extent="1.0cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="DIN-A4">
<fo:static-content flow-name="header">
<fo:block font-size="14pt" font-family="Helvetica" font-weight="bold" text-align="left">
Report
</fo:block>
<fo:block text-align-last="justify"><fo:leader leader-pattern="rule" rule-thickness="0.5" />
</fo:block>
</fo:static-content>
<fo:static-content flow-name="footer">
<fo:block text-align-last="justify"><fo:leader leader-pattern="rule" rule-thickness="0.5" />
</fo:block>
<fo:block font-size="10pt" text-align="center" >
Page <fo:page-number/> of <fo:page-number-citation ref-id="LastPage"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<xs:apply-templates/>
<fo:block id="LastPage"/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xs:template>
<!-- Table-Head -->
<xs:template name="table-head">
<fo:table-row>
<xs:for-each select="tableRow/tableHead">
<fo:table-cell xs:use-attribute-sets="cell-style">
<fo:block xs:use-attribute-sets="block-style" text-align="center">
<xs:apply-templates/>
</fo:block>
</fo:table-cell>
</xs:for-each>
</fo:table-row>
</xs:template>
<!-- Table-Rows -->
<xs:template name="table-rows">
<fo:table-row>
<xs:for-each select="tableRow/entry">
<fo:table-cell xs:use-attribute-sets="cell-style">
<fo:block xs:use-attribute-sets="block-style">
<xs:apply-templates/>
</fo:block>
</fo:table-cell>
</xs:for-each>
</fo:table-row>
</xs:template>
<!-- Table -->
<xs:template match="report/table">
<fo:block><fo:leader /></fo:block>
<fo:table border-style="solid" table-layout="fixed" width="100%">
<fo:table-header>
<xs:call-template name="table-head"/>
</fo:table-header>
<fo:table-body>
<xs:for-each select="tableRow">
<xs:call-template name="table-rows"/>
</xs:for-each>
</fo:table-body>
</fo:table>
</xs:template>
</xs:stylesheet>
修改后的 xml 文件 ReportFO.xml
<?xml version="1.0" encoding="UTF-8"?>
<report>
<table>
<tableRow>
<tableHead>Name</tableHead>
etc .....
FopReport.java
import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.fop.apps.*;
import org.apache.*;
// compile by: javac -classpath ".:lib/fop.jar:lib/xmlgraphics-commons-2.3.jar" FopReport.java
// run by:
// java -classpath ".:lib/fop.jar:lib/xmlgraphics-commons-2.3.jar:lib/commons-logging-1.0.4.jar:lib/avalon-framework-api-4.3.1.jar:lib/avalon-framework-impl-4.3.1.jar:lib/commons-io-1.3.1.jar:lib/batik-all-1.10.jar" FopReport ReportFO.xsl ReportFO.xml ReportFO.pdf
// see : https://xmlgraphics.apache.org/fop/2.3/embedding.html
public class FopReport
{
public static void main( String[] args ) throws Exception
{
if( args.length != 3 ) {
System.out.println( "Enter XSL- and XML-Inputfile, PDF-Outputfile." );
return;
}
FopReport.xmlToPdfPerXsl( args[0], args[1], args[2] );
System.out.println( args[0] + " + " + args[1] + " --> " + args[2] );
}
public static void xmlToPdfPerXsl( String inputXSL, String inputXML, String outputPDF ) throws Exception
{
// Step 1: Construct a FopFactory by specifying a reference to the configuration file
// (reuse if you plan to render multiple documents!)
FopFactory fopFactory = FopFactory.newInstance(new File("fop.xconf"));
// Step 2: Set up output stream
OutputStream pdf = new FileOutputStream( outputPDF );
// Step 3: Construct fop with desired output format
// Fop fop = FopFactory.newFop( MimeConstants.MIME_PDF, pdf );
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, pdf);
// Step 5: Setup input and output for XSLT transformation
// Setup input stream
Source xml = new StreamSource( inputXML );
Source xsl = new StreamSource( inputXSL );
// Resulting SAX events (the generated FO) must be piped through to FOP
Result sax = new SAXResult( fop.getDefaultHandler() );
// Step 4: Setup JAXP using identity transformer
// Transformer transformer = TransformerFactory.newInstance().newTransformer( xsl );
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer( xsl ); // identity transformer
// Step 6: Start XSLT transformation and FOP processing
transformer.transform( xml, sax );
}
}
验证异常
Nov. 11, 2018 7:34:05 NACHM. org.apache.fop.apps.FopConfParser configure
INFORMATION: Default page-height set to: 11in
Nov. 11, 2018 7:34:05 NACHM. org.apache.fop.apps.FopConfParser configure
INFORMATION: Default page-width set to: 8.26in
ERROR: '"fo:table-row" is missing child elements. Required content model: (table-cell+) (No context info available)'
Exception in thread "main" javax.xml.transform.TransformerException: org.apache.fop.fo.ValidationException: "fo:table-row" is missing child elements. Required content model: (table- cell+) (Keine Kontextinformationen verfügbar)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:786)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:370)
at FopReport.xmlToPdfPerXsl(FopReport.java:52)
at FopReport.main(FopReport.java:22)
Caused by: org.apache.fop.fo.ValidationException: "fo:table-row" is missing child elements. Required content model: (table-cell+) (Keine Kontextinformationen verfügbar)
at org.apache.fop.events.ValidationExceptionFactory.createException(ValidationExceptionFactory.java:38)
at org.apache.fop.events.EventExceptionManager.throwException(EventExceptionManager.java:58)
at org.apache.fop.events.DefaultEventBroadcaster$1.invoke(DefaultEventBroadcaster.java:173)
at com.sun.proxy.$Proxy2.missingChildElement(Unknown Source)
at org.apache.fop.fo.FONode.missingChildElementError(FONode.java:588)
at org.apache.fop.fo.flow.table.TableRow.finalizeNode(TableRow.java:115)
at org.apache.fop.fo.FONode.endOfNode(FONode.java:330)
at org.apache.fop.fo.flow.table.TableRow.endOfNode(TableRow.java:108)
at org.apache.fop.fo.FOTreeBuilder$MainFOHandler.endElement(FOTreeBuilder.java:360)
at org.apache.fop.fo.FOTreeBuilder.endElement(FOTreeBuilder.java:190)
at java.xml/com.sun.org.apache.xml.internal.serializer.ToXMLSAXHandler.endElement(ToXMLSAXHandler.java:263)
at java.xml/com.sun.org.apache.xml.internal.serializer.ToXMLSAXHandler.endElement(ToXMLSAXHandler.java:557)
at jdk.translet/die.verwandlung.ReportFO.table$dash$rows()
at jdk.translet/die.verwandlung.ReportFO.template$dot$3()
at jdk.translet/die.verwandlung.ReportFO.applyTemplates()
at jdk.translet/die.verwandlung.ReportFO.applyTemplates()
at jdk.translet/die.verwandlung.ReportFO.template$dot$0()
at jdk.translet/die.verwandlung.ReportFO.applyTemplates()
at jdk.translet/die.verwandlung.ReportFO.transform()
at java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet.transform(AbstractTranslet.java:624)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:776)
... 3 more
---------
org.apache.fop.fo.ValidationException: "fo:table-row" is missing child elements. Required content model: (table-cell+) (Keine Kontextinformationen verfügbar)
at org.apache.fop.events.ValidationExceptionFactory.createException(ValidationExceptionFactory.java:38)
at org.apache.fop.events.EventExceptionManager.throwException(EventExceptionManager.java:58)
at org.apache.fop.events.DefaultEventBroadcaster$1.invoke(DefaultEventBroadcaster.java:173)
at com.sun.proxy.$Proxy2.missingChildElement(Unknown Source)
at org.apache.fop.fo.FONode.missingChildElementError(FONode.java:588)
at org.apache.fop.fo.flow.table.TableRow.finalizeNode(TableRow.java:115)
at org.apache.fop.fo.FONode.endOfNode(FONode.java:330)
at org.apache.fop.fo.flow.table.TableRow.endOfNode(TableRow.java:108)
at org.apache.fop.fo.FOTreeBuilder$MainFOHandler.endElement(FOTreeBuilder.java:360)
at org.apache.fop.fo.FOTreeBuilder.endElement(FOTreeBuilder.java:190)
at java.xml/com.sun.org.apache.xml.internal.serializer.ToXMLSAXHandler.endElement(ToXMLSAXHandler.java:263)
at java.xml/com.sun.org.apache.xml.internal.serializer.ToXMLSAXHandler.endElement(ToXMLSAXHandler.java:557)
at jdk.translet/die.verwandlung.ReportFO.table$dash$rows()
at jdk.translet/die.verwandlung.ReportFO.template$dot$3()
at jdk.translet/die.verwandlung.ReportFO.applyTemplates()
at jdk.translet/die.verwandlung.ReportFO.applyTemplates()
at jdk.translet/die.verwandlung.ReportFO.template$dot$0()
at jdk.translet/die.verwandlung.ReportFO.applyTemplates()
at jdk.translet/die.verwandlung.ReportFO.transform()
at java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet.transform(AbstractTranslet.java:624)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:776)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:370)
at FopReport.xmlToPdfPerXsl(FopReport.java:52)
at FopReport.main(FopReport.java:22)
文件 ReportFO.pdf ReportFO.pdf 的图像
解决方案
在这里,依次xs:for-each
将上下文更改为每个上下文,并以当前作为上下文项调用“表行”模板:tableRow/entry
tableRow
<xs:for-each select="tableRow">
<xs:call-template name="table-rows"/>
</xs:for-each>
在这里,将xs:for-each
上下文更改为上下文项的每个子项entry
的每个tableRow
子项:
<!-- Table-Rows -->
<xs:template name="table-rows">
<fo:table-row>
<xs:for-each select="tableRow/entry">
<fo:table-cell xs:use-attribute-sets="cell-style">
<fo:block xs:use-attribute-sets="block-style">
<xs:apply-templates/>
</fo:block>
</fo:table-cell>
</xs:for-each>
当上下文是 atableRow
时,没有tableRow/entry
选择。
快速的解决方案是删除tableRow
in tableRow/entry
。
我建议长期的解决方案是多用xs:apply-templates
少用xs:for-each
. 如果您使用过xs:apply-templates
,那么 XSLT 处理器每次都会选择子元素,然后找到xs:template
用于每个元素的最佳匹配。类似(未经测试):
<xs:template match="table">
<fo:table>
<fo:table-header>
<xs:apply-templates select="tableRow[tableHead]" />
</fo:table-header>
<fo:table-body>
<xs:apply-templates select="tableRow[entry]" />
</fo:table-body>
</fo:table>
</xs:template>
<xs:template match="tableRow">
<fo:table-row>
<xs:apply-templates />
</fo:table-row>
</xs:template>
<xs:template match="tableHead">
<fo:table-cell xs:use-attribute-sets="cell-style">
<fo:block xs:use-attribute-sets="block-style" text-align="center">
<xs:apply-templates/>
</fo:block>
</fo:table-cell>
</xs:template>
<xs:template match="entry">
<fo:table-cell xs:use-attribute-sets="cell-style">
<fo:block xs:use-attribute-sets="block-style">
<xs:apply-templates/>
</fo:block>
</fo:table-cell>
</xs:template>
推荐阅读
- vbscript - Task Scheduler,调度vbs脚本
- arduino - 使用一个按钮在 oled 显示器上显示不同的图像
- pandas - 熊猫栏目分类
- wpf - 我想控制 wpf 中的动态 subMenu 自动列。如果 5 行自动中断 1 列。如何?
- firebase - 如何使用 exoplayer 从 url 获取 mp3 标题、图像和描述
- python - 跳过文档中的类型提示
- api - 如何通过名称获取特定的 api,然后从此“Apis”结构列表中获取它的 ID?
- python - Pytorch:嵌入层后,无法获得代表
- c - C中的结构扩展
- mysql - 如何根据mysql中的客户ID显示两个表中的数据