Click or drag to resize
Pdf Library for .NET

Generate Table of Contents

The html to pdf converter can give a document a table of contents with page numbers, through TableOfContents. The Mode property chooses how it is built:

  • FillExisting - the page already has a table of contents (internal links to its sections); each entry gets a leader and the number of the page its section is on.

  • InsertHtml - the table of contents is written into the page from its headings, styled by the page's CSS, then numbered.

  • GeneratePages - the table of contents is drawn on pages of its own, inserted into the pdf document, with the fonts, colors and spacing set in the options.

In every mode the page numbers are the numbers the document shows: they start at the FirstPageNumber of the displayed footer, and pages inserted by GeneratePages are counted. Leaders and numbers are decorative in a tagged document, so accessible (PDF/UA) and archival (PDF/A) documents stay conformant. JavaScript must be enabled.

Numbering the page's own table of contents

Set ContainerSelector to the element that holds the table of contents. Every <a href="#id"> link inside it gets a leader and the number of the page its target is on, drawn at the end of the line where the link's text ends and right-aligned to the container, so the table keeps the look the page gives it. A link whose target is not in the document gets no number.

HtmlToPdf converter = new HtmlToPdf();
converter.Options.TableOfContents.Mode = PdfTableOfContentsMode.FillExisting;
converter.Options.TableOfContents.ContainerSelector = "#toc";
converter.Options.TableOfContents.Leader = PdfTableOfContentsLeader.Dots;

string url = "https://www.example.com/document2.html";
PdfDocument doc = converter.ConvertUrl(url);
doc.Save("toc.pdf");
doc.Close();
Writing the table of contents into the page

HeadingSelectors lists the headings, one selector per level. The table is written at the start of the page, at its end or inside a placeholder element (HtmlPlacement), as a <nav class="selectpdf-toc"> with an optional <h2 class="selectpdf-toc-title"> and nested <ol> lists whose items have the classes selectpdf-toc-entry and selectpdf-toc-level-N, so the page's CSS can style it. It gets a page of its own (PageBreak).

HtmlToPdf converter = new HtmlToPdf();
PdfTableOfContentsOptions toc = converter.Options.TableOfContents;
toc.Mode = PdfTableOfContentsMode.InsertHtml;
toc.HeadingSelectors = new string[] { "h1", "h2" };
toc.Title = "Table of Contents";
converter.Options.CustomCSS = "nav.selectpdf-toc li { font-size: 14px; }";

string url = "https://www.example.com/document3.html";
PdfDocument doc = converter.ConvertUrl(url);
doc.Save("toc.pdf");
doc.Close();
Generating table of contents pages

The table is drawn on pages inserted at the start, after the first page or at the end (PagePosition), with the page size, margins, header and footer of the converted pages. Each entry is a link to its heading. The title has its own font, color and alignment, and each level its own font, color, indent and spacing (PdfTableOfContentsLevelStyle); NumberEntries numbers the entries.

HtmlToPdf converter = new HtmlToPdf();
PdfTableOfContentsOptions toc = converter.Options.TableOfContents;
toc.Mode = PdfTableOfContentsMode.GeneratePages;
toc.HeadingSelectors = new string[] { "h1", "h2" };
toc.PagePosition = PdfTableOfContentsPagePosition.AfterFirstPage;
toc.Title = "Table of Contents";
toc.TitleAlign = PdfTextHorizontalAlign.Center;
toc.NumberEntries = true;
toc.LevelStyles = new PdfTableOfContentsLevelStyle[]
{
    new PdfTableOfContentsLevelStyle
    {
        Font = PdfFont.CreateStandardFont(PdfStandardFont.TimesBold, 13),
        SpacingBefore = 10,
    },
    new PdfTableOfContentsLevelStyle
    {
        Font = PdfFont.CreateStandardFont(PdfStandardFont.TimesRoman, 11),
        Indent = 20,
    },
};

string url = "https://www.example.com/document3.html";
PdfDocument doc = converter.ConvertUrl(url);
doc.Save("toc.pdf");
doc.Close();
Custom layouts: drawing the numbers yourself

For a layout the modes above do not cover, the page numbers can be drawn by your own code: the Get Web Elements Location in PDF feature reports where the table's entries and their targets landed in the pdf, and the entries and their targets are given the CSS class "toc" and paired IDs (TOC_Title1 / TOC_Target1, and so on), so the page of each target is known. This is how the table of contents was numbered before the TableOfContents option existed.

// number of items in the table of contents (TOC)
int TOC_Items = 13;

// CSS class for TOC items (titles and targets)
string TOC_Class = "toc";

