Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inserting text after a bookmark in openxml

I am looking for a way to insert some text after a bookmark in a word doc using openxml. So far, i have been able to locate the bookmark using the following:

var bookmarks = mainPart.Document.Descendants<BookmarkStart>().ToList();
var bookMarkToWriteAfter = bookmarks.FirstOrDefault(bm => bm.Name == insertAfterBoomark.Name);

This bookmark in the word doc is a selection of two lines in the doc. I have to insert some text right after the two line selection. I have tried to insert text using the following:

var run = new Run();
run.Append(new Text("Hello World"));
bookMarkToWriteAfter .Parent.InsertAfterSelf(run);

mainPart.Document.Save();

This however does not produce the desired result. Does anyone know of the correct way to insert text right after a bookmark in a word doc using openxml?

like image 436
John Baum Avatar asked Mar 19 '12 16:03

John Baum


People also ask

How do I add a reference to DocumentFormat OpenXML packaging?

Right click on your project on Solution Explorer, and you should see the Add Reference option. Once selected, click on the Browser tab and browser the folder C:\Program Files\Open XML SDK\V2. 0\lib and select DocumentFormat. OpenXml.

What is a run in OpenXML?

The content of the paragraph is contained in one or more runs (<w:r>). Runs are non-block content; they define regions of text that do not necessarily begin on a new line. Like paragraphs, they are comprised of formatting/property definitions, followed by content.

How do I make text bold in OpenXML?

Bold fbld = new Bold();

What is DocumentFormat OpenXML?

The Open XML SDK provides tools for working with Office Word, Excel, and PowerPoint documents. It supports scenarios such as: - High-performance generation of word-processing documents, spreadsheets, and presentations. - Populating content in Word files from an XML data source.


2 Answers

using

bookMarkToWriteAfter.Parent.InsertAfterSelf(run);

you are trying to work with XML directly which is not always advisable with OpenXML.

Try This..

    Body body = mainPart.Document.GetFirstChild<Body>();
    var paras = body.Elements<Paragraph>();

    //Iterate through the paragraphs to find the bookmarks inside
    foreach (var para in paras)
    {
        var bookMarkStarts = para.Elements<BookmarkStart>();
        var bookMarkEnds = para.Elements<BookmarkEnd>();


        foreach (BookmarkStart bookMarkStart in bookMarkStarts)
        {
            if (bookMarkStart.Name == bookmarkName)
            {
                //Get the id of the bookmark start to find the bookmark end
                var id = bookMarkStart.Id.Value;
                var bookmarkEnd = bookMarkEnds.Where(i => i.Id.Value == id).First();

                var runElement = new Run(new Text("Hello World!!!"));

                para.InsertAfter(runElement, bookmarkEnd);

            }
        }
   }
   mainPart.Document.Save();
like image 136
Flowerking Avatar answered Oct 03 '22 21:10

Flowerking


You can not assume that a bookmark starts and ends in one paragraph. Bookmarks can start and end in different elements and be children of:

bdo (§17.3.2.3); body (§17.2.2); comment (§17.13.4.2); customXml (§17.5.1.6); customXml (§17.5.1.4); customXml (§17.5.1.5); customXml (§17.5.1.3); deg (§22.1.2.26); del (§17.13.5.14); den (§22.1.2.28); dir (§17.3.2.8); docPartBody (§17.12.6); e (§22.1.2.32); endnote (§17.11.2); fldSimple (§17.16.19); fName (§22.1.2.37); footnote (§17.11.10); ftr (§17.10.3); hdr (§17.10.4); hyperlink (§17.16.22); ins (§17.13.5.18); lim (§22.1.2.52); moveFrom (§17.13.5.22); moveTo (§17.13.5.25); num (§22.1.2.75); oMath (§22.1.2.77); p (§17.3.1.22); rt (§17.3.3.24); rubyBase (§17.3.3.27); sdtContent (§17.5.2.34); sdtContent (§17.5.2.33); sdtContent (§17.5.2.35); sdtContent (§17.5.2.36); smartTag (§17.5.1.9); sub (§22.1.2.112); sup (§22.1.2.114); tbl (§17.4.38); tc (§17.4.66); tr (§17.4.79)

https://msdn.microsoft.com/en-gb/library/documentformat.openxml.wordprocessing.bookmarkstart(v=office.15).aspx

This means you need to look at all the BookmarkEnd elements in the document when checking for the required BookmarkEnd element.

Body body = mainPart.Document.GetFirstChild<Body>();
var bookMarkStarts = body.Descendants<BookmarkStart>();
var bookMarkEnds = body.Descendants<BookmarkEnd>();

foreach (BookmarkStart bookMarkStart in bookMarkStarts)
{
    if (bookMarkStart.Name == bookmarkName)
    {
        //Get the id of the bookmark start to find the bookmark end
        var id = bookMarkStart.Id.Value;
        var bookmarkEnd = bookMarkEnds.Where(i => i.Id.Value == id).First();

        var runElement = new Run(new Text("Hello World!!!"));

        bookmarkEnd.Parent.InsertAfter(runElement, bookmarkEnd);
    }
}
mainPart.Document.Save();

You may want to check that a Run can be added to the parent and add to a different ancestor or create a new Paragraph.

(I would have liked to have added this as a comment to Flowerking's answer but I can't comment so I have modified their code in this answer.)

like image 22
Adam Avatar answered Oct 03 '22 21:10

Adam