как преобразовать разные таблицы в XML-файле в PDF с помощью таблицы стилей XSL-FO и Apache FOP - PullRequest
0 голосов
/ 11 ноября 2018

Я преобразовал XML-файл ReportXHTML.xml с 2 таблицами в xhtml, используя таблицу стилей xsl ReportXHTML.xsl . В качестве следующего шага я создал сопоставимый файл XSL-FO ReportFO.xsl , чтобы использовать его вместе с измененным xml-файлом ReportFO.xml и Apache FOP для получения файла PDF, но мое java-приложение FopReport.java не выполнено из-за этого ValidationException

"fo: table-row" отсутствует дочерние элементы. Требуемая модель контента: (Таблица-клетки +) * * 1016

В поисках решения я прочитал спецификацию XSL-Fo и изменил файл XSL-FO, удалив оператор xsl for-each в шаблоне xsl с именем table-row. Я получил файл PDF ReportFO.pdf .

Это показывает, что xsl-оператор for-each в xsl-шаблоне с именем table-head работает. Шаблон xsl с именем таблицы строк сопоставим с ним, но не удалось. Это моя проблема. Что нужно изменить в моем файле 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

1 Ответ

0 голосов
/ 11 ноября 2018

Здесь 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>

Когда контекст является tableRow, tableRow/entry для выбора нет.

Быстрое решение состоит в том, чтобы удалить tableRow в 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>
...