Re: [xsl] How to make auto row-spanned table

Subject: Re: [xsl] How to make auto row-spanned table
From: Toshihiko Makita <tmakita@xxxxxxxxxxxxx>
Date: Thu, 25 Apr 2013 23:27:16 +0900
Dear Mr. Holman,

Thank you for your detailed and excellent reply.
I was very excited seeing your XSLT stylesheet. It is actually working!
Because authoring tables without @morerows attribute and applying row-span after in XSLT stylesheet step is my idea to express some kind of information set in DITA.
Many of DITA user does not adopt this method. Rather they choose to express this kind of information set by dl/dt,dd (definition lists) elements. But it is not acceptable for me.


I will propose to my DITA customer that this method is effective.

Best regards,

Toshihiko Makita

(2013/04/25 14:10), xsl-list-digest-help@xxxxxxxxxxxxxxxxxxxxxx wrote:
------------------------------
Date: Wed, 24 Apr 2013 13:56:29 -0400 To: XSL List <xsl-list@xxxxxxxxxxxxxxxxxxxxxx>
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Subject: Re: [xsl] How to make auto row-spanned table
Message-Id: <7.0.1.0.2.20130424132039.0245b5a0@xxxxxxxxxxxxxxxxxxxx> At 2013-04-24 12:40 -0400, I wrote:
At 2013-04-25 01:29 +0900, Toshihiko Makita wrote:
I'm making a stylesheet that converts DITA documents to XSL-FO.
Actually, the answer is a *lot* simpler than I thought because I
misread your request to convert DITA to DITA and because you made
reference to a morerows attribute.  The principle is the same, but in
XSL-FO one can take advantage of the cell-based row-grouping
approach.  I even have an example of this as an exercise in my
training class in the module on sorting and grouping.  Most of my
students have never heard of filling an XSL-FO table with only cell
elements and not row elements, so the exercise is meant to help
remember the technique.

Usually a table is authored with entry/@morerows attribute. But
some table has no @morerows attribute.
In XSL-FO it is acceptable to say number-rows-spanned="1" when there
is no spanning.  I don't know if it is allowed to say morerows="0" in DITA.

It would help us help you to have the XML input data of your table
example.  You can post it as clear text in your message and we can
cut it out and create a file from it.
I mocked up a test file that mimics your data and produces an
acceptable XSL-FO result using a simplified stylesheet.  Note the use
of the ends-row="true/false" attribute and that there are no
<table-row> elements.  The output test.fo can be formatted with an
XSL-FO engine to produce a result with the rows you need.

t:\ftemp>type test.xml
<?xml version="1.0" encoding="UTF-8"?>
<table>
    <tgroup>
      <tbody>
        <row><entry>A</entry><entry>B</entry><entry>C</entry><entry>D</entry><entry>E</entry><entry>F</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>H</entry><entry>I</entry><entry>J</entry><entry>K</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>H</entry><entry>L</entry><entry>M</entry><entry>N</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>O</entry><entry>P</entry><entry>M</entry><entry>Q</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>R</entry><entry>S</entry><entry>T</entry><entry>Q</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>V</entry><entry>S</entry><entry>W</entry><entry>X</entry></row>
        <row><entry>A</entry><entry>Y</entry><entry>Z</entry><entry>1</entry><entry>2</entry><entry>3</entry></row>
        <row><entry>A</entry><entry>Y</entry><entry>4</entry><entry>1</entry><entry>5</entry><entry>6</entry></row>
        <row><entry>A</entry><entry>Y</entry><entry>7</entry><entry>1</entry><entry>5</entry><entry>8</entry></row>
      </tbody>
    </tgroup>
</table>

t:\ftemp>call xslt2 test.xml testfo.xsl test.fo

t:\ftemp>type testfo.xsl
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.w3.org/1999/XSL/Format";
        font-family="Times" font-size="20pt"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
        xsl:version="2.0">

    <layout-master-set>
      <simple-page-master master-name="frame"
                          page-height="11in" page-width="8.5in"
                          margin-top=".5in" margin-bottom=".5in"
                          margin-left=".5in" margin-right=".5in">
        <region-body region-name="frame-body"/>
      </simple-page-master>
    </layout-master-set>

    <page-sequence master-reference="frame">
      <flow flow-name="frame-body">
        <block>This is a test of spanning rows in XSL-FO</block>

<table text-align="center">
    <table-body>
      <xsl:for-each select="table/tgroup/tbody">
      <!--create a stream of table cells, marking the end of each row-->
        <!--only the first three columns of the table are grouped-->
        <xsl:for-each-group select="row" group-by="entry[1]">
          <table-cell number-rows-spanned="{count(current-group())[.>1]}">
            <block>
              <xsl:apply-templates select="entry[1]/node()"/>
            </block>
          </table-cell>
          <xsl:for-each-group select="current-group()" group-by="entry[2]">
            <table-cell number-rows-spanned="{count(current-group())[.>1]}">
              <block>
                <xsl:apply-templates select="entry[2]/node()"/>
              </block>
            </table-cell>
            <xsl:for-each-group select="current-group()" group-by="entry[3]">
              <table-cell number-rows-spanned="{count(current-group())[.>1]}">
                <block>
                  <xsl:apply-templates select="entry[3]/node()"/>
                </block>
              </table-cell>
              <!--the rest of the columns are not grouped-->
              <xsl:for-each select="current-group()">
                <xsl:for-each select="entry[position()>3]">
                  <!--mark the last cell of each row as true, others as false-->
                  <table-cell ends-row="{position()=last()}">
                   <block>
                     <xsl:apply-templates/>
                   </block>
                  </table-cell>
                </xsl:for-each>
              </xsl:for-each>
            </xsl:for-each-group>
          </xsl:for-each-group>
        </xsl:for-each-group>
      </xsl:for-each>
    </table-body>
