Tables of contents (TOC)

The DocBook stylesheets have several features for generating tables of contents. In addition to the traditional list of chapters and sections at the beginning of a book or article, you can optionally generate lists of figures, tables, examples, equations, and procedures. You can also generate mini tables of contents for each chapter or even each section, down to a section level of your choice.

Some aspects of customizing tables of contents can be controlled with parameters, and others require a customization layer.

Which components have a TOC

The DocBook XSL stylesheets use the generate.toc parameter to determine which elements have a TOC generated at the beginning of the element in the output. For print output or non-chunked HTML output, a single TOC at the beginning may suffice. But when you are generating chunked HTML files, you may want certain sublevels to provide TOCs to help orient the reader.

Although generate.toc is a parameter that can be set on the command line, it is a bit awkward to use that way because it can contain a lot of information. The default value of generate.toc (HTML version) is:

<xsl:param name="generate.toc">
appendix  toc,title
article/appendix  nop
article   toc,title
book      toc,title,figure,table,example,equation
chapter   toc,title
part      toc,title
preface   toc,title
qandadiv  toc
qandaset  toc
reference toc,title
sect1     toc
sect2     toc
sect3     toc
sect4     toc
sect5     toc
section   toc
set       toc,title
</xsl:param>

The parameter value is read as white-space separated pairs (leading whitespace is trimmed off). The first word of each pair is an element name, and the second word is a comma-separated list that specifies what kind of TOCs it should have. Most of them just use toc, which is a list of section titles. But the entry for the book element will also generate tables of figures, tables, examples, and equations. The word title triggers printing a title for the list, such as "Table of Contents". Use a value of nop to turn off all TOCs for an element. You can also turn off a TOC by removing the element entirely from the parameter.

You can change which elements have TOCs by putting a new version of this parameter in your customization layer. For example, to remove the TOC from the elements preface, part, qandadiv, qandaset, appendix, and sections, and remove the TOC title from chapter, use this parameter value:

<xsl:param name="generate.toc">
 appendix  nop
 article   toc,title
 book      toc,title,figure,table,example,equation
 chapter   toc
 part      nop
 preface   nop
 qandadiv  nop
 qandaset  nop
 reference toc,title
 section   nop
 set       toc
 </xsl:param>

If your document is a book and you only want a book-level TOC and no others, then you can use a very simple value:

<xsl:param name="generate.toc" select="'book toc'"/>

That is a space between book and toc, and do not forget the single quotes to make it a string. You can even set this simple value on the command line:

xsltproc  --stringparam generate.toc "book toc" ...

Because the list uses white space to separate items in the list, and then counts through the list to establish pairs of items, you have to follow a few rules with this parameter:

  • A "white space" includes any sequence of blanks spaces, tabs, and carriage returns. So you could put all the information on one line with single blanks between items. In fact, that is what the processor does using the normalize-space XSL function.

  • do not leave a value blank, because that messes up the pairing. Either remove the element name or enter nop to turn off an element you leave in the list.

  • do not insert spaces in a comma separated list like toc,figure,table. The spaces will mess up the pairing.

You can get even finer control of when TOCs are used by adding context information. For example, an article in a book can be treated differently from an article as a whole document. See the reference page for the generate.toc parameter for more information.

Note

Section TOCs are also controlled by the generate.section.toc.level parameter, which is by default set to zero. See the section “Turning on section TOCs”.

Levels in book and chapter TOCs

You can control how many nested levels of headings a TOC list should have. A book TOC always lists the titles for part and chapter elements, as well as any other components at the chapter level such as preface, appendix, glossary and index. A book TOC may also contain titles of sections within the chapters, depending on the value of the toc.section.depth parameter. If chapter TOCs are turned on by the generate.toc parameter, then what appears in the chapter TOC is completely controlled by the toc.section.depth parameter. The following table summarizes its effect on book and chapter TOCs.

Use the toc.section.depth parameter to indicate how many levels of section titles should appear in the TOCs. If you set it to a value of 3, for example, then TOCs will include up to sect3 titles. The default value is 2. The following table summarizes the effect of the parameter.

Table 10.1. How toc.section.depth affects book and chapter TOCs

toc.section.depthBook TOC includes:Chapter TOC includes:
0
chapter
No TOC
1
chapter
  sect1
sect1
2 (default)
chapter
  sect1
    sect2
sect1
  sect2
3
chapter
  sect1
    sect2
      sect3
sect1
  sect2
    sect3

If you use bridgehead titles in your document, you also have the option of including those titles in your TOCs. To do so, set the bridgehead.in.toc parameter to 1. Then all bridgehead titles will be included at the appropriate level in the TOC.

