Auto Create Table of Contents

Winnovative HTML to PDF Converter can be configured to automatically create a table of contents in a PDF document based on H1 to H6 heading tags found in HTML document by simply turning on the PdfDocumentOptionsGenerateTableOfContents option. An object of the PdfDocumentOptions type is exposed by the HtmlToPdfConverterPdfDocumentOptions property. The code below can be used to enable the table of contents generation in the PDF document.

C#
// Create a HTML to PDF converter object with default settings
HtmlToPdfConverter htmlToPdfConverter = new HtmlToPdfConverter();

// Enable or disable the automatic creation of a table of contents in the PDF document based on H1 to H6 HTML tags
htmlToPdfConverter.PdfDocumentOptions.GenerateTableOfContents = true;

Table of Contents Structure

The table of contents container is the equivalent of a block element with the ID 'html-to-pdf-toc'. Inside this block element there is another block element that contains the title followed by an unordered list of table entries, similar to an unordered list element from HTML. Each entry in the table of contents, corresponding to a heading tag from HTML, has a text, a level which determines the entry appearance in table and a target PDF page number. The table of contents style can be controlled by a CSS stylesheet which reflects the internal structure described in this section.

Table of Contents Options

The table of contents properties are controlled by the PdfDocumentOptionsTableOfContents property which exposes an object of TableOfContentsOptions type. By setting this object's properties, you can control the table of contents style and the page numbering.

Table Title

The table of contents title can be set using the TableOfContentsOptionsTitle property. The code below can be used to set the table of contents title.

C#
// Set the table of contents title
htmlToPdfConverter.PdfDocumentOptions.TableOfContents.Title = "Table of Contents";

Table Style

The table of contents style can be controlled using a standard CSS stylesheet defined in the TableOfContentsOptionsStyle property. You can control the global table of contents style, the title style, the style for each entry level and the page numbers style. There is a separate section dedicated to style customization.

Inline Table of Contents

The table of contents is normally created at the beginning of the PDF document, but there is also the possibility to generate the table of contents inline in any position within the converted HTML document by setting the TableOfContentsOptionsCreateInline property The code below can be used to create the table of contents inline.

C#
// Set the table of contents title
htmlToPdfConverter.PdfDocumentOptions.TableOfContents.CreateInline = true;

The table of contents position in the converted HTML document is given by a DIV element with the ID 'html-to-pdf-toc', defined as below.

XML
<div id="html-to-pdf-toc"></div>

If there is no such DIV element defined in the converted HTML document, the converter will automatically create it at the top of the HTML content being converted.

Page Numbers

The table of contents can display the target PDF page number at the right side of each table of contents entry.

  • Enable Page Numbers. Using the property TableOfContentsOptionsShowPageNumbers you can enable or disable the page numbers display in the table of contents. When the page numbers are disabled, you might also optionally need to adjust the table style to no longer show the dotted line between the table entry and the page number.

  • Page Numbers Offset. Another option to control the page numbering is to use the TableOfContentsOptionsPageNumbersOffset property to adjust the page numbering with a constant positive or negative integer. This property can be useful when merging a PDF containing a table of contents with other PDF documents similar to the 'Merge Multiple HTML to PDF' example from the demo application.

  • Count TOC and Start Pages. For non-inline table of contents you can also configure whether to include or exclude from the page numbering the PDF pages on which the table of contents is rendered, using the TableOfContentsOptionsCountTocAndStartPages property. This property also includes the PDF pages coming from the PDF documents added before the main HTML content using the PdfDocumentOptionsAddStartPdf(Byte) method of the converter. If you ignore the table of contents and start PDF pages, the page numbers in the table of contents will start from page 1, which refers to the next page after the table of contents. This property does not apply to inline table of contents.

Table of Contents Mode

For non-inline table of contents it is also possible to configure the creation mode to be custom or browser with TableOfContentsOptionsUseBrowserMode property.

  • Custom Mode. This is the default mode and the converter uses a custom algorithm to generate the table of contents entries.

  • Browser Mode. In this mode the table of contents is generated using the browser capabilities. To enable this mode you can use the code below.

    C#
    htmlToPdfConverter.PdfDocumentOptions.TableOfContents.UseBrowserMode = true;

Table of Contents Style

The table of contents style can be controlled using a standard CSS stylesheet set in the TableOfContentsOptionsStyle property. The converter initializes this property with a default style that you can overwrite with your own CSS stylesheet to control the global table of contents style, the title style, styles for each entry level and the page numbers style. The code below sets the Style property with a style similar to the default style used by converter.

