Replace bookmark text in Word file using Open XML SDK

前端 未结 11 1870
眼角桃花
眼角桃花 2020-12-01 08:24

I assume v2.0 is better... they have some nice \"how to:...\" examples but bookmarks don\'t seem to act as obviously as say a Table... a bookmark is defined by two

相关标签:
11条回答
  • 2020-12-01 08:50

    Replace bookmarks with a single content (possibly multiple text blocks).

    public static void InsertIntoBookmark(BookmarkStart bookmarkStart, string text)
    {
        OpenXmlElement elem = bookmarkStart.NextSibling();
    
        while (elem != null && !(elem is BookmarkEnd))
        {
            OpenXmlElement nextElem = elem.NextSibling();
            elem.Remove();
            elem = nextElem;
        }
    
        bookmarkStart.Parent.InsertAfter<Run>(new Run(new Text(text)), bookmarkStart);
    }
    

    First, the existing content between start and end is removed. Then a new run is added directly behind the start (before the end).

    However, not sure if the bookmark is closed in another section when it was opened or in different table cells, etc. ..

    For me it's sufficient for now.

    0 讨论(0)
  • 2020-12-01 08:53

    I took the code from the answer, and had several problems with it for exceptional cases:

    1. You might want to ignore hidden bookmarks. Bookmarks are hidden if the name starts with an _ (underscore)
    2. If the bookmark is for one more more TableCell's, you will find it in the BookmarkStart in the first Cell of the row with the property ColumnFirst refering to the 0-based column index of the cell where the bookmark starts. ColumnLast refers to the cell where the bookmark ends, for my special case it was always ColumnFirst == ColumnLast (bookmarks marked only one column). In this case you also won't find a BookmarkEnd.
    3. Bookmarks can be empty, so a BookmarkStart follows directly a BookmarkEnd, in this case you can just call bookmarkStart.Parent.InsertAfter(new Run(new Text("Hello World")), bookmarkStart)
    4. Also a bookmark can contain many Text-elements, so you might want to Remove all the other elements, otherwise parts of the Bookmark might be replaced, while other following parts will stay.
    5. And I'm not sure if my last hack is necessary, since I don't know all the limitations of OpenXML, but after discovering the previous 4, I also didn't trust anymore that there will be a sibling of Run, with a child of Text. So instead I just look at all my siblings (until BookmarEnd which has the same ID as BookmarkStart) and check all the children until I find any Text. - Maybe somebody with more experience with OpenXML can answer if it is necessary?

    You can view my specific implementation here)

    Hope this helps some of you who experienced the same issues.

    0 讨论(0)
  • 2020-12-01 08:55

    Here is how I do it in VB.NET:

    For Each curBookMark In contractBookMarkStarts
    
          ''# Get the "Run" immediately following the bookmark and then
          ''# get the Run's "Text" field
          runAfterBookmark = curBookMark.NextSibling(Of Wordprocessing.Run)()
          textInRun = runAfterBookmark.LastChild
    
          ''# Decode the bookmark to a contract attribute
          lines = DecodeContractDataToContractDocFields(curBookMark.Name, curContract).Split(vbCrLf)
    
          ''# If there are multiple lines returned then some work needs to be done to create
          ''# the necessary Run/Text fields to hold lines 2 thru n.  If just one line then set the
          ''# Text field to the attribute from the contract
          For ptr = 0 To lines.Count - 1
              line = lines(ptr)
              If ptr = 0 Then
                  textInRun.Text = line.Trim()
              Else
                  ''# Add a <br> run/text component then add next line
                  newRunForLf = New Run(runAfterBookmark.OuterXml)
                  newRunForLf.LastChild.Remove()
                  newBreak = New Break()
                  newRunForLf.Append(newBreak)
    
                  newRunForText = New Run(runAfterBookmark.OuterXml)
                  DirectCast(newRunForText.LastChild, Text).Text = line.Trim
    
                  curBookMark.Parent.Append(newRunForLf)
                  curBookMark.Parent.Append(newRunForText)
              End If
          Next
    Next
    
    0 讨论(0)
  • 2020-12-01 08:56

    Most solutions here assume a regular bookmarking pattern of starting before and ending after runs, which is not always true e.g. if bookmark starts in a para or table and ends somewhere in another para (like others have noted). How about using document order to cope with the case where bookmarks are not placed in a regular structure - the document order will still find all the relevant text nodes in between which can then be replaced. Just do root.DescendantNodes().Where(xtext or bookmarkstart or bookmark end) which will traverse in document order, then one can replace text nodes that appear after seeing a bookmark start node but before seeing an end node.

    0 讨论(0)
  • 2020-12-01 08:56

    Here is how i do it and VB to add/replace text between bookmarkStart and BookmarkEnd.

    <w:bookmarkStart w:name="forbund_kort" w:id="0" /> 
            - <w:r>
              <w:t>forbund_kort</w:t> 
              </w:r>
    <w:bookmarkEnd w:id="0" />
    
    
    Imports DocumentFormat.OpenXml.Packaging
    Imports DocumentFormat.OpenXml.Wordprocessing
    
        Public Class PPWordDocx
    
            Public Sub ChangeBookmarks(ByVal path As String)
                Try
                    Dim doc As WordprocessingDocument = WordprocessingDocument.Open(path, True)
                     'Read the entire document contents using the GetStream method:
    
                    Dim bookmarkMap As IDictionary(Of String, BookmarkStart) = New Dictionary(Of String, BookmarkStart)()
                    Dim bs As BookmarkStart
                    For Each bs In doc.MainDocumentPart.RootElement.Descendants(Of BookmarkStart)()
                        bookmarkMap(bs.Name) = bs
                    Next
                    For Each bs In bookmarkMap.Values
                        Dim bsText As DocumentFormat.OpenXml.OpenXmlElement = bs.NextSibling
                        If Not bsText Is Nothing Then
                            If TypeOf bsText Is BookmarkEnd Then
                                'Add Text element after start bookmark
                                bs.Parent.InsertAfter(New Run(New Text(bs.Name)), bs)
                            Else
                                'Change Bookmark Text
                                If TypeOf bsText Is Run Then
                                    If bsText.GetFirstChild(Of Text)() Is Nothing Then
                                        bsText.InsertAt(New Text(bs.Name), 0)
                                    End If
                                    bsText.GetFirstChild(Of Text)().Text = bs.Name
                                End If
                            End If
    
                        End If
                    Next
                    doc.MainDocumentPart.RootElement.Save()
                    doc.Close()
                Catch ex As Exception
                    Throw ex
                End Try
            End Sub
    
        End Class
    
    0 讨论(0)
提交回复
热议问题