I need to use a hyphen for the bullet on a nested list applying xsl:fo. I have two templates to match but only one is being applied. If I use just the first template, the outer list gets the template applied. If I use just the second template, the nested list gets the template applied. The pattern I am attempting to match in the second template is any unordered list with a list item as a parent. Any help on getting my desired output is greatly appreciated.
XML
<ThisNode>
<ul>
<li>Item One</li>
<li>Item Two</li>
<li>Item Three
<ul>
<li>Sub-Item One</li>
<li>Sub-Item Two</li>
</ul>
</li>
<li>Item Four</li>
<li>Item Five</li>
</ul>
</ThisNode>
XSLT
<xsl:template match="ul">
<fo:list-block>
<xsl:for-each select="./li">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block font-weight="bold">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="8pt">
<fo:block><xsl:value-of select="."/></fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:for-each>
</fo:list-block>
</xsl:template>
<xsl:template match="li//ul">
<fo:list-block start-indent="8pt">
<xsl:for-each select="./li">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block font-weight="bold">-</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="16pt">
<fo:block><xsl:value-of select="."/></fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:for-each>
</fo:list-block>
</xsl:template>
<fo:block text-align="left">
<xsl:apply-templates select="./ThisNode"/>
</fo:block>
DESIRED OUTPUT
• Item One
• Item Two
• Item Three
- Sub-Item One
- Sub-Item Two
• Item Four
• Item Five
ACTUAL OUTPUT USING EITHER JUST THE 1ST TEMPLATE OR USING BOTH
• Item One
• Item Two
• Item ThreeSub-Item OneSub-Item Two
• Item Four
• Item Five
ACTUAL OUTPUT USING ONLY THE 2ND TEMPLATE
Item One Item Two Item Three
- Sub-Item One
- Sub-Item Two
Item Four Item Five
This looks like a good place for a call-template with a mode, to distinguish between the 2 cases. The outer template calls the inner template:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
<fo:block text-align="left">
<xsl:apply-templates select="ThisNode"/>
</fo:block>
</xsl:template>
<xsl:template match="ul">
<fo:list-block>
<xsl:for-each select="li">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block font-weight="bold">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="8pt">
<fo:block>
<!-- I'm not quite sure what you want here -->
<xsl:value-of select="text()"/>
<xsl:apply-templates select="ul" mode="inner"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:for-each>
</fo:list-block>
</xsl:template>
<xsl:template match="ul" mode="inner">
<fo:list-block start-indent="8pt">
<xsl:for-each select="./li">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block font-weight="bold">-</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="16pt">
<fo:block>
<xsl:value-of select="."/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:for-each>
</fo:list-block>
</xsl:template>
</xsl:stylesheet>
The following stylesheet illustrates a possible solution. Your approach is not far off - however, it fails to account for the behaviour of XSLT processors. This expression:
<xsl:value-of select="."/>
by default returns any text nodes that are children of the context node. But in your case (template match for li elements), all descendant text nodes are output, not only the immediate children of li.
Therefore, the stylesheet below uses
<xsl:value-of select="child::text()"/>
to retrieve the text content of a li element instead and <xsl:apply-templates> to process any li elements that are sub-items of it.
As your title states,
xsl:fo template match not firing on nested list
You diagnosed it correctly. This is because - once inside a template match for li - you do not let the XSLT processor process descendant li elements.
Stylesheet
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.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="/ThisNode">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="simple"
page-height="29.7cm"
page-width="21cm"
margin-top="1cm"
margin-bottom="2cm"
margin-left="2.5cm"
margin-right="2.5cm">
<fo:region-body margin-top="3cm"/>
<fo:region-before extent="3cm"/>
<fo:region-after extent="1.5cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="simple">
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="ul[parent::ThisNode]">
<fo:list-block>
<xsl:apply-templates/>
</fo:list-block>
</xsl:template>
<xsl:template match="ul[parent::li]">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="li">
<fo:list-item>
<xsl:choose>
<xsl:when test="parent::ul/parent::ThisNode">
<fo:list-item-label start-indent="1cm">
<fo:block font-weight="bold">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="1.5cm">
<fo:block><xsl:value-of select="child::text()"/></fo:block>
</fo:list-item-body>
</xsl:when>
<xsl:otherwise>
<fo:list-item-label start-indent="3cm">
<fo:block font-weight="bold">-</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="3.5cm">
<fo:block><xsl:value-of select="."/></fo:block>
</fo:list-item-body>
</xsl:otherwise>
</xsl:choose>
</fo:list-item>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Output (XSL-FO)
<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="simple" page-height="29.7cm" page-width="21cm" margin-top="1cm"
margin-bottom="2cm"
margin-left="2.5cm"
margin-right="2.5cm">
<fo:region-body margin-top="3cm"/>
<fo:region-before extent="3cm"/>
<fo:region-after extent="1.5cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="simple">
<fo:flow flow-name="xsl-region-body">
<fo:list-block>
<fo:list-item>
<fo:list-item-label start-indent="1cm">
<fo:block font-weight="bold">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="1.5cm">
<fo:block>Item One</fo:block>
</fo:list-item-body>
</fo:list-item>Item One
<fo:list-item>
<fo:list-item-label start-indent="1cm">
<fo:block font-weight="bold">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="1.5cm">
<fo:block>Item Two</fo:block>
</fo:list-item-body>
</fo:list-item>Item Two
<fo:list-item>
<fo:list-item-label start-indent="1cm">
<fo:block font-weight="bold">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="1.5cm">
<fo:block>Item Three
</fo:block>
</fo:list-item-body>
</fo:list-item>Item Three
<fo:list-item>
<fo:list-item-label start-indent="3cm">
<fo:block font-weight="bold">-</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="3.5cm">
<fo:block>Sub-Item One</fo:block>
</fo:list-item-body>
</fo:list-item>Sub-Item One
<fo:list-item>
<fo:list-item-label start-indent="3cm">
<fo:block font-weight="bold">-</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="3.5cm">
<fo:block>Sub-Item Two</fo:block>
</fo:list-item-body>
</fo:list-item>Sub-Item Two
<fo:list-item>
<fo:list-item-label start-indent="1cm">
<fo:block font-weight="bold">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="1.5cm">
<fo:block>Item Four</fo:block>
</fo:list-item-body>
</fo:list-item>Item Four
<fo:list-item>
<fo:list-item-label start-indent="1cm">
<fo:block font-weight="bold">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="1.5cm">
<fo:block>Item Five</fo:block>
</fo:list-item-body>
</fo:list-item>Item Five
</fo:list-block>
</fo:flow>
</fo:page-sequence>
</fo:root>
Output (PDF)
Related
I am trying to group values of individual column in xsl fo:table, some values not grouped as expected, it get grouped according to previous column's grouped value,
i need individual grouping in columns, check with my xsl and xml file, i am using these files to generate PDF file using apache FOP.
My XSL File
<?xml version="1.0" encoding="UTF-8"?>
<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="#all"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="data">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="simple"
page-height="8.5in" page-width="11in" margin-top=".5in"
margin-bottom=".5in" margin-left=".5in" margin-right=".5in">
<fo:region-body margin-top="2cm" margin-bottom="2cm" />
<fo:region-before extent="2cm" overflow="hidden" />
<fo:region-after extent="1cm" overflow="hidden" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="simple"
initial-page-number="1">
<fo:static-content flow-name="xsl-region-before">
<fo:block font-size="13.0pt" font-family="serif"
padding-after="2.0pt" space-before="4.0pt" text-align="center"
border-bottom-style="solid" border-bottom-width="1.0pt">
<xsl:text>PDF Test</xsl:text>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block font-size="12.0pt" font-family="sans-serif"
padding-after="2.0pt" space-before="2.0pt" text-align="center"
border-top-style="solid" border-bottom-width="1.0pt">
<xsl:text>Page</xsl:text>
<fo:page-number />
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates select="data-body" />
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="data-body">
<fo:block text-align="center">
<fo:table table-layout="fixed" width="100%"
border-style="dashed">
<fo:table-column border-style="solid" />
<fo:table-column border-style="solid" />
<fo:table-column border-style="solid" />
<fo:table-header>
<xsl:apply-templates select="table-header" />
</fo:table-header>
<fo:table-body>
<xsl:for-each-group select="table-data" group-adjacent="column-two">
<xsl:apply-templates select="current-group()">
<xsl:with-param name="row-span" select="count(current-group())" tunnel="yes"/>
</xsl:apply-templates>
</xsl:for-each-group>
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
<xsl:template match="table-header">
<fo:table-row keep-together.within-page="always"
border-style="solid">
<fo:table-cell>
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="column-one"></xsl:value-of>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="column-two"></xsl:value-of>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="column-three"></xsl:value-of>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="table-data">
<fo:table-row keep-together.within-page="always"
border-style="solid">
<xsl:apply-templates>
<xsl:with-param name="row-group-index" tunnel="yes" select="position()"/>
</xsl:apply-templates>
</fo:table-row>
</xsl:template>
<xsl:template match="table-data/*">
<fo:table-cell>
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="."></xsl:value-of>
</fo:block>
</fo:table-cell>
</xsl:template>
<xsl:template match="table-data/column-two">
<xsl:param name="row-span" tunnel="yes"/>
<xsl:param name="row-group-index" tunnel="yes"/>
<xsl:choose>
<xsl:when test="$row-span = 1">
<xsl:next-match/>
</xsl:when>
<xsl:when test="$row-span > 1 and $row-group-index = 1">
<fo:table-cell number-rows-spanned="{$row-span}">
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="."></xsl:value-of>
</fo:block>
</fo:table-cell>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="table-data/column-three">
<xsl:param name="row-span" tunnel="yes"/>
<xsl:param name="row-group-index" tunnel="yes"/>
<xsl:choose>
<xsl:when test="$row-span = 1">
<xsl:next-match/>
</xsl:when>
<xsl:when test="$row-span > 1 and $row-group-index = 1">
<fo:table-cell number-rows-spanned="{$row-span}">
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="."></xsl:value-of>
</fo:block>
</fo:table-cell>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
My XML File
<?xml version="1.0" encoding="UTF-8"?>
<data>
<data-body>
<table-header>
<column-one>Column One</column-one>
<column-two>Column Two</column-two>
<column-three>Column Three</column-three>
</table-header>
<table-data>
<column-one>One</column-one>
<column-two>5000</column-two>
<column-three>Three</column-three>
</table-data>
<table-data>
<column-one>One</column-one>
<column-two>5000</column-two>
<column-three>Three</column-three>
</table-data>
<table-data>
<column-one>One</column-one>
<column-two>1200</column-two>
<column-three>Three</column-three>
</table-data>
<table-data>
<column-one>One</column-one>
<column-two>2000</column-two>
<column-three>Four</column-three>
</table-data>
<table-data>
<column-one>One</column-one>
<column-two>2000</column-two>
<column-three>Four</column-three>
</table-data>
<table-data>
<column-one>One</column-one>
<column-two>1234</column-two>
<column-three>Five</column-three>
</table-data>
<table-data>
<column-one>One</column-one>
<column-two>5666</column-two>
<column-three>Five</column-three>
</table-data>
<table-data>
<column-one>One</column-one>
<column-two>5666</column-two>
<column-three>Five</column-three>
</table-data>
</data-body>
</data>
I think one way to solve that is to use two passes, one which computes and adds the row-span to columns, and the second that transforms to XSL-FO.
In https://xsltfiddle.liberty-development.net/3NSSEuY/2 I have used XSLT 3 and xsl:iterate to implement the first step for a sequence of column names provided as an xs:string* parameter:
<?xml version="1.0" encoding="UTF-8"?>
<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="#all"
version="3.0">
<xsl:param name="columns-to-group" as="xs:string*" select="'column-two', 'column-three'"/>
<xsl:mode name="compute-row-spans" on-no-match="shallow-copy"/>
<xsl:mode name="compute-row-span" on-no-match="shallow-copy"/>
<xsl:template match="data-body" mode="compute-row-spans">
<xsl:iterate select="$columns-to-group">
<xsl:param name="table" select="."/>
<xsl:on-completion select="$table"/>
<xsl:next-iteration>
<xsl:with-param name="table">
<xsl:apply-templates select="$table" mode="compute-row-span">
<xsl:with-param name="column-to-group" tunnel="yes" select="."/>
</xsl:apply-templates>
</xsl:with-param>
</xsl:next-iteration>
</xsl:iterate>
</xsl:template>
<xsl:template match="data-body" mode="compute-row-span">
<xsl:param name="column-to-group" as="xs:string" tunnel="yes"/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="table-header"/>
<xsl:for-each-group select="table-data" group-adjacent="*[name() = $column-to-group]">
<xsl:apply-templates select="current-group()" mode="compute-row-span">
<xsl:with-param name="column-to-group" tunnel="yes" select="$column-to-group"/>
<xsl:with-param name="row-span" select="count(current-group())" tunnel="yes"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="table-data" mode="compute-row-span">
<xsl:copy>
<xsl:apply-templates select="#*" mode="#current"/>
<xsl:apply-templates select="*" mode="#current">
<xsl:with-param name="current-row-index" tunnel="yes" select="position()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="table-data/*" mode="compute-row-span">
<xsl:param name="column-to-group" tunnel="yes"/>
<xsl:param name="row-span" tunnel="yes"/>
<xsl:param name="current-row-index" tunnel="yes"/>
<xsl:choose>
<xsl:when test="name() = $column-to-group and $row-span > 1">
<xsl:if test="$current-row-index = 1">
<xsl:copy>
<xsl:apply-templates select="#*" mode="#current"/>
<xsl:attribute name="row-span" select="$row-span"/>
<xsl:apply-templates mode="#current"/>
</xsl:copy>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:next-match/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="data">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="simple"
page-height="8.5in" page-width="11in" margin-top=".5in"
margin-bottom=".5in" margin-left=".5in" margin-right=".5in">
<fo:region-body margin-top="2cm" margin-bottom="2cm" />
<fo:region-before extent="2cm" overflow="hidden" />
<fo:region-after extent="1cm" overflow="hidden" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="simple"
initial-page-number="1">
<fo:static-content flow-name="xsl-region-before">
<fo:block font-size="13.0pt" font-family="serif"
padding-after="2.0pt" space-before="4.0pt" text-align="center"
border-bottom-style="solid" border-bottom-width="1.0pt">
<xsl:text>PDF Test</xsl:text>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block font-size="12.0pt" font-family="sans-serif"
padding-after="2.0pt" space-before="2.0pt" text-align="center"
border-top-style="solid" border-bottom-width="1.0pt">
<xsl:text>Page</xsl:text>
<fo:page-number />
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates select="data-body" />
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="data-body">
<fo:block text-align="center">
<fo:table table-layout="fixed" width="100%"
border-style="dashed">
<fo:table-column border-style="solid" />
<fo:table-column border-style="solid" />
<fo:table-column border-style="solid" />
<fo:table-header>
<xsl:apply-templates select="table-header" />
</fo:table-header>
<xsl:variable name="table-with-row-spans">
<xsl:apply-templates select="." mode="compute-row-spans"/>
</xsl:variable>
<fo:table-body>
<xsl:apply-templates select="$table-with-row-spans/data-body/table-data"/>
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
<xsl:template match="table-header">
<fo:table-row keep-together.within-page="always"
border-style="solid">
<fo:table-cell>
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="column-one"></xsl:value-of>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="column-two"></xsl:value-of>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:value-of select="column-three"></xsl:value-of>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="table-data">
<fo:table-row keep-together.within-page="always"
border-style="solid">
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
<xsl:template match="table-data/*">
<fo:table-cell>
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
<xsl:template match="table-data/*[#row-span]">
<fo:table-cell number-rows-spanned="{#row-span}">
<fo:block font-size="10pt" font-family="sans-serif"
padding-top="3pt">
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
</xsl:stylesheet>
I have an xml document as shown below. Each repeating doc is a page in PDF file
<AFPXMLFile>
<docs>
<regList>
<region>1</region>
<secList>
<col>1</col>
<lines></lines>
</secList>
</regList>
<regList>
<region>2</region>
<secList>
<col>2</col>
<lines>
<line>IBM BELGIUM xxx</line>
<line>xxxxxx</line>
<line>xxxx</line>
</lines>
</secList>
</regList>
<regList></regList>
<regList></regList>
</docs>
<docs></docs>
</AFPXMLFile>
My XSL is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"
exclude-result-prefixes="fo">
<xsl:template match="AFPXMLFile">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master page-width="21cm" page-height="29.7cm" margin-top="1.27cm" margin-bottom="1.27cm" margin-left="1.75cm" master-name="A4">
<fo:region-body margin-top="1mm" margin-bottom="1mm"/>
<fo:region-before extent="0mm"/>
<fo:region-after extent="0mm"/>
<fo:region-start writing-mode="tb-rl" precedence="true" extent="10mm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4" font-family="sans-serif">
<xsl:for-each select="docs">
<xsl:for-each select="./regList">
<xsl:choose>
<xsl:when test="region = 1">
<fo:static-content flow-name="xsl-region-before">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="purple">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = 2">
<fo:static-content flow-name="xsl-region-start">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="4pt" padding-before="4pt" text-align="left" color="green">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = 3">
<fo:static-content flow-name="xsl-region-body">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="blue">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = 4">
<fo:flow flow-name="xsl-region-after">
<xsl:for-each select="./secList">
<xsl:for-each select="./lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="orange">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</fo:flow>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
When I run the transformation I get the following error:
org.apache.fop.fo.ValidationException: For "fo:page-sequence", "fo:static-content" must be declared before "fo:flow"! (No context info available)
This I suspect because it is repeating the static content region for each page. So I believe I need to change the simple-page-master:master-name for every page
I need help with that.
According to the XSL-FO recommendation, the contents of 'fo:page-sequence' is:
(title?,folio-prefix?,folio-suffix?,static-content*,flow+)
which means that all the fo:static-content elements must be before the fo:flow elements.
Your stylesheet dynamically creates either fo:static-content (if region is 1, 2 or 3) or fo:flow (if region is 4). From the reduced input file in your question it is not possible to see whether the regList elements are alway correctly sorted, so that they produce a valid output when sequentially processed.
Moreover, as each docs element represents a different document with different headers/footers, you have to create distinct fo:page-sequences instead of a single one (otherwise you get too many static contents and flows for a single page sequence):
<xsl:for-each select="docs">
<fo:page-sequence master-reference="A4" font-family="sans-serif">
<xsl:for-each select="./regList">
....
</xsl:for-each>
</fo:page-sequence>
</xsl:for-each>
Moreover, there is a strange thing in the stylesheet: you map an fo:static-content to the xsl-region-body region, and the fo:flow to the xsl-region-after region, which is quite unusual. If the "real" content for the body region is the one with region = 3, you must process region 1, 2 and 4 first, and then region 3:
<xsl:for-each select="docs">
<fo:page-sequence master-reference="A4" font-family="sans-serif">
<!-- create static contents -->
<xsl:apply-templates select="./regList[region != '3']"/>
<!-- create flow -->
<xsl:apply-templates select="./regList[region = '3']"/>
</fo:page-sequence>
</xsl:for-each>
with an additional template to match reglist:
<xsl:template match="regList">
<xsl:choose>
<xsl:when test="region = '1'">
<fo:static-content flow-name="xsl-region-before">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="purple">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = '2'">
<fo:static-content flow-name="xsl-region-start">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="4pt" padding-before="4pt" text-align="left" color="green">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = '3'">
<fo:flow flow-name="xsl-region-body">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="blue">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:flow>
</xsl:when>
<xsl:when test="region = '4'">
<fo:static-content flow-name="xsl-region-after">
<xsl:for-each select="./secList">
<xsl:for-each select="./lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="orange">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
</xsl:choose>
</xsl:template>
or several, smaller templates:
<xsl:template match="reglist[region = '1']">
<fo:static-content flow-name="xsl-region-before">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="purple">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:template>
<xsl:template match="reglist[region = '2']">
...
</xsl:template>
...
I have a table in FOP and it is working nicely until I get a very long word. The word then overwrites the cell ending in the table. I tried the wrap-option="wrap" within the table-cell and/or the block of the cell but it doesn't work
**Total Edit**
since I guess it is to complicated to just show bits here is the complete xsl file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" exclude-result-prefixes="fo">
<xsl:output method="xml" version="1.0" omit-xml-declaration="no" indent="yes" />
<xsl:param name="tableCell" select="'1.0'" />
<!-- Globale Variablen START -->
<xsl:param name="tabbgcolor" select="'#EEEEEE'" />
<!-- Globale Variablen ENDE -->
<xsl:template match="/datasheet">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<fo:layout-master-set>
<fo:simple-page-master page-height="29.7cm" page-width="21cm" margin-top="1cm" margin-bottom="1cm" margin-left="1.5cm" margin-right="1.5cm" master-name="first">
<fo:region-body margin-top="20mm" margin-bottom="20mm" />
<fo:region-before extent="15mm" />
<fo:region-after extent="15mcm" />
</fo:simple-page-master>
<fo:simple-page-master master-name="new" margin-right="1.0cm" margin-left="1.0cm" margin-bottom="1cm" margin-top="1cm" page-height="21cm" page-width="29.7cm">
<fo:region-body margin-top="30mm" margin-bottom="20mm" />
<fo:region-before extent="30mm" />
<fo:region-after extent="15mm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence language="de" country="DE" master-reference="new" initial-page-number="1">
<fo:static-content flow-name="xsl-region-before">
<fo:block text-align="center" font-size="12pt" padding="5pt" font-weight="bold">
<xsl:value-of select="title" />
</fo:block>
<fo:block text-align="right" font-size="12pt" padding="5pt" font-weight="bold">
<xsl:value-of select="date" />
</fo:block>
<fo:block text-align="right" font-size="12pt" padding="0pt" font-weight="bold">
<xsl:value-of select="time" />
</fo:block>
<fo:block>
<fo:leader leader-length="100%" leader-pattern="rule" rule-thickness="2pt" color="black" />
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block text-align="center">
Seite
<fo:page-number />
von
<fo:page-number-citation ref-id="TheVeryLastPage" />
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<fo:block>
<fo:leader />
</fo:block>
<fo:block>
<fo:leader />
</fo:block>
<fo:block>
<fo:leader />
</fo:block>
<fo:block font-weight="bold" padding="5pt" padding-top="25pt">
Jahre <xsl:value-of select="fromYear" /> bis <xsl:value-of select="toYear" />
</fo:block>
<fo:block text-align="center">
<xsl:choose>
<xsl:when test="dataList != ''">
<fo:table table-layout="fixed" width="100%" border-style="solide" border-width="1pt">
<fo:table-column column-width="25%" border-style="solid" border-width="1pt" />
<fo:table-column column-width="25%" border-style="solid" border-width="1pt" />
<fo:table-column column-width="25%" border-style="solid" border-width="1pt" />
<fo:table-column column-width="25%" border-style="solid" border-width="1pt" />
<fo:table-header>
<fo:table-row>
<fo:table-cell border-style="solid" font-weight="bold" border-width="0pt">
<fo:block>
Cell1
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" font-weight="bold" border-width="0pt">
<fo:block>
Cell2
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" font-weight="bold" border-width="0pt">
<fo:block>
Cell3
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" font-weight="bold" border-width="0pt">
<fo:block>
Cell4
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body>
<xsl:for-each select="dataList">
<fo:table-row>
<fo:table-cell padding="5pt" border-style="solid" border-width="1pt">
<fo:block text-align="left">
<xsl:value-of select="data1" />
</fo:block>
</fo:table-cell>
<fo:table-cell padding="5pt" border-style="solid" border-width="1pt">
<fo:block text-align="left">
<xsl:value-of select="data2" />
</fo:block>
</fo:table-cell>
<fo:table-cell padding="5pt" border-style="solid" border-width="1pt">
<fo:block text-align="left">
<xsl:value-of select="data3" />
</fo:block>
</fo:table-cell>
<fo:table-cell padding="5pt" border-style="solid" border-width="1pt">
<fo:block text-align="left">
<xsl:call-template name="intersperse-with-zero-spaces">
<xsl:with-param name="str" select="data4"/>
</xsl:call-template>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</xsl:when>
<xsl:otherwise>
<fo:block padding="6cm" text-align="center" font-weight="bold" font-size="16pt">No data.
</fo:block>
</xsl:otherwise>
</xsl:choose>
</fo:block>
<fo:block id="TheVeryLastPage">
</fo:block>
</fo:flow>
</fo:page-sequence>
<!-- ___________________________________________________________________________________________________________ -->
</fo:root>
</xsl:template>
<xsl:template name="intersperse-with-zero-spaces">
<xsl:param name="str"/>
<xsl:variable name="spacechars">
</xsl:variable>
<xsl:if test="string-length($str) > 0">
<xsl:variable name="c1" select="substring($str, 1, 1)"/>
<xsl:variable name="c2" select="substring($str, 2, 1)"/>
<xsl:value-of select="$c1"/>
<xsl:if test="$c2 != '' and
not(contains($spacechars, $c1) or
contains($spacechars, $c2))">
<xsl:text></xsl:text>
</xsl:if>
<xsl:call-template name="intersperse-with-zero-spaces">
<xsl:with-param name="str" select="substring($str, 2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The cell data4 is the one with the problems.... like 255 digits or characters at once with no hyphen oder space.
The input is from a database but could be like:
<datasheet>
<dataList>
<data1>intro</data1>
<data2>section</data2>
<data3>cutters</data3>
<data4>743576746876357467569384657654687465874638563465873487435767468763574675693846576546874658746385634658734874357674687635746756938465765468746587463856346587348743576746876357467569384657654687465874638563465873487435767468763574675693846576546874658746385634658734874357674687635746756938465765468746587463856346587348743576746876357467569384657654687465874638563465873487435767468763574675693846576546874658746385634658734874357674687635746756938465765468746587463856346587348743576746876357467569384657654687465874638563465873487435767468763574675693846576546874658746385634658734874357674687635746756938465765468746587463856346587348</data4>
</dataList>
</datasheet>
the result should be a table like:
|cell1 |cell2 |cell3 |cell4 |
_________________________________
|intro |section|cutters|7435767|
|4687635|
|7467569|
|3846576|
_________________________________
and so on in cell 4
Now the above works
<xsl:template name="intersperse-with-zero-spaces">
<xsl:param name="str"/>
<xsl:variable name="spacechars">
</xsl:variable>
<xsl:if test="string-length($str) > 0">
<xsl:variable name="c1" select="substring($str, 1, 1)"/>
<xsl:variable name="c2" select="substring($str, 2, 1)"/>
<xsl:value-of select="$c1"/>
<xsl:if test="$c2 != '' and
not(contains($spacechars, $c1) or
contains($spacechars, $c2))">
<xsl:text></xsl:text>
</xsl:if>
<xsl:call-template name="intersperse-with-zero-spaces">
<xsl:with-param name="str" select="substring($str, 2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
calling place use to call like below:
<fo:block text-align="left">
<xsl:call-template name="intersperse-with-zero-spaces">
<xsl:with-param name="str" select="data4"/>
</xsl:call-template>
</fo:block>
I tested, it's working fine.
One option would be for you to break up the long number/word in XSLT while you're transforming to FO. You'd have to decide where you want it to break.
See also:
XSL-FO fop. Long text flows into adjacent cells/block, obscuring stuff there
Update (copied from comment):
There must be copy/paste errors in the stylesheet... there is an <xsl:param>, which is only supposed to occur at the beginning of a template, in the middle of the template. The stylesheet is therefore invalid. Are there supposed to be two templates? Also, I don't yet see the template named intersperse-with-zero-spaces; but maybe you're still adding it.
Update 2:
The above has been fixed in the Question.
I had assumed the second template couldn't be intersperse-with-zero-spaces, since it was calling that template. It didn't occur to me that it was supposed to be a recursive template!
By the way, if you have the option of using XSLT 2.0, the intersperse-with-zero-spaces template could be replaced by a much more efficient regexp replace.
Add hyphenate="true", then it will automatically adjust the spaces in the table cell.
<fo:table-cell border-bottom-width="0px" border-left-width="0px"
border-right-width="0px" border-top-width="0px" font-size="9px"
padding-left="0px" border-style="solid" border-width="1pt"
border-color="white" padding-start="0px" padding-end="2px"
padding-before="0px" padding-after="0px" width="16%" display-align="center"
text-align="start" hyphenate="true">
I'm using ApacheFOP to create a pdf which has multiple pages of content and a watermark (semi-transparent) on every page. I'm struggling quite a bit with XSLFO and got a proof of concept working using the list functionality - however I imagine there is a simpler way. Can someone more familiar with xslfo provide a simpler solution? Below is my code:
<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:param name="watermarkPath" />
<xsl:param name="pdfPages" />
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page"
page-height="11in" page-width="8.5in" margin="0.5in">
<fo:region-body />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="my-page">
<fo:flow flow-name="xsl-region-body">
<xsl:for-each select="$pdfPages">
<fo:block-container>
<fo:list-block>
<fo:list-item>
<fo:list-item-label>
<fo:block>
<fo:external-graphic
content-width="7.5in">
<xsl:attribute name="src">
<xsl:value-of
select="concat('data:image/png;base64,',.)" />
</xsl:attribute>
</fo:external-graphic>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<fo:external-graphic
content-width="7.5in">
<xsl:attribute name="src">
<xsl:value-of select="$watermarkPath" />
</xsl:attribute>
</fo:external-graphic>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block-container>
</xsl:for-each>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Not sure it works with FOP but if it was a full size image of the page ...
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page"
page-height="11in" page-width="8.5in" margin="0.5in">
<fo:region-body />
<fo:region-before extent="11in" region-name="myheader" background-image="{path-to-my-image}"/>
</fo:simple-page-master>
</fo:layout-master-set>
If not, then put an absolute-positioned block-container inside the actual static-content for region "myheader" and don't use the background-image above.
<fo:page-sequence master-reference="my-page">
<fo:static-content flow-name="myheader">
<fo:block-container position="absolute" top="XX" left="XX">
<fo:block>
<fo:external-graphic .../>
</fo:block>
</fo:block-container>
</fo:static-content>
If you truly want an overlay (meaning over the top of all content) then put it in region-after and not before.
I put my background image in the region-body :
fo:region-body background-image="{$Url}"/
Does somebody know how to achieve this with XSL-FO transformation? The question details should be clear from the codes below.
Input:
<section>
<title>Section 1</title>
<orderedlist>
<listitem><para>item 1.1</para></listitem>
<listitem>
<para>item 1.2</para>
<orderedlist>
<listitem><para>item a</para></listitem>
<listitem><para>item b</para></listitem>
</orderedlist>
</listitem>
</orderedlist>
</section>
<section>
<title>Section 2</title>
<orderedlist>
<listitem><para>item 2.1</para></listitem>
<listitem><para>item 2.2</para></listitem>
</orderedlist>
</section>
Desired output:
Section 1
1. item 1.1
2. item 1.2
a. item a
b. item b
Section 2
3. item 2.1
4. item 2.2
Here is the XSL file for lists:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<!-- templates for lists - supports numbered and itemized types -->
<!-- the maximum depth is currently 2 -->
<xsl:template match="orderedlist">
<fo:list-block start-indent="0.5cm" space-before="0.2cm"
provisional-distance-between-starts="0.7cm">
<xsl:apply-templates />
</fo:list-block>
</xsl:template>
<xsl:template match="orderedlist//orderedlist">
<fo:list-block start-indent="1.2cm" provisional-distance-between-starts="0.7cm"
padding-top="-0.2cm" padding-bottom="0.2cm">
<xsl:apply-templates />
</fo:list-block>
</xsl:template>
<xsl:template match="itemizedlist">
<fo:list-block start-indent="0.5cm" space-before="0.2cm"
provisional-distance-between-starts="0.7cm">
<xsl:apply-templates />
</fo:list-block>
</xsl:template>
<xsl:template match="itemizedlist//itemizedlist">
<fo:list-block start-indent="1.2cm" provisional-distance-between-starts="0.7cm"
padding-top="-0.2cm" padding-bottom="0.2cm">
<xsl:apply-templates />
</fo:list-block>
</xsl:template>
<xsl:template match="orderedlist/listitem">
<fo:list-item margin-top="0.1cm">
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:number count="listitem" format="1." />
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates />
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="orderedlist//orderedlist/listitem">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:number count="listitem" format="a." />
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates />
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="itemizedlist/listitem">
<fo:list-item margin-top="0.1cm">
<fo:list-item-label end-indent="label-end()">
<fo:block>•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates />
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="itemizedlist//itemizedlist/listitem">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates />
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
</xsl:stylesheet>
Assuming you input sample is (added the sections topmost element to make the sample well-formed):
<sections>
<section>
<title>Section 1</title>
<orderedlist>
<listitem><para>item 1.1</para></listitem>
<listitem>
<para>item 1.2</para>
<orderedlist>
<listitem><para>item a</para></listitem>
<listitem><para>item b</para></listitem>
</orderedlist>
</listitem>
</orderedlist>
</section>
<section>
<title>Section 2</title>
<orderedlist>
<listitem><para>item 2.1</para></listitem>
<listitem><para>item 2.2</para></listitem>
</orderedlist>
</section>
</sections>
at a given listitem of first level you can use:
count(
preceding-sibling::listitem
|
../../preceding-sibling::section/orderedlist/listitem)
+ 1
For example:
<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 indent="yes"/>
<xsl:template match="sections">
<fo:block>
<xsl:apply-templates select="section"/>
</fo:block>
</xsl:template>
<xsl:template match="section">
<fo:list-block>
<xsl:apply-templates select="title | orderedlist/listitem"/>
</fo:list-block>
</xsl:template>
<xsl:template match="title">
<fo:list-item>
<xsl:value-of select="."/>
</fo:list-item>
</xsl:template>
<xsl:template match="listitem">
<fo:list-item>
<xsl:number
value="count(
preceding-sibling::listitem
|
../../preceding-sibling::section/orderedlist/listitem)
+ 1" format="1. "/>
</fo:list-item>
</xsl:template>
</xsl:stylesheet>
produces:
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:list-block>
<fo:list-item>Section 1</fo:list-item>
<fo:list-item>1. </fo:list-item>
<fo:list-item>2. </fo:list-item>
</fo:list-block>
<fo:list-block>
<fo:list-item>Section 2</fo:list-item>
<fo:list-item>3. </fo:list-item>
<fo:list-item>4. </fo:list-item>
</fo:list-block>
</fo:block>
Do you have a particular reason for not using DocBook and the DocBook-XSL stylesheets? That would give you a lot "for free" (perhaps you know that already).
In DocBook, what you ask for is already implemented. There is a continuation attribute on <orderedlist> that indicates whether the numbering in a list continues from the preceding list. This is supported in the DocBook-XSL styleheets for FO output (check out fo/lists.xsl and common/common.xsl to see how it's done).