C#
            htmlToPdfConverter.PdfDocumentOptions.TableOfContents.Style = """
#html-to-pdf-toc {
    font-family: Arial, sans-serif;
    margin: 0px;
    padding: 10px;
    box-sizing: border-box;
    background-color: #FFFFFF;
    width: 100%;
    position: relative;
    display : block;
    z-index: 100;
    opacity: 1;

    /* uncomment the lines below to force PDF page breaks before and after inline TOC */
    /*page-break-after : always;
    page-break-before : always;*/

    /* uncomment the line below for RTL table of contents*/
    /*direction : rtl;*/
}

/* TOC title style */
#html-to-pdf-toc .toc-title {
    font-size: 22px;
    font-weight: bold;
    margin-bottom: 15px;
    color: #444444;
    text-align: center;
}

/* Common style for all TOC entries text */
#html-to-pdf-toc li .toc-link {
    order: 1;
    text-decoration: none;
    color: inherit;
    padding: 0px;
    margin: 0px;
}

/* Level specific style for TOC entries text */

#html-to-pdf-toc li .toc-link-level-1 {
    font-weight: bold;
    font-size: 18px;
    color: #111111;
}

#html-to-pdf-toc li .toc-link-level-2 {
    font-weight: bold;
    font-size: 16px;
    color: #222222;
}

#html-to-pdf-toc li .toc-link-level-3 {
    font-weight: bold;
    font-size: 14px;
    color: #333333;
}

#html-to-pdf-toc li .toc-link-level-4 {
    font-weight: normal;
    font-size: 12px;
    color: #333333;
}

#html-to-pdf-toc li .toc-link-level-5 {
    font-weight: normal;
    font-size: 11px;
    color: #333333;
    font-style: italic;
}

#html-to-pdf-toc li .toc-link-level-6 {
    font-weight: normal;
    font-size: 10px;
    color: #333333;
    font-style: italic;
}

/* Common style for all page numbers */
#html-to-pdf-toc li .page-link {
    order: 3;
    font-family: Arial, sans-serif;
    font-size: 16px;
    font-weight: bold;
    font-style: normal;
    text-decoration: none;
    padding: 0px;
    margin: 0px;
    color: #111111;
}

/* Style for space between entry text and page number */ 
#html-to-pdf-toc li::after {
    flex-grow: 1;
    order: 2;
    content: "";
    height: 1em;
    /* comment the line below to remove the dotted line from TOC */
    border-bottom: 2px dotted lightgray;
}

#html-to-pdf-toc ul {
    list-style-type: none; 
    padding: 0px;
}

/* Common style for all TOC entries*/
#html-to-pdf-toc li {
    font-size: 14px;
    display: flex;
    padding: 0px;
    margin: 0px;
}

/* Level specific style for TOC entries */

#html-to-pdf-toc ul li.level-1 {    
    margin-bottom: 10px;
    margin-inline-start: 0px;    
}

#html-to-pdf-toc ul li.level-2 {
    margin-bottom: 8px;
    margin-inline-start: 20px;
}

#html-to-pdf-toc ul li.level-3 {
    margin-bottom: 6px;
    margin-inline-start: 40px;
}

#html-to-pdf-toc ul li.level-4 {
    margin-bottom: 4px;
    margin-inline-start: 60px;
}

#html-to-pdf-toc ul li.level-5 {
    margin-bottom: 2px;
    margin-inline-start: 80px;
}

#html-to-pdf-toc ul li.level-6 {
    margin-bottom: 2px;
    margin-inline-start: 100px;
}
""";

Global Table Style

The global table style is given by the '#html-to-pdf-toc' selector. You can configure the padding, the background color, the font family, text direction and other properties. For inline table of contents, you can force page breaks in PDF before and after the table of contents by uncommenting the 'page-break-after: always' and 'page-break-before: always' styles

Title Style

The table of contents style is controlled by the '#html-to-pdf-toc .toc-title' selector.

Table Entries Style

The '#html-to-pdf-toc li' selector controls the common style of all entries in the table of contents. The style of the entries for various levels can be controlled by using the '#html-to-pdf-toc ul li.level-n' selectors, where n can be an integer from 1 to 6.

The common styles for all text in the table of contents are controlled by the '#html-to-pdf-toc li .toc-link' selector. The text style of the entries for various levels can be controlled by using the '#html-to-pdf-toc li .toc-link-level-n' selectors, where n can be an integer from 1 to 6.

The common style for all page numbers is controlled by the '#html-to-pdf-toc li .page-link' selector. To remove the dotted line between the table entry text and the page number, comment out the 'border-bottom: 2px dotted lightgray' style in '#html-to-pdf-toc li::after' selector.

Code Sample - Auto Create Table of Contents

C#
using System;
using System.IO;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Winnovative_Next_AspNetDemo.Models;
using Winnovative_Next_AspNetDemo.Models.HTML_to_PDF;

// Use Winnovative Namespace
using Winnovative.Pdf.Next;