There is a different style of hierarchy for TOCs that the toc.max.depth parameter controls. In this style, each TOC regardless of where it is located has the same number of levels (if the content is there and the TOC is enabled with the generate.toc parameter). The toc.max.depth parameter controls the maximum number of levels in any TOC. This parameter first appeared in version 1.61 of the stylesheets, and only applies to HTML output. The following table summarizes the effect of the parameter.

Table 10.2. How toc.max.depth affects book and chapter TOCs (HTML output only)

toc.max.depthBook TOC includes:Chapter TOC includes:
0No TOCNo TOC
1
chapter
sect1
2
chapter
  sect1
sect1
  sect2
3
chapter
  sect1
    sect2
sect1
  sect2
    sect3

You will notice that both the book and chapter TOCs contain the same number of levels for each value of the parameter. The table assumes that the toc.section.depth parameter has been increased to at least 3. If not, then the default value of 2 would limit the chapter TOC in the last row to sect1 and sect2 entries. The default value of toc.max.depth is 7, so normally the toc.section.depth parameter is the limiting factor in a given TOC's depth.

Customized TOC levels

The stylesheet parameters described in the section “Levels in book and chapter TOCs” give you some control of what levels of headings appear in a book, chapter, and section TOCs. There are some combinations that cannot be achieved with just parameters and require template customization.

For example, you might want your book TOC in HTML to just list the chapter titles, and then rely on complete TOCs in each chapter to provide section titles. The following short customization does that.

<xsl:template match="preface|chapter|appendix|article" mode="toc">
  <xsl:param name="toc-context" select="."/>

  <xsl:choose>
    <xsl:when test="local-name($toc-context) = 'book'">
      <xsl:call-template name="subtoc">
        <xsl:with-param name="toc-context" select="$toc-context"/>
        <xsl:with-param name="nodes" select="foo"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="subtoc">
        <xsl:with-param name="toc-context" select="$toc-context"/>
        <xsl:with-param name="nodes"
              select="section|sect1|glossary|bibliography|index
                     |bridgehead[$bridgehead.in.toc != 0]"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

It is a copy of a template in html/autotoc.xsl, modified to add a choose statement. It processes the chapter element (and other elements) in mode="toc" to generate lines in your TOC. The $toc-context template parameter contains the element in which the TOC is appearing. So if you take the local-name() of that element and compare it to 'book', the template can take a different action. In this case, you tell it to select foo children of the chapter element, which will be an empty node set since there is no such element. Then the depth of the chapter toc can be controlled by the toc.section.depth parameter.

Set TOC

If you process a group of books inside a set element, then the set can have a table of contents too. Normally it would be a complete table of contents, listing the books, chapters, and sections to whatever level was specified for books.

You might not need such detail in a set TOC, since each book TOC has those details. If you want your set TOC to list just the book titles, then you can customize a template. The template is different if you are doing print or HTML output.

For print output, you would customize the template with match="book|setindex" mode="toc" from the stylesheet module fo/autotoc.xsl. After calling the template named toc.line to format the book title, the template selects the child nodes to process under it. Since this template is only called by the set.toc template, you can reduce it to formatting just its own title.

<xsl:template match="book|setindex" mode="toc">
  <xsl:param name="toc-context" select="."/>
  <xsl:call-template name="toc.line"/>
</xsl:template>

For HTML output, the set.toc template processes each book element in mode="toc". Normally that template calls the subtoc template with a parameter named nodes that selects the book's child elements. Instead, select an empty node set by selecting a non-element name, such as EMPTY (since DocBook has no element of that name).

<xsl:template match="book" mode="toc">
  <xsl:param name="toc-context" select="."/>

  <xsl:call-template name="subtoc">
    <xsl:with-param name="toc-context" select="$toc-context"/>
    <xsl:with-param name="nodes" select="EMPTY"/>
  </xsl:call-template>
</xsl:template>

The subtoc template generates the book title, and then stops because it has no child nodes to process. This change will not affect the book's own TOC because it does not use this template, instead calling division.toc.

Turning on section TOCs

By default, sections do not have their own TOCs. But you can use parameters to turn on TOCs for sections and control what levels are listed. Section TOCs are particularly useful with chunked HTML output for which the user might need some context for complex documents.

Two parameters control which section levels have a TOC: generate.toc and generate.section.toc.level. You will notice that the default value of the generate.toc parameter described in the section “Which components have a TOC” includes entries for all the section levels, yet the default output does not have section TOCs. That is because a second parameter generate.section.toc.level also controls which section levels have a TOC. For example, if you set generate.section.toc.level to a value of 2, then you will get a TOC for all sect1 and sect2 elements, or their equivalents in nested section elements. Why two parameters? This arrangement lets you establish a style for which TOCs could appear at various section levels by modifying the complex generate.toc parameter in your customization layer. Then you can select the actual output level at runtime with the simple generate.section.toc.level parameter. Both parameters must enable a section level for its TOC to appear.

