Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

poi-tl处理Word表格(Table)的最佳实践 #21

Closed
Sayi opened this issue Nov 23, 2017 · 7 comments
Closed

poi-tl处理Word表格(Table)的最佳实践 #21

Sayi opened this issue Nov 23, 2017 · 7 comments

Comments

@Sayi
Copy link
Owner

Sayi commented Nov 23, 2017

⚠️ 这里是旧版1.2.0文档,最新文档参见http://deepoove.com/poi-tl/

表格对于页面的布局具有重大的意义,正因为其灵活性,所以用模板引擎处理word中的Table时,显得并不是那么简单,本文将讨论如何利用poi-tl(1.2.0版本)提供的工具来简化表格处理。

表格模板

poi-tl默认实现了N行N列的样式(如下图),同时提供了当数据为空时,展示一行空数据的文案(如下图中的No Data Descs)。
image

在poi-tl的1.2.0版本中,表格模板语法是#,数据结构是com.deepoove.poi.data.TableRenderData。

  1. 表格头使用headers[]定义,支持设置背景色
  2. 表格数据使用datas[]定义,不同列的数据在datas中使用分号隔开
  3. 宽度使用width定义
  4. 无数据文案使用noDatadesc定义
{
  "datas": [
    "beijing;beijing",
    "zhejiang;hangzhou"
  ],
  "headers": [
    {
      "style": {
        "color": "1E915D",
        "fontSize": 0
      },
      "text": "province"
    },
    {
      "style": {
        "color": "1E915D",
        "fontSize": 0
      },
      "text": "city"
    }
  ],
  "noDatadesc": "no datas",
  "width": 0
}

具体Java代码参考:

@Test
public void testTable() throws Exception {
  Map<String, Object> datas = new HashMap<String, Object>() {
    {
      // 有表格头 有数据
      put("table", new TableRenderData(new ArrayList<RenderData>() {
        {
          add(new TextRenderData("1E915D", "province"));
          add(new TextRenderData("1E915D", "city"));
        }
      }, new ArrayList<Object>() {
        {
          add("beijing;beijing");
          add("zhejiang;hangzhou");
        }
      }, "no datas", 0));
    }
  };
  XWPFTemplate template = XWPFTemplate.compile("src/test/resources/table.docx").render(datas);

  FileOutputStream out = new FileOutputStream("out_table.docx");
  template.write(out);
  out.flush();
  out.close();
  template.close();
}

表格的宽度怎么定义的

是一个点的二十分之一,或者是1440分之一英寸。官方解释如下:

dxa - Specifies that the value is in twentieths of a point (1/1440 of an inch).
首先1英寸=2.54厘米,A4纸大小为21cm*29.7cm。
如果这个width设置成5670,则表示这个表格的宽度是10cm。

抛开对这个单位理解的难度,我们最常见的应该是宽度自适应和宽度最大。
如果在poi-tl中设置了width=0,则表格是宽度自适应的。
以A4纸为例,页面宽度为21cm,左右页边距各位3.17cm,则表格的width=(21-3.172)/2.541440,大约为8310。

合并单元格

但是,很多业务场景并不仅限于如此简单的表格布局,产品需求总是丰富多彩的。poi-tl对XWPFDocument进行了封装,增强实现了一些基本功能。在com.deepoove.poi.NiceXWPFDocument中提供了合并的功能。

/**
 * 合并行单元格
 * @param table
 * @param row
 * @param fromCol
 * @param toCol
 */
public void mergeCellsHorizonal(XWPFTable table, int row, int fromCol,
    int toCol)

/**
 * 合并列单元格
 * @param table
 * @param col
 * @param fromRow
 * @param toRow
 */
public void mergeCellsVertically(XWPFTable table, int col, int fromRow,
    int toRow)

自定义表格之新建表格

我们完全可以从无到有去创建一个新的表格。

  1. 无需事先创建表格,在docx中,直接输入{{table}}
  2. 默认{{table}}是文本模板,我们需要通过registerPolicy设置此模板为自定义模板。
XWPFTemplate template = XWPFTemplate.compile("src/test/resources/complex.docx");
template.registerPolicy("table", new MyTableRenderPolicy());
  1. 新建MyTableRenderPolicy.java,实现RenderPolicy接口
@Override
public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
  NiceXWPFDocument doc = template.getXWPFDocument();
  RunTemplate runTemplate = (RunTemplate) eleTemplate;
  XWPFRun run = runTemplate.getRun();
  if (null == data) return;

  //doc.insertNewTable(run, row, col);
  //doc.mergeCellsHorizonal(table, 1, 0, 1);
  //...
  runTemplate.getRun().setText("", 0);
}

至此,我们持有了NiceXWPFDocument和XWPFRun对象,可以插入表格,合并单元格等操作。

自定义表格之动态处理已有表格

对于事先已知道部分表格样式,我们只需要处理剩余部分的表格可以采用此方式。

比如下图,我们在模板中设计好表格头和表格未的样式,表格中间的数据则可以动态处理。
image

  1. 定义如图的模板,在表格内输入模板元素{{table}}
  2. 通过registerPolicy设置此模板为自定义模板
  3. 新建MyTableRenderPolicy.java,继承DynamicTableRenderPolicy
public class MyTableRenderPolicy extends DynamicTableRenderPolicy {

  @Override
  public void render(XWPFTable table, Object data) {
      //table.getRow(1).getCell(0)
      //XWPFTableRow row = table.insertNewTableRow(1);
      //table.removeRow(1);
  }
}

至此,我们可以通过XWPFTable对象对表格进行删除行列、增加行列、设置文字等操作。

More

有时间的话,会对表格的API作一次优化。

  • 重新设计TableRenderData,对数据结构进行一次重构
  • 单元格文字样式处理,文字对齐处理。
@Sayi Sayi added the 杂文 label Nov 23, 2017
@OlnyBigRoc
Copy link

有没有循环语法,多行数据填充的方式有哪些?

@Sayi
Copy link
Owner Author

Sayi commented Feb 7, 2018

@OlnyBigRoc TableRenderData.datas属性或者自定义处理

@OlnyBigRoc
Copy link

为什么没有找到设置字体的方法

@Sayi
Copy link
Owner Author

Sayi commented Feb 7, 2018

@OlnyBigRoc 设置字体目前只能自定义调用POI的API了,下个版本会加上这个功能。

@OlnyBigRoc
Copy link

OlnyBigRoc commented Feb 8, 2018

image
大佬这种表头怎么写 就是怎么使用java代码来写

@wempers
Copy link

wempers commented May 3, 2018

java.lang.ClassCastException: org.openxmlformats.schemas.wordprocessingml.x2006.main.impl.DocumentDocumentImpl cannot be cast to org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl

@Watermelonljf
Copy link

Watermelonljf commented Sep 23, 2018

这个文档怎么渲染
11111111111

@Sayi Sayi closed this as completed Jan 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants