Search code examples
xmlxsltxsl-foxmltype

Displaying images in a grid with XSL-FO


<xsl:template match="PrintingImages">
<fo:block keep-together.within-page="always">
<fo:table table-layout="fixed" width="100%" border-collapse="separate" border-separation="2pt" column-width="50pt">
        <fo:table-body>
            <fo:table-row>
                <xsl:apply-templates/>
            </fo:table-row>
        </fo:table-body>
</fo:table>

<xsl:template match='PrintImagesData'>
   <fo:table-cell>
   <fo:block keep-together.within-page="always" float="left">
       <xsl:if test="Title">
           <fo:block><xsl:value-of select="Title" /></fo:block>
       </xsl:if>
       <xsl:if test="DateCreated">
           <fo:block><xsl:value-of select="DateCreated" /></fo:block>
       </xsl:if>
       <fo:block line-stacking-strategy="max-height">
           <xsl:element name="fo:external-graphic">
               <xsl:attribute name="width"><xsl:value-of select="Width"/></xsl:attribute>
               <xsl:attribute name="height"><xsl:value-of select="Height"/></xsl:attribute>
               <xsl:attribute name="content-width">scale-down-to-fit</xsl:attribute>
               <xsl:attribute name="content-height">scale-down-to-fit</xsl:attribute>
               <xsl:attribute name="scaling">uniform</xsl:attribute>
               <xsl:attribute name="src">url('<xsl:value-of select="ImgData"/>')</xsl:attribute>
           </xsl:element>
       </fo:block>
   </fo:block>

On the backend I call PrintImagesData 4 times, and within the PDF, it generates four images on the same line. I want two images on each line (e.g. two rows, two columns). How can I do that?


