Table styles in print output

The stylesheets provide several attribute-sets and templates for controlling table styles at different levels in a table. Here is a summary of them, and each is further described in the following sections.

Table 30.2. Table styles for print output

NameXSL elementProperties are applied to:
table.propertiesattribute-setThe outer fo:block that wraps the table and its title.
formal.title.propertiesattribute-setThe fo:block containing the table title. Applies also to example, figure, and equation titles.
informaltable.propertiesattribute-setThe fo:block containing the informaltable.
table.table.propertiesattribute-setThe fo:table element, for both table and informaltable.
tabstyletemplateUtility template that returns the value of the current table's tabstyle attribute from within any table element context.
table.frametemplateThe fo:table element, computing border properties.
table.row.propertiestemplateEach fo:table-row element.
table.cell.propertiestemplateEach fo:table-cell element.
table.cell.block.propertiestemplateThe fo:block element inside each fo:table-cell.

Many publications define certain styles that are to be used for all tables within a publication. With DocBook XSL, you can define a set of table style names, each of which is associated with a particular table style. Then your authors can select which style to use by specifying the style name in a tabstyle attribute on a given table. This system lets the designer control the overall styles for tables, and makes it easy for an author to select a style that suitable for a particular table. If necessary, the author can override certain features of a table style, depending on how the customization is written.

table.properties attribute-set

Use the table.properties attribute-set to set styles for the fo:block that contains a table and its title (but not an informaltable). By default, this attribute-set just uses the attributes from the formal.object.properties attribute-set:

<xsl:attribute-set name="formal.object.properties">
  <xsl:attribute name="space-before.minimum">0.5em</xsl:attribute>
  <xsl:attribute name="space-before.optimum">1em</xsl:attribute>
  <xsl:attribute name="space-before.maximum">2em</xsl:attribute>
  <xsl:attribute name="space-after.minimum">0.5em</xsl:attribute>
  <xsl:attribute name="space-after.optimum">1em</xsl:attribute>
  <xsl:attribute name="space-after.maximum">2em</xsl:attribute>
  <xsl:attribute name="keep-together.within-column">always</xsl:attribute>
</xsl:attribute-set>

By setting any or all of these attributes in the table.properties attribute-set, you override the values copied from formal.object.properties. You can also add new properties. Whatever properties you use must be applicable to fo:block elements. If they are inheritable, they will be inherited by all blocks in the table, including the table title. You can use the formal.title.properties attribute-set to apply properties to just the number and title, although those properties will also be applied to example, figure, and equation titles.

Note in particular that the last attribute inherited from formal.object.properties adds a keep to the table block. This keeps the table title with the table, but it also means the table will be forced to a new page if the whole table does not fit on the current page. That may be inappropriate for long tables that could start on the current page. For individual tables, you can use a processing instruction to allow a table to break, as described in the section “Keep-together processing instruction”. Or you can change the attribute value to auto to turn off the keep for all tables, and use the PI in individual tables to keep them together. This change can be made in the table.properties attribute-set. You do not have to worry about keeping a table's title together with the table, because the title block itself has a keep-with-next property.

The following example adjusts the space-before and adds a background shading to all tables. Because the background-color property is on the block, it encompasses the table and its title.

<xsl:attribute-set name="table.properties">
  <xsl:attribute name="space-before.optimum">12pt</xsl:attribute>
  <xsl:attribute name="background-color">#EEEEEE</xsl:attribute>
</xsl:attribute-set>
  

If you want a property's value in this attribute-set to be dependent on a table's tabstyle attribute, then you can make the value conditional using xsl:choose. In the next example, when tabstyle="shaded" the block containing the table and its title will have a background color.

<xsl:attribute-set name="table.properties">
  <xsl:attribute name="background-color">
    <xsl:choose>
      <xsl:when test="@tabstyle='shaded'">#EEEEEE</xsl:when>
      <xsl:otherwise>inherit</xsl:otherwise>
    </xsl:choose>
  </xsl:attribute>
