Print TOC control

The main features for controlling tables of contents are described in the section “Tables of contents (TOC)”. This section describes features specific to print output.

TOC Page margins

You can adjust the page margins for a table of contents by customizing the toc.margin.properties attribute set. The following shows the default settings:

<xsl:attribute-set name="toc.margin.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-set>

These properties do not change the margins for the page-master, but for the fo:block that contains the table of contents on the page. The space-before values add space above the TOC and space-after add space below the toc. You would use start-indent to indent from left, but end-indent will not work to indent from the right. The right (end) indent is set in the template named toc.line.properties, which is applied to the block containing each line in a TOC. A customization to indent both sides by 1 inch might look like the following:

<xsl:attribute-set name="toc.margin.properties">
  <xsl:attribute name="start-indent">0.5in</xsl:attribute>
</xsl:attribute-set>

<xsl:attribute-set name="toc.line.properties">
  <xsl:attribute name="text-align-last">justify</xsl:attribute>
  <xsl:attribute name="text-align">start</xsl:attribute>
  <xsl:attribute name="end-indent">1.25in</xsl:attribute>
  <xsl:attribute name="last-line-end-indent">-0.25in</xsl:attribute>
</xsl:attribute-set>

In toc.line.properties, the end-indent property sets the block value, then the last-line-end-indent adds a negative value for the last line of the block. The result is that single lines get both properties, and hence a net indent of 1in. Any long lines that roll over to a second line have the first line indented by 1.25in. Note that the overall block alignment is start (left), and only the last line is justified. This combination of properties keeps long lines out of the space reserved for the page numbers, making it more readable. These properties work in the attribute-set starting with version 1.72 of the stylesheets.

As with all attribute-sets, your customized properties are merged with the default set. You could add other properties that would apply to the whole TOC block. For example, a TOC in an article does not have a page break after it by default. The following example would add that page break.

<xsl:attribute-set name="toc.margin.properties">
  <xsl:attribute name="break-after">page</xsl:attribute>
  </xsl:attribute-set>

If you want to change the page layout beyond these properties, you will need to add a customized page-master, in this case for the lot (list of titles) page class. See the section “Custom page design” for a description of how to do that. In addition to a new page master, you will need to customize the select.user.pagemaster template to select your new page master file during processing.

TOC title

The Table of Contents title that appears at the top of a TOC is turned on or off with the generate.toc parameter. See the section “Which components have a TOC” to see how to change that parameter.

The title text in a table of contents is generated text, using the TableofContents key word. For English, the text comes from the gentext file common/en.xml, where the text associated with the key word is "Table of Contents". You can change it for a given language in a customization layer with something like the following:

<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:gentext key="TableofContents" text="Contents"/>
  </l:l10n>
</l:i18n>

The formatting of the title is controlled by the same machinery that controls title pages. While a TOC could have a complete title page, the default behavior is just to start a page and put the title at the top. If you want to change how the title is formatted, then you will need to customize the titlepage spec file and generate a new titlepage stylesheet module. See the section “Title page spec file” information on how to do that. Specifically, you will want to change the following entry in the spec file:

<t:titlepage element="table.of.contents" wrapper="fo:block">
  <t:titlepage-content side="recto">
    <title
           force="1"
           named-template="gentext"
           param:key="'TableofContents'"
           fo:space-before.minimum="1em"
           fo:space-before.optimum="1.5em"
           fo:space-before.maximum="2em"
           fo:space-after="0.5em"
           fo:margin-left="{$title.margin.left}"
           fo:font-size="&hsize3;"
           fo:font-weight="bold"
           fo:font-family="{$title.font.family}"/>
  </t:titlepage-content>

The spec file needs to be processed into a stylesheet module that gets included in your customization layer.

If you do not want to generate a new titlepage spec file just to modify the table of contents title, you can instead replace the template named table.of.contents.titlepage. If your customization has a template of this name, then it will override the processing done by the regular title page machinery. You may need to give your version a priority attribute with a value of greater than zero, in case there is a problem with import precedence. The template must get the title using the gentext template, and then generate an fo:block with the appropriate properties. After outputting the title, it must close the fo:block. For example:

<xsl:template name="table.of.contents.titlepage" priority="1">
  <fo:block xsl:use-attribute-sets="section.title.level1.properties"
            space-before="1in"
            space-before.conditionality="retain"
            space-after="12pt"
            border-bottom="0.5pt solid black">
    <xsl:call-template name="gentext">
      <xsl:with-param name="key" select="'TableofContents'"/>
    </xsl:call-template>
  </fo:block>
</xsl:template>

The space-before.conditionality="retain" property forces the formatter to use the specified space-before value, even though it is at the top of the page where it would normally be ignored.

Styling print TOC entries

An entry in a table of contents is produced by applying templates that have mode="toc" and that match the element associated with that entry. So the template that formats an entry for a sect1 title would start with:

<xsl:template match="sect1" mode="toc">

These templates are located in the fo/autotoc.xsl stylesheet file. They are not good candidates for customization because they are pretty complex. The complexity comes from the recursive nature of processing all the elements in the document in the toc mode. Each mode="toc" template generates the entry for its element, and then checks to see if the element has children that should appear in the TOC. If so, then it increments the indent by the amount of the parameter toc.indent.width and applies templates in toc mode to process the children.

To change to overall left indent, or the right indent in a table of contents, see the section “TOC Page margins”. To change the indent increment, set the toc.indent.width parameter to a pure number which is interpreted as points by the stylesheet (do not add the pt unit to the parameter). To further customize the incremental indents, you can customize the template named set.toc.indent from fo/autotoc.xsl which applies that parameter.

Some styles can be set using the toc.line.properties attribute-set that is applied to the block containing each line. You can use it to set overall properties for all entries, or you can make an attribute value conditional on the current element. For example, if you want to set the font size for all entries to 10pt, and display chapter-level entries in bold:

<xsl:attribute-set name="toc.line.properties">
  <xsl:attribute name="font-size">10pt</xsl:attribute>
  <xsl:attribute name="font-weight">
    <xsl:when test="self::chapter | self::preface | self::appendix">bold</xsl:when>
    <xsl:otherwise>normal</xsl:otherwise>
  </xsl:attribute>
</xsl:attribute-set>

If you need further control, the template that can be customized more easily is named toc.line, because it formats a single entry of a TOC. By default, it formats all TOC lines the same, since the left indent has been set by the template that called toc.line. If you want to customize certain TOC entries, then your customization would need to test for the name of the context node (the element whose TOC entry is being generated) and act accordingly. The template also has a toc-context parameter, which is set to the element containing the TOC. That way you can style an entry in a book TOC differently from one in a chapter or article TOC.

In the following example customization, chapter and appendix entries add the label Chapter or Appendix preceding the number label:

<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:apply-templates select="." mode="label.markup"/>  
  </xsl:variable>

  <fo:block xsl:use-attribute-sets="toc.line.properties">  
    <fo:inline keep-with-next.within-line="always">
      
      <fo:basic-link internal-destination="{$id}">  

        <xsl:if test="self::appendix or self::chapter">
          <xsl:call-template name="gentext">
            <xsl:with-param name="key" select="local-name()"/>
          </xsl:call-template>
          <xsl:text> </xsl:text>
        </xsl:if>

        <xsl:if test="$label != ''">
          <xsl:copy-of select="$label"/>
          <xsl:value-of select="$autotoc.label.separator"/>
        </xsl:if>
        <xsl:apply-templates select="." mode="title.markup"/>  
      </fo:basic-link>
    </fo:inline>
    <fo:inline keep-together.within-line="always"> 
      <xsl:text> </xsl:text>
      <fo:leader leader-pattern="dots"
                 leader-pattern-width="3pt"
                 leader-alignment="reference-area"
                 keep-with-next.within-line="always"/>
      <xsl:text> </xsl:text>
      <fo:basic-link internal-destination="{$id}">
        <fo:page-number-citation ref-id="{$id}"/>
      </fo:basic-link>
    </fo:inline>
  </fo:block>
</xsl:template>

Note

For best results in your TOC, be sure to omit any leading or trailing white space in your title elements. In XML, white space includes linefeeds, tabs, and space characters. Any white space at the end of a title may cause its dot leaders in the TOC to start on the next line instead of staying with the last word in the title. Any white space at the beginning of a title will show as a single blank space when the title is quoted in cross references.

TOC page numbering

