How to generate a blank table cell in XSL? - xslt

why does the table cell disappear when there is no photo existing to generate?
i am using the following code which is not working. What do i need to change if i want to generate a blank table cell if there is no photo existing to generate?
<xsl:for-each select="...............">
<xsl:choose>
<xsl:when test="*">
<xsl:if test=".....">
<xsl:if test=".......">
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:external-graphic src="url('{concat($FILEPATH,.....'])}')"
inline-progression-dimension.maximum="4.1cm" block-progression-dimension.maximum="4cm"
content-width="scale-to-fit" content-height= "scale-to-fit" scaling="uniform"/>
</fo:block>
</fo:table-cell>
</xsl:if>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>

Your have <xsl:when test="*"> ... is that node empty? If that is your outside test and passes, but the other IFs (you do not show) do not pass their test, your template yields nothing.
Breaking this down with comments:
<xsl:when test="*">
<xsl:if test=".....">
<!-- If this does not pass, you get nothing -->
<xsl:if test=".......">
<!-- If this does not pass, you get nothing -->
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:external-graphic src="url('{concat($FILEPATH,.....'])}')"
inline-progression-dimension.maximum="4.1cm" block-progression-dimension.maximum="4cm"
content-width="scale-to-fit" content-height= "scale-to-fit" scaling="uniform"/>
</fo:block>
</fo:table-cell>
</xsl:if>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>

Kevin is right, given your example, your when clause is likely satisfied but if one of your if statements evaluates as false, you will get an empty table-cell. My advice is to add the conditions of your if statements to the when clause using logic operators such as AND/OR, for example, say the conditions in your template where like this...
<xsl:when test="$node = 'A'">
<xsl:if test="$node/child = 'B'">
<xsl:if test="not(contains($node/child,'C'))">
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
...
</fo:table-cell>
</xsl:if>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
Could be expressed as
<xsl:when test="$node = 'A' AND $node/child = 'B' AND not(contains($node/child,'C'))">
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
...
</fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
And in doing so, you will make sure that if any of those three logical conditions aren't met, that your otherwise block will be called and the leader should keep the cell from collapsing, rather than the when statement being called despite the fact that your logic conditions aren't technically satisfied, and winding up with an empty cell which will be collapsed by FOP by default. Hope this clears things up a bit for you.

Related

Smartest way to switch attribute sets in XSLT