</xsl:attribute-set>

Since the context in which table.properties is applied is the table element, the attribute can simply test for its @tabstyle attribute value. The test is executed each time the attribute-set is applied.

informaltable.properties attribute-set

If you use an informaltable element (a table without a number and title), then you will find that the attributes in table.properties do not apply. The informaltable.properties attribute-set is used for informaltable elements. So if you want a property to apply to both, you have to add the attribute to both attribute-sets. Or you can have one attribute-set use the other:

<xsl:attribute-set name="informaltable.properties" 
                   xsl:use-attribute-sets="table.properties"/>

Just as table.properties uses all the attributes from formal.object.properties attribute-set, so does informaltable.properties use all the attributes from informal.object.properties.

See the section “table.properties attribute-set” for tips on using these attribute-sets.

table.table.properties attribute-set

While the table.properties attribute set can be used to add properties to the fo:block containing a table, you can use the table.table.properties attribute-set to add properties to the fo:table element itself. Table-specific properties such as table-layout or table-omit-header-at-break must be applied this way. The default attributes in this set are used for managing borders:

<xsl:attribute-set name="table.table.properties">
  <xsl:attribute name="border-before-width.conditionality">retain</xsl:attribute>
  <xsl:attribute name="border-collapse">collapse</xsl:attribute>
</xsl:attribute-set>

If you want a property's value in this attribute-set to be dependent on a table's tabstyle attribute, then you can make the value conditional using xsl:choose. In the next example, when tabstyle="shaded" the table will have a background color. It differs from the similar example in the section “table.properties attribute-set” in that the area behind a table's title is not shaded.

<xsl:attribute-set name="table.table.properties">
  <xsl:attribute name="background-color">
    <xsl:choose>
      <xsl:when test="ancestor-or-self::table[1]/@tabstyle='shaded' or
        ancestor-or-self::informaltable[1]/@tabstyle='shaded'">#EEEEEE</xsl:when>
      <xsl:otherwise>inherit</xsl:otherwise>
    </xsl:choose>
  </xsl:attribute>
</xsl:attribute-set>

Since the context in which table.table.properties is applied is the tgroup element, the attribute must test for the @tabstyle attribute on the ancestor table element. In the case of HTML markup tables (permitted in DocBook since version 4.3), the tgroup element is not used so the test must include ancestor-or-self. The test is executed each time the attribute-set is applied.

Properties such as background-color that are are set on the fo:table can be overridden in the table rows or cells by local attributes or processing instructions.

tabstyle template

The tabstyle named template is a utility template that can be used by any table element's template to determine the current tabstyle attribute value. A table formatting property may be implemented at any of several levels in a table, such as at the table, row, or cell levels. Any table element template can call the tabstyle template, which returns the value of the current table's tabstyle attribute. Using such a utility template makes it easy for all levels to work with the same value.

Note

If a table element (table or informaltable) does not have a tabstyle attribute, the template also checks for a tgroupstyle attribute on the tgroup element. That attribute would only be needed if a table uses more than one tgroup and they need different styles.

Generally a template handling a table element calls the tabstyle template and saves the result in a variable. Then it can use the variable in an xsl:choose statement to implement different properties at that level for different table styles. See Example 30.6, “Customized table.cell.properties” for an example of such usage. The tabstyle template was added to the stylesheets starting with version 1.72.

table.row.properties template

The template named table.row.properties applies properties to each fo:table-row in a table. Note that this is a named template, not an attribute-set. By making it a template, it is easier to customize for multiple properties on each row. This template is called just after the fo:table-row opening tag in the output. The template should output one or more xsl:attribute elements, which are applied to the table row just opened. The template must not output anything else, or it will generate errors in the table processing.

By default, the template processes any dbfo processing instructions for background color and adds that property to the current row. See the section “Row background color” for more information on that processing instruction.

