Re: Tables filling page

"Mike Haarman" <[email protected]> Wed, 26 Mar 2003 11:56:20 -0600
Newsgroups gmane.text.xml.xsl.general
Message-ID <[email protected]>
From: <[email protected]>
>
> > The problem I am having is that when I run out of
> > information to put in the table, it stops there, and I can't find a way
> > to make it go to the end of the page.  Is there a way to make a table go
> > to then end of the page on the last page it is on?  *The cells are not
> > equal in height.
>
> This is not possible in XSL-FO right now.

I submit this solution to the problem.  It illustrates a technique carried
forward from my days laying type.  I've included the complete stylesheet to shed
some light on one particular manner of using XSLT to introduce dynamism to FO
generation.  Mea culpa the attachments.

The busywork with variables at start of the stylesheet is to demonstrate that
the technique can be utilized with a degree of flexibility, including parameters
passed from calling process.  The solution can be improved directly by
increasing the precision of $font.size.factor:
instance.xml (text/xml, 96 B)
<?xml version="1.0" encoding="utf-8"?>
<xmlroot>
  <report>
    <table/>
  </report>
</xmlroot>
page-table.xsl (text/xml, 10.1 KB)
<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <!-- parameterize page and line properties -->
  <!-- line values -->
  <!-- base.size and base.lead specified in units assumed pts -->
  <xsl:variable name="base.size" select="6"/>
  <xsl:variable name="base.lead" select="2"/>

  <!-- constant ratio of of true size to named size -->
  <!-- varies for any given font and decreases as font size increases -->
  <!-- this is for FOP's default serif font @ 10 on 12 -->
  <xsl:variable name="font.size.factor" select=".8"/>

  <!-- font-size values constructed -->
  <xsl:variable name="body.font.size" select="concat($base.size + 4, 'pt')"/>
  <xsl:variable name="body.line.height" select="concat($base.size + 4 + $base.lead, 'pt')"/>
  <!-- modified by the font factor -->
  <!-- this is the divisor for calculating body.depth.rows -->
  <xsl:variable name="body.lead" select="string($base.size * $font.size.factor + 4 + $base.lead)"/>

  <!-- page values -->
  <xsl:variable name="page.height" select="66"/>
  <xsl:variable name="page.width" select="51"/>
  <xsl:variable name="margin.top" select="6"/>
  <xsl:variable name="margin.bottom" select="6"/>
  <xsl:variable name="margin.left" select="6"/>
  <xsl:variable name="margin.right" select="6"/>
  <xsl:variable name="region.before.extent" select="6"/>
  <xsl:variable name="region.after.extent" select="0"/>

  <!-- calculate body depth by rows -->
  <!-- passed as parameter to template repeatRows -->
  <!-- creates bounds for table to fill region-body -->
  <xsl:variable name="region.body.depth" select="$page.height - $margin.top - $margin.bottom - $region.before.extent - $region.after.extent"/>
  <xsl:variable name="body.depth.rows" select="floor(($region.body.depth * 12) div $body.lead)"/>


  <!-- 8.5 X 11 page w/ 1 inch margins -->
  <xsl:attribute-set name="page.definition">
    <xsl:attribute name="page-height">
      <xsl:value-of select="concat($page.height, 'pc')"/>
    </xsl:attribute>
    <xsl:attribute name="page-width">
      <xsl:value-of select="concat($page.width, 'pc')"/>
    </xsl:attribute>
    <xsl:attribute name="margin-top">
      <xsl:value-of select="concat($margin.top, 'pc')"/>
    </xsl:attribute>
    <xsl:attribute name="margin-bottom">
      <xsl:value-of select="concat($margin.bottom, 'pc')"/>
    </xsl:attribute>
    <xsl:attribute name="margin-left">
      <xsl:value-of select="concat($margin.left, 'pc')"/>
    </xsl:attribute>
    <xsl:attribute name="margin-right">
      <xsl:value-of select="concat($margin.right, 'pc')"/>
    </xsl:attribute>
  </xsl:attribute-set>

  <xsl:attribute-set name="region.body.definition">
    <xsl:attribute name="margin-top">
      <xsl:value-of select="concat($region.before.extent, 'pc')"/>
    </xsl:attribute>
    <xsl:attribute name="margin-bottom">
      <xsl:value-of select="concat($region.after.extent, 'pc')"/>
    </xsl:attribute>
  </xsl:attribute-set>

  <!-- style properties -->
  <xsl:attribute-set name="body.serif">
    <xsl:attribute name="font-family">serif</xsl:attribute>
    <xsl:attribute name="font-size">
      <xsl:value-of select="$body.font.size"/>
    </xsl:attribute>
    <xsl:attribute name="line-height">
      <xsl:value-of select="$body.line.height"/>
    </xsl:attribute>
    <xsl:attribute name="font-weight">normal</xsl:attribute>
    <xsl:attribute name="font-style">normal</xsl:attribute>
    <xsl:attribute name="text-align">start</xsl:attribute>
  </xsl:attribute-set>

  <!-- utility template -->
  <!-- repeatRows template is passed a param rows=*number-of-rows-to-repeat* -->
  <xsl:template name="repeatRows">
    <xsl:param name="rows"/>
    <xsl:choose>
      <xsl:when test="$rows &gt; 0">
        <fo:table-row>
          <fo:table-cell>
            <fo:wrapper xsl:use-attribute-sets="body.serif">
              <fo:block>&#xA0;</fo:block>
            </fo:wrapper>
          </fo:table-cell>
        </fo:table-row>
        <xsl:call-template name="repeatRows">
          <xsl:with-param name="rows" select="$rows - 1"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise/>
    </xsl:choose>
  </xsl:template>


  <!-- root template -->
  <xsl:template match="/xmlroot/report">
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

      <fo:layout-master-set>
        <!-- first page of report -->
        <fo:simple-page-master 
          master-name="first" 
          xsl:use-attribute-sets="page.definition">
          <!-- gives working page of 39pc X 54pc -->
          <!-- header takes 6pc, leaving 48pc for body flow -->
          <xsl:element name="fo:region-before">
            <xsl:attribute name="region-name">masthead</xsl:attribute>
            <xsl:attribute name="extent">
              <xsl:value-of select="concat($region.before.extent, 'pc')"/>
            </xsl:attribute>
          </xsl:element>
          <fo:region-body xsl:use-attribute-sets="region.body.definition"/>
          <xsl:element name="fo:region-after">
            <xsl:attribute name="extent">
              <xsl:value-of select="concat($region.after.extent, 'pc')"/>
            </xsl:attribute>
          </xsl:element>
        </fo:simple-page-master>

        <!-- rest of pages -->
        <fo:simple-page-master 
          master-name="rest" 
          xsl:use-attribute-sets="page.definition">
          <!-- gives working page of 39pc X 54pc -->
          <!-- header takes 6pc, leaving 48pc for body flow -->
          <xsl:element name="fo:region-before">
            <xsl:attribute name="region-name">runninghead</xsl:attribute>
            <xsl:attribute name="extent">
              <xsl:value-of select="concat($region.before.extent, 'pc')"/>
            </xsl:attribute>
          </xsl:element>
          <fo:region-body xsl:use-attribute-sets="region.body.definition"/>
          <xsl:element name="fo:region-after">
            <xsl:attribute name="extent">
              <xsl:value-of select="concat($region.after.extent, 'pc')"/>
            </xsl:attribute>
          </xsl:element>
        </fo:simple-page-master>

        <!-- last of pages -->
        <!-- top margin adjusted for smaller region-before -->
        <fo:simple-page-master 
          master-name="last" 
          xsl:use-attribute-sets="page.definition">
          <!-- gives working page of 39pc X 54pc -->
          <!-- header takes 6pc, leaving 48pc for body flow -->
          <xsl:element name="fo:region-before">
            <xsl:attribute name="region-name">runninghead</xsl:attribute>
            <xsl:attribute name="extent">
              <xsl:value-of select="concat($region.before.extent, 'pc')"/>
            </xsl:attribute>
          </xsl:element>
          <fo:region-body xsl:use-attribute-sets="region.body.definition"/>
          <xsl:element name="fo:region-after">
            <xsl:attribute name="extent">
              <xsl:value-of select="concat($region.after.extent, 'pc')"/>
            </xsl:attribute>
          </xsl:element>
        </fo:simple-page-master>

        <fo:page-sequence-master master-name="report">
          <fo:repeatable-page-master-alternatives 
            maximum-repeats="no-limit">
            <fo:conditional-page-master-reference
              master-reference="first"
              page-position="first"
              odd-or-even="any"
              blank-or-not-blank="any"/>
            <fo:conditional-page-master-reference
              master-reference="rest"
              page-position="rest"
              odd-or-even="any"
              blank-or-not-blank="any"/>
            <fo:conditional-page-master-reference
              master-reference="last"
              page-position="last"
              odd-or-even="any"
              blank-or-not-blank="any"/>
          </fo:repeatable-page-master-alternatives>
        </fo:page-sequence-master>

      </fo:layout-master-set>

      <fo:page-sequence master-reference="report" 
        white-space-collapse="false" 
        initial-page-number="1">

        <fo:flow flow-name="xsl-region-body">
          <xsl:apply-templates select="table"/>
        </fo:flow>

      </fo:page-sequence>
    </fo:root>

  </xsl:template>


  <xsl:template match="table">

    <!-- fo:block span="all" -->
      <fo:table table-layout="fixed">
        <fo:table-column column-width="0.1pt"/>
        <fo:table-column column-width="39pc - 0.1pt"/>
        <fo:table-body>

          <fo:table-row>
            <fo:table-cell/>
            <fo:table-cell number-rows-spanned="{$body.depth.rows}"
              border="1.5pt solid black">
              <fo:wrapper xsl:use-attribute-sets="body.serif">

                <!-- call your interesting table here -->
                <xsl:call-template name="interesting.table"/>

              </fo:wrapper>
            </fo:table-cell>
          </fo:table-row>

          <xsl:call-template name="repeatRows">
            <xsl:with-param name="rows" select="$body.depth.rows - 1"/>
          </xsl:call-template>

        </fo:table-body>
      </fo:table>
    <!-- /fo:block -->

  </xsl:template>


  <xsl:template name="interesting.table">

    <fo:table table-layout="fixed">
      <fo:table-column column-width="10pt"/>
      <fo:table-column column-width="23pc"/>
      <fo:table-column column-width="5pc"/>
      <fo:table-column column-width="5pc"/>
      <fo:table-column column-width="5pc"/>

      <fo:table-body>
        
        <fo:table-row>
          <fo:table-cell/>
          <fo:table-cell>
            <fo:block>
              <xsl:text>An interesting item</xsl:text>
            </fo:block>
          </fo:table-cell>
          <fo:table-cell text-align="center">
            <fo:block>
              <xsl:text>2</xsl:text>
            </fo:block>
          </fo:table-cell>
          <fo:table-cell text-align="center">
            <fo:block>
              <xsl:text>$12.95</xsl:text>
            </fo:block>
          </fo:table-cell>
          <fo:table-cell text-align="center">
            <fo:block>
              <xsl:text>$25.90</xsl:text>
            </fo:block>
          </fo:table-cell>
        </fo:table-row>

      </fo:table-body>
    </fo:table>

  </xsl:template>

</xsl:stylesheet>