I'm currently doing something like this, and it feels like really bad coding style. The structure of the <fo:table-row> and <fo:table-cell> elements is exactly the same, only the xsl:use-attribute-sets is different. What is the smartest way to switch the attribute sets? The XSL version is no limitation.
<xsl:template name="myTemplate">
<xsl:param name="number-of-parts" as="xs:integer"/>
<xsl:choose>
<xsl:when test="$number-of-parts <= 16">
<fo:table-row xsl:use-attribute-sets="__toc__mini__table__row__empty">
<fo:table-cell xsl:use-attribute-sets="__toc__mini__table__row__empty__cell">
<fo:block/>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="__toc__mini__table__row__empty__cell">
<fo:block/>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="__toc__mini__table__row__empty__cell">
<fo:block/>
</fo:table-cell>
</fo:table-row>
</xsl:when>
<!-- If more than 16 languages -->
<xsl:otherwise>
<fo:table-row xsl:use-attribute-sets="__toc__mini__table__row__empty__small">
<fo:table-cell xsl:use-attribute-sets="__toc__mini__table__row__empty__cell__small">
<fo:block/>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="__toc__mini__table__row__empty__cell__small">
<fo:block/>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="__toc__mini__table__row__empty__cell__small">
<fo:block/>
</fo:table-cell>
</fo:table-row>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The attribute-set mechanism is very static, as you have discovered. If you have a dynamic requirement, I think attribute sets are best avoided (actually, I hardly ever use them myself).
Do something like
<fo:table-cell>
<xsl:sequence select="f:my-attribute-sets('small')">
<fo:block/>
</fo:table-cell>
and generate the attributes from your f:my-attribute-sets function.
Something like this? (Untested because I don't have your input to test with.)
<xsl:template name="myTemplate">
<xsl:param name="number-of-parts" as="xs:integer"/>
<xsl:variable name="table_row_variable">
<xsl:choose>
<xsl:when test="$number-of-parts <= 16">__toc__mini__table__row__empty</xsl:when>
<xsl:otherwise>__toc__mini__table__row__empty__cell__small</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:table-row xsl:use-attribute-sets="{$table_row_variable}">
<fo:table-cell xsl:use-attribute-sets="{$table_row_variable}">
<fo:block/>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="{$table_row_variable}">
<fo:block/>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="{$table_row_variable}">
<fo:block/>
</fo:table-cell>
</fo:table-row>
</xsl:template>

Using a param as the argument to XSL select statement

I am using apache fop to produce a PDF, I have an XSL-FO template that produces a table and I would like to be able to call the template multiple times, with different select parameters.
here is my xsl stylesheet
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
version="1.0">
<xsl:output method="html"/>
<xsl:template name="checklist">
<xsl:param name="H1"/>
<xsl:param name="H2"/>
<xsl:param name="H3"/>
<xsl:param name="src"/>
<fo:block width="19cm" >
<fo:table font-size="8pt" table-layout="fixed" width="100%">
<fo:table-column column-width="1.2cm"/>
<fo:table-column column-width="16.5cm"/>
<fo:table-column column-width="1.2cm"/>
<fo:table-header font-weight="bold" background-color="lightgrey">
<fo:table-row>
<fo:table-cell>
<fo:block><xsl:value-of select="$H1"/></fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block text-align="center"><xsl:value-of select="$H2"/></fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block><xsl:value-of select="$H3"/></fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body>
<xsl:apply-templates select="$src" />
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
<xsl:template match="item">
<fo:table-row line-stacking-strategy="line-height" margin="0mm" space-before="1mm" background-color="white">
<xsl:variable name="hdr" select="hdr"/>
<xsl:choose>
<xsl:when test="$hdr = 'y'">
<fo:table-cell number-columns-spanned="3" background-color="lightgrey" border="1px solid #b8b6b6">
<fo:block>
<fo:inline>
<xsl:value-of select="id"/> 
<xsl:value-of select="description"/>
</fo:inline>
</fo:block>
</fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="1px solid #b8b6b6" vertical-align="middle" text-align="center">
<fo:block line-height="4mm">
<xsl:value-of select="id"/>
</fo:block>
</fo:table-cell>
<fo:table-cell border="1px solid #b8b6b6" padding-left="3pt">
<fo:block>
<xsl:value-of select="description"/>
</fo:block>
</fo:table-cell>
<fo:table-cell border="1px solid #b8b6b6" text-align="center" padding-right="3pt">
<xsl:variable name="outcome" select="outcome"/>
<fo:block color="white">
<!--<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/c1.svg" width="4.0mm" height="4.0mm" content-width="scale-to-fit" content-height="scale-to-fit"/>-->
<xsl:choose>
<xsl:when test="$outcome = 'ok'">
<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/c1n.svg" vertical-align="middle" height="4.0mm" content-height="scale-to-fit"/>
</xsl:when>
<xsl:when test="$outcome = 'c1'">
<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/ok.svg" vertical-align="middle" height="3.9mm" content-height="scale-to-fit"/>
</xsl:when>
<xsl:when test="$outcome = 'c2'">
<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/c2.svg" vertical-align="middle" height="3.8mm" content-height="scale-to-fit"/>
</xsl:when>
<xsl:when test="$outcome = 'c3'">
<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/c3.svg" vertical-align="middle" height="3.7mm" content-height="scale-to-fit"/>
</xsl:when>
<xsl:when test="$outcome = 'fi'">
<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/fi.svg" vertical-align="middle" height="3.6mm" content-height="scale-to-fit"/>
</xsl:when>
<xsl:when test="$outcome = 'nv'">
<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/nv.svg" vertical-align="middle" height="3.5mm" content-height="scale-to-fit"/>
</xsl:when>
<xsl:when test="$outcome = 'na'">
<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/na.svg" vertical-align="middle" height="3.4mm" content-height="scale-to-fit"/>
</xsl:when>
<xsl:when test="$outcome = 'lim'">
<fo:external-graphic src="file:///F:/Projects/Active/eCert/src/resources/lim.svg" vertical-align="middle" height="3.3mm" content-height="scale-to-fit"/>
</xsl:when>
</xsl:choose>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
</fo:table-row>
</xsl:template>
</xsl:stylesheet>
And here is one of the flows that calls the template
<fo:flow flow-name="xsl-region-body">
<fo:block />
<xsl:call-template name="checklist">
<xsl:with-param name="H1" select="'Item No.'"/>
<xsl:with-param name="H2" select="'Description'"/>
<xsl:with-param name="H3" select="'Outcome'"/>
<xsl:with-param name="src" select="'eicr/checklist/item'"/>
</xsl:call-template>
</fo:flow>
When I execute FOP I get the following error ** Can not convert #STRING to a NodeList!**
Parameters H1, H2 and H3 are used as the column headers and that part works fine
The src param is the one that should be used to select the items from from an xml list.
I am new xsl fo so any help or pointers to documentation that can help me achieve the desired result would be appreciated.
Many thanks
Remove the single-quotes around eicr/checklist/item. 'eicr/checklist/item' is a string literal; eicr/checklist/item is a location path that will select elements relative to the context node.
The alternative that I was trying to avoid (because it's more verbose) is to apply templates before or as part of calling the template and then just copying the result to the result tree:
<xsl:with-param name="src">
<xsl:apply-templates select="eicr/checklist/item" />
</xsl:with-param>
<fo:table-body>
<xsl:copy-of select="$src" />
</fo:table-body>
It's a technique that you see in the DocBook XSLT 1.0 stylesheets, e.g.:
https://github.com/docbook/xslt10-stylesheets/blob/6ff683948c5a85949e4b7661f302e8b5f12f7bf2/xsl/fo/block.xsl#L181

Prevent XSL loop from printing every value

I need to get the servtypes and servamt value when servtypes is equal to "Service 3". I used the XSL logic and it somehow gets the "Service 3" and its corresponding amount.
XSL Code:
<xsl:for-each select="service">
<fo:table-row>
<fo:table-cell padding-left="14pt" display-align="center">
<fo:block>
<xsl:if test="servtypes='Service 3'">
<xsl:value-of select="servtypes"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" >
<fo:block>
<xsl:if test="servtypes='Service 3'">
<xsl:value-of select="servamt"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding-left="14pt" display-align="center">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
XML Example
<charges>
<fees>25</fees>
<desc>Description</desc>
<service>
<servtypes>Service 1</servtypes>
<servamt>150</servamt>
</service>
<service>
<servtypes>Service 2</sertypes>
<servamt>10</servamt>
</service>
<service>
<servtypes>Service 3</servtypes>
<servamt>150</servamt>
</service>
<charges>
The problem is that whenever it loops through different values of service it also creates table row for each service that doesn't match and the cell contains blank values see the table below.
How do I prevent creation of blank rows and cells and just create a table whose value matches only the Service 3?
<table>
<tr>
<td>Blank</td>
<td>Blank</td>
<td>Blank</td>
</tr>
<tr>
<td>Blank</td>
<td>Blank</td>
<td>Blank</td>
</tr>
<tr>
<td>Service 3</td>
<td>Service Value</td>
<td>Service</td>
</tr>
</table>
I was able to make this work check xsl below.
Solution
<xsl:for-each select="service">
<xsl:if test="servtypes='Service 3'">
<fo:table-row>
<fo:table-cell padding-top="2pt" padding-left="14pt" padding-bottom="2pt" display-align="center">
<fo:block>
Amount
</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="2pt" text-align="left" padding-bottom="2pt">
<fo:block>
<xsl:value-of select="servamt"/>
</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="2pt" padding-left="14pt" padding-bottom="2pt" display-align="center">
<fo:block><fo:leader/></fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:if>
</xsl:for-each>
The solution is simple: don't match what you don't need!
In the following code I put the matching expression in the xsl:template match="" rule and excluded the rest of the text() from matching. I also removed the xsl:ifs.
<xsl:template match="text()" />
<xsl:template match="/charges">
<xsl:apply-templates select="service" />
</xsl:template>
<xsl:template match="service[servtypes='Service 3']">
<fo:table-row>
<fo:table-cell padding-left="14pt" display-align="center">
<fo:block>
<xsl:value-of select="servtypes"/>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" >
<fo:block>
<xsl:value-of select="servamt"/>
</fo:block>
</fo:table-cell>
<fo:table-cell padding-left="14pt" display-align="center">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>

How to put a certain text or string after the first occurrence of loop in xsl?

I just need to put "(Constituent Corporation)" after the first occurrence of the absorbed company. Or in other words, put that string on the 2nd absorbedComapny occurrence and so on except the 1st. Thank you in advance!
<xsl:for-each select="absorbedCompanies/absorbedCompany">
<fo:block font-size="14pt" font-weight="bold" text-align="center" space-after.optimum="0pt"> <xsl:value-of select="absorbedCompanyName"/> <xsl:value-of select="absorbedSuffix"/>
</fo:block>
<fo:block font-size="14pt" font-weight="bold" text-align="center" space-after.optimum="1pt"> <xsl:value-of select="absorbedDoingBusiness"/>
</fo:block>
<fo:block font-size="14pt" font-weight="bold" text-align="center" space-after.optimum="0pt">
</fo:block>
<fo:block font-size="14pt" font-weight="bold" text-align="center" space-after.optimum="20pt">( Constituent Corporations )
</fo:block>
<xsl:if test="position() < last()-1">
<fo:block font-size="14pt" font-weight="bold" text-align="center" space-after.optimum="10pt">and
</fo:block>
</xsl:if>
<xsl:if test="position() = last()-1">
<fo:block font-size="14pt" font-weight="bold" text-align="center" space-after.optimum="10pt">and
</fo:block>
</xsl:if>
<xsl:if test="position() = last()"></xsl:if>
</xsl:for-each>
"position() > 1" should work for you.
<xsl:if test="position() > 1">
<fo:block font-size="14pt" font-weight="bold" text-align="center" space-after.optimum="20pt">( Constituent Corporations ) </fo:block>
</xsl:if>

Keep header with first line of next block

At the end of the page, I dont want to have the label of 'examClin' isolated. So if ever, the label arrives at the end of the page, I need ONE and no more than one line of examClin to be attached with the #label of examClin... Or both elements should go to next page.
Am i clear enough?
different elements... we arrive at the end of the page
<fo:table-row>
<fo:table-cell number-columns-spanned="5">
<fo:block space-before="2mm">
<xsl:value-of select="./examClin/#label"/>: </fo:inline>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell number-columns-spanned="5" padding-top="2mm" padding-bottom="2mm"
padding-left="1mm" padding-right="1mm">
<fo:block white-space-collapse="false" font-style="italic" >
<xsl:value-of select="./examClin/child::text()"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
Put them into a single block (that means you must merge the two table rows into one) and use keep-together.
Thank you Aaron. But then I am afraid that if is a very long text, everything will keep together and not just the first line. As a result, it can leave a long white block on previous page.
I created the following template: the idea is to find what the first line will be: either the 75 first characters but if we find a return carriage before the 75 first characters, we will take the string before the first return carriage.
<xsl:template name="elem3">
<xsl:choose>
<xsl:when test="child::text()">
<xsl:variable name="test0" select="substring(child::text(),1,100000)"/>
<xsl:variable name="test1" select="substring(child::text(),0,75)"/>
<xsl:variable name="test2" select="substring(child::text(),75,100000)"/>
<xsl:variable name="test3" select="substring-before($test2,' ')"/>
<xsl:variable name="test4" select="concat($test1,$test3)"/>
<xsl:variable name="test5" select="substring-after($test2,' ')"/>
<xsl:variable name="test6" select="substring-before($test1,'
')"/>
<xsl:variable name="test7" select="substring-after($test0,'
')"/>
<fo:table-row>
<fo:table-cell number-columns-spanned="5">
<fo:block space-before="2mm">
<fo:inline font-weight="bold"><xsl:value-of select="#label"/>: </fo:inline>
</fo:block>
</fo:table-cell>
</fo:table-row>
<xsl:choose>
<xsl:when test="child::text()">
<fo:table-row keep-with-previous="always">
<fo:table-cell number-columns-spanned="6" padding-top="2mm" padding-left="1mm" padding-right="1mm">
<fo:block white-space-collapse="false" font-style="italic" >
<xsl:choose>
<xsl:when test="contains($test1,'
')"> <xsl:value-of select="$test6"/></xsl:when>
<xsl:otherwise><xsl:value-of select="$test4"/></xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell number-columns-spanned="5" padding-left="1mm" padding-right="1mm">
<fo:block white-space-collapse="false" font-style="italic" >
<xsl:choose>
<xsl:when test="contains($test1,'
')"><xsl:value-of select="$test7"/></xsl:when>
<xsl:otherwise> <xsl:value-of select="$test5"/></xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:when>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:template>