word表格理解-llm

https://github.com/VikParuchuri/surya

开源领域对docx表格处理的

在实现类似chatDoc ai应用时候,如果处理word的表格是一大重要业务点。

word表格识别、填充、等是对word理解的重要组成部分。

一般word理解,包含段落理解、图片理解、表格理解。

目前段落、图片理解 算是比较简单. 比较单一输入源。。

我们可以使用ocr理解图片信息、或者多模态大模型理解图片和文本。 但表格的读取理解多了一层输入。

当然我们可以直接将docx的xml所有内容扔给多模态大模型,但是资源和理解范围要求比较高,目前各类大模型均无法实现更好的效果。

我们可以跳过ocr、或者多模态处理方式,主要专注在llm对文本处理的。 我们想办法将docx中的表格,输出为html或者xml表格形式,让大模型进行处理。

我们演示:用java实现docx读取,并合并单元格等,转为同等类型html。

import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;

/**

  • 读取word中的表格,包括复杂表格(合并的单元格)
    */
    public class TransferWordTable {

    /**

    • 保存生成HTML时需要被忽略的单元格
      */
      private List omitCellsList = new ArrayList<>();

    /**

    • 生成忽略的单元格列表中的格式
    • @param row
    • @param col
    • @return
      */
      public String generateOmitCellStr(int row, int col) {
      return row + ":" + col;
      }

    /**

    • 获取当前单元格的colspan(列合并)的列数
    • @param tcPr 单元格属性
    • @return
      */
      public int getColspan(CTTcPr tcPr) {
      // 判断是否存在列合并
      CTDecimalNumber gridSpan = null;
      if ((gridSpan = tcPr.getGridSpan()) != null) { // 合并的起始列
      // 获取合并的列数
      BigInteger num = gridSpan.getVal();
      return num.intValue();
      } else { // 其他被合并的列或正常列
      return 1;
      }
      }

    /**

    • 获取当前单元格的rowspan(行合并)的行数

    • @param table 表格

    • @param row 行值

    • @param col 列值

    • @return
      */
      public int getRowspan(XWPFTable table, int row, int col) {

      XWPFTableCell cell = table.getRow(row).getCell(col);
      // 正常独立单元格
      if (!isContinueRow(cell) && !isRestartRow(cell)) {
      return 1;
      }
      // 当前单元格的宽度
      int cellWidth = getCellWidth(table, row, col);
      // 当前单元格距离左侧边框的距离
      int leftWidth = getLeftWidth(table, row, col);

      // 用户保存当前单元格行合并的单元格数-1(因为不包含自身)
      List list = new ArrayList<>();
      getRowspan(table, row, cellWidth, leftWidth, list);

      return list.size() + 1;
      }

    private void getRowspan(XWPFTable table, int row, int cellWidth, int leftWidth,
    List list) {
    // 已达到最后一行
    if (row + 1 >= table.getNumberOfRows()) {
    return;
    }
    row = row + 1;
    int colsNum = table.getRow(row).getTableCells().size();
    // 因为列合并单元格可能导致行合并的单元格并不在同一列,所以从头遍历列,通过属性、宽度以及距离左边框间距来判断是否是行合并
    for (int i = 0; i < colsNum; i++) {
    XWPFTableCell testTable = table.getRow(row).getCell(i);
    // 是否为合并单元格的中间行(包括结尾行)
    if (isContinueRow(testTable)) {
    // 是被上一行单元格合并的单元格
    if (getCellWidth(table, row, i) == cellWidth
    && getLeftWidth(table, row, i) == leftWidth) {
    list.add(true);
    // 被合并的单元格在生成html时需要忽略
    addOmitCell(row, i);
    // 去下一行继续查找
    getRowspan(table, row, cellWidth, leftWidth, list);
    break;
    }
    }
    }
    }

    /**

    • 判断是否是合并行的起始行单元格
    • @param tableCell
    • @return
      */
      public boolean isRestartRow(XWPFTableCell tableCell) {
      CTTcPr tcPr = tableCell.getCTTc().getTcPr();
      if (tcPr.getVMerge() == null) {
      return false;
      }
      if (tcPr.getVMerge().getVal() == null) {
      return false;
      }
      if (tcPr.getVMerge().getVal().toString().equalsIgnoreCase("restart")) {
      return true;
      }
      return false;
      }

    /**

    • 判断是否是合并行的中间行单元格(包括结尾的最后一行的单元格)
    • @param tableCell
    • @return
      */
      public boolean isContinueRow(XWPFTableCell tableCell) {
      CTTcPr tcPr = tableCell.getCTTc().getTcPr();
      if (tcPr.getVMerge() == null) {
      return false;
      }
      if (tcPr.getVMerge().getVal() == null) {
      return true;
      }
      return false;
      }

    public int getLeftWidth(XWPFTable table, int row, int col) {
    int leftWidth = 0;
    for (int i = 0; i < col; i++) {
    leftWidth += getCellWidth(table, row, i);
    }
    return leftWidth;
    }

    public int getCellWidth(XWPFTable table, int row, int col) {
    BigInteger width = table.getRow(row).getCell(col).getCTTc().getTcPr().getTcW().getW();
    return width.intValue();
    }

    /**

    • 添加忽略的单元格(被行合并的单元格,生成HTML时需要忽略)
    • @param row
    • @param col
      */
      public void addOmitCell(int row, int col) {
      String omitCellStr = generateOmitCellStr(row, col);
      omitCellsList.add(omitCellStr);
      }

    public boolean isOmitCell(int row, int col) {
    String cellStr = generateOmitCellStr(row, col);
    return omitCellsList.contains(cellStr);
    }

    public String readTable(XWPFTable table) throws IOException {
    // 表格行数
    int tableRowsSize = table.getRows().size();
    StringBuilder tableToHtmlStr = new StringBuilder("

    ");

     for (int i = 0; i < tableRowsSize; i++) {
         tableToHtmlStr.append("<tr>");
         int tableCellsSize = table.getRow(i).getTableCells().size();
         for (int j = 0; j < tableCellsSize; j++) {
             if (isOmitCell(i, j)) {
                 continue;
             }
             XWPFTableCell tableCell = table.getRow(i).getCell(j);
             // 获取单元格的属性
             CTTcPr tcPr = tableCell.getCTTc().getTcPr();
             int colspan = getColspan(tcPr);
             if (colspan > 1) { // 合并的列
                 tableToHtmlStr.append("<td colspan='" + colspan + "'");
             } else { // 正常列
                 tableToHtmlStr.append("<td");
             }
    
             int rowspan = getRowspan(table, i, j);
             if (rowspan > 1) { // 合并的行
                 tableToHtmlStr.append(" rowspan='" + rowspan + "'>");
             } else {
                 tableToHtmlStr.append(">");
             }
             String text = tableCell.getText();
             tableToHtmlStr.append(text + "</td>");
    
         }
         tableToHtmlStr.append("</tr>");
     }
     tableToHtmlStr.append("</table>");
    
     clearTableInfo();
    
     return tableToHtmlStr.toString();
    

    }

    public void clearTableInfo() {
    // System.out.println(omitCellsList);
    omitCellsList.clear();
    }

    public static void main(String[] args) {
    TransferWordTable transferWordTable = new TransferWordTable();

     try (FileInputStream fileInputStream = new FileInputStream("E:\\下载\\table1.docx");
             XWPFDocument document = new XWPFDocument(fileInputStream);) {
         List<XWPFTable> tables = document.getTables();
         for (XWPFTable table : tables) {
             System.out.println(transferWordTable.readTable(table));
         }
     } catch (IOException e) {
         e.printStackTrace();
     }
    

    }

}