namespace Winnovative_Next_AspNetDemo.Controllers.HTML_to_PDF
{
    public class Table_of_ContentsController : Controller
    {
        private readonly IWebHostEnvironment m_hostingEnvironment;
        public Table_of_ContentsController(IWebHostEnvironment hostingEnvironment)
        {
            m_hostingEnvironment = hostingEnvironment;
        }

        public IActionResult Index()
        {
            var model = SetViewModel();

            return View(model);
        }

        [HttpPost]
        public ActionResult ConvertHtmlToPdf(Table_of_Contents_ViewModel model)
        {
            if (!ModelState.IsValid)
            {
                var errorMessage = ModelStateHelper.GetModelErrors(ModelState);
                throw new ValidationException(errorMessage);
            }

            // Set license key received after purchase to use the converter in licensed mode
            // Leave it not set to use the library in demo mode
            Licensing.LicenseKey = "3FJDU0ZDU0NTQkddQ1NAQl1CQV1KSkpKU0M=";

            // Create a HTML to PDF converter object with default settings
            HtmlToPdfConverter htmlToPdfConverter = new HtmlToPdfConverter();

            // Enable or disable the automatic creation of a table of contents in the PDF document based on H1 to H6 HTML tags
            htmlToPdfConverter.PdfDocumentOptions.GenerateTableOfContents = model.GenerateToc;

            // Optionally set the table of contents to display inline within the 'html-to-pdf-toc' DIV
            // By default, the table of contents is created at the start of the generated PDF document
            htmlToPdfConverter.PdfDocumentOptions.TableOfContents.CreateInline = model.InlineToc;

            // Optionally enable the usage of browser capabilities when creating the table of contents
            // This option is applicable only if the table of contents is not inline and it is false by default
            htmlToPdfConverter.PdfDocumentOptions.TableOfContents.UseBrowserMode = model.UseBrowserMode;

            // Set if the page numbers from table of contents are displyed
            htmlToPdfConverter.PdfDocumentOptions.TableOfContents.ShowPageNumbers = model.ShowPageNumbers;

            // Set if the TOC pages are included in the page numbers displayed in the TOC.
            // This option is not applicable to the inline table of contents
            htmlToPdfConverter.PdfDocumentOptions.TableOfContents.CountTocAndStartPages = model.CountTocPages;

            // Set an offset to be applied to all page numbers in the table of contents.
            // It can be useful when merging a PDF with a table of contents with other PDF documents
            htmlToPdfConverter.PdfDocumentOptions.TableOfContents.PageNumbersOffset = model.PageNumbersOffset;

            // Set the table of contents title
            htmlToPdfConverter.PdfDocumentOptions.TableOfContents.Title = model.TocTitle;

            // Optionally set a custom CSS style for the table of contents
            // A default style is applied by the library if this property is not set
            htmlToPdfConverter.PdfDocumentOptions.TableOfContents.Style = model.TocStyleTextBox;

            // Set HTML Viewer width in pixels which is the equivalent in converter of the browser window width
            htmlToPdfConverter.HtmlViewerWidth = model.HtmlViewerWidth;

            // Set the initial HTML viewer height in pixels
            if (model.HtmlViewerHeight.HasValue)
                htmlToPdfConverter.HtmlViewerHeight = model.HtmlViewerHeight.Value;

            // Set the HTML content zoom percentage similar to zoom level in a browser
            htmlToPdfConverter.HtmlViewerZoom = model.HtmlViewerZoom;

            // Automatically resize the PDF page width to match the HtmlViewerWidth property
            // The default value is true
            htmlToPdfConverter.PdfDocumentOptions.AutoResizePdfPageWidth = model.AutoResizePdfPageWidth;

            // Set the PDF page size, which can be a predefined size like A4 or a custom size in points
            // The default is A4
            // Important Note: The PDF page width is automatically determined from the HTML viewer width
            // when the AutoResizePdfPageWidth property is true
            htmlToPdfConverter.PdfDocumentOptions.PdfPageSize = SelectedPdfPageSize(model.PdfPageSize);

            // Set the PDF page orientation to Portrait or Landscape. The default is Portrait
            htmlToPdfConverter.PdfDocumentOptions.PdfPageOrientation = SelectedPdfPageOrientation(model.PdfPageOrientation);

            // Set the PDF page margins in points. The default is 0
            htmlToPdfConverter.PdfDocumentOptions.LeftMargin = model.LeftMargin;
            htmlToPdfConverter.PdfDocumentOptions.RightMargin = model.RightMargin;
            htmlToPdfConverter.PdfDocumentOptions.TopMargin = model.TopMargin;
            htmlToPdfConverter.PdfDocumentOptions.BottomMargin = model.BottomMargin;

            // Set the maximum time in seconds to wait for HTML page to be loaded 
            // Leave it not set for a default 120 seconds maximum wait time
            htmlToPdfConverter.NavigationTimeout = model.NavigationTimeout;

            // Set an additional delay in seconds to wait for JavaScript or AJAX calls after page load completed
            // Set this property to 0 if you don't need to wait for such asynchronous operations to finish
            if (model.ConversionDelay.HasValue)
                htmlToPdfConverter.ConversionDelay = model.ConversionDelay.Value;

            // The buffer to receive the generated PDF document
            byte[] outPdfBuffer = null;

            if (model.HtmlPageSource == "Url")
            {
                string url = model.Url;

                // Convert the HTML page given by an URL to a PDF document in a memory buffer
                outPdfBuffer = htmlToPdfConverter.ConvertUrl(url);
            }
            else
            {
                string htmlString = model.HtmlStringTextBox;
                string baseUrl = model.BaseUrlTextBox;

                // Convert a HTML string with a base URL to a PDF document in a memory buffer
                outPdfBuffer = htmlToPdfConverter.ConvertHtml(htmlString, baseUrl);
            }

            // Send the PDF file to browser
            FileResult fileResult = new FileContentResult(outPdfBuffer, "application/pdf");
            fileResult.FileDownloadName = "Table_of_Contents.pdf";

            return fileResult;
        }

