【问题标题】:How to dynamically change xslt page-sequence based on node attributes?如何根据节点属性动态更改 xslt 页面序列?
【发布时间】:2011-10-13 17:58:52
【问题描述】:

我遇到的问题的淡化版本是这样的。对于像这样的 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>

当然,问题是我要么必须在我的样式表中包含一个 ,要么不,我不能“动态”决定根据注释属性放入一个。 (除非我有一个一开始就动态创建样式表的元程序,但我希望只使用普通的静态样式表来完成这项工作)。

【问题讨论】:

  • 好问题。我想知道 XSLT 2.0 的分组特性是否会为您解决这个问题。请为您的示例输入显示所需的输出 XML (FO),以便我们更好地了解您的目标。

标签: xml xslt xsl-fo apache-fop


【解决方案1】:

我需要能够在单列页面布局中打印“cols=1”的“项目”,在双列页面布局中打印“cols=2”的“项目” .必须保留项目的顺序。

您最终希望根据@cols 的值在正确的页面顺序中对相邻的item 元素进行分组。

xsl:choosexsl:for-each 这样的XSLT 1.0 指令并不适合这项任务。我认为你必须改变你的想法。这里是一个如何通过递归实现结果分组的例子。

不清楚你想在每个流元素中包含什么,然后我决定向你展示如何对元素进行分组;然后,您可以根据您的要求调整代码。


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

【讨论】:

    【解决方案2】:

    这是一个使用xsl:for-each-groupgroup-adjacent 的XSLT 2.0 解决方案:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 感谢您的反馈。那你为什么接受另一个答案?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-11
      • 2017-07-11
      • 1970-01-01
      • 1970-01-01
      • 2012-09-07
      • 1970-01-01
      相关资源
      最近更新 更多