Is it possible to show subsections with page numbers in TOC using XSL:FO? - xslt-2.0

I am using XSL FO to generate PDF file. I am trying to show the chapter, section1, subsection1, subsubsection1 with Page numbers. But I am unable to do it. Here is the code that I am trying to achieve. But I can able to see the page number and internal link only for the "Chapter" part alone. See the below image. I want to show the page number and internal destination link for subsections too. Any help?
<xsl:template name="genTOC">
<fo:block break-before='page'>
<fo:block font-size="16pt" text-align="center" font-weight="bold">TABLE OF CONTENTS</fo:block>
<xsl:for-each select="//chapter|//section1|//subsection1|//subsubsection1">
<fo:block text-align-last="justify">
<fo:basic-link internal-destination="{generate-id(.)}">
<xsl:value-of select="index"/>
<xsl:text> </xsl:text>
<xsl:value-of select="title"/>
<fo:leader leader-pattern="dots"/>
<fo:page-number-citation ref-id="{generate-id(.)}"/>
</fo:basic-link>
</fo:block>
</xsl:for-each>
</fo:block>
</xsl:template>
<xsl:template match="chapter">
<fo:block id="{generate-id()}">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="section1">
<fo:block id="{generate-id()}">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="subsection1">
<fo:block id="{generate-id()}">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="subsubsection1">
<fo:block id="{generate-id()}">
<xsl:apply-templates/>
</fo:block>
</xsl:template>

Related

Parsing a string using more than one delimiter XSLT 2 3

I have to insert a dot leader at the end of the first line of what could be several lines of text. If the string is over 37 characters, I display the first 37 characters, then the dot leader, and then the rest of the string flows to the following lines. I can get this to work using a space as a delimiter, but if there are no spaces the entire description gets pushed to the second line. So commas and hyphens need to be delimiters, as well, at the very least.
<xsl:template name="substring-before-last">
<xsl:param name="input" />
<xsl:variable name="del" select="'[,|-|\s]+'"/>
<xsl:variable name="string-tokens" select="tokenize($input, $del)"/>
<xsl:variable name="substring">
<xsl:value-of select="$string-tokens[not(. = $string-tokens[last()])]"/>
</xsl:variable>
<xsl:value-of select="$substring"/>
</xsl:template>
<xsl:template name="getFirstLine">
<xsl:param name="input" />
<xsl:variable name="remaining">
<xsl:call-template name="substring-before-last">
<xsl:with-param name="input" select="$input"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="string-length($remaining) >= 37">
<xsl:call-template name="getFirstLine">
<xsl:with-param name="input" select="$remaining"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$remaining"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="addDotLeader">
<xsl:variable name="descript" select="REMOVE-THIS-IS-A-TEST-LINE,XXX,XXXXXXXXXXXX XXXXX,XXX-XXX,XXXX-XXX,XXXXXXXXXXXXXX,XXXX,XXXXXXXX-XXXXXX-XXXXXXXXX" />
<xsl:variable name="length" select="string-length($descript)" as="xs:integer"/>
<xsl:choose>
<xsl:when test="$length >= 37">
<xsl:variable name="remaining">
<xsl:call-template name="getFirstLine">
<xsl:with-param name="input" select="$descript"/>
</xsl:call-template>
</xsl:variable>
<!-- print out first line -->
<xsl:value-of select="$remaining"/>
<!-- print out dot leader -->
<xsl:text> </xsl:text><fo:leader leader-pattern="dots"/><xsl:text> </xsl:text>
<fo:block text-align-last="left" margin-left="8px">
<xsl:value-of select="substring-after($descript,$remaining)"/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<!-- Description fits on first line -->
<xsl:value-of select="$descript"/>
<xsl:text> </xsl:text><fo:leader leader-pattern="dots" /><xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This almost works, except the dashes and commas are stripped out when it gets tokenized. When I try to print out the rest of the string, the substring-after fails because of the missing delimiters.
Actual output:
. . REMOVE THIS IS A TEST...........................
The actual string was REMOVE-THIS-IS-A-TEST-LINE,XXX,XXXXXXXXXXXX XXXXX,XXX-XXX,XXX-XXXXX,XXXXXXXXXXXXXX,XXXX,XXXXXXXX-XXXXXX-XXXXXXXXX
The desired output is:
. . REMOVE-THIS-IS-A-TEST-..........................
LINE,XXX,XXXXXXXXXXXX XXXXX,XXX-XXX,XXX-XXXXX,
XXXXXXXXXXXXXX,XXXX,XXXXXXXX-XXXXXX-XXXXXXXXX
I'm sure there is a better way to do this, maybe start with capturing the first 37 characters before checking delimiters?

