我正在使用Java和Apache POI用表创建Word文档。
我可以很容易地创建表格,将每列设置为不同的宽度,然后合并单元格以产生所需的效果(请参见下图),但是当我打开word文档时,某些单元格已经过调整,因此它们的边缘齐齐。我发现,在表的开头添加了另一行,并且不合并所有单元格,其余的行则保持不变,但是稍后使用table.removeRow(0);删除了该行;影响其余的行。如果我打开word文档并手动删除该行,则单元格将保持原样。我有什么办法可以保留单元格的布局?
correct layout with an additional unmerged top row
the result after removing the top row
这是创建单词doc和table的功能:
public static void createWord() {
// Blank Document
XWPFDocument document = new XWPFDocument();
CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
CTPageMar pageMar = sectPr.addNewPgMar();
pageMar.setLeft(BigInteger.valueOf(300L));
pageMar.setTop(BigInteger.valueOf(300L));
pageMar.setRight(BigInteger.valueOf(300L));
pageMar.setBottom(BigInteger.valueOf(300L));
XWPFParagraph paragraph = document.createParagraph();
paragraph.setSpacingBefore(0);
paragraph.setSpacingAfter(0);
// determine the number of rows and columns required
int rows = 3;
int cols = 6;
// create table
XWPFTable table = document.createTable(rows+1, cols);
CTTblPr tblPr = table.getCTTbl().getTblPr();
if (null == tblPr) {
tblPr = table.getCTTbl().addNewTblPr();
}
// set table width
CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();
width.setType(STTblWidth.PCT);
width.setW(BigInteger.valueOf(5000)); // 5000 * 1/50 = 100%
//set row height
for(XWPFTableRow row:table.getRows()) {
row.setHeight(22);
}
// set width of each column
for (int row = 0; row <= rows; row++) {
setCellWidthPercentage(table, row, 0, 0.188);
setCellWidthPercentage(table, row, 1, 0.125);
setCellWidthPercentage(table, row, 2, 0.063);
setCellWidthPercentage(table, row, 3, 0.25);
setCellWidthPercentage(table, row, 4, 0.25);
setCellWidthPercentage(table, row, 5, 0.125);
}
mergeCellHorizontally(table, 1, 0, 2);
mergeCellHorizontally(table, 2, 0, 1);
mergeCellHorizontally(table, 2, 2, 4);
mergeCellHorizontally(table, 3, 1, 3);
// remove first row (comment out this line to see issue)
table.removeRow(0);
// Write the Document in file system
try {
File docFile = new File("C:\\doc.docx");
docFile.createNewFile();
FileOutputStream out = new FileOutputStream(docFile, false);
document.write(out);
out.close();
document.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
我正在使用下面的代码来水平合并单元格:
static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
for(int colIndex = fromCol; colIndex <= toCol; colIndex++){
XWPFTableCell cell = table.getRow(row).getCell(colIndex);
CTHMerge hmerge = CTHMerge.Factory.newInstance();
if(colIndex == fromCol) {
// The first merged cell is set with RESTART merge value
hmerge.setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
hmerge.setVal(STMerge.CONTINUE);
}
// Try getting the TcPr. Not simply setting an new one every time.
CTTcPr tcPr = cell.getCTTc().getTcPr();
if (tcPr != null) {
tcPr.setHMerge(hmerge);
} else {
// only set an new TcPr if there is not one already
tcPr = CTTcPr.Factory.newInstance();
tcPr.setHMerge(hmerge);
cell.getCTTc().setTcPr(tcPr);
}
}
}
,此函数可在合并前为列分配宽度值:
private static void setCellWidthPercentage(XWPFTable table, int row, int col, double width) {
// prevent out of bounds exception
if (row < 0 || row >= table.getRows().size()) return;
if (col < 0 || col >= table.getRow(row).getTableCells().size()) return;
// assign widths in units of 1/50 of a percentage
CTTblWidth tblW = table.getRow(row).getCell(col).getCTTc().addNewTcPr().addNewTcW();
tblW.setType(STTblWidth.PCT);
tblW.setW(BigInteger.valueOf(Math.round(width * 50)));
}
谢谢!
答案 0 :(得分:1)
您看到的问题是Word
会根据其中包含最多列的行的列宽设置来呈现表。如果其他行与该行的列宽设置矛盾,则将忽略其列宽设置。合并单元格后,您无需更正列宽设置。例如,在mergeCellHorizontally(table, 0, 0, 2);
之后,第0行的第0列现在到第2列。因此,第0列现在需要以前的列0 +1 + 2的宽度。但是,由于您没有进行更正,因此它仅保留以前的第0列的宽度,并且如果与最多列的行的宽度设置矛盾,则在渲染时将被忽略。
所以主要问题是您的代码在合并单元格后缺少纠正行中列宽设置的功能。
我已经在how to set specific cell width in different row in apache poi table?中显示了此内容。
但是还有更多问题。
首先,方法mergeCellHorizontally
应该通过设置网格范围而不是使用CTHMerge来水平合并单元格。与使用*.docx
相比,这与打开CTHMerge
文件的所有文字处理应用程序兼容得多。
第二,始终应使用最新的apache poi
版本。当前的apache poi 4.1.2
提供了XWPFTable.setWidth和XWPFTableCell.setWidth。因此,不需要自己的set-width-method。
第三,您应该为表格创建一个带有列宽的表格网格。 Libreoffice
/ OpenOffice
必须接受列宽。不幸的是,由于TblGrid
-GridCol
不接受百分比值,因此这需要计算以点的二十分之一(1/1440英寸)为单位的列宽。
以下完整示例显示了所有这些内容并创建了所需的表。
import java.io.FileOutputStream;
import java.math.BigInteger;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
public class CreateWordTableMergedCells {
//merging horizontally by setting grid span instead of using CTHMerge
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.
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).getCtRow().removeTc(colIndex);
table.getRow(row).removeCell(colIndex);
}
}
public static void main(String[] args) throws Exception {
XWPFDocument document= new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The table:");
// determine the number of rows and columns required
int rows = 3;
int cols = 6;
//create table
XWPFTable table = document.createTable(rows, cols);
//set table width
table.setWidth("100%");
double[] columnWidths = new double[] { // columnWidths in percent
0.188, 0.125, 0.062, 0.25, 0.25, 0.125
};
//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)
int w100Percent = 6*1440; // twentieths of a point (1/1440 of an inch); 6 inches
//first column
table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(
Math.round(w100Percent*columnWidths[0])));
//other columns
for (int c = 1; c < cols; c++) {
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(
Math.round(w100Percent*columnWidths[c])));
}
// set width of each column in each row
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
table.getRow(r).getCell(c).setWidth("" + (columnWidths[c]*100.0) + "%");
}
}
//using the merge method
mergeCellHorizontally(table, 0, 0, 2); // after that column 0 is up to column 2
//column 0 now need width of formerly columns 0 + 1 + 2
table.getRow(0).getCell(0).setWidth("" + ((columnWidths[0]+columnWidths[1]+columnWidths[2])*100.0) + "%");
mergeCellHorizontally(table, 1, 0, 1); // after that column 0 is up to column 1
//column 0 now need width of formerly columns 0 + 1
table.getRow(1).getCell(0).setWidth("" + ((columnWidths[0]+columnWidths[1])*100.0) + "%");
mergeCellHorizontally(table, 1, 1, 3); // formerly col 2 is now col 1 and after that formerly column 2 is up to column 4
//current column 1 now need width of formerly columns 2 + 3 + 4
table.getRow(1).getCell(1).setWidth("" + ((columnWidths[2]+columnWidths[3]+columnWidths[4])*100.0) + "%");
mergeCellHorizontally(table, 2, 1, 3); // after that column 1 is up to column 3
//column 1 now need width of formerly columns 1 + 2 + 3
table.getRow(2).getCell(1).setWidth("" + ((columnWidths[1]+columnWidths[2]+columnWidths[3])*100.0) + "%");
paragraph = document.createParagraph();
FileOutputStream out = new FileOutputStream("create_table.docx");
document.write(out);
out.close();
}
}