Skip to main content

  | मौसम…

411 समाचार
Public archive edition · Attributed source reporting with original dates · AI drafts are excluded

411 REFERENCE · Wikibooks

XQuery/XSL-FO Tables

Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.

Motivation You want to be able to create high-quality tabular outputs suitable for book-publishing. Method To accomplish this we will convert our XML into XSL-FO tables. Unlike HTML, XML-FO allows you to create flows of text and you can set up rules on how objects span page boundaries. Sample Input Here is a sample XML file that contains a table with two columns. <table heading="Department Phone Extensions"> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Sue Smith</Name> <Extension>5678</Extension> </Person> </table> We would like this XML file to be rendered with two columns, the first containing the person's name and the second their phone extension. It should look like the following. + Department Phone Extensions Name Extension John Doe 1234 Sue Smith 5678 Example FO File The following is the core of the XML-FO layout that you will need to create the table (without control on the column widths). <fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:block font-size="14pt" padding="10px" font-family="Verdana">Department Phone Extensions</fo:block> <fo:block font-size="10pt"> <fo:table border="solid" border-collapse="collapse"> <fo:table-header> <fo:table-row space-after="10px"> <fo:table-cell> <fo:block font-weight="bold">Name</fo:block> </fo:table-cell> <fo:table-cell> <fo:block font-weight="bold">Extension</fo:block> </fo:table-cell> </fo:table-row> </fo:table-header> <fo:table-body> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>John Doe</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>1234</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block>Sue Smith</fo:block> </fo:table-cell> <fo:table-cell> <fo:block>5678</fo:block> </fo:table-cell> </fo:table-row> </fo:table-body> </fo:table> </fo:block> </fo:block> Transform with XQuery Transform with XSLT NOTE: This example should be moved to a book on XSLT. XQuery typeswitch transforms should be used to do this. We can transform the XML structure to XSL-FO using an XSLT script. This generic script only requires that the root of the XML table is called table with a heading attribute. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fo="http://www.w3.org/1999/XSL/Format" exclude-result-prefixes="xs" version="2.0"> <xsl:template match="/table"> <fo:block> <fo:block font-size="14pt" padding="10px" font-family="Verdana"> <xsl:value-of select="@heading"/> </fo:block> <fo:block font-size="10pt"> <fo:table border="solid" border-collapse="collapse" > <fo:table-header> <fo:table-row> <xsl:for-each select="*[1]/*"> <fo:table-cell> <fo:block font-weight="bold"> <xsl:value-of select="name(.)"/> </fo:block> </fo:table-cell> </xsl:for-each> </fo:table-row> </fo:table-header> <fo:table-body> <xsl:apply-templates select="*"/> </fo:table-body> </fo:table> </fo:block> </fo:block> </xsl:template> <xsl:template match="*"> <fo:table-row> <xsl:for-each select="*"> <fo:table-cell> <fo:block> <xsl:value-of select="."/> </fo:block> </fo:table-cell> </xsl:for-each> </fo:table-row> </xsl:template> </xsl:stylesheet> XQuery integration Finally we can generate the full XSL-FO document and render as PDF with an XQuery script. We use the XSLT to transform the table, and then embed that XSL-FO fragment in the XSL-FO master before rendering as PDF and streaming the binary document. There are of course other ways to assemble the full XSLT-FO document. xquery version "1.0"; import module namespace xslfo="http://exist-db.org/xquery/xslfo"; import module namespace transform="http://exist-db.org/xquery/transform"; declare namespace fo="http://www.w3.org/1999/XSL/Format"; let $table := <table heading="Department Phone Extensions"> <Person> <Name>John Doe</Name> <Extension>1234</Extension> </Person> <Person> <Name>Doe John</Name> <Extension>4321</Extension> </Person> <Person> <Name><span style="color: #f00;">Sue Smith</span></Name> <Extension>5678</Extension> </Person> </table> let $table-fo := transform:transform($table,doc("/db/Wiki/eXist/xsl-fo/table2fo.xsl"),()) let $fo := <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="my-page"> <fo:region-body margin="1in"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="my-page"> <fo:flow flow-name="xsl-region-body"> <fo:block> {$table-fo} </fo:block> </fo:flow> </fo:page-sequence> </fo:root> let $pdf := xslfo:render($fo, "application/pdf", ()) return response:stream-binary($pdf, "application/pdf", "output.pdf") Execute Database data As a further example, the following XQuery selects all employees and renders them in a PDF table: xquery version "1.0"; import module namespace xslfo="http://exist-db.org/xquery/xslfo"; import module namespace transform="http://exist-db.org/xquery/transform"; declare namespace fo="http://www.w3.org/1999/XSL/Format"; let $table := <table heading="Employees"> {//Emp} </table> let $table-fo := transform:transform($table,doc("/db/Wiki/eXist/xsl-fo/table2fo.xsl"),()) let $fo := <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="my-page"> <fo:region-body margin="1in"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="my-page"> <fo:flow flow-name="xsl-region-body"> <fo:block> {$table-fo} </fo:block> </fo:flow> </fo:page-sequence> </fo:root> let $pdf := xslfo:render($fo, "application/pdf", ()) return response:stream-binary($pdf, "application/pdf", "output.pdf") Execute