How to loop through the alphabet with XSLT? - xslt

I need to create a dynamic XSL-FO table in XSLT 2.0, that looks similar to this:
+------+------+------+------+------+------+
| | 1 | 2 | 3 | 4 | 5 |
+------+------+------+------+------+------+
| A | | | | | |
+------+------+------+------+------+------+
| B | | | | | |
+------+------+------+------+------+------+
| C | | | | | |
+------+------+------+------+------+------+
| D | | | | | |
+------+------+------+------+------+------+
| E | | | | | |
+------+------+------+------+------+------+
Both end-stops (rows, columns) are dynamic and specified with the last number/letter in the source data.
<input>
<end-stop-column>5</end-stop-column>
<end-stop-row>E</end-stop-row>
</input>
I thought of an for-each approach (pseudo code):
<fo:table>
<fo:table-body>
<fo:table-row>
<fo:table-cell><fo:block></fo:block></fo:table-cell>
<xsl:for-each select="1 to 5">
<xsl:variable name="colid" select="current()"/>
<fo:table-cell><fo:block><xsl:value-of select="$colid"/></fo:block></fo:table-cell>
</xsl:for-each>
</fo:table-row>
<xsl:for-each select="A to E">
<xsl:variable name="rowid" select="current()"/>
<fo:table-row>
<fo:table-cell><fo:block><xsl:value-of select="$rowid"/></fo:block></fo:table-cell> <xsl:for-each select="1 to 5">
<xsl:variable name="colid" select="current()"/>
<fo:table-cell>
<fo:block>
<xsl:text>Value for </xsl:text>
<xsl:value-of select="$rowid"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$colid"/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
The above XSLT does not work, because for-each within the in context can only handle numbers.
How to handle the letters in the rows? The rows may not stop at Z and start again from ZA, ZB, ..., ZZ, ZZA, ....