Transform only the last element of XML and copy the rest in XSLT

I have an XML like below -
<root>
<row>
<col1>16</col1>
<col2>466</col2>
<col3>144922</col3>
<col4>0</col4>
<col5>5668</col5>
<col6>475</col6>
</row>
</root>
The number of columns can vary inside the root element. It can also be up to col9. My requirement is to modify the last column and copy others as it is for an incoming XML.
I have something like this till now where I am assigning the value to used as the last element in a variable and then trying to call it when the last position is reached-
<?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" exclude-result-prefixes="xs"
version="2.0">
<xsl:param name="line88.field2" />
<xsl:param name="rec16.col2" />
<xsl:variable name="col3">
<xsl:choose>
<xsl:when test="$rec16.col2 ='165'">
<xsl:value-of select="'Y'"/>
</xsl:when>
<xsl:when>
------
<xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"></xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="row[position() = last()]">
<col9>
<xsl:call-template name="AnotherTemplate">
<xsl:with-param name="inputData">
<xsl:value-of select="$col3" />
</xsl:with-param>
</xsl:call-template>
</col9>
</xsl:template>
<xsl:template name="AnotherTemplate">
<xsl:param name="inputData"></xsl:param>
<xsl:value-of select="$inputData" />
</xsl:template>
</xsl:stylesheet>
But this is not working for me. Just giving me one column with the modified value.Please help.
The desired outcome should be as below where the last column has the value from the variable.
<root>
<row>
<col1>16</col1>
<col2>466</col2>
<col3>144922</col3>
<col4>0</col4>
<col5>5668</col5>
<col6>Y</col6>
</row>
</root>
Without knowing your whole XSLT code. You can use this row template:
<xsl:template match="row/*[starts-with(local-name(),'col') and position() = last()]">
<xsl:element name="{concat('col',position() div 2)}">
<xsl:call-template name="AnotherTemplate">
<xsl:with-param name="inputData">
<xsl:value-of select="$col3" />
</xsl:with-param>
</xsl:call-template>
</xsl:element>
</xsl:template>
It replaces the last col? element by the given value (the result of the xsl:call-template code).

Some help on how groupings actually work would be much appreciated

