chris85
chris85

Reputation: 23880

XSLT Dynamic FO Block Creation

I'm getting this error:

Error reported by XML parser: The element type "fo:table-row" must be terminated by the matching end-tag "</fo:table-row>".

When trying to dynamically create fo:table-rows.

This is my current XSLT code:

<xsl:template match="sec|ack">
    <xsl:call-template name="subtitle">
        <xsl:with-param name="text" select="title"/>
    </xsl:call-template>
        <fo:table width="100%">
            <fo:table-column column-width="45%"/>
            <fo:table-column column-width="45%"/>
            <fo:table-body>     
                <xsl:for-each select="sec/*">
                    <xsl:variable name="i" select="position()" />
                    <xsl:if test="($i == 1)">
                        <fo:table-row>
                    </xsl:if>
                    <xsl:if test="($i mod 2) && $i > 1">
                        </fo:table-row>
                        <fo:table-row>
                    </xsl:if>
                    <fo:table-cell>
                        <fo:block><xsl:value-of select="*" /></fo:block>
                    </fo:table-cell>
                </xsl:for-each>
                </fo:table-row>
            </fo:table-body>
        </fo:table>
</xsl:template>

and this the XML I'm using (although I think it is my syntax not the actual XML that is the issue):

<sec>
    <title>Section Title</title>
    <sec>
        <title>Paragraph 1 Title</title>
        <p>Body paragraph</p>
    </sec>
    <sec>
        <title>Paragraph 2 Title</title>
        <list list-type="order">
            <list-item>
                <p>Step 1.</p>
            </list-item>
            <list-item>
                <p>Step 2.</p>
            </list-item>
            <list-item>
                <p>Step 3.</p>
            </list-item>
            <list-item>
                <p>Step 4.</p>
            </list-item>
            <list-item>
                <p>Step 5.</p>
            </list-item>
            <list-item>
                <p>Step 6.</p>
            </list-item>
            <list-item>
                <p>Step  7.</p>
            </list-item>
            <list-item>
                <p>Step 8.</p>
            </list-item>
            <list-item>
                <p>Step 9.</p>
            </list-item>
            <list-item>
                <p>Step 10.</p>
            </list-item>
            <list-item>
                <p>Step 11.</p>
            </list-item>
            <list-item>
                <p>Step 12.</p>
            </list-item>
            <list-item>
                <p>Step 13.</p>
            </list-item>
            <list-item>
                <p>Step 14.</p>
            </list-item>
        </list>
    </sec>
</sec>

I'm converting our historical code from a 1 column display to a 2 column display. If there is a better, non-table approach I'd be interested in that as well. I tried using floats but wasn't able to get it to work. With this approach I can get a static 2 column display working.

This was my static approach:

<xsl:template match="sec|ack">
    <xsl:call-template name="subtitle">
        <xsl:with-param name="text" select="title"/>
    </xsl:call-template>
    <fo:table width="100%" >
        <fo:table-column column-width="45%"/>
        <fo:table-column column-width="45%"/>
        <fo:table-body>
            <fo:table-row>
                <fo:table-cell>
                    <fo:block><xsl:apply-templates select="*[name()!='title']|text()" mode="xhtml"/></fo:block>
                </fo:table-cell>
                <fo:table-cell>
                    <fo:block>This is the second content block.</fo:block>
                </fo:table-cell>
            </fo:table-row>
        </fo:table-body>
    </fo:table>
</xsl:template>

Upvotes: 0

Views: 455

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167716

As you indicate you use XSLT 2 you can use positional grouping with for-each-group group-adjacent="(position() - 1) idiv 2" to group every two selected adjacent nodes into a table-row so instead of the <xsl:for-each select="sec/*"> you use

<xsl:for-each-group select="*" group-adjacent="(position() - 1) idiv 2">
    <fo:table-row>
        <xsl:apply-templates select="current-group()" mode="cell"/>
    </fo:table-row>
</xsl:for-each-group>

and then write a template transforming any element into a cell in that named mode:

<xsl:template match="*" mode="cell">
    <fo:table-cell>
        <fo:block>
          <xsl:apply-templates/>
        </fo:block>
    </fo:table-cell>
</xsl:template>

Upvotes: 2

Related Questions