如何正确调整 .DOCX 中嵌套表格的大小?

问题描述 投票:0回答:1

我正在尝试使用 Apache POI 创建一个 .docx 文档,该文档预计包含一个键值对表。有些键可能有另一组键值对(而不是单个字符串值),这意味着我需要在另一个表中创建一个表。

示例表

我希望如果我从在 MS Word 中创建的文档模板开始,那么这将很容易实现,以便它已经具有所有所需的格式。而且,在大多数情况下,它确实有效。唯一的问题是,如果生成的文档中存在嵌套表格,当用户尝试打开该文件时,MS Word 将显示一条消息,说明该文件存在一些错误以及是否希望 Word 恢复该错误。单击“是”后,“恢复”的文件将显示正确。

正如您在上面的示例图片中看到的,我希望它为下一级(嵌套表)有一个“缩进”。这是通过将模板表格式化为 97% 宽度并右对齐来实现的。

据我所知,“恢复”文档与我创建的文档之间的 document.xml 差异仅在于 元素中列宽的值。然而,我仍然不清楚这些值是如何计算的。

我尝试通过将宽度设置为父表中宽度的 97% 来调整嵌套表中的 ,并围绕这个想法进行了一些尝试。但是,还是不太幸运。

如果我强制使用完全相同的值生成它,那么 MS Word 将打开它而不会出现任何错误。所以这证实了网格大小是问题所在。但是,当然,我不能依赖硬编码值。

java apache-poi
1个回答
0
投票

我不会看到你问题中图片中的表格是表格中的表格。表中表的结构并不是一个很好的实践。如果可能的话应该避免。

我看到一个有 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();

 }
}

它产生:

© www.soinside.com 2019 - 2024. All rights reserved.