Pull Topic & Page Number from within Document

I have a request to pull/get page numbers for the topics in our report. I understand the TOC, and that is not the problem. The struggle is that within the report, we have to create references to a topic from the TOC that links to the client section of the report.

So, in other words, if the client is on page 33 and sees the link/content for another reference on page 64 with TITLE and Page Number and decides to click on the link/content, it will take the client to that section of the report.

section.Blocks.Add(New TableOfEntries(document, FieldType.XE.TOC And ā€œNew Horizonsā€))

Hi Raymond,

If I understood you correctly, you want to create something like the content that you have inside the TOC somewhere in the documentā€™s body.

Note that TOC entries are Paragraph elements with a Hyperlink containing text, tab, and PageRef field. So basically, you would need to recreate that or create a copy of the Paragraph from the TOC entries collection and use that instead.

I hope this helps. If you need more, please send us your Word file and specify what you would like to do with it and Iā€™ll help you with that.

Regards,
Mario

Hi Mario,

I uploaded one of my home inspection reports. Link To Dropbox

On page 11 is the start of the table of contents.

My summary starts on page 19. Under Major Concerns, youā€™ll see ā€œGutters.ā€

Under the comments, I need to insert a page number. In this example, I would need page 82 as a link to page 82.

I was thinking of automatically linking each section with its page number so I wouldnā€™t have to do it manually.

I was hoping that all I would need was a string ā€œGutters: Exterior - Aluminum Guttersā€ and it would generate a page number.

Regards,
Ray

Hi Ray,

Try this:

var document = DocumentModel.Load("Inspection Report - 50286170.docx");

// Get TOC entry.
var toc = (TableOfEntries)document.GetChildElements(true, ElementType.TableOfEntries).First();
var tocEntry = (Paragraph)toc.Entries.First(e => e.Content.ToString().Contains("Gutters: Exterior - Aluminum Gutters"));

/* This TOC entry is a paragraph that contains the following elements:
 *
 *  Paragraph:
 *   |_ Hyperlink:
 *       |_ Run ("Gutters: Exterior - Aluminum Gutters")
 *       |_ SpecialCharacter ("\t")
 *       |_ PageRef ("80")
 */

// Create "Page X" content by cloning the above TOC entry.
// We'll edit the text of the first element and remove the tab.
var cloneEntry = tocEntry.Clone(true);
var cloneHyperlink = (Hyperlink)cloneEntry.Inlines[0];
((Run)cloneHyperlink.DisplayInlines[0]).Text = "Page ";
cloneHyperlink.DisplayInlines.RemoveAt(1);

// Create comment.
var comment = new Comment(document);
comment.Blocks.Add(cloneEntry);

// Insert comment.
var content = document.Content.Find("Area: Gutters").First();
content.Start.InsertRange(new CommentStart(document, comment).Content);
content.End.InsertRange(new CommentEnd(document, comment).Content);

document.Save("output.docx");

I hope this helps.

Regards,
Mario

Thank you, Mario. Your suggestion was helpful.

Thank you, Nehakumari. Your suggestion of the TableOfEntries as a clickable link fits our scenario a bit better, given the algorithm we are forming in our code.

Hi Nehakumari,

Iā€™ve tried the TableOfEnties about a hundred different ways with no success based on the topic string. The closest that Iā€™ve gotten was utilizing the .XE function and calling the topic string name.

Would you have any other suggestions?

Hi Mario,

I converted your code to VB and Iā€™m getting some errors. I was wondering if you could help me out again.

  1. "Local variable ā€˜tocā€™ hides a variable in the enclosing block.
  2. Lambda parameter ā€˜eā€™ hides a variable in an enclosing block.
  3. ā€˜Hyperlinkā€™ is a class type and cannot be used as an expression.

Hi Raymond,

Try this:

Dim document = DocumentModel.Load("Inspection Report - 50286170.docx")

' Get TOC entry.
Dim toc = CType(document.GetChildElements(True, ElementType.TableOfEntries).First(), TableOfEntries)
Dim tocEntry = CType(toc.Entries.First(Function(e) e.Content.ToString().Contains("Gutters: Exterior - Aluminum Gutters")), Paragraph)

' This TOC entry Is a paragraph that contains the following elements:
'
'  Paragraph:
'   |_ Hyperlink
'       |_ Run ("Gutters: Exterior - Aluminum Gutters")
'       |_ SpecialCharacter ("\t")
'       |_ PageRef ("80")

' Create "Page X" content by cloning the above TOC entry.
' We'll edit the text of the first element and remove the tab.
Dim cloneEntry = tocEntry.Clone(True)
Dim cloneHyperlink = CType(cloneEntry.Inlines(0), Hyperlink)
Dim cloneRun = CType(cloneHyperlink.DisplayInlines(0), Run)
cloneRun.Text = "Page "
cloneHyperlink.DisplayInlines.RemoveAt(1)

