How to add bookmarks and bookmark text in the word template having Headings and Sub headings dynamically?

Hi,

I have a word template which has headings and sub headings in it. I need to insert the bookmark values (text or input box) (which is retrieved from the database) dynamically in the template using Aspose.Words in .Net
The template is having the format only to insert one heading and subheading (e.g., 0 ,0.1, 0.1.1) and i have to insert the headings repeatedly
(0 Heading0
0.1 Subheading0.1
0.1.1 Subheading0.1.1
1 Heading1
1.1 Subheading0.1
1.1.1 Subheading1.1.1 etc.) in the template.

@harshamanil

You can use mail merge with regions to achieve your requirement. We suggest you please read the following article.
How to Execute Mail Merge with Regions

It would be great if you please ZIP and attach your input and expected output documents along with some more detail about your requirement. We will then provide you more information about your query.

SkeletonTemplateInterop.pdf (226.4 KB)
(filename-SkeletonInterop) Frame Template.pdf (125.0 KB)

I have a word template file of name “Frame Template” and i want to generate the skeleton word template “SkeletonTemplateInterop”.
Using microsoft.interop.words the code has been implemented as below:

    Private Sub BuildSkeleton(ByVal framework As String, ByVal WordApp As Microsoft.Office.Interop.Word.Application, ByVal doc As Microsoft.Office.Interop.Word.Document, ByVal sFrame As String, ByVal sView As String, ByVal sOption As String)
        Dim iLevel As Short 'Outline Level of current node
        Dim bLeaf As Boolean 'leaf level
        Dim bControlDisplay As Boolean ' control to display or not
        Dim sBmk As String 'Book mark name string holder
        Dim iSection As Short 'Counter for sections
        Dim i As Short 'Counter for sections
        Dim sTemp As String = "" 'temp var
        'Dim dsTree As New DataSet
        Dim dsTree As DataSet = Nothing

        Try
            Dim gStrARCTemplatesPath, strWordFrame, password As String
            gStrARCTemplatesPath = Settings.GetAppConfigValue("DQAFTemplates")
            strWordFrame = gStrARCTemplatesPath & "Frame\"
            password = Settings.GetAppConfigValue("UnprotectPasswordSDDS")

            iSection = doc.Sections.Count

            'Get the complete DQAF tree information into the dataset
            dsTree = GetTree(sFrame, sView, sOption)
            If (dsTree.Tables.Count > 0 And dsTree.Tables(0).Rows.Count > 0) Then
                'Loop through each node and construct skeleton by Copy and paste
                Dim bControls As Boolean
                Dim bText As Boolean
                Dim fBool As Microsoft.Office.Interop.Word.FormField
                Dim fNum As Microsoft.Office.Interop.Word.FormField 'Rich Tex input exists for the node 'Controls exist for the node

                'Iterate
                For Each drow As DataRow In dsTree.Tables(0).Rows
                    iLevel = Convert.ToInt16(drow("kc_level"))
                    bLeaf = Convert.ToBoolean(drow("kc_leaf"))
                    'In the reference code its oring the same content like this, exp1 or exp1 = exp1 itself right?
                    bControlDisplay = Convert.ToBoolean(drow("control_display")) Or Convert.ToBoolean(drow("control_display"))
                    'Create short text headings
                    Select Case sView
                        Case "DQAF"
                            sBmk = "ShortText_" & CStr(iLevel) 'Frame name for book mark of short text for current level
                            doc.Bookmarks.Item(sBmk).Range.Select() 'Select the book mark text so that next statement can replace the text
                            WordApp.Selection.TypeText(Text:=drow("dm_desc").ToString()) 'Replace the text with actual heading
                        Case "Original"

                            If bControlDisplay Then
                                If sTemp <> drow("dm_desc").ToString() Then
                                    sTemp = drow("dm_desc").ToString()
                                    sBmk = "ShortText_" & CStr(iLevel - 2) 'Frame name for book mark of short text for current level
                                    doc.Bookmarks.Item(sBmk).Range.Select() 'Select the book mark text so that next statement can replace the text
                                    WordApp.Selection.TypeText(Text:=drow("dm_desc").ToString()) 'Replace the text with actual heading
                                    doc.Bookmarks.Item(sBmk).Range.Select() 'Select the cell containing form controls
                                    WordApp.Selection.Copy() 'Copy the controls
                                    doc.Bookmarks.Item("build").Select() 'Starting point for building skeleton
                                    WordApp.Selection.MoveUp(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdLine, Count:=1) 'Select the position to paste at
                                    WordApp.Selection.Paste() 'Paste the heading
                                End If
                                sBmk = "ShortText_" & CStr(iLevel) 'Frame name for book mark of short text for current level
                                doc.Bookmarks.Item(sBmk).Range.Select() 'Select the book mark text so that next statement can replace the text
                                If IsDBNull(drow("kc_short")) = True Then
                                    WordApp.Selection.TypeText(Text:="")
                                Else
                                    WordApp.Selection.TypeText(Text:=drow("kc_short").ToString()) 'Replace the text with actual heading
                                End If
                            Else
                                sBmk = "ShortText_" & CStr(iLevel) 'Frame name for book mark of short text for current level
                                doc.Bookmarks.Item(sBmk).Range.Select() 'Select the book mark text so that next statement can replace the text

                                If IsDBNull(drow("dm_desc")) = True Then
                                    WordApp.Selection.TypeText(Text:="")
                                Else
                                    WordApp.Selection.TypeText(Text:=drow("dm_desc").ToString()) 'Replace the text with actual heading
                                End If
                            End If

                    End Select


                    doc.Bookmarks.Item(sBmk).Range.Select() 'Select again for copying
                    WordApp.Selection.Copy() 'Copy the heading

                    doc.Bookmarks.Item("build").Select() 'Starting point for building skeleton
                    WordApp.Selection.MoveUp(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdLine, Count:=1) 'Select the position to paste at
                    WordApp.Selection.Paste() 'Paste the heading

                    'Create Long text headings and input boxes/controls
                    If bControlDisplay Then 'Long text needed only for this level on wards - MAY BE WE SHOULD DO ONLY FOR LAST LEVEL NODES
                        'Long text headings
                        sBmk = "LongText_" & CStr(iLevel) 'Long text bookmark name in Frame
                        doc.Bookmarks.Item(sBmk).Range.Select() 'Select the book mark text so that next statement can replace the text
                        If IsDBNull(drow("kc_desc")) = True Then
                            WordApp.Selection.TypeText(Text:="")
                        Else
                            WordApp.Selection.TypeText(Text:=drow("kc_desc").ToString()) 'Replace the text with actual text
                        End If
                        doc.Bookmarks.Item(sBmk).Range.Select() 'Select again for copying
                        WordApp.Selection.Copy() 'Copy the long text

                        doc.Bookmarks.Item("build").Select() 'Starting point for building skeleton
                        WordApp.Selection.MoveUp(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdLine, Count:=1) ''Select the position to paste at
                        WordApp.Selection.Paste() 'Paste the heading

                        'Create Controls and rich text boxes by copy/paste

                        'Do controls exist for the node
                        bControls = Convert.ToBoolean(drow("dt_bool_display")) Or Convert.ToBoolean(drow("dt_num_display"))

                        'Does text input box exists for the node
                        bText = Convert.ToBoolean(drow("dt_text_display"))

                        If bControls Then 'Controls exist i.e. bool and num fields
                            sBmk = "Controls_" & CStr(iLevel) 'Book mark for cell in which controls exist in Frame
                            doc.Bookmarks.Item(sBmk).Range.Select() 'Select the cell containing form controls
                            WordApp.Selection.Copy() 'Copy the controls

                            doc.Bookmarks.Item("build").Select() 'Starting point for building skeleton
                            WordApp.Selection.MoveUp(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdLine, Count:=1) 'Select the position to copy to
                            WordApp.Selection.Paste() 'Paste the input box

                            If bText = False Then
                                WordApp.Selection.TypeParagraph() 'Give a blank line between heading and the controls
                                WordApp.Selection.MoveUp(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdLine, Count:=1)
                            End If

                            WordApp.Selection.MoveUp(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdLine, Count:=1) 'Select the position input box so that it can be book marked with the ID of the node

                            'Name the form fields and delete if not relevent
                            fBool = WordApp.Selection.Cells.Item(1).Range.FormFields(1) 'Reference to check box control
                            fNum = WordApp.Selection.Cells.Item(1).Range.FormFields(2) 'Reference to Num box control

                            If Convert.ToBoolean(drow("dt_bool_display")) Then 'Give book mark name to bool control if needed else delete
                                fBool.Select()
                                With WordApp.Dialogs.Item(Microsoft.Office.Interop.Word.WdWordDialog.wdDialogFormFieldOptions)
                                    .Name = "Bool_" & drow("kc_id").ToString()
                                    .Execute()
                                End With
                            Else
                                fBool.Delete()
                                'move control forward to remove white space due to removal of fBool bookmark & control
                                fNum.Range.Move(Microsoft.Office.Interop.Word.WdUnits.wdSentence, -1)
                                WordApp.Selection.Delete(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdCharacter, Count:=1)
                            End If

                            If Convert.ToBoolean(drow("dt_num_display")) Then 'Give book mark name to num control if needed else delete
                                fNum.Select()
                                With WordApp.Dialogs.Item(Microsoft.Office.Interop.Word.WdWordDialog.wdDialogFormFieldOptions)
                                    .Name = "Num_" & drow("kc_id").ToString()
                                    .Execute()
                                End With
                            Else
                                fNum.Range.Delete()
                            End If
                        End If

                        'Create Input rich text boxes
                        If bText Then

                            sBmk = "Text_" & CStr(iLevel)
                            doc.Bookmarks.Item(sBmk).Range.Select() 'Select the input box cell
                            WordApp.Selection.Copy() 'Copy the input box

                            doc.Bookmarks.Item("build").Select() 'Starting point for building skeleton
                            WordApp.Selection.MoveUp(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdLine, Count:=1) 'Select the position to copy to
                            'If Not bControls Then WordApp.Selection.TypeParagraph 'Give a blank line between heading and the input box
                            WordApp.Selection.Paste() 'Paste the input box
                            WordApp.Selection.Move(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdTable, Count:=-1) 'Select the position input box so that it can be book marked with the ID of the node
                            doc.Bookmarks.Add(Range:=WordApp.Selection.Range, Name:="Text_" & drow("kc_id").ToString())

                        End If
                    End If 'If iLevel >= 4 ; later replaced with if leaf

                Next

                doc.Bookmarks.Item("Framework").Range.Cut() 'Delete the Frame area
                doc.Bookmarks.Item("Build").Range.Paragraphs.Item(1).Range.Delete() 'Delete starting point book mark and text
                doc.Bookmarks.Item("Build").Delete()

                'Protect document - alternate sections should be protected
                Dim iSections As Short
                iSections = doc.Sections.Count
                For i = 1 To iSections
                    If i Mod 2 = 0 Then 'even sections should not be protected
                        doc.Sections.Item(i).ProtectedForForms = False
                    Else
                        doc.Sections.Item(i).ProtectedForForms = True
                    End If
                Next i
                doc.Sections.Item(iSections).ProtectedForForms = False 'last section is table of contents which should not be protected for hyper links to work

                WordApp.WordBasic.UpdateTableOfContents() 'Update TOC

                'Set skeleton header
                'The values hardcoded here must be function paratmeters in main development
                doc.Bookmarks.Item("Special_Template").Range.Select()

                If framework.ToUpper() = "SDDSPLUS" Then
                    WordApp.Selection.TypeText("SDDSPLUS - DQAF View")
                Else
                    WordApp.Selection.TypeText(sFrame & " - " & sView & " View")
                End If

                doc.Bookmarks.Item("Special_ConceptType").Range.Select()
                If sOption.Contains("Category") Then
                    WordApp.Selection.TypeText("Category")
                Else
                    WordApp.Selection.TypeText(sOption)
                End If


                'Set Custom document properties to identify the skeleton
                doc.CustomDocumentProperties("View") = sView 'Can also be Original view
                doc.CustomDocumentProperties("FrameWork") = sFrame 'Can also be GDDS
                doc.CustomDocumentProperties("ConceptType") = sOption 'Can also be Agency

                WordApp.Selection.HomeKey(Unit:=Microsoft.Office.Interop.Word.WdUnits.wdStory) 'Goto the beginning of the document

                doc.Protect(Microsoft.Office.Interop.Word.WdProtectionType.wdAllowOnlyFormFields, False) 'Protect the skeleton - reset fromfields

                Dim strWordSkeletons As String
                gStrARCTemplatesPath = ConfigurationManager.AppSettings("DQAFTemplates").ToString()
                strWordSkeletons = gStrARCTemplatesPath & "Skeletons\"

                'View the generated skeleton - this should be changed to saving as a file
                doc.SaveAs(strWordSkeletons & sFrame & "_" & sView & "_" & sOption & "_Skeleton.doc")
            Else
                strMessage = "No data in the database to generate the Skeleton"
            End If

        Catch ex As Exception
            Throw ex
        Finally
            dsTree = Nothing
        End Try
    End Sub