        private PdfPageSize SelectedPdfPageSize(string selectedValue)
        {
            switch (selectedValue)
            {
                case "A0":
                    return PdfPageSize.A0;
                case "A1":
                    return PdfPageSize.A1;
                case "A10":
                    return PdfPageSize.A10;
                case "A2":
                    return PdfPageSize.A2;
                case "A3":
                    return PdfPageSize.A3;
                case "A4":
                    return PdfPageSize.A4;
                case "A5":
                    return PdfPageSize.A5;
                case "A6":
                    return PdfPageSize.A6;
                case "A7":
                    return PdfPageSize.A7;
                case "A8":
                    return PdfPageSize.A8;
                case "A9":
                    return PdfPageSize.A9;
                case "ArchA":
                    return PdfPageSize.ArchA;
                case "ArchB":
                    return PdfPageSize.ArchB;
                case "ArchC":
                    return PdfPageSize.ArchC;
                case "ArchD":
                    return PdfPageSize.ArchD;
                case "ArchE":
                    return PdfPageSize.ArchE;
                case "B0":
                    return PdfPageSize.B0;
                case "B1":
                    return PdfPageSize.B1;
                case "B2":
                    return PdfPageSize.B2;
                case "B3":
                    return PdfPageSize.B3;
                case "B4":
                    return PdfPageSize.B4;
                case "B5":
                    return PdfPageSize.B5;
                case "Flsa":
                    return PdfPageSize.Flsa;
                case "HalfLetter":
                    return PdfPageSize.HalfLetter;
                case "Ledger":
                    return PdfPageSize.Ledger;
                case "Legal":
                    return PdfPageSize.Legal;
                case "Letter":
                    return PdfPageSize.Letter;
                case "Letter11x17":
                    return PdfPageSize.Letter11x17;
                case "Note":
                    return PdfPageSize.Note;
                default:
                    return PdfPageSize.A4;
            }
        }

        private PdfPageOrientation SelectedPdfPageOrientation(string selectedValue)
        {
            return selectedValue == "Portrait" ? PdfPageOrientation.Portrait : PdfPageOrientation.Landscape;
        }

        private Table_of_Contents_ViewModel SetViewModel()
        {
            var model = new Table_of_Contents_ViewModel();

            var contentRootPath = Path.Combine(m_hostingEnvironment.ContentRootPath, "wwwroot");

            HttpRequest request = ControllerContext.HttpContext.Request;
            UriBuilder uriBuilder = new UriBuilder();
            uriBuilder.Scheme = request.Scheme;
            uriBuilder.Host = request.Host.Host;
            if (request.Host.Port != null)
                uriBuilder.Port = (int)request.Host.Port;
            uriBuilder.Path = request.PathBase.ToString() + request.Path.ToString();
            uriBuilder.Query = request.QueryString.ToString();

            string currentPageUrl = uriBuilder.Uri.AbsoluteUri;
            string rootUrl = currentPageUrl.Substring(0, currentPageUrl.Length - "Table_of_Contents".Length);

            model.HtmlStringTextBox = System.IO.File.ReadAllText(Path.Combine(contentRootPath, "DemoAppFiles/Input/HTML_Files/Table_of_Contents.html"));
            model.TocStyleTextBox = System.IO.File.ReadAllText(Path.Combine(contentRootPath, "DemoAppFiles/Input/HTML_Files/TOC_Style.css"));
            model.BaseUrlTextBox = rootUrl + "DemoAppFiles/Input/HTML_Files/";

            return model;
        }
    }
}

See Also