Solution

  • One option is to create a new fo:table-row at every odd positioned PrintImagesData and then create an fo:table-cell for that PrintImagesData and the first following-sibling PrintImagesData.

    Here's an example based on the limited amount of info in the original question...

    XML Input

    <doc>
        <PrintingImages>
            <PrintImagesData>
                <Title>Title 1</Title>
                <Width>100px</Width>
                <Height>100px</Height>
                <ImgData>some/graphic1.png</ImgData>
            </PrintImagesData>
            <PrintImagesData>
                <Title>Title 2</Title>
                <Width>100px</Width>
                <Height>100px</Height>
                <ImgData>some/graphic2.png</ImgData>
            </PrintImagesData>
            <PrintImagesData>
                <Title>Title 3</Title>
                <Width>100px</Width>
                <Height>100px</Height>
                <ImgData>some/graphic3.png</ImgData>
            </PrintImagesData>
            <PrintImagesData>
                <Title>Title 4</Title>
                <Width>100px</Width>
                <Height>100px</Height>
                <ImgData>some/graphic4.png</ImgData>
            </PrintImagesData>
        </PrintingImages>
    </doc>
    

    XSLT 1.0 (Made a couple of changes to your original XSLT; mainly the use of AVT's.)

    <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="/*">
            <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
                <fo:layout-master-set>
                    <fo:simple-page-master master-name="my-page" page-width="8.5in" page-height="11in">
                        <fo:region-body margin="1in" margin-top="1.5in" margin-bottom="1.5in"/>
                    </fo:simple-page-master>
                </fo:layout-master-set>
                <fo:page-sequence master-reference="my-page">
                    <fo:flow flow-name="xsl-region-body"> 
                        <xsl:apply-templates/>
                    </fo:flow>
                </fo:page-sequence>
            </fo:root>
        </xsl:template>
    
        <xsl:template match="PrintingImages">
            <fo:block keep-together.within-page="always">
                <fo:table table-layout="fixed" width="100%" border-collapse="separate" border-separation="2pt" column-width="50pt">
                    <fo:table-body>
                        <xsl:apply-templates select="PrintImagesData[not(position() mod 2 = 0)]" mode="row"/>
                    </fo:table-body>
                </fo:table>
            </fo:block>
        </xsl:template>
    
        <xsl:template match="PrintImagesData" mode="row">
            <xsl:variable name="nbr">
                <xsl:number/>
            </xsl:variable>
            <fo:table-row>
                <xsl:apply-templates select="."/>
                <xsl:if test="not($nbr mod 2 = 0)">
                    <xsl:choose>
                        <xsl:when test="following-sibling::PrintImagesData">
                            <xsl:apply-templates select="following-sibling::PrintImagesData[1]"/>                        
                        </xsl:when>
                        <xsl:otherwise>
                            <fo:table-cell>
                                <fo:block/>
                            </fo:table-cell>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:if>
            </fo:table-row>
        </xsl:template>
    
        <xsl:template match="PrintImagesData">
            <fo:table-cell>
                <fo:block keep-together.within-page="always" float="left">
                    <xsl:apply-templates select="Title|DateCreated"/>
                    <fo:block line-stacking-strategy="max-height">
                        <fo:external-graphic 
                            width="{Width}" 
                            height="{Height}" 
                            content-width="scale-down-to-fit"
                            content-height="scale-down-to-fit" 
                            scaling="uniform" 
                            src="url('{ImgData}')"/>
                    </fo:block>
                </fo:block>
            </fo:table-cell>
        </xsl:template>
    
        <xsl:template match="Title|DateCreated">
            <fo:block><xsl:value-of select="."/></fo:block>
        </xsl:template>
    </xsl:stylesheet>
    

    XSL-FO Output

    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
       <fo:layout-master-set>
          <fo:simple-page-master master-name="my-page" page-width="8.5in" page-height="11in">
             <fo:region-body margin="1in" margin-top="1.5in" margin-bottom="1.5in"/>
          </fo:simple-page-master>
       </fo:layout-master-set>
       <fo:page-sequence master-reference="my-page">
          <fo:flow flow-name="xsl-region-body">
             <fo:block keep-together.within-page="always">
                <fo:table table-layout="fixed" width="100%" border-collapse="separate" border-separation="2pt" column-width="50pt">
                   <fo:table-body>
                      <fo:table-row>
                         <fo:table-cell>
                            <fo:block keep-together.within-page="always" float="left">
                               <fo:block>Title 1</fo:block>
                               <fo:block line-stacking-strategy="max-height">
                                  <fo:external-graphic width="100px" height="100px" content-width="scale-down-to-fit" content-height="scale-down-to-fit" scaling="uniform" src="url('some/graphic1.png')"/>
                               </fo:block>
                            </fo:block>
                         </fo:table-cell>
                         <fo:table-cell>
                            <fo:block keep-together.within-page="always" float="left">
                               <fo:block>Title 2</fo:block>
                               <fo:block line-stacking-strategy="max-height">
                                  <fo:external-graphic width="100px" height="100px" content-width="scale-down-to-fit" content-height="scale-down-to-fit" scaling="uniform" src="url('some/graphic2.png')"/>
                               </fo:block>
                            </fo:block>
                         </fo:table-cell>
                      </fo:table-row>
                      <fo:table-row>
                         <fo:table-cell>
                            <fo:block keep-together.within-page="always" float="left">
                               <fo:block>Title 3</fo:block>
                               <fo:block line-stacking-strategy="max-height">
                                  <fo:external-graphic width="100px" height="100px" content-width="scale-down-to-fit" content-height="scale-down-to-fit" scaling="uniform" src="url('some/graphic3.png')"/>
                               </fo:block>
                            </fo:block>
                         </fo:table-cell>
                         <fo:table-cell>
                            <fo:block keep-together.within-page="always" float="left">
                               <fo:block>Title 4</fo:block>
                               <fo:block line-stacking-strategy="max-height">
                                  <fo:external-graphic width="100px" height="100px" content-width="scale-down-to-fit" content-height="scale-down-to-fit" scaling="uniform" src="url('some/graphic4.png')"/>
                               </fo:block>
                            </fo:block>
                         </fo:table-cell>
                      </fo:table-row>
                   </fo:table-body>
                </fo:table>
             </fo:block>
          </fo:flow>
       </fo:page-sequence>
    </fo:root>