I used aspose.words code for the above case as below:

Private Sub BuildSkeletonAspose(ByVal framework As String, ByVal sFrame As String, ByVal sView As String, ByVal sOption As String)
        Dim iLevel As Short 'Outline Level of current node
        Dim bLeaf As Boolean 'leaf level
        Dim bControlDisplay As Boolean ' control to display or not
        Dim sBmk As String = "" 'Book mark name string holder
        Dim iSection As Short 'Counter for sections
        Dim i As Short 'Counter for sections
        Dim sTemp As String = "" 'temp var
        'Dim dsTree As New DataSet
        Dim dsTree As DataSet = Nothing

        Try
            Dim gStrARCTemplatesPath, strWordFrame, password As String
        
          
           strWordFrame = strWordFrame & "SDDS_DQAF_Frame.doc"
           
            Dim document As Aspose.Words.Document = OpenDocument(strWordFrame, "SDDSSkeleton", password)

            'iSection = doc.Sections.Count
            iSection = document.Sections.Count
            Dim docBuilder As DocumentBuilder = New DocumentBuilder(document)

            'Get the complete DQAF tree information into the dataset
            dsTree = GetTree(sFrame, sView, sOption)
            If (dsTree.Tables.Count > 0 And dsTree.Tables(0).Rows.Count > 0) Then
                'Loop through each node and construct skeleton by Copy and paste
                Dim bControls As Boolean
                Dim bText As Boolean
               
                Dim fBool As Aspose.Words.Fields.FormField
                Dim fNum As Aspose.Words.Fields.FormField 'Rich Tex input exists for the node 'Controls exist for the node
                'Iterate
                For Each drow As DataRow In dsTree.Tables(0).Rows
                    iLevel = Convert.ToInt16(drow("kc_level"))
                    bLeaf = Convert.ToBoolean(drow("kc_leaf"))
                    'In the reference code its oring the same content like this, exp1 or exp1 = exp1 itself right?
                    bControlDisplay = Convert.ToBoolean(drow("control_display")) Or Convert.ToBoolean(drow("control_display"))
                    'Create short text headings
                    docBuilder.InsertCell()
                    docBuilder.MoveToBookmark("build")
                    Select Case sView
                        Case "DQAF"
                            sBmk = "ShortText_" & CStr(iLevel) 'Frame name for book mark of short text for current level
                           
                            document.Range.Bookmarks.Item(sBmk).Text = drow("dm_desc").ToString() 'Replace the text with actual heading
                         
                        Case "Original"

                            If bControlDisplay Then
                                If sTemp <> drow("dm_desc").ToString() Then
                                    sTemp = drow("dm_desc").ToString()
                                    sBmk = "ShortText_" & CStr(iLevel - 2) 'Frame name for book mark of short text for current level
                                   
                                    document.Range.Bookmarks.Item(sBmk).Text = drow("dm_desc").ToString() 'Replace the text with actual heading
                                   
                                    document.Range.Bookmarks.Item("build").Text = document.Range.Bookmarks.Item(sBmk).Text
                                End If
                                sBmk = "ShortText_" & CStr(iLevel) 'Frame name for book mark of short text for current level
                                
                                If IsDBNull(drow("kc_short")) = True Then
                                   
                                    document.Range.Bookmarks.Item(sBmk).Text = "" 'Replace the text with actual heading

                                Else
                                    'WordApp.Selection.TypeText(Text:=drow("kc_short").ToString()) 'Replace the text with actual heading
                                    document.Range.Bookmarks.Item(sBmk).Text = drow("kc_short").ToString() 'Replace the text with actual heading

                                End If
                            Else
                                sBmk = "ShortText_" & CStr(iLevel) 'Frame name for book mark of short text for current level
                               

                                If IsDBNull(drow("dm_desc")) = True Then
                                    document.Range.Bookmarks.Item(sBmk).Text = ""
                                  
                                Else
                                  
                                    document.Range.Bookmarks.Item(sBmk).Text = drow("dm_desc").ToString()

                                End If
                            End If

                    End Select

                  
                    docBuilder.Write(document.Range.Bookmarks.Item(sBmk).Text)

                    'Create Long text headings and input boxes/controls
                    If bControlDisplay Then 'Long text needed only for this level on wards - MAY BE WE SHOULD DO ONLY FOR LAST LEVEL NODES
                        'Long text headings
                        sBmk = "LongText_" & CStr(iLevel) 'Long text bookmark name in Frame
                       
                        If IsDBNull(drow("kc_desc")) = True Then
                            document.Range.Bookmarks.Item(sBmk).Text = ""
                            ' WordApp.Selection.TypeText(Text:="")
                        Else
                            'WordApp.Selection.TypeText(Text:=drow("kc_desc").ToString()) 'Replace the text with actual text
                            document.Range.Bookmarks.Item(sBmk).Text = drow("kc_desc").ToString()
                        End If
                       
                        docBuilder.Write(document.Range.Bookmarks.Item(sBmk).Text)
                        'Do controls exist for the node
                        bControls = Convert.ToBoolean(drow("dt_bool_display")) Or Convert.ToBoolean(drow("dt_num_display"))

                        'Does text input box exists for the node
                        bText = Convert.ToBoolean(drow("dt_text_display"))

                        If bControls Then 'Controls exist i.e. bool and num fields
                            sBmk = "Controls_" & CStr(iLevel) 'Book mark for cell in which controls exist in Frame
                   
                            document.Range.FormFields("build").SetTextInputValue(document.Range.FormFields(sBmk).GetText)
                            If bText = False Then
                             
                                docBuilder.Write(docBuilder.ParagraphFormat.LineSpacing)
                            End If

                           
                            fBool = docBuilder.CurrentNode.Range.FormFields(1)                           
                            fNum = docBuilder.CurrentNode.Range.FormFields(2)

                            If Convert.ToBoolean(drow("dt_bool_display")) Then 'Give book mark name to bool control if needed else delete
                                'fBool.Select()
                                If fBool.Type = FieldType.FieldFormCheckBox Then
                                
                                    fBool.Name = "Bool_" & drow("kc_id").ToString()
                                End If
                            Else
                                
                                fBool.Remove()
                               
                            End If

                            If Convert.ToBoolean(drow("dt_num_display")) Then 'Give book mark name to num control if needed else delete
                                'fNum.Select()
                                If fNum.Type = FieldType.FieldFormTextInput Then

                                    fNum.Name = "Num_" & drow("kc_id").ToString()
                                End If
                            Else
                                fNum.Remove()
                                
                            End If
                        End If

                        'Create Input rich text boxes
                        If bText Then

                            sBmk = "Text_" & CStr(iLevel)
                           
                            docBuilder.MoveToBookmark("build")
                            docBuilder.InsertTextInput("build", TextFormFieldType.Regular, "", "", 500)
                           
                            document.Range.Bookmarks.Item("build").Name = document.Range.Bookmarks.Item("build").Name.Replace("Build", "Text_" & drow("kc_id").ToString())

                        End If
                    End If 'If iLevel >= 4 ; later replaced with if leaf

                Next
               
                'Protect document - alternate sections should be protected
                Dim iSections As Short
                iSections = doc.Sections.Count
                For i = 1 To iSections
                    If i Mod 2 = 0 Then 'even sections should not be protected
                        
                        document.Sections.Item(i).ProtectedForForms = False
                    Else
                       
                        document.Sections.Item(i).ProtectedForForms = True
                    End If
                Next i
            
                document.Sections.Item(iSections).ProtectedForForms = False
                

               
                docBuilder.MoveToBookmark("Special_Template")
                If framework.ToUpper() = "SDDSPLUS" Then
                    docBuilder.Write("SDDSPLUS - DQAF View")
                    
                Else
                    docBuilder.Write(sFrame & " - " & sView & " View")
                  
                End If
              
                docBuilder.MoveToBookmark("Special_ConceptType")
                If sOption.Contains("Category") Then
                    docBuilder.Write("Category")
                  
                Else
                    'WordApp.Selection.TypeText(sOption)
                    docBuilder.Write(sOption)
                End If

                'Set Custom document properties to identify the skeleton
               
                document.CustomDocumentProperties.Item("View").Value = sView
              
                document.CustomDocumentProperties.Item("FrameWork").Value = sFrame 'Can also be GDDS
               
                document.CustomDocumentProperties.Item("ConceptType").Value = sOption 'Can also be Agency

              
                Dim strWordSkeletons As String
               
                strWordSkeletons = gStrARCTemplatesPath & "Skeletons\"

               
                document.UpdateFields()
                document.UpdatePageLayout()
                document.Protect(ProtectionType.AllowOnlyFormFields, password)
                If (Not document Is Nothing) Then
                    Dim options As DocSaveOptions = New DocSaveOptions(Aspose.Words.SaveFormat.Doc)
                    options.Password = ""
                    Dim documentStream As MemoryStream = New MemoryStream()
                    document.Save(strWordSkeletons & sFrame & "_" & sView & "_" & sOption & "_Skeleton.doc")
                End If

            Else
                strMessage = "No data in the database to generate the Skeleton"
            End If

        Catch ex As Exception
            Throw ex
        Finally
            dsTree = Nothing
        End Try
    End Sub

