Как динамически изменить последовательность страниц xslt на основе атрибутов узла? - PullRequest
5 голосов
/ 27 июля 2011

У меня есть разбавленная версия проблемы.Для XML-файла, такого как:

<?xml version="1.0" encoding="UTF-8"?>
<items>
    <item cols="1">Item 1</item>
    <item cols="1">Item 2</item>
    <item cols="1">Item 3</item>
    <item cols="1">Item 4</item>
    <item cols="1">Item 5</item>
    <item cols="1">Item 6</item>
    <item cols="1">Item 7</item>
    <item cols="1">Item 8</item>
    <item cols="1">Item 9</item>
    <item cols="2">Item 10</item>
    <item cols="1">Item 11</item>
    <item cols="1">Item 12</item>
    <item cols="1">Item 13</item>
    <item cols="1">Item 14</item>
    <item cols="1">Item 15</item>
    <item cols="1">Item 16</item>
    <item cols="1">Item 17</item>
    <item cols="1">Item 18</item>
</items>

, мне нужно иметь возможность печатать «элементы, у которых есть« cols = 1 »в макете страницы с одним столбцом, и« элементы, у которых есть «cols = 2»в макете страницы с двумя столбцами.Порядок пунктов должен быть сохранен.Все смежные элементы с одинаковым значением @cols должны отображаться как непрерывный поток.Каждый раз, когда изменяется значение @cols, мне нужно перейти на новую страницу и изменить макет по мере необходимости.

Я делаю что-то вроде этого:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format">

    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
            <fo:layout-master-set>

                <fo:simple-page-master master-name="one-column-page-master">
                    <fo:region-body margin-top="3cm" region-name="body" column-count="1"/>
                </fo:simple-page-master>

                <fo:simple-page-master master-name="two-column-page-master">
                    <fo:region-body margin-top="3cm" region-name="body" column-count="2"/>
                    <fo:region-before region-name="header" extent="2cm"/>
                </fo:simple-page-master>

                <fo:page-sequence-master master-name="one-column-page">
                    <fo:repeatable-page-master-reference master-reference="one-column-page-master"/>
                </fo:page-sequence-master>

                <fo:page-sequence-master master-name="two-column-page">
                    <fo:repeatable-page-master-reference master-reference="two-column-page-master"/>
                </fo:page-sequence-master>

            </fo:layout-master-set>

            <xsl:for-each select="//item">
                <xsl:choose>
                    <xsl:when test="@cols = preceding-sibling::item[1]/@cols">
                        <!--cols value hasn't changed, don't create a new page-sequence-->
                        <!--But we cannot directly add fo:flow as the child of fo:root! -->
                        <xsl:call-template name="itemtemplate"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:choose>
                            <xsl:when test="@cols = 1">
                                <fo:page-sequence master-reference="one-column-page">
                                    <xsl:call-template name="itemtemplate"/>
                                </fo:page-sequence>
                            </xsl:when>
                            <xsl:otherwise>
                                <fo:page-sequence master-reference="two-column-page">
                                    <xsl:call-template name="itemtemplate"/>
                                </fo:page-sequence>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </fo:root>

    </xsl:template>

    <xsl:template name="itemtemplate">
            <fo:flow flow-name="body">
                <fo:block margin-bottom="5cm">
                    <xsl:apply-templates/>
                </fo:block>
            </fo:flow>

    </xsl:template>

</xsl:stylesheet>

Но, конечно,проблема в том, что я должен либо включитьв моей таблице стилей, или нет, я не могу «динамически» принять решение добавить ее на основе атрибутов заметки.(Если у меня нет метапрограммы, которая сначала создает таблицу стилей динамически, но я надеялся сделать это, используя только простые статические таблицы стилей).

Ответы [ 3 ]

4 голосов
/ 27 июля 2011

