I am creating an xsl-fo to rtf style sheet. One of the problems I have is to convert the numerous units of measure in a xsl-fo document to twips (rtf unit of measure).
One particular piece of code caluclates the widths of the columns:
<xsl:value-of select="sum(preceding-sibling:
:fo:table-column/@column-width) + @column-width"/>
...problem being the value of /@column-width
could be anything from 1in
(1 inch) to 20px
(20 pixels), so when I do the sum it will fail.
I need to somehow convert @column-width
to a twip equivelant:
1pt = 19.95 twips, 1px = 15 twips, 1pc = 240 twips, 1in = 1440 twips, 1cm = 567 twips, 1mm = 56.7 twips, 1em = 240 twips
I can probably write a method that can do the conversion, but I am convinced there is some way to make use of the translate()
function to do this much more efficiantly.
Please take note that my xsl is not all that great, so an example of how to achieve this will be appreciated
EDIT
I managed to find something I want, but have no idea how to call this template from the above calculation:
<xsl:template match="@*" mode="convert-to-twips">
<xsl:variable name="scaling-factor">
<xsl:choose>
<xsl:when test="contains (., 'pt')">19.95</xsl:when>
<xsl:when test="contains (., 'px')">15</xsl:when>
<xsl:when test="contains (., 'pc')">240</xsl:when>
<xsl:when test="contains (., 'in')">1440</xsl:when>
<xsl:when test="contains (., 'cm')">567</xsl:when>
<xsl:when test="contains (., 'mm')">56.7</xsl:when>
<xsl:when test="contains (., 'em')">240</xsl:when>
<!-- guess: 1em = 12pt -->
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="numeric-value"
select="translate (., '-0123456789.ptxcinme', '-0123456789.')"/>
<xsl:value-of select="$numeric-value * $scaling-factor"/>
</xsl:template>
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:fo="some:fo" xmlns:my="my:my" >
<xsl:output method="text"/>
<my:units>
<unit name="pt">19.95</unit>
<unit name="in">1440</unit>
<unit name="cm">567</unit>
<unit name="mm">56.7</unit>
<unit name="em">240</unit>
<unit name="px">15</unit>
<unit name="pc">240</unit>
</my:units>
<xsl:variable name="vUnits" select=
"document('')/*/my:units/*"/>
<xsl:template match="/">
<xsl:apply-templates select="*/*/*/@column-width"/>
</xsl:template>
<xsl:template match="@column-width">
<xsl:variable name="vQuantity" select=
"substring(.,1, string-length() -2)"/>
<xsl:variable name="vUnit" select=
"substring(., string-length() -1)"/>
<xsl:variable name="vrtfAccumWidth">
<num>0</num>
<xsl:for-each select=
"../preceding-sibling::fo:table-column/@column-width">
<xsl:variable name="vQ" select=
"substring(.,1, string-length() -2)"/>
<xsl:variable name="vU" select=
"substring(., string-length() -1)"/>
<num>
<xsl:value-of select=
"$vQ * $vUnits[@name=$vU]"/>
</num>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select=
"$vQuantity * $vUnits[@name=$vUnit]
+
sum(ext:node-set($vrtfAccumWidth)/num)
"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document (as none was provided!):
<fo:fo xmlns:fo="some:fo">
<fo:table>
<fo:table-column column-width="2pt"/>
<fo:table-column column-width="2in"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2mm"/>
<fo:table-column column-width="2em"/>
<fo:table-column column-width="2px"/>
<fo:table-column column-width="2pc"/>
</fo:table>
</fo:fo>
produces the wanted, correct result:
39.9
2919.9
4053.9
4167.3
4647.3
4677.3
5157.3
Notice: If a more efficient solution is needed for a big sequence of fo:table-column\@column-width
, then the FXSL - scanl
template/function can be used -- see my answer to your previous question for a complete code example.
You can use this template to make use of the existing one you have:
<xsl:template match="@column-width">
<xsl:variable name="previous">
0<xsl:apply-templates select="../preceding-sibling::*[1]/@column-width" />
</xsl:variable>
<xsl:variable name="this">
<xsl:apply-templates select="." mode="convert-to-twips"/>
</xsl:variable>
<xsl:value-of select="$previous + $this" />
</xsl:template>
It's fairly straightforward as you can see, simply calculating the width of previous columns, then adding it to the current one. You'll probably notice there's a 0 in front of the <xsl:apply-templates
instruction in the first variable; that's to make sure that a valid number is given to the variable. If there are no previous columns, then it'll store '0' instead of ''.
Strictly speaking, you could include the body of your existing template in place of the second variable, and have <xsl:value-of select="$previous + ($numeric-value * $scaling-factor)" />
at the bottom; that's entirely up to you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With