XWPFDocument.insertTable does not seem to work at all. Created small example word doc that looks like: 0: Paragraph 1 1: Table 1 2: Paragraph 2 3: Paragraph 3 4: Table 2 5: Paragraph 4 The indexes are what's in the bodyElements array. Verified its being read as expected. Then running this example on it: try (XWPFDocument doc = new XWPFDocument (getClass ().getResourceAsStream ("/InsertTable.docx"))) { // This is just here to verify the indexes into the bodyElements list are as expected int index = 0; for (IBodyElement elem : doc.getBodyElements ()) { System.out.println (index + ": " + elem); index++; } // Copy one of the existing tables and insert it at specific point XWPFTable src = (XWPFTable) doc.getBodyElements ().get (1); XWPFTable dest = new XWPFTable ((CTTbl) src.getCTTbl ().copy (), doc); doc.insertTable (3, dest); File outFile = File.createTempFile ("TestInsertTable", ".docx"); outFile.delete (); try (FileOutputStream out = new FileOutputStream (outFile)) { doc.write (out); } } The copied table doesn't come out in the word doc at all. Moreover if you look at what insertTable is doing, it adds the new table into the bodyElements list, but that's just an ArrayList so doing so doesn't trigger any special processing or updates of the underlying XML. So then it is searching for where the newly added table XML (CTTbl) was added into the document XML, and fails to find it because nothing added it. So then it defaults to adding the table at the end of the list of tables, but again this is just an ArrayList so isn't triggering any special processing or updating of the underlying XML. If I use createTable instead of copying an existing table, the table does get added, but at the end of the doc instead of at the desired index into the bodyElements list. This is because createTable() has already called ctDocument.getBody().addNewTbl() and added at the end of the XML before we call insertTable to say where we actually want it.
The only workaround I can find is to create an XmlCursor for the insertion point, then call XWPFDocument.insertNewTbl to create a throwaway table, then use XWPFDocument.setTable to replace the throwaway table with the real one. public XmlCursor newCursorForBodyElement (IBodyElement elem) { XmlCursor cursor; if (elem instanceof XWPFParagraph) { XWPFParagraph para = (XWPFParagraph) elem; cursor = para.getCTP ().newCursor (); } else if (elem instanceof XWPFTable) { XWPFTable table = (XWPFTable) elem; cursor = table.getCTTbl ().newCursor (); } else throw new RuntimeException ("Don't know how to create a cursor from body element of type " + elem.getClass ()); return cursor; } .. and then can do ... index = 3; XWPFTable throwawayTable; if (index < doc.getBodyElements ().size ()) { // Insert before numbered element XmlCursor insertionPoint = newCursorForBodyElement (doc.getBodyElements ().get (index)); throwawayTable = doc.insertNewTbl (insertionPoint); } else { // Insert at end throwawayTable = doc.createTable (); } doc.setTable (doc.getTables ().indexOf (throwawayTable), dest);
There are some unreleased changes in XWPF code, eg https://bz.apache.org/bugzilla/show_bug.cgi?id=66312 Would you be able to try the latest code? You can build jars from latest source code or you could get jars from https://ci-builds.apache.org/job/POI/job/POI-DSL-1.8/
Tried with latest JARs from Jenkins and find no difference. I think its just not intended to be used the way I am expecting to be able to.