I'm trying to get my head around how the different grouping techniques work. What causes a group, how are each group defined, and how the key are formed for each group.
If I wanted to use "group-adjacent" to move all following siblings of a specific element name into the first of preceding sibling of a given type. Would this be doable? I know how I can do this with recursive templates, and to some extent with keys in xslt 1.0. But I cannot get the 2.0 groups to work for me.
Lets say that I want to move all fig elements into the first preceding para, given that there are no other kinds of elements in between the fig(s) and the preceding para element, in this simple xml.
<root>
<first_lvl>
<title>First heading</title>
<para>First para under first heading</para>
<para>Second para under first heading</para>
<fig>fig1</fig>
<fig>fig 2</fig>
<table>Table A</table>
<fig>fig 3</fig>
<para>Third para under first heading</para>
<para>Fourth para under first heading</para>
<fig>fig4</fig>
</first_lvl>
</root>
Desired result:
<root>
<first_lvl>
<title>First heading</title>
<para>First para under first heading</para>
<para>Second para under first heading
<fig>fig1</fig>
<fig>fig 2</fig>
</para>
<table>Table A</table>
<fig>fig 3</fig>
<para>Third para under first heading</para>
<para>Fourth para under first heading
<fig>fig4</fig>
</para>
</first_lvl>
</root>
How can I set a grouping up that takes care of every directly following fig element?
This doesn't work:
<xsl:template match=para[following-sibling::*[1][self::fig]]>
<xsl:for-each-group select"folowing-sibling::*" group-adjacent="boolean(self::fig)">
<xsl:apply-templates select="current-group()" mode="move"/>
</xsl:for-each-group>
</xsl:template>
And then I've added atemplate to build content for each fig inside the para, and one to ignore those figs when they appear later on in the processing.
No luck though.
I have no other values to group by, other that the fact that they are fig elements.
What am I missing here?
I would start with a group-starting-with on those para followed by a fig and then inside use group-adjacent to identify only the first group of adjacent figs. With the verbosity of XSLT that looks a bit convoluted but does the job as far as I have understood your requirements:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes" />
<xsl:template match="*[para and fig]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each-group select="*" group-starting-with="para[following-sibling::*[1][self::fig]]">
<xsl:choose>
<xsl:when test="self::para[following-sibling::*[1][self::fig]]">
<xsl:variable name="para-head" select="."/>
<xsl:for-each-group select="tail(current-group())" group-adjacent="boolean(self::fig)">
<xsl:choose>
<xsl:when test="position() = 1">
<xsl:copy select="$para-head">
<xsl:apply-templates select="node(), current-group()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6qVRKxh
I have taken the liberty to use XSLT 3 instead of 2 but you simply would have to spell out the identity transformation declared by the xsl:mode on-no-match="shallow-copy" and make sure you use
<xsl:element name="{name($para-head)}" namespace="{namespace-uri($para-head)}">
<xsl:apply-templates select="$para-head/node(), current-group()"/>
</xsl:element>
instead of the XSLT 3 only xsl:copy with a select:
<xsl:copy select="$para-head">
<xsl:apply-templates select="node(), current-group()"/>
</xsl:copy>
and instead of the XPath 3 tail function you use subsequence e.g.
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:output method="xml" indent="yes" />
<xsl:template match="*[para and fig]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each-group select="*" group-starting-with="para[following-sibling::*[1][self::fig]]">
<xsl:choose>
<xsl:when test="self::para[following-sibling::*[1][self::fig]]">
<xsl:variable name="para-head" select="."/>
<xsl:for-each-group select="subsequence(current-group(), 2)" group-adjacent="boolean(self::fig)">
<xsl:choose>
<xsl:when test="position() = 1">
<xsl:element name="{name($para-head)}" namespace="{namespace-uri($para-head)}">
<xsl:apply-templates select="$para-head/node(), current-group()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:transform>
http://xsltransform.hikmatu.com/bFDb2BN
On the other hand, I am not sure whether an attempt not using xsl:for-each-group but rather a template matching para[following-sibling::*[1][self::fig]] and then consuming following sibling figs (which can be done easily in XSLT 3 with xsl:iterate) is not more compact:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="para[following-sibling::*[1][self::fig]]">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
<xsl:iterate select="following-sibling::*">
<xsl:choose>
<xsl:when test="self::fig">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:break/>
</xsl:otherwise>
</xsl:choose>
</xsl:iterate>
</xsl:copy>
</xsl:template>
<xsl:template match="fig[preceding-sibling::*[not(self::fig)][1][self::para]]"/>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6qVRKxh/3

XSL-FO - Center Text not working