Вот решение XSLT 2.0, в котором используется xsl:for-each-group с group-adjacent:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>

    <xsl:template match="/">
      <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
        <fo:layout-master-set>

          <fo:simple-page-master master-name="one-column-page-master">
            <fo:region-body margin-top="3cm" region-name="body" 
                            column-count="1"/>
          </fo:simple-page-master>

          <fo:simple-page-master master-name="two-column-page-master">
            <fo:region-body margin-top="3cm" region-name="body" 
                            column-count="2"/>
            <fo:region-before region-name="header" extent="2cm"/>
          </fo:simple-page-master>

          <fo:page-sequence-master master-name="one-column-page">
            <fo:repeatable-page-master-reference 
                master-reference="one-column-page-master"/>
          </fo:page-sequence-master>

          <fo:page-sequence-master master-name="two-column-page">
            <fo:repeatable-page-master-reference 
                master-reference="two-column-page-master"/>
          </fo:page-sequence-master>

        </fo:layout-master-set>
        <xsl:apply-templates/>
      </fo:root>
    </xsl:template>

    <xsl:template match="items">
      <xsl:for-each-group select="item" 
                          group-adjacent="@cols">

        <xsl:choose>
          <xsl:when test="@cols = 1">
            <fo:page-sequence master-reference="one-column-page">
              <fo:flow flow-name="body">
                <xsl:for-each select="current-group()">
                  <xsl:apply-templates select="."/>
                </xsl:for-each>
              </fo:flow>
            </fo:page-sequence>
          </xsl:when>

          <xsl:otherwise>
            <fo:page-sequence master-reference="two-column-page">
              <fo:flow flow-name="body">
                <xsl:for-each select="current-group()">
                  <xsl:apply-templates select="."/>
                </xsl:for-each>
              </fo:flow>
            </fo:page-sequence>
          </xsl:otherwise>

        </xsl:choose>
      </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="item">
      <fo:block margin-bottom="5cm">
        <xsl:apply-templates/>
      </fo:block>
    </xsl:template>

</xsl:stylesheet>

Вывод:

<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
   <fo:layout-master-set>
      <fo:simple-page-master master-name="one-column-page-master">
         <fo:region-body margin-top="3cm" region-name="body" column-count="1"/>
      </fo:simple-page-master>
      <fo:simple-page-master master-name="two-column-page-master">
         <fo:region-body margin-top="3cm" region-name="body" column-count="2"/>
         <fo:region-before region-name="header" extent="2cm"/>
      </fo:simple-page-master>
      <fo:page-sequence-master master-name="one-column-page">
         <fo:repeatable-page-master-reference master-reference="one-column-page-master"/>
      </fo:page-sequence-master>
      <fo:page-sequence-master master-name="two-column-page">
         <fo:repeatable-page-master-reference master-reference="two-column-page-master"/>
      </fo:page-sequence-master>
   </fo:layout-master-set>
   <fo:page-sequence master-reference="one-column-page">
      <fo:flow flow-name="body">
         <fo:block margin-bottom="5cm">Item 1</fo:block>
         <fo:block margin-bottom="5cm">Item 2</fo:block>
         <fo:block margin-bottom="5cm">Item 3</fo:block>
         <fo:block margin-bottom="5cm">Item 4</fo:block>
         <fo:block margin-bottom="5cm">Item 5</fo:block>
         <fo:block margin-bottom="5cm">Item 6</fo:block>
         <fo:block margin-bottom="5cm">Item 7</fo:block>
         <fo:block margin-bottom="5cm">Item 8</fo:block>
         <fo:block margin-bottom="5cm">Item 9</fo:block>
      </fo:flow>
   </fo:page-sequence>
   <fo:page-sequence master-reference="two-column-page">
      <fo:flow flow-name="body">
         <fo:block margin-bottom="5cm">Item 10</fo:block>
      </fo:flow>
   </fo:page-sequence>
   <fo:page-sequence master-reference="one-column-page">
      <fo:flow flow-name="body">
         <fo:block margin-bottom="5cm">Item 11</fo:block>
         <fo:block margin-bottom="5cm">Item 12</fo:block>
         <fo:block margin-bottom="5cm">Item 13</fo:block>
         <fo:block margin-bottom="5cm">Item 14</fo:block>
         <fo:block margin-bottom="5cm">Item 15</fo:block>
         <fo:block margin-bottom="5cm">Item 16</fo:block>
         <fo:block margin-bottom="5cm">Item 17</fo:block>
         <fo:block margin-bottom="5cm">Item 18</fo:block>
      </fo:flow>
   </fo:page-sequence>
</fo:root>
2 голосов
/ 27 июля 2011

Мне нужно иметь возможность печатать 'item's with' cols = 1 'в макете страницы с одним столбцом, а' item's, у которых есть 'cols = 2' - в двойноммакет страницы столбца.Порядок элементов должен быть сохранен.

Вы хотите, наконец, сгруппировать смежные элементы item в соответствии со значением @cols в соответствующей последовательности страниц.

Инструкции XSLT 1.0, такие как xsl:choose и xsl:for-each, не очень подходят для этой задачи.Я думаю, что вы должны немного передумать.Вот пример того, как добиться группировки результатов по рекурсии.

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


