我正在尝试使用 Apache POI 创建一个 .docx 文档,该文档预计包含一个键值对表。有些键可能有另一组键值对(而不是单个字符串值),这意味着我需要在另一个表中创建一个表。
我希望如果我从在 MS Word 中创建的文档模板开始,那么这将很容易实现,以便它已经具有所有所需的格式。而且,在大多数情况下,它确实有效。唯一的问题是,如果生成的文档中存在嵌套表格,当用户尝试打开该文件时,MS Word 将显示一条消息,说明该文件存在一些错误以及是否希望 Word 恢复该错误。单击“是”后,“恢复”的文件将显示正确。
正如您在上面的示例图片中看到的,我希望它为下一级(嵌套表)有一个“缩进”。这是通过将模板表格式化为 97% 宽度并右对齐来实现的。
据我所知,“恢复”文档与我创建的文档之间的 document.xml 差异仅在于
我尝试通过将宽度设置为父表中宽度的 97% 来调整嵌套表中的
如果我强制使用完全相同的值生成它,那么 MS Word 将打开它而不会出现任何错误。所以这证实了网格大小是问题所在。但是,当然,我不能依赖硬编码值。
我不会看到你问题中图片中的表格是表格中的表格。表中表的结构并不是一个很好的实践。如果可能的话应该避免。
我看到一个有 4 列的表格,其中第 1、2 和 5 行中的第 1 列和第 2 列以及第 3 列和第 4 列被合并。在第 3 行和第 4 行中,第 1 列被隐藏,第 2 列和第 3 列被合并。
以下代码使用 Apache POI(当前版本 5.2.3)生成此代码。
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import java.util.Map;
import java.util.TreeMap;
import java.math.BigInteger;
public class CreateWordKeyValueTable {
static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
XWPFTableCell cell = table.getRow(row).getCell(fromCol);
// Try getting the TcPr. Not simply setting an new one every time.
org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr tcPr = cell.getCTTc().getTcPr();
if (tcPr == null) tcPr = cell.getCTTc().addNewTcPr();
// The first merged cell has grid span property set
if (tcPr.isSetGridSpan()) {
tcPr.getGridSpan().setVal(BigInteger.valueOf(toCol-fromCol+1));
} else {
tcPr.addNewGridSpan().setVal(BigInteger.valueOf(toCol-fromCol+1));
}
// Cells which join (merge) the first one, must be removed
for(int colIndex = toCol; colIndex > fromCol; colIndex--) {
table.getRow(row).removeCell(colIndex); // use only this for apache poi versions greater than 4.1.1
}
}
static void createGrid(XWPFTable table, int[] colWidths) {
//create CTTblGrid for this table with widths of the columns.
//necessary for Libreoffice/Openoffice to accept the column widths.
//values are in unit twentieths of a point (1/1440 of an inch)
//if widths of table cells are in percent, then this is only to store the relations
org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGrid tblGrid = table.getCTTbl().getTblGrid();
if (tblGrid == null) tblGrid = table.getCTTbl().addNewTblGrid();
for (org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGridCol tblGridCol : tblGrid.getGridColList()) {
tblGrid.removeGridCol(0);
}
//first column
tblGrid.addNewGridCol().setW(BigInteger.valueOf(colWidths[0]));
//other columns
for (int c = 1 ; c < colWidths.length; c++) {
tblGrid.addNewGridCol().setW(BigInteger.valueOf(colWidths[c]));
}
}
static void hideFirstCell(XWPFTableRow tableRow) {
//set gridBefore
org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTrPr trPr = tableRow.getCtRow().getTrPr();
if (trPr==null) trPr = tableRow.getCtRow().addNewTrPr();
for (org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber gridBefore : trPr.getGridBeforeList()) {
trPr.removeGridBefore(0);
}
org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber gridBefore = trPr.addNewGridBefore();
gridBefore.setVal(BigInteger.valueOf(1));
//set wBefore
for (org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth wBefore : trPr.getWBeforeList()) {
trPr.removeWBefore(0);
}
org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth wBefore = trPr.addNewWBefore();
XWPFTableCell tableCell = tableRow.getCell(0);
wBefore.setW(tableCell.getWidth());
wBefore.setType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth.PCT);
tableRow.removeCell(0);
}
static XWPFTableRow getOrCreateTableRow(XWPFTable table, int r, String[] colWidths) {
//create a row having count of cells having special width
XWPFTableRow tableRow = null;
if (r==0) {
tableRow = table.getRow(0);
} else {
tableRow = table.createRow();
}
//fill up cell list up to needed count
while (tableRow.getTableCells().size() < colWidths.length) {
tableRow.createCell();
}
//set the widths
for (int c = 0 ; c < colWidths.length; c++) {
tableRow.getCell(c).setWidth(colWidths[c]);
}
return tableRow;
}
public static void main(String[] args) throws Exception {
Map<String, Object> keyValue = new TreeMap<String, Object>();
keyValue.put("Key1", "Value1");
Map<String, String> subKeyValue = new TreeMap<String, String>();
subKeyValue.put("Key2a", "Value2a");
subKeyValue.put("Key2b", "Value2b");
keyValue.put("Key2", subKeyValue);
keyValue.put("Key3", "Value3");
System.out.println(keyValue);
String multiValues = "Multivalued Key";
double[] dWidthsPCT = new double[]{10d, 30d, 10d, 50d};
java.util.stream.Stream<String> stream =
java.util.stream.DoubleStream.of(dWidthsPCT).boxed()
.map(d -> String.valueOf(d)+"%");
String[] widthsPCT = stream.toArray(String[]::new);
XWPFDocument document = new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("The table:");
XWPFTable table = document.createTable();
table.setWidth("100%");
//set borders
table.setTopBorder(XWPFTable.XWPFBorderType.SINGLE , 32, 0, "FFFFFF");
table.setBottomBorder(XWPFTable.XWPFBorderType.SINGLE , 32, 0, "FFFFFF");
table.setLeftBorder(XWPFTable.XWPFBorderType.SINGLE , 32, 0, "FFFFFF");
table.setRightBorder(XWPFTable.XWPFBorderType.SINGLE , 32, 0, "FFFFFF");
table.setInsideHBorder(XWPFTable.XWPFBorderType.SINGLE , 32, 0, "FFFFFF");
table.setInsideVBorder(XWPFTable.XWPFBorderType.SINGLE , 32, 0, "FFFFFF");
createGrid(table, new int[]{ // 5000 = 100%
(int)Math.round(5000d/(100d/dWidthsPCT[0])),
(int)Math.round(5000d/(100d/dWidthsPCT[1])),
(int)Math.round(5000d/(100d/dWidthsPCT[2])),
(int)Math.round(5000d/(100d/dWidthsPCT[3]))
}
);
XWPFTableRow tableRow;
XWPFTableCell tableCell;
String key;
Object objectValue;
String stringValue;
int r = -1;
for(Map.Entry<String, Object> entry : keyValue.entrySet()) {
r++;
tableRow = getOrCreateTableRow(table, r, widthsPCT);
key = entry.getKey();
objectValue = entry.getValue();
tableCell = tableRow.getCell(0);
tableCell.setText(key);
mergeCellHorizontally(table, r, 0, 1);
tableCell.setWidth((int)(dWidthsPCT[0]+dWidthsPCT[1])+"%"); // col width 0 and 1
tableCell.setColor("00B0F0");
if (objectValue instanceof String) {
stringValue = (String)objectValue;
tableCell = tableRow.getCell(1);
tableCell.setText(stringValue);
mergeCellHorizontally(table, r, 1, 2);
tableCell.setWidth((int)(dWidthsPCT[2]+dWidthsPCT[3])+"%"); // col width 2 and 3
tableCell.setColor("C0C0C0");
} else if (objectValue instanceof Map) {
tableCell = tableRow.getCell(1);
tableCell.setText(multiValues);
mergeCellHorizontally(table, r, 1, 2);
tableCell.setWidth((int)(dWidthsPCT[2]+dWidthsPCT[3])+"%"); // col width 2 and 3
tableCell.setColor("C0C0C0");
@SuppressWarnings("unchecked")
Map<String, String> mapValue = (Map<String, String>)objectValue;
for(Map.Entry<String, String> subEntry : mapValue.entrySet()) {
r++;
tableRow = getOrCreateTableRow(table, r, widthsPCT);
hideFirstCell(tableRow);
key = subEntry.getKey();
stringValue = subEntry.getValue();
tableCell = tableRow.getCell(0);
tableCell.setText(key);
mergeCellHorizontally(table, r, 0, 1);
tableCell.setWidth((int)(dWidthsPCT[1]+dWidthsPCT[2])+"%"); // col width 1 and 2
tableCell.setColor("00B0F0");
tableCell = tableRow.getCell(1);
tableCell.setText(stringValue);
tableCell.setWidth(widthsPCT[3]); // col width 3
tableCell.setColor("C0C0C0");
}
}
}
paragraph = document.createParagraph();
FileOutputStream out = new FileOutputStream("./CreateWordKeyValueTable.docx");
document.write(out);
out.close();
document.close();
}
}
它产生: