Prince is a HTML-to-PDF-via-CSS converter which is often for creating books from HTML and CSS. Most books have a Table of Contents (ToC) to help readers navigate the content. In the ToC, headings appear alongside page numbers. Page numbers, however, must be calculated by the formatter, and not a human author. This guide will show you how to automatically generate a Table of Contents in your book.
We start with a simple HTML document, which has a title page and two short chapters:
htmlh1, h2 { break-before: page } <h1>Waterfowl</h1> <h2>Ducks</h2> ... <h2>Geese</h2> ... ...
There is no ToC in the above document. Let's add one!
Adding a ToC is easy – a script does most of the work. First, we add an empty element into which the ToC will be poured. A ToC is an ordered list, so we use the <ol>
element for this:
<ol id=toc></ol>
Second, we must tell the script which elements should copied into the ToC. This is done by providing a CSS selector in a function call:
<script type="text/javascript" src="https://princexml.com/howcome/2021/guides/charms.js"></script> <body onload="toc('h2')">
Third, we must add some lines of CSS to style the elements generated by the script:
#toc li { list-style-type: none } #toc a:after { content: leader('. ') target-counter(attr(href), page) }
Adding these code snippets into our barebone example, we get:
htmlh1, h2 { break-before: page } #toc li { list-style-type: none } #toc a:after { content: leader('. ') target-counter(attr(href), page) } ... <script type="text/javascript" src="https://princexml.com/howcome/2020/guides/charms.js"> <body onload="toc('h2')"> <h1>Waterfowl</h1> <h2>Table of Contents</h2> <ol id=toc></ol> <h2>Ducks</h2> ... <h2>Geese</h2> ...
The resulting ToC can be seen on page 2 above. Try clicking on the blue links in the PDF document!
Textbooks and other complex documents often have multi-level chapter structures where CSS pseudo-elements are used to number chapters and sections. Here is an example which uses CSS counters to add numbering. Not all headlines should be numbered, though, therefore fancy :not
selectors are used:
htmlh2:not(h2.toc) { counter-increment: section; counter-reset: subsection 0 } h2:not(h2.toc):before { content: "Chapter " counter(section) ": " } h3 { counter-increment: subsection } h3::before { content: counter(section) "." counter(subsection) ": " } ... <script type="text/javascript" src="https://princexml.com/howcome/2021/guides/charms.js"> <body onload="toc('h2, h3')"> <h2>Table of Contents</h2> <ol id=toc></ol> <h1>Waterfowl</h1> <h2>Ducks</h2> ... <h3>Feet and feathers</h3> ... <h3>Habitat</h3> ... <h2>Geese</h2> ... <h3>Neck length</h3> ... <h3>Instincts</h3> ...
The previous examples use a script which is custom-made for Prince. A simplified version of the script produces clicakble links that work both in browsers and in PDF documents. The only downside to using this solution is that CSS pseudo-elements (which were used for counting sections above) are not supported:
htmlh1, h2 { break-before: page } #toc li { list-style-type: none } #toc li.h3 { margin-left: 3em; font-style: italic } #toc a:after { content: leader('. ') target-counter(attr(href), page) } ... <script type="text/javascript" src="https://princexml.com/howcome/2021/guides/charms.js"> <body onload="simpleToc('h2, h3')"> <h2>Table of Contents</h2> <ol id=toc></ol> <h1>Waterfowl</h1> <h2>Ducks</h2> ... <h3>Feet and feathers</h3> ... <h3>Habitat</h3> ... <h2>Geese</h2> ... <h3>Neck length</h3> ... <h3>Instincts</h3> ...
Creating indexes in Prince 14 relies on the multipass
feature, which formats the document several times. Between each run, a script can modify the document to improve layout. These scripts do amazing things in Prince, but will not work in browsers.