[XSLT 1.0]

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="text()"/>

    <xsl:template match="/*">
        <fo:root>
            <!-- layout master stuff -->
            <xsl:apply-templates select="item"/>
        </fo:root>
    </xsl:template>

    <!-- match @cols 1, first group occurrences -->  
    <xsl:template match="/*/item[@cols=1]
     [not(preceding-sibling::item[1][@cols=1])]">
        <fo:page-sequence master-reference="one-column-page">
            <xsl:copy-of select="."/>
            <xsl:apply-templates select="
                following-sibling::*[1][self::item[@cols=1]]" mode="flow">
                <xsl:with-param name="cols" select="1"/>
            </xsl:apply-templates>
        </fo:page-sequence>
    </xsl:template>

    <!-- match @cols 2, first group occurrences -->
    <xsl:template match="/*/item[@cols=2]
     [not(preceding-sibling::item[1][@cols=2])]">
        <fo:page-sequence master-reference="two-column-page">
            <xsl:copy-of select="."/>
            <xsl:apply-templates select="
                following-sibling::*[1][self::item[@cols=2]]" mode="flow">
                <xsl:with-param name="cols" select="2"/>
            </xsl:apply-templates>
        </fo:page-sequence>
    </xsl:template>

    <!-- recursive match adjacent @cols -->
    <xsl:template match="item" mode="flow">
        <xsl:param name="cols"/>
        <xsl:copy-of select="."/>
        <xsl:apply-templates select="
            following-sibling::*[1][self::item[@cols=$cols]]" mode="flow">
            <xsl:with-param name="cols" select="$cols"/>
        </xsl:apply-templates>
    </xsl:template>

</xsl:stylesheet>

При применении к вводимому примеру, представленному в вопросе, получается:

<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
   <fo:page-sequence master-reference="one-column-page">
      <item cols="1">Item 1</item>
      <item cols="1">Item 2</item>
      <item cols="1">Item 3</item>
      <item cols="1">Item 4</item>
      <item cols="1">Item 5</item>
      <item cols="1">Item 6</item>
      <item cols="1">Item 7</item>
      <item cols="1">Item 8</item>
      <item cols="1">Item 9</item>
   </fo:page-sequence>
   <fo:page-sequence master-reference="two-column-page">
      <item cols="2">Item 10</item>
   </fo:page-sequence>
   <fo:page-sequence master-reference="one-column-page">
      <item cols="1">Item 11</item>
      <item cols="1">Item 12</item>
      <item cols="1">Item 13</item>
      <item cols="1">Item 14</item>
      <item cols="1">Item 15</item>
      <item cols="1">Item 16</item>
      <item cols="1">Item 17</item>
      <item cols="1">Item 18</item>
   </fo:page-sequence>
</fo:root>
1 голос
/ 27 июля 2011

@ empo: Отлично! Таким образом, основной подход заключается в обработке критических элементов (где изменяется @cols) в главном цикле и обработке соседних узлов с ними в рекурсивном вызове шаблона. Я использовал ваш подход и внес несколько изменений, чтобы сделать код проще, но это прекрасно работает!

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="text()"/>

    <xsl:template match="/*">
        <fo:root>

            <xsl:for-each select="item">
                <xsl:choose>
                    <xsl:when test="preceding-sibling::item[1]/@cols != @cols or position()=1">
                        <xsl:choose>
                            <xsl:when test="@cols = 1">
                                <fo:page-sequence master-reference="one-column-page">
                                    <xsl:apply-templates select="." mode="recurse">
                                        <xsl:with-param name="cols" select="@cols"/>
                                    </xsl:apply-templates>
                                </fo:page-sequence>

                            </xsl:when>
                            <xsl:when test="@cols = 2">
                                <fo:page-sequence master-reference="two-column-page">
                                    <xsl:apply-templates select="." mode="recurse">
                                        <xsl:with-param name="cols" select="@cols"/>
                                    </xsl:apply-templates>
                                </fo:page-sequence>
                            </xsl:when>
                        </xsl:choose>
                    </xsl:when>
                </xsl:choose>
            </xsl:for-each>
        </fo:root>
    </xsl:template>


    <!-- recursive match adjacent @cols -->
    <xsl:template match="item" mode="recurse">
        <xsl:param name="cols"/>
        <xsl:copy-of select="."/>
        <xsl:apply-templates select="
            following-sibling::*[1][self::item[@cols=$cols]]"
            mode="recurse">
            <xsl:with-param name="cols" select="$cols"/>
        </xsl:apply-templates>
    </xsl:template>

</xsl:stylesheet>
...