Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xsl convert / translate template

Tags:

xslt

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>
like image 972
stoic Avatar asked May 21 '11 18:05

stoic


2 Answers

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>&#xA;</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.

like image 186
Dimitre Novatchev Avatar answered Sep 24 '22 23:09

Dimitre Novatchev


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.

like image 42
Flynn1179 Avatar answered Sep 24 '22 23:09

Flynn1179