Search code examples
ms-wordapache-poibookmarks

how to set bookmark for XWPFRun object in word by poi


I am using Word to create templates that contain many variables. I will read and parse the Word document to set bookmarks for the variables. Each variable is a XWPFRun object. How can I set a bookmark for a XWPFRun object? Below is the response from chatgpt, but CTR doesn't have addNewBookmarkStart and addNewBookmarkEnd.

for (XWPFParagraph paragraph : document.getParagraphs()) {
for (XWPFRun run : paragraph.getRuns()) {
    // Check if the run meets your criteria
    if (/* condition */) {
        // Create a bookmark for the run
        run.getCTR().addNewBookmarkStart().setName("bookmark_name");
        run.getCTR().addNewBookmarkEnd();
    }
}

}


Solution

  • In java poi XWPF word - create bookmark in new document I have shown how to create bookmarks in a new XWPFDocument.

    If you look at the document.xml created, you will find:

    <w:p>
     ...
     <w:bookmarkStart w:name="testing_string" w:id="1"/>
     <w:r>
      <w:t xml:space="preserve">testing string </w:t>
     </w:r>
     <w:bookmarkEnd w:id="1"/>
     ...
    </w:p>
    

    The bookmarkStart is immediately before the text run and the bookmarkEnd is immediately after the text run in paragraph. All elements are children of the paragraph elements on same level.

    If the need is to bookmark already existent XWPFRuns, then one need to insert the bookmarkStart immediately before and bookmarkEnd immediately after the XWPFRun in XWPFParagraph. This only is possible using a XmlCursor.

    Example:

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    
    import org.apache.poi.xwpf.usermodel.*;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
    import org.apache.xmlbeans.XmlCursor;
    import java.math.BigInteger;
    
    public class WordBookmarkXWPFRun {
        
     static void bookmarkXWPFRun(XWPFRun run, String name, int id) {
      // insert bookmarkStart immediately before the run
      XmlCursor cursor = run.getCTR().newCursor();
      String uri = CTBookmark.type.getName().getNamespaceURI();
      String localPart = "bookmarkStart";
      cursor.beginElement(localPart, uri);
      cursor.toParent();
      CTBookmark bookmarkStart = (CTBookmark)cursor.getObject();
      bookmarkStart.setName(name);
      bookmarkStart.setId(BigInteger.valueOf(id));
      
      // insert bookmarkEnd immediately after the run
      cursor = run.getCTR().newCursor();
      if (!cursor.toNextSibling()) { // is there a next sibling?
       // if not, go to end of parent element to insert bookmarkEnd there
       cursor.toParent();
       cursor.toEndToken();   
      }
      localPart = "bookmarkEnd";
      cursor.beginElement(localPart, uri);
      cursor.insertAttributeWithValue("id", uri, String.valueOf(id));
     }
    
     public static void main(String[] args) throws Exception {
    
      XWPFDocument document = new XWPFDocument(new FileInputStream("./WordDocument.docx"));
      
      int id = 1;
      for (XWPFParagraph paragraph : document.getParagraphs()) {
       for (XWPFRun run : paragraph.getRuns()) {
        // Check if the run meets your criteria
        if (/* condition */ true ) { // this will bookmark every single tect run in document
         // Create a bookmark for the run
         bookmarkXWPFRun(run, "bookmark_name_" + id, id++);
        }
       }
      }
    
      FileOutputStream out = new FileOutputStream("./WordDocumentBookmarked.docx");
      document.write(out);
      out.close();
      document.close();
    
     }
    }