Tables of Contents
Long contracts, reports and manuals open with a list of their headings, the page each one starts on and a link to it. TX Text Control generates such a table of contents from the structure levels of the paragraphs. In code, you mark the headings, insert a table of contents, update it after a merge has added headings and pages, change the look of its entries and read or remove the tables of contents of documents you load.
The dialog and the JavaScript API that users work with in the browser are described in Tables of Contents in the Document Editor section. Paragraph styles, which carry the structure levels, are introduced in Paragraphs.
How Tables of Contents Work
A table of contents lists the paragraphs whose structure level lies in a range you choose:
- Structure levels. ParagraphFormat.StructureLevel is a number from 0 to 10. 0, the default, is body text; 1 is a top-level heading, 2 a heading below it and so on. A level can be set for a single paragraph, but it's usually part of a paragraph style, so that every heading with the style belongs to the level.
- Generation. When a table of contents is inserted, TX Text Control searches the document for the paragraphs in the level range and writes their text, with or without page numbers, at the input position.
- Styles of the entries. For the title and each level, a paragraph style is generated:
TOC_Title,TOC_Level1,TOC_Level2and so on. Changing these styles changes the look of the entries. - Links. Each entry can be a link to its heading: a
DocumentLinkthat points to aDocumentTargetgenerated at the heading. - Several tables. A document can contain several tables of contents, for example one per chapter, but they can't be nested.
- Formats. Tables of contents are imported from and exported to DOCX, RTF and DOC, with DOC only on the Classic engine, and kept in the TX Text Control format. In PDF documents, the entries are links, and the logical structure of the PDF marks the table of contents.
Insert a Table of Contents
The following program writes a short agreement, defines two heading styles with structure levels, applies them and inserts a table of contents below the title. It saves the result as PDF and DOCX:
using TXTextControl;
using (ServerTextControl tx = new ServerTextControl())
{
tx.Create();
tx.Text = "Service Agreement\r\n" +
"\r\n" +
"Scope\r\n" +
"The provider delivers the software.\r\n" +
"Support Services\r\n" +
"Support is available on business days.\r\n" +
"Payment Terms\r\n" +
"Invoices are payable within 30 days.";
// Heading styles that carry the structure levels
ParagraphStyle heading = new ParagraphStyle("Contract Heading");
heading.FontSize = 320; // 16 pt
heading.Bold = true;
heading.ParagraphFormat.StructureLevel = 1;
heading.ParagraphFormat.PageBreakBefore = true;
heading.ParagraphFormat.KeepWithNext = true;
tx.ParagraphStyles.Add(heading);
ParagraphStyle subheading = new ParagraphStyle("Contract Subheading", "Contract Heading");
subheading.FontSize = 280; // 14 pt
subheading.ParagraphFormat.StructureLevel = 2;
subheading.ParagraphFormat.PageBreakBefore = false;
tx.ParagraphStyles.Add(subheading);
tx.Paragraphs[3].FormattingStyle = "Contract Heading";
tx.Paragraphs[5].FormattingStyle = "Contract Subheading";
tx.Paragraphs[7].FormattingStyle = "Contract Heading";
// The input position in the empty second paragraph (Paragraph.Start is one-based)
tx.Select(tx.Paragraphs[2].Start - 1, 0);
TableOfContents contents = new TableOfContents(1, 2)
{
Title = "Contents",
Name = "contents",
ID = 1,
HasPageNumbers = true,
HasRightAlignedPageNumbers = true,
HasLinks = true
};
TableOfContentsCollection.AddResult result = tx.TablesOfContents.Add(contents);
if (result != TableOfContentsCollection.AddResult.Successful)
{
Console.WriteLine($"No table of contents was inserted: {result}");
return;
}
Console.WriteLine(contents.Text);
tx.Save("agreement.pdf", StreamType.AdobePDF);
tx.Save("agreement.docx", StreamType.WordprocessingML);
}
The code works as follows:
PageBreakBeforestarts each top-level heading on a new page, so the entries get different page numbers. The subheading style is based on the heading style and turns the page break off again.- The TableOfContents constructor takes the lowest and the highest structure level to include, each from 1 to 10.
- Title is the heading of the table of contents; an empty string means none. Name and the one-based ID identify it for your code.
- HasPageNumbers adds the page numbers. HasRightAlignedPageNumbers aligns them at the right border with a tab of the type
TabType.RightBorderTab. HasLinks turns every entry into a link to its heading. - TableOfContentsCollection.Add returns an AddResult:
ContentNotFoundwhen no paragraph has a level in the range,Errorat a position where no table of contents can be inserted, such as inside a field or another table of contents, and otherwiseSuccessful. - Text returns the generated text of the table of contents.
On the first page of agreement.pdf, below the title, the table of contents lists the two headings of level 1 and, indented, the subheading of level 2, each with a dotted leader and its page number at the right border:

Update a Table of Contents After Changes
A table of contents doesn't follow later changes to the document by itself. After you add or remove headings, or after a merge has repeated blocks with headings and added pages, call Update. The following program merges JSON data into a template that contains a table of contents and updates it before saving the PDF:
using TXTextControl;
using TXTextControl.DocumentServer;
using (ServerTextControl tx = new ServerTextControl())
{
tx.Create();
tx.Load("Documents/report-template.tx", StreamType.InternalUnicodeFormat);
using (MailMerge mailMerge = new MailMerge())
{
mailMerge.TextComponent = tx;
mailMerge.MergeJsonData(File.ReadAllText("Documents/data.json"));
}
// Merged blocks added headings and pages: regenerate the entries and page numbers
foreach (TableOfContents toc in tx.TablesOfContents)
{
TableOfContentsCollection.AddResult result = toc.Update();
Console.WriteLine($"Table of contents {toc.Number}: {result}");
}
tx.Save("report.pdf", StreamType.AdobePDF);
}
Update regenerates the entries and page numbers and returns the same AddResult values as Add. The template is a document whose merge block contains a heading paragraph with a structure level, as in the blog article Combining MailMerge and Table of Contents. Merge blocks and the JSON data that fills them are described in Merge Blocks.
Change, Read and Remove Tables of Contents
The following program changes the style of the top-level entries, lists the tables of contents of a document with the links they generated and removes the table of contents named "contents":
using TXTextControl;
using (ServerTextControl tx = new ServerTextControl())
{
tx.Create();
tx.Load("agreement.docx", StreamType.WordprocessingML);
// The style generated for the entries of level 1
ParagraphStyle level1 = tx.ParagraphStyles.GetItem("TOC_Level1");
if (level1 != null)
{
level1.Bold = true;
level1.ParagraphFormat.TopDistance = 120;
level1.Apply();
}
foreach (TableOfContents toc in tx.TablesOfContents)
{
toc.Update();
Console.WriteLine($"{toc.Number} \"{toc.Name}\": levels {toc.MinimumStructureLevel} to {toc.MaximumStructureLevel}, " +
$"{toc.Length} characters from position {toc.Start}");
}
// The links of the entries
foreach (DocumentLink link in tx.DocumentLinks)
{
if (link.AutoGenerationType == AutoGenerationType.TableOfContents)
{
Console.WriteLine($"Entry \"{link.Text}\" links to {link.DocumentTarget?.TargetName}");
}
}
// Remove the table of contents with its text and the targets of its links
TableOfContents contents = tx.TablesOfContents.GetItem("contents");
if (contents != null)
{
tx.TablesOfContents.Remove(contents);
}
tx.Save("agreement-without-contents.docx", StreamType.WordprocessingML);
}
The code works as follows:
ParagraphStyles.GetItemreturns the generated style by its name, and Apply applies the changed style to the document.- Number counts the tables of contents in the text flow from 1. Start is the one-based position of the first character and Length the number of characters.
- DocumentLink.AutoGenerationType is
TableOfContentsfor the links that a table of contents generated andNonefor all others. DocumentTarget.AutoGenerationType does the same for targets, whose generated names begin withTOC_. - GetItem takes a name or an ID and returns
nullif none matches. Remove deletes the table of contents with all its text and all document targets its links point to.
Tables of Contents in Other Text Parts
Headers, footers and text frames have their own collection, IFormattedText.TablesOfContents. To update every table of contents of a document, loop through ServerTextControl.TextParts. The following program also lists the paragraphs that have a structure level, which shows which headings a table of contents of a loaded document can include:
using TXTextControl;
using (ServerTextControl tx = new ServerTextControl())
{
tx.Create();
tx.Load("Documents/sample.docx", StreamType.WordprocessingML);
foreach (Paragraph paragraph in tx.Paragraphs)
{
if (paragraph.Format.StructureLevel > 0)
{
Console.WriteLine($"Level {paragraph.Format.StructureLevel} ({paragraph.FormattingStyle}): {paragraph.Text}");
}
}
foreach (IFormattedText textPart in tx.TextParts)
{
foreach (TableOfContents toc in textPart.TablesOfContents)
{
toc.Update();
}
}
tx.Save("sample-updated.docx", StreamType.WordprocessingML);
}
To save a document without the table of contents data, set SaveSettings.OmittedContent to OmittedContent.TablesOfContents: the text and formatting of the entries are saved, but not the table of contents itself. TableOfContentsCreated and TableOfContentsDeleted occur when a table of contents is created or deleted, as Events describes.
Next Steps
- Paragraphs: define the heading styles.
- Merge Blocks: repeat sections with headings for each record of your data.
- Fields: add links and bookmarks of your own.
- Tables of Contents in the Document Editor: let users insert and update tables of contents.