You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Apache POI复制含公式、图片的XWPFTableCell至新文档对应单元格的实现方案

使用Apache POI复制含公式、图片的XWPFTableCell至新文档对应单元格的实现方案

嗨,你遇到的问题其实很典型——Apache POI的基础文本复制逻辑不会自动处理数学公式、图片这类特殊内容,因为它们不是单纯的文本Run。我帮你完善了代码,不仅能复制普通文本和样式,还能完整保留单元格里的数学公式(OMML格式)、图片,以及类似化学公式的嵌入式对象,同时也补上了单元格样式的复制(比如宽度、边框这些)。

关键改进点说明

  • 单元格样式复制:补上了源单元格宽度、边框、背景等样式的完整复制,避免目标单元格样式丢失
  • 段落公式处理:数学公式以OMML格式存储在段落XML中,直接复制对应的<m:oMath>节点就能完整保留公式结构
  • Run中的图片复制:提取源图片的二进制数据和尺寸参数,在目标Run中重新创建完全一致的图片
  • 嵌入式对象支持:针对化学公式这类OLE嵌入式对象,直接复制Run中的<w:object>节点

完整修改后代码

主方法修正(修复输出顺序错误)

public ApiResponse generateDocument(HttpServletResponse response) throws Exception {
    try (XWPFDocument targetDoc = new XWPFDocument()){
        ByteArrayOutputStream b = new ByteArrayOutputStream();
        XWPFDocument courseDoc = new XWPFDocument(new FileInputStream("upload/yukla.docx"));
        XWPFTable targetTable = targetDoc.createTable(10, 5);

        for (IBodyElement ibodyelement : courseDoc.getBodyElements()) {
            if (ibodyelement instanceof XWPFTable courseTable) {
                cloneTable(courseTable, targetTable);
            }
        }

        // 修正:先写入文档到字节流,再复制到响应输出
        targetDoc.write(b);
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=VARIANTS.docx");
        FileCopyUtils.copy(b.toByteArray(), response.getOutputStream());

        courseDoc.close();
        targetDoc.close();
        return new ApiResponse("SUCCESS", true);
    }
}

表格复制方法(保留原有逻辑)

private void cloneTable(XWPFTable courseTable, XWPFTable targetTable) {
    int cntRow = 0;
    for (XWPFTableRow row : courseTable.getRows()) {
        for (int i = 0; i < 5; i++) {
            XWPFTableCell courseCell = row.getCell(i);
            XWPFTableCell targetCell = targetTable.getRow(cntRow).getCell(i);
            try {
                cloneCell(courseCell, targetCell);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        cntRow++;
    }
}

单元格复制方法(新增样式复制)

private void cloneCell(XWPFTableCell sourceCell, XWPFTableCell targetCell) throws Exception {
    // 复制单元格样式(宽度、边框、背景等)
    CTTcPr sourceTcPr = sourceCell.getCTTc().getTcPr();
    if (sourceTcPr != null) {
        CTTcPr targetTcPr = targetCell.getCTTc().getTcPr();
        if (targetTcPr == null) {
            targetTcPr = targetCell.getCTTc().addNewTcPr();
        }
        targetTcPr.set(sourceTcPr);
    }

    // 复制段落及内容
    for (XWPFParagraph sourcePr : sourceCell.getParagraphs()) {
        XWPFParagraph targetPr = targetCell.addParagraph();
        cloneParagraph(targetPr, sourcePr);
    }
}

段落复制方法(新增公式复制)

private void cloneParagraph(XWPFParagraph targetPr, XWPFParagraph sourcePr) throws Exception {
    // 复制段落样式
    CTPPr sourcePPr = sourcePr.getCTP().getPPr();
    if (sourcePPr != null) {
        CTPPr targetPPr = targetPr.getCTP().getPPr();
        if (targetPPr == null) {
            targetPPr = targetPr.getCTP().addNewPPr();
        }
        targetPPr.set(sourcePPr);
    }

    // 复制段落中的数学公式(OMML格式)
    List<CTOMath> sourceOMaths = sourcePr.getCTP().getOMathList();
    for (CTOMath oMath : sourceOMaths) {
        targetPr.getCTP().addNewOMath().set(oMath);
    }

    // 复制Run内容
    for (XWPFRun sourceRun : sourcePr.getRuns()) {
        XWPFRun targetRun = targetPr.createRun();
        cloneRun(targetRun, sourceRun);
    }
}

Run复制方法(新增图片、嵌入式对象复制)

private void cloneRun(XWPFRun targetRun, XWPFRun sourceRun) throws Exception {
    // 复制Run样式
    CTRPr sourceRPr = sourceRun.getCTR().getRPr();
    if (sourceRPr != null) {
        CTRPr targetRPr = targetRun.getCTR().getRPr();
        if (targetRPr == null) {
            targetRPr = targetRun.getCTR().addNewRPr();
        }
        targetRPr.set(sourceRPr);
    }

    // 复制文本内容
    String text = sourceRun.getText(0);
    if (text != null) {
        targetRun.setText(text);
    }

    // 复制Run中的图片
    List<XWPFPicture> sourcePictures = sourceRun.getEmbeddedPictures();
    for (XWPFPicture pic : sourcePictures) {
        XWPFPictureData picData = pic.getPictureData();
        byte[] picBytes = picData.getData();
        int pictureType = picData.getPictureType();
        // 按原图尺寸插入图片
        targetRun.addPicture(new ByteArrayInputStream(picBytes), pictureType, picData.getFileName(), 
                            Units.toEMU(pic.getWidth()), Units.toEMU(pic.getHeight()));
    }

    // 复制嵌入式对象(如化学公式OLE对象)
    CTR sourceCTR = sourceRun.getCTR();
    List<CTObject> sourceObjects = sourceCTR.getObjectList();
    for (CTObject obj : sourceObjects) {
        targetRun.getCTR().addNewObject().set(obj);
    }
}

额外说明

如果你的单元格里还有嵌套表格这类特殊内容,可以在cloneCell方法中加入对XWPFTable类型元素的判断和复制,逻辑和外层表格的cloneTable方法类似。

备注:内容来源于stack exchange,提问作者Anvar Primov

火山引擎 最新活动