But I am getting the skeleton document as (refer png images)

aspose1.PNG (4.8 KB)
aspose2.PNG (4.1 KB)
Aspose3.PNG (4.0 KB)

Please help in correcting the code using Aspose.Words .Net

Thanks

@harshamanil

Unfortunately, we have not found the input Word document in your post. To ensure a timely and accurate response, please attach the following resources here for testing:

  • Your input Word document.
  • Please attach the output Word file that shows the undesired behavior.
  • Please attach the expected output Word file that shows the desired behavior.
  • Please create a simple console application ( source code without compilation errors ) that helps us to reproduce your problem on our end and attach it here for testing.

As soon as you get these pieces of information ready, we will start investigation into your issue and provide you more information. Thanks for your cooperation.

PS: To attach these resources, please zip and upload them.

The input document file name is Frame Template.doc and the expected output file is SkeletonInterop.doc.
Output Word file that shows the undesired behavior- SkeletonAspose.doc
I have attached all the required documents including the console app which has been done in both ways i.e. using Interop.Words and Aspose.Words. Kindly have a look into it.
Files.zip (5.6 MB)

Hi,
Any updates on the above case?

Thanks

@harshamanil

Thanks for sharing the documents. We are working over your query and will get back to you soon.

@harshamanil

Please note that Aspose.Words’ model is quite different from the Microsoft Word’s Object Model in that it represents the document as a tree of objects more like an XML DOM tree. If you worked with any XML DOM library you will find it is easy to understand and work with Aspose.Words. When you load a Word document into Aspose.Words, it builds its DOM and all document elements and formatting are simply loaded into memory. Please read the following article for more information on DOM:
Aspose.Words Document Object Model

In your case, you are inserting text from data source to bookmarks of document. You are repeating the same scenario for specific bookmarks.

We suggest you please use mail merge with regions. You need to replace bookmarks with mail merge fields in your template document. Please read the following articles.
Mail Merge with Regions Explained
How to Execute Mail Merge with Regions

In case you still want to use bookmarks to insert text into document, we suggest you following solution.

  1. Create an empty document using ‘new Document()’.
  2. Create template document for bookmarks that you want to update/populate with data for each record.
  3. Iterate over data row of DataTable.
  4. Load the template document.
  5. Update the bookmark’s text from data source. At this stage you will have one document for one record.
  6. Append this document to empty document (created at step 1). Please use Document.AppendDocument method to append one document into another.
  7. Save the final document.

Please read the following article about working with bookmark and joining documents.
Working with Bookmarks
Joining and Appending Documents