Use a mapping from letters to numbers, if string-to-codepoints(end-stop-row) suffices (https://www.w3.org/TR/xpath-functions/#func-string-to-codepoints) (will depend on the alphabet you have in mind), use that. The formatting of numbers you process in a second step as letters can be handled by xsl:number or format-number.

Related

Calling a named template from an imported .xsl file explicitely

I have the following situation:
I am using a 3rd party xsl template system (kosit XRechnung), which allows to configure custom adaptions via specification of your own .xsl files as processing entry points.
Here's the process:
+--------+
| |
| |
| |
| in.xml |
| |
+--------+
|
|
|
V
+--------+
| |
| | import +--------+
| my.xsl | <-------- | |
| | | adapt | import +---------+
| | | .xsl | <-------| |
+--------+ | | | default |
| | | | .xsl |
-| +--------+ | |
| | |
V +---------+
+--------+
| |
| |
| out |
| .xml |
| |
+--------+
my.xsl is the processing entry point fed with some .xml data, which is provided by me
adapt.xslis an adaption which provides a named template called at some point by
default.xsl which does a lot of stuff to create the final output XML document.
default.xsl:
<!-- ... -->
<xsl:when test="s:scenario">
<rep:scenarioMatched>
<xsl:apply-templates select="s:scenario" mode="copy" />
<xsl:call-template name="documentData" />
<xsl:sequence select="$validationStepResults" />
</rep:scenarioMatched>
</xsl:when>
<!-- ... -->
adapt.xsl:
<!-- Overwritten (default.xsl) -->
<xsl:template name="documentData">
<rep:documentData>
<xsl:for-each select="
$input-document/*/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName,
$input-document/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:Name">
<seller>
<xsl:value-of select="."/>
</seller>
</xsl:for-each>
<xsl:for-each select="
$input-document/*/cbc:ID,
$input-document/rsm:CrossIndustryInvoice/rsm:ExchangedDocument/ram:ID">
<id>
<xsl:value-of select="."/>
</id>
</xsl:for-each>
<xsl:for-each select="$input-document/*/cbc:IssueDate,
$input-document/rsm:CrossIndustryInvoice/rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString">
<issueDate>
<xsl:value-of select="."/>
</issueDate>
</xsl:for-each>
</rep:documentData>
</xsl:template>
Now in my top level .xsl I can again override the named template which is called in the default.xsl:
my.xsl:
<?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:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rep="http://www.xoev.de/de/validator/varl/1"
xmlns:s="http://www.xoev.de/de/validator/framework/1/scenarios"
xmlns:in="http://www.xoev.de/de/validator/framework/1/createreportinput"
xmlns:svrl="http://purl.oclc.org/dsdl/svrl"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
xmlns:myns="urn:mycompany:xrechnung:data:enhancements"
exclude-result-prefixes="xs"
version="2.0">
<xsl:import href="./adapt.xsl"/>
<xsl:output method="xml" indent="yes"/>
<!-- Overwritten (default.xsl) -->
<xsl:template name="documentData">
<rep:documentData>
<myns:MyContainer>
<myns:MyElement>
<!-- Enhanced stuff -->
</myns:MyElement>
</myns:MyContainer>
<!-- xsl:call-template name="documentData"/-->
</rep:documentData>
</xsl:template>
</xsl:stylesheet>
The question is:
How to apply the stuff defined in adapt.xsl without copying that xsl code for the documentData named template (without changing default.xsl or adapt.xsl)?
In the context of XSLT 3 packages this is possible, see https://www.w3.org/TR/xslt-30/#refer-to-overridden:
Within a named template appearing as a child of xsl:override, the name
xsl:original may appear as the value of the name attribute of
xsl:call-template: for example, <xsl:call-template name="xsl:original"/>.
Saxon 9.8 and later support XSLT 3 with packages.
For the concrete example I think you would need to use a fourth file (e.g. adapt-package-wrapper.xsl) that is a named package importing adapt.xsl e.g.
<xsl:package xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
name="http://example.com/adapt1" package-version="1.0">
<xsl:expose component="template" names="documentData" visibility="public"/>
<xsl:import href="adapt.xsl"/>
</xsl:package>
Then in my.xsl you would need to declare the override:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:use-package name="http://example.com/adapt1" version="1.0">
<xsl:override>
<xsl:template name="documentData">
<rep:documentData>
<myns:MyContainer>
<myns:MyElement>
<!-- Enhanced stuff -->
</myns:MyElement>
</myns:MyContainer>
<!-- xsl:call-template name="documentData"/-->
<xsl:call-template name="xsl:original"/>
</rep:documentData>
</xsl:template>
</xsl:override>
</xsl:use-package>
...
</xsl:stylesheet>
The main issue then is letting Saxon know how to map the package name http://example.com/adapt1 to a package file, on the command line you would use e.g. -lib:adapt-package-wrapper.xsl. Or you can use a configuration file. Not sure whether you can do that in your environment.

XSL FO Customized Table requirement

I have below requirement.
I have below table in xml.
------------------------------------------------------------
|NAME |NUMBER|<<Empty>>|NAME |NUMBER|<<Empty>>|NAME |NUMBER|
------------------------------------------------------------
|A001 | 1 | |A005 | 5 | |A009 | 9 |
-------------- -------------- --------------
|A002 | 2 | |A006 | 6 | |A010 | 10 |
-------------- -------------- --------------
|A003 | 3 | |A007 | 7 | |A011 | 11 |
-------------- -------------- --------------
|A004 | 4 | |A008 | 8 | |A011 | 12 |
------------------------------------------------------------
Using XSL FO and Rendrex the above XML has been shown as below:
Case 1: If the above table appeared in single page it should appear as below:
|NAME |NUMBER|<<Empty>>|NAME |NUMBER|<<Empty>>|NAME |NUMBER|
------------------------------------------------------------
|A001 | 1 | |A005 | 5 | |A009 | 9 |
-------------- -------------- --------------
|A002 | 2 | |A006 | 6 | |A010 | 10 |
-------------- -------------- --------------
|A003 | 3 | |A007 | 7 | |A011 | 11 |
-------------- -------------- --------------
|A004 | 4 | |A008 | 8 | |A011 | 12 |
------------------------------------------------------------
Case 2: If the table appeared in two page it should appear as below:
-------------------------------------------------
|NAME |NUMBER| |NAME |NUMBER| |NAME |NUMBER|
-------------------------------------------------
|A001 | 1 | |A003 | 3 | |A005 | 5 | ---> Page 1
|A002 | 2 | |A004 | 4 | |A006 | 6 |
-------------------------------------------------
-------------------------------------------------
|NAME |NUMBER| |NAME |NUMBER| |NAME |NUMBER|
-------------------------------------------------
|A007 | 7 | |A009 | 9 | |A011 | 11 | ---> Page 2
|A008 | 8 | |A010 | 10 | |A012 | 12 |
-------------------------------------------------
I am able to achieve the Case 1 but case 2 i am getting like below:
-------------------------------------------------
|NAME |NUMBER| |NAME |NUMBER| |NAME |NUMBER|
-------------------------------------------------
|A001 | 1 | |A005 | 5 | |A009 | 9 | --> Page 1
|A002 | 2 | |A006 | 6 | |A010 | 10 |
-------------------------------------------------
-------------------------------------------------
|NAME |NUMBER| |NAME |NUMBER| |NAME |NUMBER|
-------------------------------------------------
|A003 | 3 | |A007 | 7 | |A011 | 11 | --> Page 2
|A004 | 4 | |A008 | 8 | |A011 | 12 |
-------------------------------------------------
Used XSL:
<fo:table-and-caption id="Table2" caption-side="before" >
<fo:table hyphenate="true" >
<fo:table-header> <!--- Header start -->
<fo:table-row keep-together.within-page="always">
<fo:table-cell >
<fo:block><xsl:value-of select="$headerValues[1]/para"/></fo:block> <!-- NAME-->
</fo:table-cell>
<fo:table-cell >
<fo:block><xsl:value-of select="$headerValues[2]/para"/></fo:block> <!--NUMBER -->
</fo:table-cell>
<fo:table-cell > <!--- Blank Cells in header-->
<fo:block> </fo:block>
</fo:table-cell>
<fo:table-cell >
<fo:block><xsl:value-of select="$headerValues[1]/para"/></fo:block> <!--NAME -->
</fo:table-cell>
<fo:table-cell >
<fo:block><xsl:value-of select="$headerValues[2]/para"/></fo:block> <!--NUMBER -->
</fo:table-cell>
<fo:table-cell > <!--- Blank Cells in header-->
<fo:block> </fo:block>
</fo:table-cell>
<fo:table-cell >
<fo:block><xsl:value-of select="$headerValues[1]/para"/></fo:block> <!--NAME -->
</fo:table-cell>
<fo:table-cell >
<fo:block><xsl:value-of select="$headerValues[2]/para"/></fo:block> <!--NUMBER -->
</fo:table-cell>
</fo:table-row>
</fo:table-header> <!--- Header End -->
<fo:table-body>
<xsl:for-each select="1 to $row-size">
<xsl:variable name="iterationValue" select="."/>
<fo:table-row keep-together.within-page="always">
<fo:table-cell align="left" > <!-- First Cell -->
<fo:block><xsl:value-of select="$nodeValues[$iterationValue]/entry[1]/para"/> </fo:block>
</fo:table-cell>
<fo:table-cell > <!--Second Cell -->
<fo:block><xsl:value-of select="$nodeValues[$iterationValue]/entry[2]/para"/></fo:block>
</fo:table-cell>
<xsl:choose>
<xsl:when test="$iterationValue = $row-size">
<fo:table-cell border-top = "0pt"> </fo:table-cell> <!--Third blank cell -->
</xsl:when>
<xsl:otherwise>
<fo:table-cell ></fo:table-cell>
</xsl:otherwise>
</xsl:choose>
<!-- For 2nd column in table -->
<xsl:choose>
<xsl:when test ="$iterationValue + $row-size > $rowCount">
<fo:table-cell >
<fo:block>-</fo:block> <!-- To Fill - in case of blank -->
</fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell >
<fo:block><xsl:value-of select="$nodeValues[$iterationValue + $row-size]/entry[1]/para"/> </fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test ="$iterationValue + $row-size > $rowCount">
<fo:table-cell >
<fo:block>-</fo:block> <!-- To Fill - in case of blank -->
</fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell >
<fo:block><xsl:value-of select="$nodeValues[$iterationValue + $row-size]/entry[2]/para"/></fo:block> </fo:table-cell>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="$iterationValue = $row-size">
<fo:table-cell border-top = "0pt"> </fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell ></fo:table-cell> <!-- Blank cell -->
</xsl:otherwise>
</xsl:choose>
<!-- For Third column in table -->
<xsl:choose>
<xsl:when test ="$iterationValue + (2*$row-size) > $rowCount">
<fo:table-cell >
<fo:block>-</fo:block>
</fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell >
<fo:block><xsl:value-of select="$nodeValues[$iterationValue + (2*$row-size)]/entry[1]/para"/> </fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test ="$iterationValue + (2*$row-size) > $rowCount">
<fo:table-cell >
<fo:block>-</fo:block>
</fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell >
<fo:block><xsl:value-of select="$nodeValues[$iterationValue + (2*$row-size)]/entry[2]/para"/></fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
</fo:table-row>
</xsl:for-each >
</fo:table-body>
</fo:table>
</fo:table-and-caption>
Can someone please help me to find out the solution.
Expected Output:
Because you are using RenderX, you can use rx:flow-section for the table to make a three-column area within the flow. Remember to add the rx: extension namespace to your XSL and you can completely forgo the complicated numbering.
If you examine the below XSL FO it achieves your desire. Note that I have set the column-gap to "0pt" so that it looks like a continuous table. This will work if all your row-heights are the same.
XSL FO:
<rx:flow-section column-count="3" column-gap="0pt">
<fo:table width="100%">
<fo:table-body>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>1</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>2</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>3</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>4</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>5</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>6</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>7</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>8</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>9</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>10</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>11</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>12</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>13</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>14</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>15</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>16</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>17</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="1pt solid black"><fo:block>Name</fo:block></fo:table-cell>
<fo:table-cell border="1pt solid black"><fo:block>18</fo:block></fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</rx:flow-section>
The output when crossing a page boundary:
You can achieve an output that is almost what you requested using a much simpler table and a slightly more complicated page-master:
the table: just two columns (one for NAME and one for NUMBER), the fo:table-header and the fo:table-body with rows in the "natural" order (1, 2, 3, ..., 12)
the page master: add columns to the fo:region-body definition, using column-count="3" and column-gap="1cm" (or something appropriate)
The result is a table whose rows flow in the three region columns, eventually creating other pages if needed.
You may use an empty <fo:block span="all"/> after the table, so that the columns will be balanced (so that, for example, you will have 2 rows in each column instead of having 4 in the first column, 2 in the second one and 0 in the third one).
The only requirement that this solution cannot satisfy is the continuous border from margin to margin.

How to limit the scope of ancestor search in XSLT?

Example input:
<body>
<ul>
<li>
<p>Some text with <b><i><span style="color:red">formatting</span></i></b></p>
</li>
</ul>
</body>
The 'inline' elements are b, i, or span
Suppose the context node is the nested span element in the example.
How do I get all ancestor inline elements, without the search going past the containing p element?
The expression ancestor-or-self::*[self::b | self::i | self::span] should actually work, as there can't be ancestor inline elements containing 'block' level elements such as p, ul, etc.
But, I wonder if there is some performance cost that could be avoided, as I think the search will continue past the containing p element. Is there a way to avoid that?
Edit
This is what I've come up with so far:
<xsl:function name="my:getInlineSeq" as="element() *">
<xsl:param name="pElem" as="element()" />
<xsl:if test="$pElem[self::b | self::i | self::span]">
<xsl:sequence select="my:getInlineSeq($pElem/parent::*)" />
<xsl:sequence select="$pElem" />
</xsl:if>
</xsl:function>
But, I'm unsure if there is an better or 'standard' way of solving this problem.
Note: the $pElem/parent::* in the code will always return an element as inline items must have a block element container such as p, h1, etc. Also, the assumption is the $pElem parameter provided initially is always an inline element.
Using
<xsl:function name="mf:block" as="element()">
<xsl:param name="inline" as="element()"/>
<xsl:sequence select="$inline/ancestor::*[self::h1 | self::h2 | self::h3 | self::h4 | self::h5 | self::h6 | self::li | self::p][1]"/>
</xsl:function>
and your assumption about block and inline elements you could rewrite ancestor-or-self::*[self::b | self::i | self::span] in the context of match="span | b | i" to
for $block in mf:block(.) return ancestor-or-self::*[. >> $block]
I think (or assuming you use <xsl:variable name="block" select="mf:block(.)"/> to ancestor-or-self::*[. >> $block]. I have no idea whether that performs any better than your attempt.
Tried to test the expression at http://xsltransform.net/3MvmrzK where
<xsl:template match="span | b | i">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="inline-ancestor" select="ancestor-or-self::*[self::b | self::i | self::span]/name()"/>
<xsl:attribute name="test-block" select="for $block in mf:block(.) return ancestor-or-self::*[. >> $block]/name()"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
gives the same result for the inline elements with
<p>Some text with <b inline-ancestor="b" test-block="b"><i inline-ancestor="b i" test-block="b i"><span style="color:red" inline-ancestor="b i span" test-block="b i span">formatting</span></i></b></p>

xsl:when test for processing instruction

I´m trying to create a choose statement, which detects an processing instruction
<xsl:choose>
<xsl:when test="chapter/descriptive/heading='processing-instruction("xm-replace_text")'">
<xsl:template match="chapter/descriptive/heading"/>
</xsl:when>
<xsl:otherwise>
<xsl:template match="chapter/descriptive/heading">
<fo:block
font-size="16pt"
font-weight="bold"
font-color="red"
space-before="5mm"
space-after="2mm">
<xsl:number
count="chapter | task | diagnosis | taskintervals | tools | lubrication | glossary"
format="1.1"
level="multiple"/>
 
<xsl:value-of select="."/>
</fo:block>
</xsl:template>
</xsl:otherwise>
</xsl:choose>
is it not possible to test for processing instructions like this?
edit: xml input (is the full xml needed?)
...
<chapter infoclass-1="description" prodclass-1="setup">
<descriptive prodclass-1="setup" infoclass-1="intro">
<heading><?xm-replace_text Themenangabe in Form einer Überschrift ?></heading>
...
No, a node test for a processing instruction is done literally e.g.
<xsl:template match="processing-instruction('xm-replace_text')">...</xsl:template>
would match a pi <?xm-replace_text ...?>.
With your example XML assuming you are trying to match the heading element containing that particular processing instruction then use
<xsl:template match="chapter/descriptive/heading[processing-instruction('xm-replace_text')]">...</xsl:template>
or
<xsl:template match="chapter/descriptive/heading/processing-instruction('xm-replace_text')">...</xsl:template>
if you want to match the processing instruction itself.

Creating dynamic XSL-FO table using XSLT

I am creating a table in xsl-fo, with undefined number of elements (indtr).
I wonder if there a smart way to do this?
Here is my input xml file
<pr-levels>
<prs>
<pr_nme><![CDATA[Level 1]]></pr_nme>
<nt><![CDATA[standards:]]></nt>
<b_is>
<indtr id="5684"><![CDATA[cell_1]]></indtr>
<indtr id="5684"><![CDATA[cell_2]]></indtr>
<indtr id="5684"><![CDATA[cell_3]]></indtr>
<indtr id="5684"><![CDATA[cell_4]]></indtr>
</b_is>
</prs>
<prs>
<pr_nme><![CDATA[Level 2]]></pr_nme>
<nt><![CDATA[standards:]]></nt>
<b_is>
<indtr id="5684"><![CDATA[cell_1]]></indtr>
</b_is>
</prs>
<prs>
<pr_nme><![CDATA[Level 3]]></pr_nme>
<nt><![CDATA[standards:]]></nt>
<b_is>
<indtr id="5684"><![CDATA[cell_1]]></indtr>
<indtr id="5684"><![CDATA[cell_2]]></indtr>
</b_is>
</prs>
<prs>
<pr_nme><![CDATA[Level 4]]></pr_nme>
<nt><![CDATA[standards:]]></nt>
<b_is>
<indtr id="5684"><![CDATA[cell_1]]></indtr>
<indtr id="5684"><![CDATA[cell_2]></indtr>
<indtr id="5684"><![CDATA[cell_3]]></indtr>
<indtr id="5684"><![CDATA[cell_4]></indtr>
<indtr id="5684"><![CDATA[cell_5]]></indtr>
<indtr id="5684"><![CDATA[cell_6]]></indtr>
<indtr id="5684"><![CDATA[cell_7]]></indtr>
</b_is>
</prs>
<prs>
<pr_nme><![CDATA[Level 5]]></pr_nme>
<nt><![CDATA[standards:]]></nt>
<b_is>
<indtr id="5684"><![CDATA[cell_1]]></indtr>
<indtr id="5684"><![CDATA[cell_2]]></indtr>
<indtr id="5684"><![CDATA[cell_3]]></indtr>
<indtr id="5684"><![CDATA[cell_4]]></indtr>
<indtr id="5684"><![CDATA[cell_5]]></indtr>
<indtr id="5684"><![CDATA[cell_6]]></indtr>
<indtr id="5684"><![CDATA[cell_7]]></indtr>
<indtr id="5684"><![CDATA[cell_8]]></indtr>
<indtr id="5684"><![CDATA[cell_9]]></indtr>
<indtr id="5684"><![CDATA[cell_10]]></indtr>
<indtr id="5684"><![CDATA[cell_11]]></indtr>
<indtr id="5684"><![CDATA[cell_12]]></indtr>
<indtr id="5684"><![CDATA[cell_13]]></indtr>
<indtr id="5684"><![CDATA[cell_14]]></indtr>
<indtr id="5684"><![CDATA[cell_15]]></indtr>
<indtr id="5684"><![CDATA[cell_16]]></indtr>
<indtr id="5684"><![CDATA[cell_17]]></indtr>
<indtr id="5684"><![CDATA[cell_18]]></indtr>
<indtr id="5684"><![CDATA[cell_19]]></indtr>
</b_is>
</prs>
</pr-levels>
Here is my XSLT template that produces the XSL-FO table:
<xsl:template match="pr-levels">
<fo:table xsl:use-attribute-sets="table_p" break-after="page" force-page-count="no-force">
<fo:table-column column-number="1" xsl:use-attribute-sets="table_col_p"/>
<fo:table-column column-number="2" xsl:use-attribute-sets="table_col_p"/>
<fo:table-column column-number="3" xsl:use-attribute-sets="table_col_p"/>
<fo:table-column column-number="4" xsl:use-attribute-sets="table_col_p"/>
<fo:table-column column-number="5" xsl:use-attribute-sets="table_col_p"/>
<fo:table-header xsl:use-attribute-sets="table_header">
<xsl:for-each select="prs/pr_nme">
<fo:table-cell>
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-header>
<fo:table-body >
<fo:table-row>
<xsl:for-each select="prs/nt">
<fo:table-cell>
<fo:block xsl:use-attribute-sets="nt" >
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
<fo:table-row>
<xsl:for-each select="prs/b_is">
<fo:table-cell padding="1pt">
<fo:block>
<xsl:value-of select="indtr[1]"/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
<fo:table-row>
<xsl:for-each select="prs/b_is">
<fo:table-cell padding="1pt">
<fo:block>
<xsl:value-of select="indtr[2]"/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
<fo:table-row>
<xsl:for-each select="prs/b_is">
<fo:table-cell padding="1pt">
<fo:block>
<xsl:value-of select="indtr[3]"/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
<fo:table-row>
<xsl:for-each select="prs/b_is">
<fo:table-cell padding="1pt">
<fo:block>
<xsl:value-of select="indtr[4]"/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
<fo:table-row>
<xsl:for-each select="prs/b_is">
<fo:table-cell padding="1pt">
<fo:block>
<xsl:value-of select="indtr[5]"/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</fo:table-body>
</fo:table>
</xsl:template>
There could be NULL,1,69 or whatever numbers of <indtr>s in a <b_is>,but right now I just hard code the numbers.My question is how I can dynamically count the number of indtrs and add rows in my table.maybe a for loop in xsl?
________________________________________________________
| level 1 | level 2 | level 3 | level 4 | level 5 |
--------------------------------------------------------
|standards:|standards:|standards:|standards:|standards:|
--------------------------------------------------------
| cell 1 | cell 1 | cell 1 | cell 1 | cell 1 |
--------------------------------------------------------
| cell 2 | cell 2 | cell 2 | cell 2 | cell 2 |
--------------------------------------------------------
| | cell 3 | cell 3 | cell 3 | cell 3 |
--------------------------------------------------------
| | cell 4 | | | cell 4 |
--------------------------------------------------------
| | | | | cell 5 |
--------------------------------------------------------
| | | | | cell 6 |
--------------------------------------------------------
Just because it's an interesting problem...
Replace the multiple hard-coded fo:table-row with:
<xsl:variable name="pr-levels" select="." />
<xsl:for-each
select="1 to max(for $prs in prs
return count($prs/b_is/indtr))">
<xsl:variable name="row" select="." as="xs:integer" />
<fo:table-row>
<xsl:for-each select="$pr-levels/prs">
<fo:table-cell padding="1pt">
<fo:block>
<xsl:value-of select="b_is/indtr[$row]"/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</xsl:for-each>