Replace text with URL in Word document using Open XML

Peter Karlström 216 Reputation points
2021-02-26T07:37:31.44+00:00

Hello
I have problems with a project where I want to replace som tagged text in a Word document with a clickable URL.
The sample code below uses a word document which contains the text [Webpage].
Here is the code with the problem:

Imports DocumentFormat.OpenXml
Imports DocumentFormat.OpenXml.Packaging

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        MsgBox(processDocument("C:\temp\testdoc.docx", "[Webpage]", "Google", "https://www.google.com"), MsgBoxStyle.ApplicationModal + vbOKOnly, "Text replace test")

    End Sub

    Private Function processDocument(ByVal tDocFilename As String, ByVal tagText As String, ByVal replText As String, ByVal replURL As String) As String


        Using doc As WordprocessingDocument = WordprocessingDocument.Open(tDocFilename, True)
            Dim mainPart As DocumentFormat.OpenXml.Packaging.MainDocumentPart = doc.MainDocumentPart

            Dim textPLaceList As IEnumerable(Of Wordprocessing.Text) = mainPart.Document.Descendants(Of Wordprocessing.Text)()

            Try
                For Each textPlaceHolder As Wordprocessing.Text In textPLaceList
                    Dim parent = textPlaceHolder.Parent
                    If (TypeOf parent Is Wordprocessing.Run) Then
                        If textPlaceHolder.Text.Contains("[") And textPlaceHolder.Text.Contains("]") Then
                            Dim tmpHyperlink As New DocumentFormat.OpenXml.Wordprocessing.Hyperlink
                            tmpHyperlink.Anchor = replText
                            tmpHyperlink.DocLocation = replURL
                            tmpHyperlink.InsertBefore(Of Wordprocessing.Hyperlink)(tmpHyperlink, textPlaceHolder.Parent)
                            textPlaceHolder.Remove()
                            Exit For
                        End If
                    End If
                Next
                processDocument = "OK"
            Catch ex As Exception
                processDocument = "Could not replace text in document (" & ex.Message & ")"
            End Try

        End Using

    End Function

End Class

When I try to use InsertBefor or InsertAfter I get an error telling me that the "state" och the object is incorrect.
What does that mean?

Regards Peter Karlström

Office Development
Office Development
Office: A suite of Microsoft productivity software that supports common business tasks, including word processing, email, presentations, and data management and analysis.Development: The process of researching, productizing, and refining new or existing technologies.
3,852 questions
{count} votes

Accepted answer
  1. Peter Karlström 216 Reputation points
    2021-03-03T10:05:28.927+00:00

    After a tip from cheong00 I discovered I had approached this task in the wrong way.

    This is how it should be done:

    Imports DocumentFormat.OpenXml
    Imports DocumentFormat.OpenXml.Wordprocessing
    Imports DocumentFormat.OpenXml.Packaging
    
    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
            MsgBox(processDocument("C:\temp\testdoc.docx", "[Webpage]", "Google", "https://www.google.com"), MsgBoxStyle.ApplicationModal + vbOKOnly, "Text replace test")
    
        End Sub
    
        Private Function processDocument(ByVal tDocFilename As String, ByVal tagText As String, ByVal replText As String, ByVal replURL As String) As String
    
            Using doc As WordprocessingDocument = WordprocessingDocument.Open(tDocFilename, True)
                Dim mainPart As DocumentFormat.OpenXml.Packaging.MainDocumentPart = doc.MainDocumentPart
    
                Dim textPLaceList As IEnumerable(Of Wordprocessing.Text) = mainPart.Document.Descendants(Of Wordprocessing.Text)()
    
                Try
                    For Each textPlaceHolder As Wordprocessing.Text In textPLaceList
                        Dim parent As Wordprocessing.Paragraph = textPlaceHolder.Parent.Parent
                        If (TypeOf parent Is Wordprocessing.Paragraph) Then
                            If textPlaceHolder.Text.Contains("[") And textPlaceHolder.Text.Contains("]") Then
                                Dim newParagraph As Paragraph = getURLParagraph(mainPart, replText, replURL)
                                parent.Parent.InsertBefore(Of Wordprocessing.Paragraph)(newParagraph, parent)
                                textPlaceHolder.Remove()
                                Exit For
                            End If
                        End If
                    Next
                    processDocument = "OK"
                Catch ex As Exception
                    processDocument = "Could not replace text in document (" & ex.Message & ")"
                End Try
    
            End Using
    
        End Function
    
        Private Function getURLParagraph(ByVal mainPart As MainDocumentPart, ByVal urlLabel As String, ByVal urlText As String) As Paragraph
    
            Dim urlExists As Boolean
            Dim hRelation As HyperlinkRelationship = Nothing
    
            Dim uri As System.Uri = New Uri(urlText)
    
            For Each hRel As HyperlinkRelationship In mainPart.HyperlinkRelationships
                If (hRel.Uri = uri) Then
                    urlExists = True
                    hRelation = hRel
                    Exit For
                End If
            Next
    
            Dim relationshipId As String
            If Not urlExists Then
                Dim rel As HyperlinkRelationship = mainPart.AddHyperlinkRelationship(uri, True)
                relationshipId = rel.Id
            Else
                relationshipId = hRelation.Id
            End If
    
            Dim newParagraph As Paragraph = New Paragraph(New Hyperlink(New ProofError() With {
            .Type = ProofingErrorValues.GrammarStart
        }, New Run(New RunProperties(New RunStyle() With {
            .Val = “Hyperlnk”}), New Text(urlLabel))) With {
            .History = OnOffValue.FromBoolean(True),
            .Id = relationshipId
        })
            Return newParagraph
    
        End Function
    End Class
    

    Hope this is of some help to others.

    Regards
    Peter Karlström

    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Cheong00 3,476 Reputation points
    2021-03-02T08:03:51.167+00:00

    When textPlaceHolder.Parent is a Run, .InsertBefore/InsertAfter will fail. (Note that although I link to an issue page of OpenXML SDK, this is the limitation of Word format and I think you know too as I see your code is also trying to evade that)

    See if replacing textPlaceHolder.Parent in the second parameter with parent.Parent will help.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.