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
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.
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.
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>
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.
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>