使用 Apache Poi 从具有固定位置的模板复制 table

copy table from templates with fixed place using Apache Poi

我在 docx 模板中有一个 table。 根据对象的数量,我必须复制 table 的次数与我有对象的次数一样多。重复的 table 必须位于模板中的 table 之后。 我在模板中有几个 table 应该表现得像这样。

XmlCursor 取代模板中的第一个 table 并将下一个放在那里。我想在上一个之后插入下一个 table ,这是我自己添加的,但是 xmlcursor 没有 return 我添加的 table 项,而是 returns "STARTDOC"

XmlCursor cursor = docx.getTables().get(pointer).getCTTbl().newCursor();
cursor.toEndToken();

while (cursor.toNextToken() != XmlCursor.TokenType.START) ;

XWPFParagraph newParagraph = docx.insertNewParagraph(cursor);
newParagraph.createRun().setText("", 0);
cursor.toParent();
cursor.toEndToken();
while (cursor.toNextToken() != XmlCursor.TokenType.START) ;
docx.insertNewTbl(cursor);

CTTbl ctTbl = CTTbl.Factory.newInstance();
ctTbl.set(docx.getTables().get(numberTableFromTemplate).getCTTbl());
XWPFTable tableCopy = new XWPFTable(ctTbl, docx);
docx.setTable(index + 1, tableCopy);

不清楚您使用 cursor.toParent(); 的目的是什么。而且我也无法重现只有您的小代码片段的问题。但是有一个完整的工作示例可能会对您有所帮助。

假设我们有以下模板:

然后是下面的代码:

import java.io.FileOutputStream;
import java.io.FileInputStream;

import org.apache.poi.xwpf.usermodel.*;

import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;

public class WordCopyTableAfterTable {

 static XmlCursor setCursorToNextStartToken(XmlObject object) {
  XmlCursor cursor = object.newCursor();
  cursor.toEndToken(); //Now we are at end of the XmlObject.
  //There always must be a next start token.
  while(cursor.hasNextToken() && cursor.toNextToken() != org.apache.xmlbeans.XmlCursor.TokenType.START);
  //Now we are at the next start token and can insert new things here.
  return cursor;
 }

 static void removeCellValues(XWPFTableCell cell) {
  for (XWPFParagraph paragraph : cell.getParagraphs()) {
   for (int i = paragraph.getRuns().size()-1; i >= 0; i--) {
    paragraph.removeRun(i);
   }  
  }
 }

 public static void main(String[] args) throws Exception {

  //The data. Each row a new table.
  String[][] data= new String[][] {
   new String[] {"John Doe", "5/23/2019", "1234.56"},
   new String[] {"Jane Doe", "12/2/2019", "34.56"},
   new String[] {"Marie Template", "9/20/2019", "4.56"},
   new String[] {"Hans Template", "10/2/2019", "4567.89"}
  };

  String value;
  XWPFDocument document = new XWPFDocument(new FileInputStream("WordTemplate.docx"));
  XWPFTable tableTemplate;
  CTTbl cTTblTemplate;
  XWPFTable tableCopy;
  XWPFTable table;
  XWPFTableRow row;
  XWPFTableCell cell;
  XmlCursor cursor;
  XWPFParagraph paragraph;
  XWPFRun run;

  //get first table (the template)
  tableTemplate = document.getTableArray(0);
  cTTblTemplate = tableTemplate.getCTTbl();
  cursor = setCursorToNextStartToken(cTTblTemplate);

  //fill in first data in first table (the template)
  for (int c = 0; c < data[0].length; c++) {
   value = data[0][c];
   row = tableTemplate.getRow(1);
   cell = row.getCell(c);
   removeCellValues(cell);
   cell.setText(value);
  }

  paragraph = document.insertNewParagraph(cursor); //insert new empty paragraph
  cursor = setCursorToNextStartToken(paragraph.getCTP());

  //fill in next data, each data row in one table
  for (int t = 1; t < data.length; t++) {
   table = document.insertNewTbl(cursor); //insert new empty table at position t
   cursor = setCursorToNextStartToken(table.getCTTbl());

   tableCopy = new XWPFTable((CTTbl)cTTblTemplate.copy(), document); //copy the template table

   //fill in data in tableCopy
   for (int c = 0; c < data[t].length; c++) {
    value = data[t][c];
    row = tableCopy.getRow(1);
    cell = row.getCell(c);
    removeCellValues(cell);
    cell.setText(value);
   }
   document.setTable(t, tableCopy); //set tableCopy at position t instead of table

   paragraph = document.insertNewParagraph(cursor); //insert new empty paragraph
   cursor = setCursorToNextStartToken(paragraph.getCTP());
  }

  paragraph = document.insertNewParagraph(cursor);
  run = paragraph.createRun(); 
  run.setText("Inserted new text below last table.");
  cursor = setCursorToNextStartToken(paragraph.getCTP());

  FileOutputStream out = new FileOutputStream("WordResult.docx");
  document.write(out);
  out.close();
  document.close();
 }
}

导致以下结果:

这就是你想要实现的目标吗?

请注意我是如何插入额外的 tables 的。

使用 table = document.insertNewTbl(cursor); 在位置 t 插入一个新的空 table。这个 table 被放入文档正文中。所以这个table一定要拿去调整光标。

然后tableCopy = new XWPFTable((CTTbl)cTTblTemplate.copy(), document);复制模板table。然后这个副本充满了数据。然后使用 document.setTable(t, tableCopy);t 位置将其设置到文档中。

遗憾的是 apache poi 此处不完整。 XWPFDocument.setTable 仅设置内部 ArrayList,但不设置基础 XMLXWPFDocument.insertNewTbl 设置基础 XML 但仅使用空 table。所以我们必须用那种丑陋复杂的方式来做。