The following is an example of a customization that applies a background color to alternating rows of a table when its tabstyle attribute is set to striped.

<xsl:template name="table.row.properties">

  <xsl:variable name="tabstyle">
    <xsl:call-template name="tabstyle"/>
  </xsl:variable>

  <xsl:variable name="bgcolor">
    <xsl:call-template name="dbfo-attribute">
      <xsl:with-param name="pis" select="processing-instruction('dbfo')"/>
      <xsl:with-param name="attribute" select="'bgcolor'"/>
    </xsl:call-template>
  </xsl:variable>
  
  <xsl:variable name="rownum">
    <xsl:number from="tgroup" count="row"/>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="$bgcolor != ''">
      <xsl:attribute name="background-color">
        <xsl:value-of select="$bgcolor"/>
      </xsl:attribute>
    </xsl:when>
    <xsl:when test="$tabstyle = 'striped'">
      <xsl:if test="$rownum mod 2 = 0">
        <xsl:attribute name="background-color">#EEEEEE</xsl:attribute>
      </xsl:if>
    </xsl:when>
  </xsl:choose>
</xsl:template>

table.cell.properties template

The table.cell.properties named template applies properties to each fo:table-cell in a table. Note that this is a named template, not an attribute-set. By making it a template, it is easier to customize for multiple properties on each cell. This template is called just after the fo:table-cell opening tag in the output. The template should output one or more xsl:attribute elements, which are applied to the table cell just opened. The template must not output anything else, or it will generate errors in the table processing.

By default, the table.cell.properties template outputs properties that are passed to it as template parameters. These parameters deliver the values of properties set on that DocBook entry element, or properties that were inherited from one of its table ancestor elements. For a discussion of how table properties such as align can be inherited, see the section “Cell alignment”. The following annotated example shows the first part of the template before any customization:

Example 30.5. Default table.cell.properties template

<xsl:template name="table.cell.properties"> 1
  <xsl:param name="bgcolor.pi" select="''"/> 2
  <xsl:param name="rowsep.inherit" select="1"/> 3
  <xsl:param name="colsep.inherit" select="1"/> 4
  <xsl:param name="valign.inherit" select="''"/> 5
  <xsl:param name="align.inherit" select="''"/> 6
  <xsl:param name="char.inherit" select="''"/> 7

  <xsl:if test="$bgcolor.pi != ''"> 8
    <xsl:attribute name="background-color">
      <xsl:value-of select="$bgcolor.pi"/>
    </xsl:attribute>
  </xsl:if>

  <xsl:if test="$rowsep.inherit &gt; 0">
    <xsl:call-template name="border">
      <xsl:with-param name="side" select="'bottom'"/>
    </xsl:call-template>
  </xsl:if>

  <xsl:if test="$colsep.inherit &gt; 0 and
                      $col &lt; ancestor::tgroup/@cols">
    <xsl:call-template name="border">
      <xsl:with-param name="side" select="'right'"/>
    </xsl:call-template>
  </xsl:if>

  ...

1

This mechanism uses a named template, not an attribute-set.

2

The inherited background color, based on the dbfo bgcolor processing instruction in either the row or the entry element.

3

The inherited rowsep value, which controls the border below the cell.

4

The inherited colsep value, which controls the border to the right of the cell.

5

The inherited vertical alignment value.

6

The inherited horizontal alignment value.

7

The inherited value of the char attribute, which specifies a character on which to align the entry.

8

The bit of logic that determines whether a background-color property is output using xsl:attribute. By default, it outputs the property only if it is passed an inherited value.


The table.cell.properties template provides the opportunity to customize how cell properties are added to the output. Instead of just outputting the inherited value, the template can use whatever logic you want. For example, you can check the table's tabstyle attribute value, and output the properties that are appropriate for a given named table style.

The following example shows how you can customize background color for a table with a tabstyle="styleA" attribute:

Example 30.6. Customized table.cell.properties

