2011-07-26 11 views
5

Rozwodniona wersja problemu, który mam, jest taka. Dla pliku XML, takich jak:Jak dynamicznie zmieniać kolejność stron xslt na podstawie atrybutów węzła?

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

muszę być w stanie wydrukować „elementu, który ma«cols = 1»w jednym układzie strony kolumny, a” pozycja, że ​​mają «cols = 2» w układ podwójnej strony kolumny. Kolejność przedmiotów musi zostać zachowana. Wszystkie ciągłe "elementy o tej samej wartości @cols muszą pojawiać się jako ciągły przepływ. Za każdym razem, gdy zmienia się wartość @cols, muszę przejść na nową stronę i zmienić układ, jeśli jest to konieczne.

robię coś takiego:

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

Oczywiście, problemem jest to, że albo trzeba zawierać < fo: page-sekwencję .. > w moim arkusza stylów, czy nie, nie może "dynamicznie" decydować o umieszczeniu jednego na podstawie atrybutów nuty. (Chyba że mam program meta, który tworzy dynamicznie arkusz stylów, ale miałem nadzieję, że osiągnę to za pomocą zwykłych statycznych arkuszy stylów).

+1

Dobre pytanie. Zastanawiam się, czy funkcje grupowania XSLT 2.0 rozwiążą ten problem.Proszę pokazać pożądany wynikowy plik XML (FO) dla danych wejściowych próbki, abyśmy mogli lepiej zrozumieć, do czego celujesz. – LarsH

Odpowiedz

4

Oto XSLT roztwór 2,0 który wykorzystuje xsl:for-each-group z 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> 

Wyjście:

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

muszę być w stanie wydrukować „elementu, który ma«cols = 1»w pojedynczym układzie strony kolumny, a” pozycja, że ​​mają «cols = 2» w podwójnym układzie strony kolumny. Kolejność przedmiotów musi zostać zachowana.

Chcesz wreszcie grupy sąsiadujące item elementy według wartości @cols w odpowiednim fo sekwencji stron.

Instrukcje XSLT 1.0, takie jak xsl:choose i xsl:for-each, nie nadają się do tego zadania. Myślę, że musisz trochę zmienić zdanie. Oto przykład, jak osiągnąć grupowanie wyników przez rekurencję.

Nie jest jasne, co chcesz umieścić w każdym elemencie przepływu, a następnie zdecydowałem się pokazać, jak grupować elementy; następnie możesz dostosować kod do swoich wymagań.


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

Gdy stosuje się do wejścia do przykładowych w pytaniu sporządza:

<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

@empo: Doskonale! Tak więc podstawowym podejściem jest obsłużenie krytycznych "pozycji (gdzie zmienia się @cols) w pętli głównej i obsłużenie sąsiednich węzłów w rekurencyjnym wywołaniu szablonu. Użyłem twojego podejścia i wprowadziłem kilka zmian, aby uprościć kod, ale działa to świetnie!

<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

Dziękujemy za opinię. Dlaczego więc przyjąłeś drugą odpowiedź? –

Powiązane problemy