The depth of section levels that appear in a given section TOC is usually controlled by the toc.section.depth parameter. This parameter indicates the deepest section level that can appear in any TOC. Its default value of 2 means only sections up to sect2 will appear in any TOC. For HTML output, a second parameter toc.max.depth can be used to produce a different style of TOC hierarchy. This parameter indicates the maximum number of levels that appear in any TOC. A value of 2 means only up to two levels of titles will appear in any TOC, regardless of where the TOC appears.

The following two tables show how these two parameters affect section TOCs for various values of the generate.section.toc.level parameter.

Table 10.3. Section TOCs with toc.section.depth

generate.section.toc.leveltoc.section.depthsect1 TOC includes:sect2 TOC includes:sect3 TOC includes:sect4 TOC includes:
12
sect2
No TOC No TOC No TOC
3
sect2
  sect3
No TOC No TOC No TOC
4
sect2
  sect3
    sect4
No TOC No TOC No TOC
22
sect2
No TOC No TOC No TOC
3
sect2
  sect3
sect3
No TOC No TOC
4
sect2
  sect3
    sect4
sect3
  sect4
No TOC No TOC
32
sect2
No TOC No TOC No TOC
3
sect2
  sect3
sect3
No TOC No TOC
4
sect2
  sect3
    sect4
sect3
  sect4
sect4
No TOC

In some cases, there is no TOC because the TOC would be in a section level outside the range of the generate.section.toc.level parameter. In other cases, there is no TOC because the toc.section.depth parameter prevents it from having any entries. This table assumes the other parameter, toc.max.depth, has a value high enough to not interfere with the selection of levels.

Table 10.4. Section TOCs with toc.max.depth (HTML only)

generate.section.toc.leveltoc.max.depthsect1 TOC includes:sect2 TOC includes:sect3 TOC includes:sect4 TOC includes:
1 1
sect2
No TOC No TOC No TOC
2
sect2
  sect3
No TOC No TOC No TOC
3
sect2
  sect3
    sect4
No TOC No TOC No TOC
21
sect2
sect3
No TOC No TOC
2
sect2
  sect3
sect3
  sect4
No TOC No TOC
3
sect2
  sect3
    sect4
sect3
  sect4
    sect5
No TOC No TOC
31
sect2
sect3
sect4
No TOC
2
sect2
  sect3
sect3
  sect4
sect4
  sect5
No TOC
3
sect2
  sect3
    sect4
sect3
  sect4
    sect5
sect4
  sect5
    [sect6]
No TOC

Using toc.max.depth, you will notice that if a TOC exists, then it has the same number of levels as every other TOC (if the content is there). This table assumes that the other parameter, toc.section.depth, has a high enough value to not interfere with the selection of levels. If it is not changed from its default value of 2, then none of these examples would show titles beyond sect2.

Keeping selected titles out of the TOC

There may be situations where you want to exclude certain titles from the table of contents. This may be because they are of minor importance, or perhaps they are meant to be only accessed using an online help system. You can assign a role attribute to such elements, and then add a small template to your customization layer. For example, you might use the attribute value NotInToc to designate elements that should not be in the TOC. The following template would work.

<xsl:template match="sect1[@role = 'NotInToc']"  mode="toc" />

Normally an element's title appears in the toc because the element is processed with a template in mode="toc" to generate the title. In this customization, the template is empty, which means it does nothing. So any element matching it will not appear in the TOC. In this case, it is matching on any sect1 element with the role="NotInToc" attribute. Create similar templates for other elements you might want to exclude.

Caution

If you use profiling using the role attribute, you must include NotInToc in your selected values. If you do not, then those elements with that attribute value will not appear in your output at all because they will be excluded in the profiling step. This is another reason why it is not a good idea to use the role attribute for profiling.

Adding elements to a TOC

Some document designs require that certain extra elements be added to the document's table of contents. Some candidate elements include refentry, article (mixed in with chapters), procedure (those with titles), block elements like sidebar or titled lists, or certain of the formal elements like table, figure, or example. For instance, you might customize example elements to contain formal exercises, and want to list those along with section titles in the TOC.

Adding an element to a TOC is generally a two-step process in DocBook XSL:

  • Add the new element to the templates that select TOC elements.

  • Add a template for the new element in mode="toc" to generate its entry.

To understand how to customize, it helps to know the sequence of processing for a table of contents. All of the these templates that generate a TOC are in the stylesheet files named autotoc.xsl (for both HTML and FO).