<xsl:template name="table.cell.properties"> 
  <xsl:param name="bgcolor.pi" select="''"/> 
  ...  
  <xsl:variable name="tabstyle">
    <xsl:call-template name="tabstyle"/>  1
  </xsl:variable>

  <xsl:variable name="bgcolor"> 2 
    <xsl:choose>
      <xsl:when test="$tabstyle = 'styleA' 
                     and ancestor::thead">#BBBBBB</xsl:when>  3
      <xsl:when test="$tabstyle = 'styleA'">#DDDDDD</xsl:when> 4
      <xsl:when test="$bgcolor.pi != ''"> 5
        <xsl:value-of select="$bgcolor.pi"/>
      </xsl:when>
    </xsl:choose>
  </xsl:variable>

  <xsl:if test="$bgcolor != ''"> 6
    <xsl:attribute name="background-color">
      <xsl:value-of select="$bgcolor"/>
    </xsl:attribute>
  </xsl:if>
  ...

1

Get the tabstyle attribute value for the containing table into a variable by calling the tabstyle named template.

2

Put the resolved color value into another variable.

3

Use color #DDDDDD if the table has tabstyle="styleA" and the current cell is inside a table header row.

4

Use color #BBBBBB if the table has tabstyle="styleA" and the current cell is not in a header row.

5

If no tabstyle match, then fall back on the inherited value if there is one.

6

Finally output a background-color attribute if one of the options provided a value.


Here is some useful information about using this template:

  • The context for the template call is the current entry element.

  • You can use xsl:number to get the current entry's position in the table. For example:

    <xsl:variable name="rownumber">
       <xsl:number count="row" from="tbody"/>
    </xsl:variable>
    <xsl:variable name="cellnumber">
       <xsl:number count="entry" from="row"/>
    </xsl:variable>
    

    The $rownumber and $cellnumber variables can be used in expressions to determine property values.

  • You can implement as many tabstyle names as you need, and you can have them generate as many properties as you need.

  • Each property must be permitted on an fo:table-cell. You can use font properties, and they will be inherited by any block elements in the cell.

  • You can decide if an inherited value should override a tabstyle value, or vice versa.

table.cell.block.properties template

The table.cell.block.properties named template applies properties to the fo:block that is inside each fo:table-cell in the output of a table. Note that this is a named template, not an attribute-set. By making it a template, it is easier to customize for multiple properties.

This template is called just after the fo:block opening tag that appears at the beginning of each fo:table-cell in the output. The template should output one or more xsl:attribute elements, which are applied to the block just opened.

This template is used mainly to set font properties on the content of table cells. This template, because its output block is nested inside the fo:table-cell, will override any properties set by the table.cell.properties template.

By default, the table.cell.block.properties template just makes table header cells bold. The following is the template before any customization:

<xsl:template name="table.cell.block.properties">
  <xsl:if test="ancestor::thead">
    <xsl:attribute name="font-weight">bold</xsl:attribute>
  </xsl:if>
</xsl:template>

See the section “table.cell.properties template” for details of how to customize setting properties in table cells.

Table page breaking

The default behavior for the print stylesheet is to try to keep each table together on a page. For short tables, this is usually desirable. But for larger tables, it can lead to awkward page breaks that leave large empty spaces on a page.

You can control how tables break across pages in the following ways:

  • You can turn on or off the keep-together property for all tables by changing the property value in the table.properties and informaltable.properties attribute-sets. See the section “table.properties attribute-set” for more information.

  • For individual tables, you can use a processing instruction to control the keep-together property. See the section “Keep-together processing instruction” for more information.

  • You can keep each row together by adding the keep-together property to table.cell.properties template. Since it is applied to each cell, it will as a side effect keep each row together. Vertically spanned cells will bring along cells in other rows.

Landscape tables

Some tables are too wide to fit on a normal portrait page. Such tables may fit if the table is rotated 90 degrees and presented in landscape mode.

