2011-07-26 14 views
5

La versión suavizada del problema que estoy teniendo es esta. Para un archivo XML como:¿Cómo cambiar dinámicamente la secuencia de página xslt en función de los atributos del nodo?

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

que necesito para poder imprimir el 'del elemento que tiene 'cols = 1' en un único diseño de página la columna, y el' del elemento que tiene 'cols = 2' en una diseño de página de doble columna. El orden de los artículos debe ser preservado. Todos los elementos contiguos con el mismo valor de @cols deben aparecer como un flujo continuo. Cada vez que cambie el valor de @cols, necesito ir a una nueva página y cambiar el diseño según sea necesario.

que estoy haciendo algo como esto:

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

Pero, por supuesto, el problema es que o bien tienen que incluir una < fo: page-secuencia .. > en mi hoja de estilo, o no, no puede 'dinámicamente' decidir poner uno basado en los atributos de la nota. (A menos que tenga un meta programa que cree la hoja de estilos de forma dinámica, en primer lugar, pero esperaba lograr esto usando solo hojas de estilo estáticas simples).

+1

Buena pregunta. Me pregunto si las características de agrupamiento de XSLT 2.0 resolverían este problema por usted.Por favor, muestre el resultado XML deseado (FO) para su entrada de muestra, para que podamos entender mejor a qué objetivo apunta. – LarsH

Respuesta

4

Aquí es una solución XSLT 2.0 que utiliza xsl:for-each-group con 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> 

Salida:

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

tengo que ser capaz de imprimir el 'punto de que tiene 'cols = 1' en un único diseño de página la columna, y el' del elemento que tiene 'cols = 2' en un diseño de página doble columna. El orden de los artículos debe ser preservado.

¿Quieres último grupo item elementos adyacentes según el valor de @cols en el buen fo secuencia de páginas.

Las instrucciones XSLT 1.0 como xsl:choose y xsl:for-each no son realmente adecuadas para esta tarea. Creo que tienes que cambiar de opinión un poco. Aquí un ejemplo de cómo lograr la agrupación de resultados por recursión.

No está muy claro qué es lo que desea incluir dentro de cada elemento de flujo, luego decidí mostrarle cómo agrupar los elementos; entonces, puedes adaptar el código a tus requerimientos.


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

Cuando se aplica a la entrada de ejemplo proporcionado en la cuestión produce:

<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: Excelente! Entonces, el enfoque básico es manejar los elementos críticos (donde @cols cambia) en el ciclo principal, y manejar los nodos adyacentes a estos en la llamada de plantilla recursiva. He utilizado su enfoque e hice algunos cambios para simplificar el código, ¡pero funciona muy bien!

<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

Gracias por su respuesta. Entonces, ¿por qué aceptaste la otra respuesta? –

Cuestiones relacionadas