шаблон преобразования / перевода xsl

#xslt

#xslt

Вопрос:

Я создаю таблицу стилей xsl-fo в rtf. Одна из проблем, с которой я сталкиваюсь, заключается в преобразовании многочисленных единиц измерения в документе xsl-fo в twips (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. Хороший вопрос, 1. Смотрите мой ответ для полного и простого решения.

2. Да, и это полное решение, а не просто псевдокод.

3. Мой ответ должен быть просто примером того, как вы можете использовать свое правило шаблона с помощью xsl:call-template . Также есть небольшая ошибка в том, как вы используете translate . Смотрите мой ответ, надеюсь, это поможет.

Ответ №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>amp;#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 -,, — полный пример кода смотрите в моем ответе на ваш предыдущий вопрос.

Комментарии:

1. @Flynn1179: Моя арифметика правильна, и иногда то, что может показаться простым, «запросто» может оказаться неправильным 🙂

2. Да, я уже заметил это и удалил свой комментарий; Я неправильно истолковал вопрос, в котором говорилось, что смысл в том, чтобы вычислить ширину столбцов, думая, что она не должна быть кумулятивной.

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

Как вы можете видеть, это довольно просто: просто вычисляем ширину предыдущих столбцов, затем добавляем ее к текущему. Вы, вероятно, заметите, что перед <xsl:apply-templates инструкцией в первой переменной стоит 0; это делается для того, чтобы убедиться, что переменной присвоено допустимое число. Если предыдущих столбцов нет, то вместо «будет сохранено ‘0’.

Строго говоря, вы могли бы включить тело вашего существующего шаблона вместо второй переменной и иметь <xsl:value-of select="$previous ($numeric-value * $scaling-factor)" /> внизу; это полностью зависит от вас.

Ответ №3:

Вы также можете использовать функцию шаблона и xsl:call-template . Кража примера ввода у @Dimitre (не надо меня ненавидеть 🙂 Я покажу вам, как вы можете использовать правило шаблона преобразования с xsl:call-template .

При преобразовании я выполняю итерацию по каждому из них table-column , таким образом собирая преобразованные значения. Чтобы преобразовать значение, я просто вызываю ваше исходное правило шаблона (немного измененное). Затем я использую sum для выполнения суммирования значений.

Обратите внимание, что процессор, совместимый с XSLT 2.0, вернет ошибку времени выполнения, если вы не преобразуете значение, возвращаемое translate , в число.


XSLT 2.0 протестирован под Saxon-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