【问题标题】:xsl convert / translate templatexsl 转换/翻译模板
【发布时间】:2011-05-21 18:56:29
【问题描述】:

我正在创建一个 xsl-fo 到 rtf 样式表。我遇到的问题之一是将 xsl-fo 文档中的众多计量单位转换为缇(rtf 计量单位)。

一段特定的代码计算列的宽度:

<xsl:value-of select="sum(preceding-sibling:
   :fo:table-column/@column-width) + @column-width"/>

...问题是/@column-width 的值可能是从1in(1 英寸)到20px(20 像素)的任何值,所以当我进行求和时它会失败。

我需要以某种方式将 @column-width 转换为 twip 等效项: 1pt = 19.95 twips, 1px = 15 twips, 1pc = 240 twips, 1in = 1440 twips, 1cm = 567 twips, 1mm = 56.7 twips, 1em = 240 twips

我可能可以编写一个可以进行转换的方法,但我相信有一些方法可以利用translate() 函数更有效地完成此操作。

请注意,我的 xsl 并不是那么好,因此我们将不胜感激提供如何实现此目标的示例

编辑

我设法找到了我想要的东西,但不知道如何从上述计算中调用此模板:

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

【问题讨论】:

  • 好问题,+1。请参阅我的答案以获得完整且简单的解决方案。
  • 是的,这是一个完整的解决方案——不仅仅是伪代码。
  • 我的回答只是一个示例,说明如何使用 xsl:call-template 使用模板规则。您使用translate 的方式也有一点错误。看看我的回答,希望对你有帮助。

标签: xslt


【解决方案1】:

这种转变

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

应用于以下 XML 文档时(因为没有提供!):

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

产生想要的正确结果

39.9
2919.9
4053.9
4167.3
4647.3
4677.3
5157.3

注意:如果fo:table-column\@column-width 的大序列需要更有效的解决方案,则FXSL - scanl 可以使用模板/函数——完整的代码示例请参见我对上一个问题的回答。

【讨论】:

  • @Flynn1179:我的算术正确的,有时看似简单的事情可能“容易”被证明是不正确的:)
  • 是的,我已经注意到并删除了我的评论;我误读了这个问题,它说重点是计算列的宽度,认为它不应该是累积的。
  • @Flynn1179:没问题,伙计。我们都是人类,至少有时应该犯错:)
【解决方案2】:

您可以使用此模板来利用现有的模板:

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

如您所见,这相当简单,只需计算前列的宽度,然后将其添加到当前列。您可能会注意到第一个变量中&lt;xsl:apply-templates 指令前面有一个0;这是为了确保给变量一个有效的数字。如果没有以前的列,那么它将存储 '0' 而不是 ''。

严格来说,您可以包含现有模板的主体来代替第二个变量,并在底部添加&lt;xsl:value-of select="$previous + ($numeric-value * $scaling-factor)" /&gt;;这完全取决于您。

【讨论】:

    【解决方案3】:

    您还可以使用模板函数和xsl:call-template。窃取@Dimitre 输入示例(不要讨厌我:)我将向您展示如何将转换模板规则与xsl:call-template 一起使用。

    在转换中,我对每个 table-column 进行迭代,从而收集转换后的值。要转换值,我只是调用您的原始模板规则(稍作调整)。然后我使用sum 来执行值的总和。

    请注意,如果您不将 translate 返回的值强制转换为数字,则符合 XSLT 2.0 的处理器将返回运行时错误。


    XSLT 2.0Saxon-B 9.0.0.4J

    下测试
    <xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:fo="some:fo">
        <xsl:output method="text"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:template match="fo:table/fo:table-column">
    
            <xsl:variable name="twips">
                <twips>
                    <xsl:for-each select="preceding-sibling::fo:table-column/@column-width">
                        <twip>
                            <xsl:call-template name="convert-to-twips">
                                <xsl:with-param name="value" select="."/>
                            </xsl:call-template>
                        </twip>
                    </xsl:for-each>
                    <twip>
                            <xsl:call-template name="convert-to-twips">
                                <xsl:with-param name="value" select="@column-width"/>
                            </xsl:call-template>
                    </twip>
                </twips>
            </xsl:variable>
    
            <xsl:value-of select="sum($twips//twip)"/><xsl:text> </xsl:text>
    
        </xsl:template>
    
        <xsl:template name="convert-to-twips">
    
            <xsl:param name="value"/>
    
            <xsl:variable name="scaling-factor">
                <xsl:choose>
                    <xsl:when test="contains ($value, 'pt')">19.95</xsl:when>
                    <xsl:when test="contains ($value, 'px')">15</xsl:when>
                    <xsl:when test="contains ($value, 'pc')">240</xsl:when>
                    <xsl:when test="contains ($value, 'in')">1440</xsl:when>
                    <xsl:when test="contains ($value, 'cm')">567</xsl:when>
                    <xsl:when test="contains ($value, 'mm')">56.7</xsl:when>
                    <xsl:when test="contains ($value, 'em')">240</xsl:when>
                    <!-- guess: 1em = 12pt -->
                    <xsl:otherwise>1</xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
    
            <xsl:variable name="numeric-value"
                select="number(translate ($value, '-0123456789.ptxcinme', '-0123456789.'))"/>
            <xsl:value-of select="$numeric-value * $scaling-factor"/>
    
        </xsl:template>
    
    </xsl:stylesheet>
    

    此变换应用于输入:

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

    生产:

    39.9 2919.9 4053.9 4167.3 4647.3 4677.3 5157.3 
    

    【讨论】:

      猜你喜欢
      • 2015-02-21
      • 2017-04-16
      • 1970-01-01
      • 1970-01-01
      • 2012-09-12
      • 1970-01-01
      • 2010-10-03
      • 1970-01-01
      相关资源
      最近更新 更多