</table>

      </flow>
    </page-sequence>
</root>

t:\ftemp>rem Done!

Since I had already written a DITA -> DITA stylesheet adding
morerows= before discovering I misread your question, I've added
below a stylesheet that does that with the same input data:

t:\ftemp>type test.xml
<?xml version="1.0" encoding="UTF-8"?>
<table>
    <tgroup>
      <tbody>
        <row><entry>A</entry><entry>B</entry><entry>C</entry><entry>D</entry><entry>E</entry><entry>F</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>H</entry><entry>I</entry><entry>J</entry><entry>K</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>H</entry><entry>L</entry><entry>M</entry><entry>N</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>O</entry><entry>P</entry><entry>M</entry><entry>Q</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>R</entry><entry>S</entry><entry>T</entry><entry>Q</entry></row>
        <row><entry>A</entry><entry>G</entry><entry>V</entry><entry>S</entry><entry>W</entry><entry>X</entry></row>
        <row><entry>A</entry><entry>Y</entry><entry>Z</entry><entry>1</entry><entry>2</entry><entry>3</entry></row>
        <row><entry>A</entry><entry>Y</entry><entry>4</entry><entry>1</entry><entry>5</entry><entry>6</entry></row>
        <row><entry>A</entry><entry>Y</entry><entry>7</entry><entry>1</entry><entry>5</entry><entry>8</entry></row>
      </tbody>
    </tgroup>
</table>

t:\ftemp>call xslt2 test.xml testdita.xsl test.out.xml

t:\ftemp>type test.out.xml
<?xml version="1.0" encoding="UTF-8"?><table>
    <tgroup>
      <tbody>
<row><entry
morerows="8">A</entry><entry>B</entry><entry>C</entry><entry>D</entry><entry>E</entry><entry>F</entry></row>
<row><entry morerows="4">G</entry><entry
morerows="1">H</entry><entry>I</entry><entry>J</entry><entry>K</entry></row>
<row><entry>L</entry><entry>M</entry><entry>N</entry></row>
<row><entry>O</entry><entry>P</entry><entry>M</entry><entry>Q</entry></row>
<row><entry>R</entry><entry>S</entry><entry>T</entry><entry>Q</entry></row>
<row><entry>V</entry><entry>S</entry><entry>W</entry><entry>X</entry></row>
<row><entry
morerows="2">Y</entry><entry>Z</entry><entry>1</entry><entry>2</entry><entry>3</entry></row>
<row><entry>4</entry><entry>1</entry><entry>5</entry><entry>6</entry></row>
<row><entry>7</entry><entry>1</entry><entry>5</entry><entry>8</entry></row></tbody>
    </tgroup>
</table>
t:\ftemp>type testdita.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
    version="2.0">

<xsl:template match="tbody">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <!--create a stream of entries, marking the end of each row-->
      <xsl:variable name="stream-entries" as="element()*">
        <!--only the first three columns of the table are grouped-->
        <xsl:for-each-group select="row" group-by="entry[1]">
          <entry>
            <xsl:copy-of select="entry[1]/@*"/>
            <xsl:for-each select="count(current-group())[.>1]">
              <xsl:attribute name="morerows" select=".-1"/>
            </xsl:for-each>
            <xsl:copy-of select="entry[1]/node()"/>
          </entry>
          <xsl:for-each-group select="current-group()" group-by="entry[2]">
            <entry>
              <xsl:copy-of select="entry[2]/@*"/>
              <xsl:for-each select="count(current-group())[.>1]">
                <xsl:attribute name="morerows" select=".-1"/>
              </xsl:for-each>
              <xsl:copy-of select="entry[2]/node()"/>
            </entry>
            <xsl:for-each-group select="current-group()" group-by="entry[3]">
              <entry>
                <xsl:copy-of select="entry[3]/@*"/>
                <xsl:for-each select="count(current-group())[.>1]">
                  <xsl:attribute name="morerows" select=".-1"/>
                </xsl:for-each>
                <xsl:copy-of select="entry[3]/node()"/>
              </entry>
              <!--the rest of the columns are not grouped-->
              <xsl:for-each select="current-group()">
                <xsl:copy-of select="entry[position()>3]"/>
                <!--mark the end of each row for compartmentalization-->
                <row-ends-here/>
              </xsl:for-each>
            </xsl:for-each-group>
          </xsl:for-each-group>
        </xsl:for-each-group>
      </xsl:variable>
      <!--reconstitute rows from the stream of entries by finding each
row end-->
      <xsl:for-each-group select="$stream-entries"
                          group-ending-with="row-ends-here">
        <xsl:text>&#xa;</xsl:text>
        <row>
          <xsl:copy-of select="current-group()[position()&lt;last()]"/>
        </row>
      </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template match="@*|node()"><!--identity for all other nodes-->
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>rem Done!

I hope these examples help.

. . . . . . . . . Ken

--
Contact us for world-wide XML consulting and instructor-led training |
Free 5-hour lecture: http://www.CraneSoftwrights.com/links/udemy.htm |
Crane Softwrights Ltd.            http://www.CraneSoftwrights.com/s/ |
G. Ken Holman                   mailto:gkholman@xxxxxxxxxxxxxxxxxxxx |
Google+ profile: https://plus.google.com/116832879756988317389/about |
Legal business disclaimers:    http://www.CraneSoftwrights.com/legal |

------------------------------

Current Thread