You can make a table print in landscape mode by putting a orient="land" attribute on the table or informaltable element. That will rotate the table 90 degrees counter-clockwise, including any table title. This assumes your XSL-FO processor is capable of rotating the content of a fo:block-container element. XEP, Antenna House, and Xml2PDF can, but the current FOP cannot.

Depending on the width of the table content, you may also want to set the width of the table. You can use a processing instruction as described in the section “Table width”.

There is one pretty big caveat for a landscape table: it must have few enough rows to fit onto one page. The current XSL-FO standard does not support carrying over content in a block-container to more than one page. The last rows of your table will simply not appear in the printed output if you try.

There is a workaround for this problem, though. G. Ken Holman of Crane Softwrights Ltd. has published a method for creating multipage landscape tables in XSL-FO. His Page Sequence Master Interleave (PSMI) method uses two passes to rearrange the pages in an FO file. PSMI is described at http://www.cranesoftwrights.com/resources/psmi/index.htm.

Table title formatting

The main stylesheet feature that is used to format table titles for print output is the formal.title.properties attribute-set. The following is the default definition of the attribute-set:

<xsl:attribute-set name="formal.title.properties" 
                   use-attribute-sets="normal.para.spacing">
  <xsl:attribute name="font-weight">bold</xsl:attribute>
  <xsl:attribute name="font-size">
    <xsl:value-of select="$body.font.master * 1.2"/>
    <xsl:text>pt</xsl:text>
  </xsl:attribute>
  <xsl:attribute name="hyphenate">false</xsl:attribute>
  <xsl:attribute name="space-after.minimum">0.4em</xsl:attribute>
  <xsl:attribute name="space-after.optimum">0.6em</xsl:attribute>
  <xsl:attribute name="space-after.maximum">0.8em</xsl:attribute>
</xsl:attribute-set>

That attribute-set is used for titles of tables, figures, examples, equations, blockquotes, and procedures. If you make a change to the attribute-set, then it will be reflected in the titles of all of those elements.

However, it is possible to make a property value conditional when setting an attribute. The following is a customization that centers table titles, but not any of the other titles. All other properties are left as they were:

<xsl:attribute-set name="formal.title.properties">
  <xsl:attribute name="text-align">
    <xsl:choose>
      <xsl:when test="self::table">center</xsl:when>
      <xsl:otherwise>left</xsl:otherwise>
    </xsl:choose>
  </xsl:attribute>
</xsl:attribute-set>

The body of the xsl:attribute whose name is text-align has an xsl:choose statement that checks to see if the current element in context is a table. If it is, then set the value of text-align to center. Otherwise, set it to left. This works because the xsl:attribute is evaluated each time it is referenced in the attribute-set.

Table titles without number labels

What if you want to print all your table titles without the number label such as Table 3.2? There is no stylesheet parameter that will turn them off. In order to eliminate table numbers from the table titles, any “table of tables”, and any cross references to tables, the following customization is necessary. A similar customization could be used for figures, examples, or equations.

<xsl:param name="local.l10n.xml" select="document('')"/>
<l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
  <l:l10n language="en">
    <l:context name="title">
      <l:template name="table" text="%t"/>
    </l:context>
    <l:context name="xref-number-and-title">
      <l:template name="table" text="the table titled &#8220;%t&#8221;"/>
    </l:context>
  </l:l10n>
</l:i18n>

<xsl:template match="table" mode="label.markup"/>

The local.l10n.xml parameter is used to alter the generated text. In this case, you are changing the gentext templates for the table element in the contexts of title and xref-number-and-title, which are the contexts that all the formal objects use. The changes eliminate the use of the word Table and the %n placeholder that generates the number. You can reword the cross reference text any way you like. Repeat the process for all the languages you are using.

The last line of the customization makes empty the template that matches on table in mode label.markup. That mode generates the number for an element. It is used in the table of contents when a table of tables is generated.