When a TOC page is started, it is assigned a format for its own page numbers. It gets the format by calling the template named page.number.format. That is a simple template that can be customized in a customization layer. The following is an example that changes the page numbering style of the TOC to 1, 2, 3 etc.

<xsl:template name="page.number.format">
  <xsl:param name="element" select="local-name(.)"/>
  <xsl:choose>
    <xsl:when test="$element = 'toc'">1</xsl:when>
    <xsl:when test="$element = 'preface'">i</xsl:when>
    <xsl:when test="$element = 'dedication'">i</xsl:when>
    <xsl:otherwise>1</xsl:otherwise>
  </xsl:choose>
</xsl:template>

Part TOC on part titlepage

If you include part in the generate.toc parameter in order to generate a table of contents for each part element, you may want to customize the output. By default, the part titlepage is a page-sequence (2 pages in double-sided output), and the part TOC is another page-sequence (two more pages). A typical customization merges the TOC onto the titlepage, and reduces the TOC to just the chapter and appendix listings.

The following customization will add a TOC to the part title page:

<xsl:template name="part.titlepage.before.verso" priority="1">
  <xsl:variable name="toc.params">
    <xsl:call-template name="find.path.params">
      <xsl:with-param name="table"
            select="normalize-space($generate.toc)"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:if test="contains($toc.params, 'toc')">
    <xsl:call-template name="division.toc">
      <xsl:with-param name="toc.context" select="."/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

Turn off the original  part toc page-sequence template
<xsl:template name="generate.part.toc">
</xsl:template>

The part.titlepage.before.verso template is one of those generated by the titlepage spec file, and you can see it in fo/titlepage.templates.xsl. Normally it is empty, so you can override it to do something else, like put the TOC before the verso titlepage (which means on the recto titlepage after the title information).

It first checks to see if you have a part toc or part toc,title string in your generate.toc parameter (it is there by default). If so, it calls division.toc to generate the part TOC, without generating a page-sequence.

The last template nulls out the template that generates a page sequence for the part TOC. The generate.part.toc template is what generates the extra pages, because it is in its own page sequence.

If you want to reduce the part TOC to just chapters and appendixes and not include sections, then one further customization is needed. An element is processed for a TOC in mode="toc". Normally each element processed in that mode also processes its children in that mode, down to the level of the toc.section.depth parameter. If you turn off processing sections in that mode completely, you also lose them in a book's TOC. Instead, use the toc-context local parameter in a conditional statement to process sections only when the context is not a part TOC. The following customization of the template from fo/autotoc.xsl does that.

<xsl:template match="preface|chapter|appendix|article"
              mode="toc">
  <xsl:param name="toc-context" select="."/>
  ...
  <xsl:if test="local-name($toc-context) != 'part'
                and $toc.section.depth > 0
                and $toc.max.depth > $depth.from.context
                and $nodes">
  ...

Editable table of contents

You may need more control over a table of contents than is provided with the customization features in the stylesheets. In those situations, you may need to create a table of contents as an XML file that you can edit and process as part of your document. You can do this for the main table of contents in HTML processing, but not currently for FO output. Fortunately, you do not have write the TOC from scratch.

The extra stylesheet file html/maketoc.xsl will output a TOC document that has a main toc element that contains a set of nested tocentry elements. If you output that to a file, you can edit it and feed it back into the processing of your document. Here are steps to do that.

Using an editable table of contents

  1. Generate a table of contents XML file from your document. For example:

    xsltproc  -o customtoc.xml \
      --stringparam chunk.section.depth 8 \
      --stringparam chunk.first.sections 1 \
      html/maketoc.xsl  mybook.xml
    

    Use the two parameters chunk.section.depth and chunk.first.sections to ensure the generated TOC includes all the sections you want.

  2. Edit the TOC file customtoc.xml as needed.

  3. Generate your document output by processing your document with the manual.toc parameter:

    xsltproc  -o mybook.html  \
        --stringparam manual.toc  customtoc.xml  \
        html/docbook.xsl  mybook.xml

The stylesheet parameter manual.toc identifies the filename of your editable XML TOC file. If you pass this parameter to the stylesheet during processing, it will use that file in place of the automatically generated TOC.

Note

If you regenerate your document, you may also have to re-edit the table of contents. Unless you can automate the changes with a script, using an editable TOC is efficient only for documents that rarely change.