Template sequence for a TOC

  1. Before a TOC is started, the stylesheet parameter generate.toc is checked to see if it has an entry for the current element. If not, then there will be no TOC started at that element level. See the section “Which components have a TOC” for information on that parameter.

  2. If the generate.toc parameter also has a title entry for the current element, then a template is called to generate the title in the current language.

  3. A TOC list starts with a call to one of the top-level templates such as set.toc, division.toc (for book or part), component.toc (for chapter, appendix, article), or section.toc.

  4. The top-level TOC template selects the child elements that should be included in the TOC. Note that a book TOC does not include the title of the book itself, only the child elements of book. Likewise for the other top-level templates. It is this selection process that must be customized to include a new element.

  5. The top-level template then processes the set of child nodes. In FO, the selected nodes have templates applied in mode="toc". In HTML, the selected nodes are passed as a parameter to a template named make.toc to process them in mode="toc".

  6. The mode="toc" template for the selected child calls toc.line to format its title. It then selects its children in turn, and processes them in mode="toc" to produce nested entries. In this way the stylesheet descends through the entire document hierarchy. Note that in HTML, the subtoc template is called to apply templates in mode="toc" in order to add the proper HTML wrapper elements to indent the sub entries.

For instance, if you want to add sidebar elements to a book TOC, you first have to customize where to select them for the TOC. Since a sidebar cannot appear as a direct child of book, you do not need to customize the division.toc template that starts the TOC. But you will need to customize these two templates to include sidebar, as shown in these two code snippets. Since a title element is optional in sidebar, these select only those sidbar elements that contain a title.

Add sidebar under chapter:
<xsl:template match="preface|chapter|appendix|article" mode="toc">
  ...
  <xsl:call-template name="toc.line"/>

  <xsl:variable name="nodes" select="section|sect1|sidebar[title]
                                     |simplesect[$simplesect.in.toc != 0]
                                     |refentry|appendix"/>

Add sidbar under section:
<xsl:template match="section" mode="toc">
  ...
  <xsl:if test="$toc.section.depth &gt;= $depth">
    <xsl:call-template name="toc.line"/>

    <xsl:if test="$toc.section.depth > $depth
                  and $toc.max.depth > $depth.from.context
                  and (section or sidebar)">
      <fo:block id="toc.{$cid}.{$id}">
        <xsl:attribute name="margin-left">
          <xsl:call-template name="set.toc.indent">
            <xsl:with-param name="reldepth" select="$reldepth"/>
          </xsl:call-template>
        </xsl:attribute>
        <xsl:apply-templates select="section|sidebar[title]" mode="toc">
          <xsl:with-param name="toc-context" select="$toc-context"/>
        </xsl:apply-templates>

The second step is to add a template matching on sidebar in mode="toc", since one does not already exist in the stylesheet.

<xsl:template match="sidebar" mode="toc">
  <xsl:call-template name="toc.line"/>
</xsl:template>

This template just calls the toc.line template to process the title, and does not try to add any children of sidebar to the TOC.

Customizing TOC presentation

If you need to further customize how TOCs are presented, you may need to modify some of the XSL templates. The following is an example of adding the word "Appendix" to appendix entries in the print table of contents (which by default just shows the appendix letter).

<xsl:template name="toc.line">
  <xsl:param name="toc-context" select="NOTANODE"/>

  <xsl:variable name="id">
    <xsl:call-template name="object.id"/>
  </xsl:variable>

  <xsl:variable name="label">
    <xsl:choose>
      <xsl:when test="self::appendix">
        <xsl:call-template name="gentext">
          <xsl:with-param name="key">appendix</xsl:with-param>
        </xsl:call-template>
        <xsl:text> </xsl:text>
        <xsl:apply-templates select="." mode="label.markup"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." mode="label.markup"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  ...
  rest of toc.line template

For print output, you can customize this template named toc.line which can be found in fo/autotoc.xsl. See the section “Styling print TOC entries” for a detailed example. For HTML output, the stylesheets also implement a toc.line template in html/autotoc.xsl that can be customized in a manner similar to that for print output.

Customizing by TOC context

Both versions of the toc.line template include a passed-in template parameter named toc-context. That parameter references the element that contains the TOC list. So if a book TOC is being generated, for example, that parameter references the book element. You can change the TOC presentation based on the toc-context parameter. In this example, the label number is printed in bold when the TOC is not at the book level:

<xsl:template name="toc.line">
  <xsl:param name="toc-context" select="NOTANODE"/>

  <xsl:variable name="id">
    <xsl:call-template name="object.id"/>
  </xsl:variable>

  <xsl:variable name="label">
    <xsl:choose>
      <xsl:when test="$toc-context/self::book">
        <xsl:apply-templates select="." mode="label.markup"/>
      </xsl:when>
      <xsl:otherwise>
        <fo:inline font-weight="bold">
          <xsl:apply-templates select="." mode="label.markup"/>
        </fo:inline>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
...

Since the toc-context template parameter contains a node, not a string, it can be used in an XPath statement that tests if it is a book element. If it is, then it just generates the label number, otherwise it wraps the label number in an fo:inline to add the bold property.