代码之家  ›  专栏  ›  技术社区  ›  Mr. Boy

使用openxmlsdk替换Word文件中的书签文本

  •  17
  • Mr. Boy  · 技术社区  · 14 年前

    我想v2.0更好。。。他们有一些很好的“如何:…” examples BookmarkStart & BookmarkEnd

    11 回复  |  直到 14 年前
        1
  •  20
  •   Mr. Boy    14 年前

    以下是我用你们作为灵感的方法:

      IDictionary<String, BookmarkStart> bookmarkMap = 
          new Dictionary<String, BookmarkStart>();
    
      foreach (BookmarkStart bookmarkStart in file.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
      {
          bookmarkMap[bookmarkStart.Name] = bookmarkStart;
      }
    
      foreach (BookmarkStart bookmarkStart in bookmarkMap.Values)
      {
          Run bookmarkText = bookmarkStart.NextSibling<Run>();
          if (bookmarkText != null)
          {
              bookmarkText.GetFirstChild<Text>().Text = "blah";
          }
      }
    
        2
  •  9
  •   Tim Post Samir J M Araujo    13 年前

    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);
    }
    

    首先,删除开始和结束之间的现有内容。然后直接在开始后面(在结束之前)添加一个新的运行。

    但是,不确定书签是在打开时在另一节中关闭的,还是在不同的表单元格中关闭的,等等。。

    对我来说现在已经足够了。

        3
  •  5
  •   gorgonzola    11 年前

        Public static void ReplaceBookmarkParagraphs(WordprocessingDocument doc, string bookmark, string text)
        {
            //Find all Paragraph with 'BookmarkStart' 
            var t = (from el in doc.MainDocumentPart.RootElement.Descendants<BookmarkStart>()
                     where (el.Name == bookmark) &&
                     (el.NextSibling<Run>() != null)
                     select el).First();
            //Take ID value
            var val = t.Id.Value;
            //Find the next sibling 'text'
            OpenXmlElement next = t.NextSibling<Run>();
            //Set text value
            next.GetFirstChild<Text>().Text = text;
    
            //Delete all bookmarkEnd node, until the same ID
            deleteElement(next.GetFirstChild<Text>().Parent, next.GetFirstChild<Text>().NextSibling(), val, true);
        }
    

    之后,我打电话给:

    Public static bool deleteElement(OpenXmlElement parentElement, OpenXmlElement elem, string id, bool seekParent)
    {
        bool found = false;
    
        //Loop until I find BookmarkEnd or null element
        while (!found && elem != null && (!(elem is BookmarkEnd) || (((BookmarkEnd)elem).Id.Value != id)))
        {
            if (elem.ChildElements != null && elem.ChildElements.Count > 0)
            {
                found = deleteElement(elem, elem.FirstChild, id, false);
            }
    
            if (!found)
            {
                OpenXmlElement nextElem = elem.NextSibling();
                elem.Remove();
                elem = nextElem;
            }
        }
    
        if (!found)
        {
            if (elem == null)
            {
                if (!(parentElement is Body) && seekParent)
                {
                    //Try to find bookmarkEnd in Sibling nodes
                    found = deleteElement(parentElement.Parent, parentElement.NextSibling(), id, true);
                }
            }
            else
            {
                if (elem is BookmarkEnd && ((BookmarkEnd)elem).Id.Value == id)
                {
                    found = true;
                }
            }
        }
    
        return found;
    }
    

    如果你没有空书签,这段代码就可以正常工作。 我希望它能帮助别人。

        4
  •  4
  •   tanascius    12 年前

    我10分钟前就知道了,所以请原谅代码的黑客性质。

    private static Dictionary<string, BookmarkEnd> FindBookmarks(OpenXmlElement documentPart, Dictionary<string, BookmarkEnd> results = null, Dictionary<string, string> unmatched = null )
    {
        results = results ?? new Dictionary<string, BookmarkEnd>();
        unmatched = unmatched ?? new Dictionary<string,string>();
    
        foreach (var child in documentPart.Elements())
        {
            if (child is BookmarkStart)
            {
                var bStart = child as BookmarkStart;
                unmatched.Add(bStart.Id, bStart.Name);
            }
    
            if (child is BookmarkEnd)
            {
                var bEnd = child as BookmarkEnd;
                foreach (var orphanName in unmatched)
                {
                    if (bEnd.Id == orphanName.Key)
                        results.Add(orphanName.Value, bEnd);
                }
            }
    
            FindBookmarks(child, results, unmatched);
        }
    
        return results;
    }
    

    var bookMarks = FindBookmarks(doc.MainDocumentPart.Document);
    
    foreach( var end in bookMarks )
    {
        var textElement = new Text("asdfasdf");
        var runElement = new Run(textElement);
    
        end.Value.InsertAfterSelf(runElement);
    }
    

    从我可以看出,插入和替换书签看起来比较困难。当我使用InsertAt而不是InsertIntoSelf时,我得到了:“非复合元素没有子元素。”

        5
  •  3
  •   peter    11 年前

    我从答案中提取了代码,在特殊情况下有几个问题:

    1. 书签可以是空的,所以BookmarkStart直接跟在BookmarkEnd后面,在这种情况下,您只需调用 bookmarkStart.Parent.InsertAfter(new Run(new Text("Hello World")), bookmarkStart)
    2. 另外,书签可以包含许多文本元素,因此您可能需要删除所有其他元素,否则可能会替换部分书签,而其他后续部分将保留。
    3. 我不确定我的最后一次破解是否有必要,因为我不知道OpenXML的所有限制,但是在发现了前面的4个之后,我也不再相信会有Run的兄弟,文本的孩子。因此,我只需查看我所有的兄弟姐妹(直到BookmarEnd与BookmarkStart具有相同的ID),然后检查所有的孩子,直到找到任何文本。-如果有必要的话,也许对OpenXML有更多经验的人可以回答?

    您可以查看我的具体实现 here )

        6
  •  2
  •   Sanorita Rm    12 年前

        7
  •  1
  •   LSFM    14 年前

    下面是我如何使用VB在bookmarkStart和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
    
        8
  •  1
  •   Gogutz Santosh Paswan    10 年前

    public void ReplaceBookmark( DatasetToTable( ds ) )
    {
        MainDocumentPart mainPart = myDoc.MainDocumentPart;
        Body body = mainPart.Document.GetFirstChild<Body>();
        var bookmark = body.Descendants<BookmarkStart>()
                            .Where( o => o.Name == "Table" )
                            .FirstOrDefault();
        var parent = bookmark.Parent; //bookmark's parent element
        if (ds!=null)
        {
            parent.InsertAfterSelf( DatasetToTable( ds ) );
            parent.Remove();
        }
        mainPart.Document.Save();
    }
    
    
    public Table DatasetToTable( DataSet ds )
    {
        Table table = new Table();
        //creating table;
        return table;
    }
    

    希望这有帮助

        9
  •  0
  •   Todd Main    14 年前

    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
    
        10
  •  0
  •   Dan Powley    12 年前

    公认的答案和其他一些答案假设书签在文档结构中的位置。下面是我的C代码,它可以处理替换跨越多个段落的书签 正确替换不以段落边界开始和结束的书签。仍然不完美,但更接近。。。希望有用。编辑,如果你找到更多的方法来改善它!

        private static void ReplaceBookmarkParagraphs(MainDocumentPart doc, string bookmark, IEnumerable<OpenXmlElement> paras) {
            var start = doc.Document.Descendants<BookmarkStart>().Where(x => x.Name == bookmark).First();
            var end = doc.Document.Descendants<BookmarkEnd>().Where(x => x.Id.Value == start.Id.Value).First();
            OpenXmlElement current = start;
            var done = false;
    
            while ( !done && current != null ) {
                OpenXmlElement next;
                next = current.NextSibling();
    
                if ( next == null ) {
                    var parentNext = current.Parent.NextSibling();
                    while ( !parentNext.HasChildren ) {
                        var toRemove = parentNext;
                        parentNext = parentNext.NextSibling();
                        toRemove.Remove();
                    }
                    next = current.Parent.NextSibling().FirstChild;
    
                    current.Parent.Remove();
                }
    
                if ( next is BookmarkEnd ) {
                    BookmarkEnd maybeEnd = (BookmarkEnd)next;
                    if ( maybeEnd.Id.Value == start.Id.Value ) {
                        done = true;
                    }
                }
                if ( current != start ) {
                    current.Remove();
                }
    
                current = next;
            }
    
            foreach ( var p in paras ) {
                end.Parent.InsertBeforeSelf(p);
            }
        }
    
        11
  •  0
  •   Lance    12 年前

    以下是我的结论-不是100%完美,但适用于简单的书签和简单的文本插入:

    private void FillBookmarksUsingOpenXml(string sourceDoc, string destDoc, Dictionary<string, string> bookmarkData)
        {
            string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
            // Make a copy of the template file.
            File.Copy(sourceDoc, destDoc, true);
    
            //Open the document as an Open XML package and extract the main document part.
            using (WordprocessingDocument wordPackage = WordprocessingDocument.Open(destDoc, true))
            {
                MainDocumentPart part = wordPackage.MainDocumentPart;
    
                //Setup the namespace manager so you can perform XPath queries 
                //to search for bookmarks in the part.
                NameTable nt = new NameTable();
                XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
                nsManager.AddNamespace("w", wordmlNamespace);
    
                //Load the part's XML into an XmlDocument instance.
                XmlDocument xmlDoc = new XmlDocument(nt);
                xmlDoc.Load(part.GetStream());
    
                //Iterate through the bookmarks.
                foreach (KeyValuePair<string, string> bookmarkDataVal in bookmarkData)
                {
                    var bookmarks = from bm in part.Document.Body.Descendants<BookmarkStart>()
                              select bm;
    
                    foreach (var bookmark in bookmarks)
                    {
                        if (bookmark.Name == bookmarkDataVal.Key)
                        {
                            Run bookmarkText = bookmark.NextSibling<Run>();
                            if (bookmarkText != null)  // if the bookmark has text replace it
                            {
                                bookmarkText.GetFirstChild<Text>().Text = bookmarkDataVal.Value;
                            }
                            else  // otherwise append new text immediately after it
                            {
                                var parent = bookmark.Parent;   // bookmark's parent element
    
                                Text text = new Text(bookmarkDataVal.Value);
                                Run run = new Run(new RunProperties());
                                run.Append(text);
                                // insert after bookmark parent
                                parent.Append(run);
                            }
    
                            //bk.Remove();    // we don't want the bookmark anymore
                        }
                    }
                }
    
                //Write the changes back to the document part.
                xmlDoc.Save(wordPackage.MainDocumentPart.GetStream(FileMode.Create));
            }
        }