In my XSL-FO, I am having problems centering text as it breaks the footnote-line2 into 2 sentences as opposed to going the full length of the page and then breaking the sentence once it runs out of room. See graphics below.
I have this code:
<fo:block font-size="9pt" text-align="center">
<xsl:value-of select="footnote-line1"/>
</fo:block>
<fo:block font-size="9pt" text-align="center">
<xsl:value-of select="footnote-line2"/>
</fo:block>
Here is my XML:
<footnote-line1>This verification of XXXXXX is self-generated and is produced by John Smith from our secure website.</footnote-line1>
<footnote-line2>If there are any questions regarding the information contained in this letter, you may contact the Joe Blow at his office at the contact information noted above.</footnote-line2>
Which generates this output::
Unfortunately, the above image is not what I want as it is doing a paragraph break after the word "you".
This 2 images below are the output I am hoping to get. The first image I achieved that was by shrinking down the font to 6pt, but I don't want to shrink the font down.
Option 1) Alternate solution, but would rather not shrink down the font size
Option 2) Optimal solution without shrinking the font
How would I achieve option #2?
Updated with colored backgrounds and both are the same length for the first and second footnotes:
Here is the full XSL-FO document:
<xsl:stylesheet version="1.0" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/>
<xsl:output indent="yes"/>
<xsl:template match="/">
<fo:root font-family="Times Roman" font-size="9pt">
<fo:layout-master-set>
<fo:simple-page-master master-name="main-voe"
margin-top="15mm" margin-bottom="15mm" margin-left="25mm" margin-right="25mm" page-width="215mm" page-height="279mm">
<fo:region-body margin-top="1.0in" margin-bottom="1.0in"/>
<fo:region-before extent="1.0in" margin-top="1.0in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="main-voe" initial-page-number="1">
<fo:static-content flow-name="xsl-region-before" font-size="9pt">
<fo:block>
<xsl:apply-templates select="enrolment/address-dept-info"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body" font-size="9pt">
<fo:block font-weight="bold" linefeed-treatment="preserve" space-after="9pt">
<xsl:apply-templates select="enrolment/letter"/>
</fo:block>
<fo:block linefeed-treatment="preserve" font-weight="bold" space-after="9pt">
Notes:
</fo:block>
<xsl:apply-templates select="enrolment/student-info/student-notes/student-notes-details"/>
<xsl:apply-templates select="enrolment/signature"/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<!-- This sections builds the header information at the top of the page -->
<xsl:template match="address-dept-info">
<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="25mm" padding="0"/>
<fo:table-column column-width="100mm"/>
<fo:table-column column-width="85mm"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:block>
Logo info
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block linefeed-treatment='preserve'
white-space-collapse='false'><xsl:value-of select="address1"/></fo:block>
<fo:block linefeed-treatment='preserve'
white-space-collapse='false'><xsl:value-of select="address2"/></fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block linefeed-treatment='preserve'
white-space-collapse='false'>
<xsl:value-of select="dept/dept-name"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="student-info">
<fo:block linefeed-treatment="preserve" space-after="9pt">
<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="5mm"/>
<fo:table-column column-width="80mm"/>
<fo:table-column column-width="100mm"/>
<fo:table-body>
<xsl:apply-templates select="//student-info/student-program/student-program-details"/>
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
<xsl:template match="student-notes/student-notes-details">
<xsl:value-of select="description"/>
</xsl:template>
<xsl:template match="signature">
<fo:block linefeed-treatment='preserve' space-after="9pt">
Sincerely,
</fo:block>
<fo:block linefeed-treatment='preserve' space-after="12pt">
<xsl:value-of select="first-name"/><xsl:text> </xsl:text> <xsl:value-of select="last-name"/><xsl:text>
</xsl:text>
<xsl:value-of select="position"/><xsl:text>
</xsl:text>
</fo:block>
<fo:block font-size="9pt" text-align="center" background-color="grey">
<xsl:value-of select="//letter/footnote-line1"/>
</fo:block>
<fo:block font-size="9pt" text-align="center" background-color="yellow">
<xsl:value-of select="//letter/footnote-line2"/>
</fo:block>
</xsl:template>
<xsl:template match="letter">
<fo:block linefeed-treatment='preserve' text-align="center">
<xsl:value-of select="title"/>
</fo:block>
</xsl:template>
</xsl:stylesheet>
Thanks
Cheers
I tested this with FOP and you get the answer you get. Sorry. It's FOP. You could report as a bug.
Using RenderX XEP you get the answer you want and should rightly expect.

Need to create a static footer with xsl-fo

At the bottom of my document, I have an address that I need to stay at the bottom so the address can be used in window mailers. I have tried using static-content tags to achieve this, but my document errors out every time. I am new to this, so I'm guessing I missing something. I want the "contractor" template to be static in the footer.
<xsl:template match="/" >
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<!-- Setup up page size (Can be in inches or centimeters)-->
<fo:simple-page-master
master-name="page"
page-width="8.50in"
page-height="11.00in"
margin-top="0.50in"
margin-bottom="0.50in"
margin-left="0.50in"
margin-right="0.50in">
<fo:region-body margin-top="0cm"/>
<fo:region-before extent="0cm"/>
<fo:region-after extent="0cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<xsl:apply-templates select="/Permits/Permit" />
</fo:root>
</xsl:template>
<xsl:template match="Permit">
<fo:page-sequence master-reference="page">
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="10pt">
<xsl:call-template name="header"/>
<xsl:call-template name="permitdetails"/>
<xsl:call-template name="permitdetails2"/>
<xsl:call-template name="parties"/>
<xsl:call-template name="feesummary"/>
<xsl:call-template name="inspections"/>
<xsl:call-template name="contractor"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</xsl:template>
<fo:static-content> should be a child of <fo:page-sequence> It should not work if you were simply trying to wrap your <xsl:call-template name="contractor"/> above in static-content tags. Can you post your template with the error?
Something like this should work:
<xsl:template match="Permit">
<fo:page-sequence master-reference="page">
<fo:static-content flow-name="xsl-region-after">
<fo:block>
<xsl:call-template name="contractor"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="10pt">
<xsl:call-template name="header"/>
<xsl:call-template name="permitdetails"/>
<xsl:call-template name="permitdetails2"/>
<xsl:call-template name="parties"/>
<xsl:call-template name="feesummary"/>
<xsl:call-template name="inspections"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</xsl:template>
</xsl:template>
<fo:static-content> should be declared before the body flow, even if it appears after the body in the output.

Resources