link

PrintCSS

Table of Contents

A widespread use case for your PrintCSS document is creating a table of contents. But how would you know the page numbers of your chapters before you render the document? The content takes as many pages as it needs, and if your content is dynamic, it is impossible to say how many pages a chapter would result in.

Before we start with our table of contents, we will need some content to create the table. So let’s take the result from my previous article Running Headers and Footers, and extend the content.

Until now, the content has only one chapter. So we will add “PrintCSS: Table of Contents” with the text above as the second chapter.

<h1>PrintCSS: Table of Contents</h1>
<p>A very common use case for your PrintCSS document is creating a table of contents. But how would you know the page numbers of your chapters before you render the document? The content takes as many pages as it needs and if your content is dynamic it is impossible to say how many pages a chapter would result in.</p>
<p>Before we start with our table of contents we will need some content to create the table for. So let's take the result from my previous article PrintCSS: Running Headers and Footers and extend the content.</p>

This HTML needs to be added inside the div with the class content.

To separate the chapters, we use the CSS property page-break-before (the new version break-before does not work with Prince yet) with the always value. This will start each chapter on a new page.

.content h1 {
    page-break-before: always;
}

You can see now that the first page of our PDF is blank, and each chapter starts on a new page. If you want to try it out yourself you can render the code on printcss.live.

Rendering Result with Prince, showing the first page empty.
Rendering Result with Prince, showing the first page empty.
The new second chapter, rendered with Prince
The new second chapter, rendered with Prince

The first blank page we will use for our table of contents. In the HTML, we add a new div element with the class toc and add the links to our chapters.

<div class="toc">
    <h1>Table of contents</h1>
    <a href="#running">PrintCSS: Running Headers and Footers</a>
    <a href="#toc">PrintCSS: Table of Contents</a>
</div>

As you see, we will use anchors to link the table of contents elements to the chapters. This means that each chapter heading needs an id attribute.

<h1 id="running">PrintCSS: Running Headers and Footers</h1>
...
<h1 id="toc">PrintCSS: Table of Contents</h1>
Links in the PDF with HTML anchors, rendered in Prince.
Links in the PDF with HTML anchors, rendered in Prince.

If you render now and click on one of the table of contents elements, it will already direct you to the correct chapter. But we are missing the page numbers. For that, we can use a CSS function called target-counter.

The first parameter for the target-counter is the target URL in our case attr(href). The second parameter is the counter; as we want the page number, it is page.

.toc a {
    display: block;
}

.toc a::after {
    content: target-counter(attr(href), page);
}
Result of target-counter, rendered with Prince.
Result of target-counter, rendered with Prince.

To make it look better, let’s add dot leaders with the leader function.

Final result with the leader function, rendered with Prince.
Final result with the leader function, rendered with Prince.

#Complete Code to copy

View on GitHub

Get Result PDF

View on the PrintCSS Playground

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>test</title>
        <style>
            @page  {
                size: A6;
                margin: 20mm;

                @top-left {
                    content: element(headerLeft);
                    border-bottom: 2px solid #434190;
                }

                @top-center {
                    border-bottom: 2px solid #434190;
                }

                @top-right {
                    content: element(headerRight);
                    border-bottom: 2px solid #434190;
                }

                @bottom-right {
                    content: element(footerRight);
                    border-top: 2px solid #434190;
                }

                @bottom-center {
                    content: counter(page) " / " counter(pages);
                    border-top: 2px solid #434190;
                    font-size: 8pt;
                }

                @bottom-left {
                    content: element(footerLeft);
                    border-top: 2px solid #434190;
                }
            }

            .headerLeft {
                position: running(headerLeft);
                font-size: 12pt;
            }

            .headerRight {
                position: running(headerRight);
                font-size: 8pt;
                font-style: italic;
                text-align: right;
                color: #667eea;
            }

            .footerLeft {
                position: running(footerLeft);
            }

            .footerLeft img {
                width: 20mm;
            }

            .footerRight {
                position: running(footerRight);
                text-align: right;
                font-size: 8pt;
            }

            .content h1 {
                page-break-before: always;
            }

            .toc a {
                display: block;
            }

            .toc a::after { 
                content: leader(".") target-counter(attr(href), page); 
            }
        </style>
    </head>
    <body>
        <div class="headerLeft">
            Table of Contents
        </div>
        <div class="headerRight">
            A sample document
        </div>
        <div class="footerLeft">
            <img src="https://printcss.live/img/logo.png" />
        </div>
        <div class="footerRight">
            <a href="mailto:info@azettl.net">info@azettl.net</a><br />
            <a href="https://printcss.live/">printcss.live</a>
        </div>
        <div class="toc">
            <h1>Table of contents</h1>
            <a href="#running">PrintCSS: Running Headers and Footers</a>
            <a href="#toc">PrintCSS: Table of Contents</a>
        </div>
        <div class="content">
            <h1 id="running">PrintCSS: Running Headers and Footers</h1>
            <p>If you want to use HTML inside the page margin boxes, you will need to use running elements. These elements are normal HTML nodes with the CSS position property set to 'running(NAME)'.</p>
            <p>The name given to the running element is used within the page margin boxes to set their content to the running elements inner HTML.</p>
            <p>To do so, you need to set the specific page margin box's CSS content property to 'element(NAME)', where NAME is the given running element name.</p>
            <p>Elements with the position running do not appear in the document until you assign them to some CSS content property.</p>
            <p>Your running elements must come first in your HTML structure. For example, if you put the footer element before the closing body tag, the running footer will only appear on the last page.</p>
            <h1 id="toc">PrintCSS: Table of Contents</h1>
            <p>A very common use case for your PrintCSS document is creating a table of contents. But how would you know the page numbers of your chapters before you render the document? The content takes as many pages as it needs and if your content is dynamic it is impossible to say how many pages a chapter would result in.</p>
            <p>Before we start with our table of contents we will need some content to create the table for. So let's take the result from my previous article PrintCSS: Running Headers and Footers and extend the content.</p>
        </div>
    </body>
</html>