InsertAtFound.vb
''
'' This code is part of GrapeCity Documents for Word samples.
'' Copyright (c) GrapeCity, Inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports System.Linq
Imports GrapeCity.Documents.Word

'' This sample shows how to insert the whole body of an existing DOCX into 
'' another document at a location where an arbitrary search string is found.
'' One interesting point in this sample is that the code ensures that the
'' insert position is valid, i.e. the source content (all paragraphs of
'' the source document) can be inserted at the found point. If necessary,
'' the target is split so that there is a content object of a valid type
'' at the point where the source is to be inserted.
'' In this sample, the target document is JsFrameworkExcerpt.
'' The source document inserted is SampleParagraphs, and it is inserted
'' on page 2 of the source document, immediately before the string
'' "software design principles".
Public Class InsertAtFound
    Public Function CreateDocx() As GcWordDocument
        '' The string to find. The source document will be inserted before this string
        Const findPattern = "software design principles"

        '' Target document where the source document will be inserted:
        Dim doc = New GcWordDocument()
        doc.Load(Path.Combine("Resources", "WordDocs", "JsFrameworkExcerpt.docx"))

        '' Source document that will be inserted into the target:
        Dim sourceDoc = New GcWordDocument()
        sourceDoc.Load(Path.Combine("Resources", "WordDocs", "SampleParagraphs.docx"))
        '' Note: the inserted document starts with a 'Heading 1', which adds a page break.
        '' We change it to 'Heading 2' to avoid this so that the result is more clear:
        sourceDoc.Body.Paragraphs.First.Style = sourceDoc.Styles(BuiltInStyleId.Heading2)

        '' Find the first occurrence of the search string:
        Dim findResult = doc.Body.Find(findPattern, New FindOptions(doc) With {.IgnoreCase = True}).FirstOrDefault()
        If findResult Is Nothing Then
            Throw New Exception("Unexpected: search string not found.")
        End If

        '' Find a valid insertion point near the found string:
        Dim insertObject = CreateInsertPoint(findResult)

        '' Copy the source document to the target at insertion point
        sourceDoc.Body.CopyTo(insertObject.GetRange(), InsertLocation.Before)

        '' Done
        Return doc
    End Function


    '' Walk up the parent chain And determine where we are - inside body, cell Or contentcontrol.
    '' Return the original object if we are inside body Or cell.
    '' Return contentcontrol if we are inside contentcontrol.
    Private Shared Function GetAnchorObject(testedObject As ContentObject) As ContentObject
        Dim originalObject = testedObject
        While True
            If testedObject.ParentContent Is Nothing Then
                Return originalObject
            ElseIf TypeOf testedObject Is Cell Then
                Return originalObject
            ElseIf TypeOf testedObject Is ContentControl Then
                Return testedObject
            Else
                testedObject = testedObject.ParentContent
            End If
        End While
        Return Nothing
    End Function

    '' This method assumes that testObject Is always an entity inside a paragraph.
    Private Shared Function GetParentParagraph(testObject As ContentObject) As Paragraph
        While testObject IsNot Nothing
            If TypeOf testObject Is Paragraph Then
                Return CType(testObject, Paragraph)
            End If
            testObject = testObject.ParentContent
        End While
        Throw New ArgumentException("testObject is not inside a paragraph.")
    End Function

    Private Shared Function CreateInsertPoint(fr As FindResult) As ContentObject
        Dim anchorObject = GetAnchorObject(fr.Range.First())
        Dim foundParagraph = GetParentParagraph(anchorObject)

        If fr.StartIndex > 0 AndAlso TypeOf anchorObject Is Text Then
            anchorObject = CType(anchorObject, Text).Split(fr.StartIndex)
        End If
        If foundParagraph Is anchorObject Then
            anchorObject = foundParagraph.Split(anchorObject, InsertLocation.End)
        Else
            anchorObject = foundParagraph.Split(anchorObject, InsertLocation.Before)
        End If
        Return anchorObject
    End Function
End Class