' Create comment.
Dim comment = New Comment(document)
comment.Blocks.Add(cloneEntry)

' Insert comment.
Dim content = document.Content.Find("Area: Gutters").First()
content.Start.InsertRange(New CommentStart(document, comment).Content)
content.End.InsertRange(New CommentEnd(document, comment).Content)

document.Save("output.docx")

Regards,
Mario

1 Like

Thank you, Mario,

I appreciate you. You are the backbone of this forum.

Mario, please modify the code to find the ā€˜Commentsā€™ part in the section and append ā€˜(Page ??)ā€™ link to it.

Hi Raymond,

Here is how to add that ā€œPage Xā€ content into a specific paragraph instead of adding it into a Comment element:

Dim document = DocumentModel.Load("Inspection Report - 50286170.docx")

' Get specific TOC entry.
Dim toc = CType(document.GetChildElements(True, ElementType.TableOfEntries).First(), TableOfEntries)
Dim tocEntry = CType(toc.Entries.First(Function(e) e.Content.ToString().Contains("Gutters: Exterior - Aluminum Gutters")), Paragraph)

' Create "Page X" content by cloning the hyperlink from the TOC entry.
Dim cloneHyperlink = CType(tocEntry.Inlines(0).Clone(True), Hyperlink)
Dim cloneRun = CType(cloneHyperlink.DisplayInlines(0), Run)
cloneRun.Text = "Page "
cloneHyperlink.DisplayInlines.RemoveAt(1)

' Add "Page X" content to specific paragraph.
Dim content = document.Content.Find("Comments: Missing/Bent/Pitched").First()
Dim paragraph = CType(content.Start.Parent.Parent, Paragraph)
paragraph.Inlines.Add(New Run(document, " ("))
paragraph.Inlines.Add(cloneHyperlink)
paragraph.Inlines.Add(New Run(document, ")"))

document.Save("output.docx")

Does this work for you?

Regards,
Mario

Hi Mario,

We do not need to load any documents because the document compilation is already in progress.

However, we continue to get two error in the ā€œError Listā€

  1. Variable ā€˜tocā€™ hides a variable in the enclosed block.
  2. Lambda parameter ā€˜eā€™ hides a variable in an enclosing block, a previously defined range variable, or an implicitly declared variable in the query expression.

In other words, if we started a new project and used the source code, we would continue to get the same errors.
We have the latest version of GemBox.Documents along with other components for this project.

Kind regards,
Ray

Ray, can you please send me a new project that uses that source code and gets those mentioned errors?
Unfortunately, Iā€™m unable to reproduce the issue.

Hi Mario,

Sorry for the delay. Didnā€™t see the reply until now. Here is the link to the test project. https://drive.google.com/file/d/1pRWZCn00hnLJj_7NWUi3pHdmm6w5XBQp/view?usp=drive_link

Regards,
Ray

I get Access Denied.
Can you please try again to create a shared link or accept the Access Request I just sent you?

The 2. error occurs because there is a name collision, both of these lines of code use ā€œeā€ as a parameter name:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim tocEntry = CType(toc.Entries.First(Function(e) e.Content.ToString().Contains("Gutters: Exterior - Aluminum Gutters")), Paragraph)

The solution is to rename one of them, for example:

Dim tocEntry = CType(toc.Entries.First(Function(entry) entry.Content.ToString().Contains("Gutters: Exterior - Aluminum Gutters")), Paragraph)

The 1. error I was unable to reproduce it, but it seems to be another name collision.
So, try renaming it:

Dim tocElement = CType(document.GetChildElements(True, ElementType.TableOfEntries).First(), TableOfEntries)
Dim tocEntry = CType(tocElement.Entries.First(Function(entry) entry.Content.ToString().Contains("Gutters: Exterior - Aluminum Gutters")), Paragraph)

Does this solve your issue?

Regards,
Mario

Hi Mario,

The logic makes 100% sense but I continue to get this error indicating that the sequence contains no matching elementā€¦

Dim tocElement = CType(document.GetChildElements(True, ElementType.TableOfEntries).First(), TableOfEntries)
Dim tocEntry = CType(tocElement.Entries.First(Function(entry) entry.Content.ToString().Contains(ā€œWalls: Basement - Wallsā€)), Paragraph)
TOC

Error

And for the ā€œFindā€ the content is pretty much copied.

Dim content = document.Content.Find(ā€œWalls Comments: Major Concern To Foundation Wall.ā€).First()

Comment

I tried less wordy searches to find in the document with the same results.

Regards,
Ray

Hi Ray,

When I search for the ā€œWalls: Basement - Wallsā€ text inside the ā€œInspection Report - 50286170.docxā€ file from Microsoft Word, it shows me that there is no such text:

Are the screenshots perhaps from another file, the one that is not being loaded with a DocumentModel.Load method?

Regards,
Mario

Hi Mario,

I sent you another link to another inspection report via Google Drive.

Kind regards,
Ray