// font size for page numbers in the table of contents
int TOC_Page_Font_Size = 12;

// CSS class for PDF bookmarks
string Bookmarks_Class = "bookmark";

// Each TOC item is represented by a title with ID TOC_Title{0}
// and a target (internal link) with ID TOC_Target{0}
// Both TOC titles and targets must have the same CSS Class ("toc" in our sample)

// the url of the document that will be converted
string url = "https://www.example.com/document2.html";

// instantiate a html to pdf converter object
HtmlToPdf converter = new HtmlToPdf();

converter.Options.MarginTop = 20;
converter.Options.MarginRight = 20;
converter.Options.MarginBottom = 20;
converter.Options.MarginLeft = 20;

// set the css selectors for the automatic bookmarks 
// (elements with CSS class "bookmark")
converter.Options.PdfBookmarkOptions.CssSelectors =
    new string[] { "*." + Bookmarks_Class };

// set the css selectors for the TOC related elements
// (elements with CSS class "toc")
// (alternatively, full list of IDs can be specified)
converter.Options.WebElementsMappingOptions.CssSelectors =
    new string[] { "*." + TOC_Class };

// display the bookmarks when the document is opened in a viewer
converter.Options.ViewerPreferences.PageMode = PdfViewerPageMode.UseOutlines;

// footer settings
converter.Options.DisplayFooter = true;
converter.Footer.Height = 30;

// page numbers can be added using a PdfTextSection object, using a system font
// (subset-embedded). Liberation Sans resolves on any host: the library bundles it
// and serves it from embedded bytes when it is not installed.
PdfFont footerFont = PdfFont.CreateFromSystemFont("Liberation Sans", 10f);
PdfTextSection text = new PdfTextSection(0, 10,
    "Page: {page_number} of {total_pages}  ",
    footerFont);
text.HorizontalAlign = PdfTextHorizontalAlign.Right;
converter.Footer.Add(text);


// create a new pdf document converting an url
PdfDocument doc = converter.ConvertUrl(url);

// create font for page numbers
PdfFont pageNumberFont = doc.Fonts.Add(
    PdfFont.CreateStandardFont(PdfStandardFont.Helvetica, TOC_Page_Font_Size));

// get the right coordinate of the table of contents 
// to position the page numbers
float tocRight = doc.Pages[0].ClientRectangle.Width - 50;

// add page numbers for the table of contents items
for (int tocItem = 1; tocItem <= TOC_Items; tocItem++)
{
    string tocTitleID = String.Format("TOC_Title{0}", tocItem);
    string tocTargetID = String.Format("TOC_Target{0}", tocItem);

    WebElement tocTitle = converter.Options.WebElementsMappingOptions
        .Result.GetElementByHtmlId(tocTitleID);
    WebElement tocTarget = converter.Options.WebElementsMappingOptions
        .Result.GetElementByHtmlId(tocTargetID);

    if (tocTitle == null || tocTarget == null)
    {
        // TOC items not found
        continue;
    }

    // get the TOC title page and rendering rectangle
    PdfPage tocPage = doc.Pages[tocTitle.PdfRectangles[0].PageIndex];
    RectangleF tocTitleRectangle = tocTitle.PdfRectangles[0].Rectangle;

    // get the page number of target where the TOC entry points
    int tocTargetPageNumber = tocTarget.PdfRectangles[0].PageIndex + 1;

    // add dashed line from TOC title to the page number
    PdfLineElement dashedLine = new PdfLineElement(
        tocTitleRectangle.Right + 5, 
        tocTitleRectangle.Y + tocTitleRectangle.Height - 4,
        tocRight, tocTitleRectangle.Y + tocTitleRectangle.Height - 4);
    dashedLine.LineStyle.LineWidth = 1;
    dashedLine.LineStyle.LineDashStyle = PdfLineDashStyle.Dash;
    dashedLine.ForeColor = Color.Black;
    tocPage.Add(dashedLine);

    // create the page number text element to the right of the TOC title
    PdfTextElement pageNumberTextElement = new PdfTextElement(tocRight + 5, 
            tocTitleRectangle.Y, -1, tocTitleRectangle.Height,
            tocTargetPageNumber.ToString(), pageNumberFont);
    pageNumberTextElement.HorizontalAlign = PdfTextHorizontalAlign.Left;
    pageNumberTextElement.VerticalAlign = PdfTextVerticalAlign.Middle;
    pageNumberTextElement.ForeColor = Color.Black;

    // add the page number to the right of the TOC entry
    tocPage.Add(pageNumberTextElement);
}

// save pdf document
doc.Save("Sample.pdf");

// close pdf document
doc.Close();
See Also