当前位置: 首页 > 知识库问答 >
问题:

使用apache POI合并word表中的单元格时,如何保持单元格宽度?

沃楷
2023-03-14

我正在使用Java和Apache POI创建一个带有表的word文档。

我可以很容易地创建表格,用不同的宽度设置每一列,然后合并单元格以产生所需的效果(见下面的图像)。然而,当我打开word文档时,一些单元格已经被调整,以便它们的边缘对齐。我发现,在表的开头添加一个额外的行并保留所有单元格不合并将保持其余行不变,但稍后使用table.removerow(0)删除该行;影响其余行。如果打开word文档并手动删除该行,单元格将保持原位。我能做些什么来保留单元格的布局吗?

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)));
}

提前感谢!

共有1个答案

司马飞
2023-03-14

您看到的问题是,Word根据列数最多的行的列宽设置来呈现表。如果其他行与该行的列宽设置相矛盾,则将忽略它们的列宽设置。并且在合并单元格后,您不更正列宽设置。例如,在MergeCellHostertally(table,0,0,2)之后;行0中的列0现在一直到列2。因此,列0现在需要以前列0+1+2的宽度。但是,由于您没有更正它,它只保持以前列0的宽度,如果这与列数最多的行的宽度设置相矛盾,则在呈现时忽略它。

因此,主要问题是您的代码在合并单元格后没有纠正行中的列宽设置。

我已经在如何在apache poi表中的不同行中设置特定的单元格宽度中展示了这一点?。

下面的完整示例显示了所有这些,并创建了所需的表。

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();
 }
}
 类似资料:
  • 我需要一个表格,第一行和第二行的单元格合并在一起。 大概是这样的: 桌子的图片(我不能张贴图片)http://i.stack.imgur.com/dAO6j.png 我一直在复习与本主题相关的所有问题,并找到了一些将网格跨度应用于单元的答案,但我找不到真正的解决方案。 以下是我从谷歌和本网站获得的示例代码: 我从这段代码中得到的信息如下: 我试图用

  • mergeCells(string $scope, string $data [, resource $formatHandler]): self string $scope $excel->fileName("test.xlsx") ->mergeCells('A1:C1', 'Merge cells') ->output();

  • 问题内容: 我在表容器中有不确定数量的表单元元素。 有没有一种纯粹的CSS方法可以使表格单元具有相等的宽度,即使它们内部具有不同大小的内容? 谢谢。 编辑:具有最大宽度将需要知道您有多少个单元格? 问题答案: 这是工作单元数不确定的有效 您可以为每个父对象固定一个宽度( 表格 ),否则通常将为100%。 诀窍是使用 每个单元上的一定宽度触发它,这里是2%。这将触发 另一个 表算法,浏览器会尽力遵守

  • 问题内容: 许多人仍然使用表格来布局控件,数据等。-流行的jqGrid就是一个例子。但是,发生了一些我似乎无法理解的魔术(它的桌子大声喊叫,可能有多少魔术?) 如何设置表的列宽并使它像jqGrid那样遵守!?如果我尝试复制此内容,即使我设置了every ,只要这些单元格之一的内容大于20px,该单元格就会展开! 有什么想法或见解吗? 问题答案: 您可以尝试对所有行使用标签管理表样式,但是您需要在或

  • 我正在使用java类创建一个大型excel。Excel包含一个存储字符串的合并单元格。字符串的长度非常大,我动态地得到这个字符串。我需要增加合并单元格的高度,使整个字符串适合该单元格。我试过使用“包装文本”,它包装文本,但不会增加合并单元格的高度,因为完整的字符串在excel中不可见。我使用的Java类是: XS SF工作簿 XSSFSheet XSSFRow XSSFCell XS SF Cel

  • 我有一个对象列表,我试图为每个对象指定三行,我使用的"Office Open Xml库"有以下例程: 然而,它弹出一个错误,说不能合并已经合并的单元格。 所以问题是如何在Excel中合并两个以上的单元格?