How to create a document with an automatic table of contents using the PDFFlow library

As a junior programmer, I have little experience with C# and in programming in general. Having joined the PDFFlow team just recently, I was quite surprised with how simple it is to use this tool to generate PDF documents of significant complexity. As a member of the team, I might be biased, of course, but in the article below I’d like to share how easy it is to create a complex PDF document with an automatic table of contents. This comes quite handy when you are working with larger documents, such as long reports or articles, and even books.

In this article, I will show how to create a complex document using the PDFFlow library. We’ll go over the library capabilities and follow a step-by-step process to create a multipage document with an automatic table of contents.

Why use the PDFFlow library?

The PDFFlow library is a .NET C# library that allows developers to generate PDF documents with custom precise designs and layouting fast and efficiently. Some of the special features of the library are:

  • Repeating areas

Along with the listed features that let you create complicated documents, the library provides a wide range of tools to make elementary documents:

  • Tables of all kinds

But enough of talking about the features of the library, let’s see them in action by building a complex real-world document with a table of contents.

Creating a multipage document

When creating a multipage document, we often need to generate a table of contents (TOC), and it’s not always trivial. Fortunately, the PDFFlow library takes care of all the heavy lifting and allows us to generate a TOC easily and with very little code. That is one of the key benefits of using the library — simply put, it allows you to write less code to achieve the same results.

To illustrate this, we will use Tutorial C, which is an example of а programming tutorial book. It is a multi-page text document with mixed content. The document includes several sections and a repeating footer with automatic page numeration.

The complete example source is available in the open-source PDFFlow.Examples repo along with many more examples, such as airplane ticket, boarding pass, real-estate contracts, agreements, medical forms, lab reports.

To understand how it works, go through the full document code availiable in the repo and explore our library with this article.

Storing data

We store a part of data for our document in a standard JSON file Content/tutorialc_keywords.json. The file contains descriptions of C keywords. Later we will use this data to build a table.

[
{
"Id": "1",
"Keyword": "auto",
"Description": "The auto keyword declares automatic variables"
},
{
"Id": "2",
"Keyword": "break",
"Description": "The break statement makes program jump out of the innermost enclosing loop (while, do, for or switch statements) explicitly."
},
]
//etc.

Output file

The code we write builds the document and creates the file TutorialC.pdf in the output bin/(debug|release)/netcoreapp2.1 folder.

Creating a project

1. Create a new console application.

1.1. Run Visual Studio.

1.2. Go to File -> Create -> Console Application (.Net Core).

2. Modify the class Program.

2.1. In the function Main() set the path to the output PDF file, call the Run() method to continue the generation, and call the Build() method for building the document into the output PDF:

namespace TutorialC
{
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine("Gehtsoft.PDFFlow.Demos");
Console.WriteLine("----------------------");
Console.WriteLine("C TUTORIAL");
Console.WriteLine("----------------------");
GenerateExample();
Console.WriteLine("");
Console.WriteLine("Press any key for exit...");
Console.ReadKey();
}
private static void GenerateExample() => TutorialCRunner.Run().Build("TutorialC.pdf");
}
}

Sources structure

As our document consists of a few logical parts, we use the following Create methods to generate each of the parts:

  • AddBookCoverSection() — to create the cover section for the tutorial.

Global definitions

Then we define the necessary paths, the required data from the JSON file, and the document font.

ProjectDir = Directory.GetCurrentDirectory();
KeywordsJsonFile = Path.Combine(ProjectDir, "Content", "tutorialc_keywords.json");
KeywordsJsonContent = File.ReadAllText(KeywordsJsonFile);
KeywordsList = JsonConvert.DeserializeObject<List<Keywords>>(KeywordsJsonContent);
ImageUrl = Path.Combine(ProjectDir, "images", "TutorialC", "customers");
DocumentFont = Fonts.Times(12f);
//etc.

Code optimization

To add several footers times with different texts, we added a new method. Defining methods for common operations is a good optimization practice to achieve better code size.

    internal static void AddFooters(SectionBuilder section, string text)
{
var imageUrlLogo = Path.Combine(ProjectDir, "images", "DocumentExample", "LearnCLogo30_30.jpg");
section.AddFooterToBothPages(40)
.AddLine()
.SetColor(ColorBackground).SetStroke(Stroke.Solid).SetWidth(1f)
.ToArea()
.AddParagraph()
.SetMargins(0, 5, 0, 0).SetFont(DocumentFontBoldOrange)
.AddInlineImageToParagraph(imageUrlLogo,
new XSize(15, 15), ScalingMode.UserDefined)
.AddTextToParagraph(text)
.AddUrl("http://www.gehtsoftusa.com/", "GEHTSOFT USA LLC.")
.SetFont(DocumentFontBoldOrange)
.SetUnderline(ColorBackground);
section.AddFooterToBothPages(15f)
.AddParagraph()
.AddPageNumberToParagraph(" Page #", 1, SmallFont)
.SetAlignment(HorizontalAlignment.Right);
}

Cover section

As a cover we use an image.

var imageUrl = Path.Combine(ProjectDir, "images", "TutorialC", "DocumentExample", "LearnCCoverPage.jpg");

Then we define a hidden font that will be necessary to include this section in the TOC:

var fontHidden = Fonts.Courier(.01f).SetColor(Color.White);

We use this font in the paragraph that we add to this section and that we mark as a TOC item. As a result, we have a paragraph that is not visible on the cover but is included in the TOC, which allows navigating from the TOC to the cover. After that, we add the cover image to the section.

builder
.AddSection()
.SetSize(PaperSize.A4)
.SetOrientation(PageOrientation.Portrait)
.AddParagraph("LEARN C PROGRAMMING")
.SetFont(fontHidden)
.SetBackColor(Color.White).SetOutline().SetOutline(0)
.ToSection()
.AddImage(imageUrl).SetScale(ScalingMode.Stretch);

The resulting cover section:

First section — Chapter#1 Environment setup

First, we define the necessary paths to the images: imageUrlLogo, imageUrl, imageExclamationMark, imageQuestionMark.

Then, we create a title that will be included in the TOC.

    .AddParagraph()
.SetFont(DocumentFontTitleWhite)
.SetAlignment(HorizontalAlignment.Center)
.SetBackColor(ColorBackground)
.SetBorder(Stroke.Solid, Color.Gray, 3)
.SetOutline(1)
.AddText("1. Environment setup")

After that, we add paragraphs and content to the section. We add inline images to the paragraphs to draw the reader’s attention to the important information.

    .AddInlineImageToParagraph(imageExclamationMark, new XSize(15, 15), ScalingMode.UserDefined)

If you need to skip line breaks in the text, use the ignoreNewLineSymbol parameter:

    .AddText(@" to set up your own environment to start learning C 
programming language. Reason is very simple, we already have set up C
Programming environment online, so that you can compile and execute all the
available examples online at the same time when you are doing your theory
work. This gives you confidence in what you are reading and to check the result
with different options. Feel free to modify any example and execute it online.",
ignoreNewLineSymbol: true)

To add a reference to additional resources, add a link:

    .AddUrlToParagraph(“http://www.compileonline.com/")

To accurately convey the text of the program, use @ strings and pass the strings as an array.

    .AddText(
new[]
{
"#include <stdio.h>",
" ",
"int main()",
"{",
@" /* my first program in C */",
@" printf(""Hello, World! \n"");",
@" return 0;",
"}"
})

A part of Chapter#1. Environment setup:

Second section — Chapter#2 Basic syntax

As in the previous section, we start with defining the necessary paths to the images again: imageUrlLogo, imageExclamationMark.

Then we create a title that will be included in the TOC.

    .AddParagraph("2. Basic syntax").SetFont(DocumentFontTitleWhite)
.SetAlignment(HorizontalAlignment.Center)
.SetBackColor(ColorBackground)
.SetBorder(Stroke.Solid, Color.Gray, 3)
.SetOutline(1)

After that, we add the other paragraphs and content to the section.

To accurately convey the text of the bitwise operators, use strings as an array.

    .AddText(
new[]
{
"A = 0011 1100", "B = 0000 1101", "A&B = 0000 1100", "A|B = 0011 1101", "A^B = 0011 0001",
"~A = 1100 0011"
})

We add a footer with the title of the chapter to this section.

    AddFooters(s, “ C tutorial. Chapter #2. Basic syntax. “);

A part of Chapter#2. Basic syntax:

Third section — Chapter#3. Decision making

Again, we define the necessary paths to the images: imageUrlLogo, imageUrl.

Then we create a title that will be included in the TOC.

    .AddParagraph()
.AddTextToParagraph("3. Decision making")
.SetFont(DocumentFontTitleWhite)
.SetAlignment(HorizontalAlignment.Center)
.SetBackColor(ColorBackground)
.SetBorder(Stroke.Solid, Color.Gray, 3)
.SetOutline(1)

After that, we add the other paragraphs and content to the section.

If you need to skip line breaks in the text, use the ignoreNewLineSymbol parameter:

    .AddTextToParagraph(@"Decision-making structures require that the programmer specifies one or more 
conditions to be evaluated or tested by the program, along with a statement or
statements to be executed if the condition is determined to be true, and
optionally, other statements to be executed if the condition is determined to be
false.", ignoreNewLineSymbol: true)

The fluent style allows us to switch from the paragraph to the section context (ToSection). After that, we add an image.

.ToSection()
.AddImage(imageUrl)
.SetScale(ScalingMode.OriginalSize)
.SetAlignment(HorizontalAlignment.Center)
.SetBorder(Stroke.Dashed, ColorBackground, 3)

We add a footer with the title of the chapter to this section.

    AddFooters(s, “ C tutorial. Chapter #3. Decision making. “);

This section builds Chapter #3. Decision making:

Table of contents

We add a title for the TOC page.

    .AddParagraph()
.AddTextToParagraph("CONTENTS")
.SetFont(DocumentFontTitleWhite)
.SetAlignment(HorizontalAlignment.Center)
.SetBackColor(ColorBackground)
.SetBorder(Stroke.Solid, Color.Gray, 3)

To add a TOC, just call the AddOutline() method:

    builder.AddOutline()
.SetSpacingUnderline(Stroke.Dashed, ColorBackground)
.SetLevelLeftIndent(10f)
.SetFont(DocumentFontBoldOrange);

We add a footer with the title of the page to this section.

    AddFooters(s, “ C tutorial. Contents. “); 

In the resulting document, you will get the following TOC:

We’ve just explored how you can create a complex document with table a of contents using the PDFFlow library. This should be sufficient enough for you to go on your own and create your PDF documents. I hope that now you will be able to create your first document with PDFFlow.

Let me know in the comments if you have any questions or some specific usage you would like me to cover, or just head over to the library’s extensive open-source real-world examples to see more complex documents and detailed articles on how they were created.

To learn more about PDFFlow, follow these links:

PDFFlow main page
PDFFlow repo

Junior programmer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store