How to dynamically change xslt page-sequence based on node attributes?

后端 未结 3 1180
慢半拍i
慢半拍i 2021-01-19 00:10

The watered-down version of the problem I\'m having is this. For an XML file like:



    

        
相关标签:
3条回答
  • 2021-01-19 00:35

    I need to be able to print the 'item's that have 'cols=1' in a single column page layout, and the 'item's that have 'cols=2' in a double column page layout. The ordering of the items has to be preserved.

    You want finally group adjacent item elements according the value of @cols in the proper fo page sequence.

    XSLT 1.0 instructions like xsl:choose and xsl:for-each are not really suitable to this task. I think you have to change your mind a bit. Here an example of how to achieve the result grouping by recursion.

    It's not really clear what you want to include inside each flow element, then I decided to show you just how to group elements; then, you can adapt the code to your requirements.


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

    When applied to the sample input provided in the question produces:

    <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>
    
    0 讨论(0)
  • 2021-01-19 00:36

    Here is an XSLT 2.0 solution that uses xsl:for-each-group with 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>
    

    Output:

    <?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>
    
    0 讨论(0)
  • 2021-01-19 00:57

    @empo: Excellent! So the basic approach is to handle the critical 'item's (where @cols changes) in the main loop, and handle the adjacent nodes to these in the recursive template call. I've used your approach and made a few changes to make the code simpler, but this works great!

    <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>
    
    0 讨论(0)
提交回复
热议问题