admin 管理员组

文章数量: 887007

1.word结构

参考:java通过poi解析word入门_java poi word-CSDN博客

word文档与xml关系_word文件是xml吧-CSDN博客

word的主体结构是包括xml和一些的压缩文件,解压后的目录类似为:

主体是document.xml。

用poi框架操作word文档,类似dom编程。但是编程较繁琐,最好有一个初始word模板。

2.word文档基本标签操作

maven包:

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>

这里只有基本操作,并没有对api深人了解。

2.1 word文档结构参考

文档内容

 大致结构

文档由段落(XWPFParagraph)、表格(XWPFTable)、引注(XWPFSDT)组成的,表格中包含行(Row),行包含单元格(Cell)。cell有能包含表格和段落。所以文档是树结构。

2.2 document和body

有一个document标签和body标签。内容都在body标签中。

可能会有的需求:将多个word文档合并。底层也就是将多个文档的body标签里的内容合并。

目前还未找到好的解决办法。解决思路估计是将body标签中各个元素拷贝并添加在一起。

参考:https://stackoverflow/questions/70648296/merge-documents-by-inserting-content-into-a-new-page-using-poi/70700519#70700519

2.3 table

能创XWPFDocument.createTable()建表格,但是没样式。最好能复制一份模板中的表格再添加。

参考代码:

static public XWPFTable copyTable(XWPFDocument document , XWPFTable origin) throws XmlException, IOException {

        CTTbl ctTbl = CTTbl.Factory.parse(origin.getCTTbl().newInputStream());
        CTBody body = document.getDocument().getBody();
        CTTbl ctTbl1 = body.insertNewTbl(body.getTblList().size());//先新建表格关联body
        body.setTblArray(body.getTblList().size()-1,ctTbl);
        XWPFTable xwpfTable = new XWPFTable(ctTbl1, document);
        document.insertTable(document.getBodyElements().size(),xwpfTable);//关联表格
        return xwpfTable;
    }

这里参考了XWPFDocument.createTable()的写法,最后第二、三行代码如何去掉似乎也不影响生成word的内容?但可能无法取到封装类XWPFTable ,也就无法对表格进行操作,如创建行。

 2.4 row

 能调用table.createRow()创建行,但没样式。实际情况一个会在某一行下面创建样式一样的新行。需要注意需要先设置行内容,再调用table.addRow(newRow,pos+1)才能生效

SpringBoot操作XWPFTable(2)创建行和列 – 每天进步一点点

参考代码:

 static public XWPFTableRow createNewRowNext(XWPFTable table,XWPFTable module, int pos){
        try {
            CTRow ctrow = CTRow.Factory.parse(module.getRow(pos).getCtRow().newInputStream());
            XWPFTableRow newRow = new XWPFTableRow(ctrow, table);
//            table.addRow(newRow,pos+1);//添加内容之后再add
            return newRow;
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

2.5 cell 

cell中能添加多个段落,多个图片等类型。查看XWPFTableCell.setText源码发现,要在cell添加文本,必须至少有一个段落(paragraph)。

参考代码:

清空文本,并添加新内容

    static public void setCellText(XWPFTableCell cell,String text) {
        if(text==null){
            text="";
        }
        //清空所有段落
        List<XWPFParagraph> paragraphs = cell.getParagraphs();
        if(paragraphs.size()==0){
            cell.setText(text);
        }else{
            //删除单元格中的所有段落
            for (int i = 1; i < paragraphs.size();i++ ) {
                cell.removeParagraph(i);
            }
            List<XWPFRun> runs = paragraphs.get(0).getRuns();
            if(runs.size()>0){
                for(int i=1;i<runs.size();i++){
                    paragraphs.get(0).removeRun(i);
                }
                XWPFRun xwpfRun = paragraphs.get(0).getRuns().get(0);
                xwpfRun.setText(text,0);
            }else{
                cell.setText(text);
            }
        }
    }

创建图片

    static public void addPicInCell(InputStream inputStream, String imgPath, XWPFTableCell cell) throws IOException, InvalidFormatException {
        XWPFParagraph xwpfParagraph1 = cell.getParagraphs().get(0);
        xwpfParagraph1.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun cellRun1 = xwpfParagraph1.createRun();
        XWPFTableRow row = cell.getTableRow();
        XWPFTable table = row.getTable();
        int left = table.getCellMarginLeft();
        int right = table.getCellMarginRight();
        int top = table.getCellMarginTop();
        int bottom = table.getCellMarginBottom();
        int[] size = calculateImgSize(inputStream,cell.getWidth()-left-right,row.getHeight()-top-bottom);
        cellRun1.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_JPEG, imgPath, (size[0])*635, (size[1])*635);
    }

这里设置了图片自适应单元格尺寸,需要设置内边距。 

2.6 paragraph         

段落(paragraph)能添加图片,文本,换行,下一页。

对应的xml格式参考:

        <w:p>
            <w:r>
                <w:t>11111111111111111111111111111</w:t>
                <w:br w:type="page"/>
                <w:t>22222222222222222222222222</w:t>
            </w:r>
            <w:r/>
            <w:r/>
            <w:r>
                <w:t>hhhhhhhhhhhhhhhhhhhhhhhhh</w:t>
            </w:r>
        </w:p>
        <w:p>
            <w:r>
                <w:t>kkkkkkkkkkkkkkkkkkkkkkk</w:t>
            </w:r>
        </w:p>

设置下一页代码参考:

XWPFParagraph paragraph = document.createParagraph();
        XWPFRun run1 = paragraph.createRun();
        run1.addBreak(BreakType.PAGE);

3.工具类

package com.example20221209.demo.util;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author yaoct
 * @date 2024/10/30 15:30
 * @description:
 */
public class WordPoiUtil {

    //在下一行添加一行,样式与前一行相同
    static public XWPFTableRow createNewRowNext(XWPFTable table, int pos){
        try {
            CTRow ctrow = CTRow.Factory.parse(table.getRow(pos).getCtRow().newInputStream());
            XWPFTableRow newRow = new XWPFTableRow(ctrow, table);
//            table.addRow(newRow,pos+1);//添加内容之后再add
            return newRow;
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    //在下一行添加一行,样式与前一行相同
    static public XWPFTableRow createNewRowNext(XWPFTable table,XWPFTable module, int pos){
        try {
            CTRow ctrow = CTRow.Factory.parse(module.getRow(pos).getCtRow().newInputStream());
            XWPFTableRow newRow = new XWPFTableRow(ctrow, table);
//            table.addRow(newRow,pos+1);//添加内容之后再add
            return newRow;
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    static public void addPicInCell(InputStream inputStream, String imgPath, XWPFTableCell cell) throws IOException, InvalidFormatException {
        XWPFParagraph xwpfParagraph1 = cell.getParagraphs().get(0);
        xwpfParagraph1.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun cellRun1 = xwpfParagraph1.createRun();
        XWPFTableRow row = cell.getTableRow();
        XWPFTable table = row.getTable();
        int left = table.getCellMarginLeft();
        int right = table.getCellMarginRight();
        int top = table.getCellMarginTop();
        int bottom = table.getCellMarginBottom();
        int[] size = calculateImgSize(inputStream,cell.getWidth()-left-right,row.getHeight()-top-bottom);
        cellRun1.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_JPEG, imgPath, (size[0])* Units.EMU_PER_DXA, (size[1])*Units.EMU_PER_DXA);
    }

    //适配图片尺寸
    private static int[] calculateImgSize(InputStream inputStream, int width, int high) {
        //TODO
        return new int[]{width,high};
    }

    static public XWPFTable copyTableNextPage(XWPFDocument document , XWPFTable origin) throws XmlException, IOException {
        XWPFParagraph paragraph = document.createParagraph();
        XWPFRun run1 = paragraph.createRun();
        run1.addBreak(BreakType.PAGE);

        CTTbl ctTbl = CTTbl.Factory.parse(origin.getCTTbl().newInputStream());
        CTBody body = document.getDocument().getBody();
        CTTbl ctTbl1 = body.insertNewTbl(body.getTblList().size());//先新建表格关联body
        body.setTblArray(body.getTblList().size()-1,ctTbl);
        XWPFTable xwpfTable = new XWPFTable(ctTbl1, document);
        document.insertTable(document.getBodyElements().size(),xwpfTable);//关联表格
        return xwpfTable;
    }

    static public void nextPage(XWPFDocument document) throws XmlException, IOException {
        XWPFParagraph paragraph = document.createParagraph();
        XWPFRun run1 = paragraph.createRun();
        run1.addBreak(BreakType.PAGE);
    }

    static public XWPFTable copyTable(XWPFDocument document , XWPFTable origin) throws XmlException, IOException {

        CTTbl ctTbl = CTTbl.Factory.parse(origin.getCTTbl().newInputStream());
        CTBody body = document.getDocument().getBody();
        CTTbl ctTbl1 = body.insertNewTbl(body.getTblList().size());//先新建表格关联body
        body.setTblArray(body.getTblList().size()-1,ctTbl);
        XWPFTable xwpfTable = new XWPFTable(ctTbl1, document);
        document.insertTable(document.getBodyElements().size(),xwpfTable);//关联表格
        return xwpfTable;
    }

    static public void setCellText(XWPFTableCell cell,String text) {
        if(text==null){
            text="";
        }
        //清空所有段落
        List<XWPFParagraph> paragraphs = cell.getParagraphs();
        if(paragraphs.size()==0){
            cell.setText(text);
        }else{
            //删除单元格中的所有段落
            for (int i = 1; i < paragraphs.size();i++ ) {
                cell.removeParagraph(i);
            }
            List<XWPFRun> runs = paragraphs.get(0).getRuns();
            if(runs.size()>0){
                for(int i=1;i<runs.size();i++){
                    paragraphs.get(0).removeRun(i);
                }
                XWPFRun xwpfRun = paragraphs.get(0).getRuns().get(0);
                xwpfRun.setText(text,0);
            }else{
                cell.setText(text);
            }
        }
    }


    /**
     * @Description: 跨列合并
     */
    static public void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
        for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
            XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
            if ( cellIndex == fromCell ) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
            }
        }
    }
    /**
     * @Description: 跨行合并
     * http://stackoverflow/questions/24907541/row-span-with-xwpftable
     */
    static public  void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
        for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
            XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
            if ( rowIndex == fromRow ) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    /**
     * 不完善
     * @param module
     * @param merge
     * @throws XmlException
     * @throws IOException
     */
    static public void copyModule(XWPFDocument module,XWPFDocument merge) throws XmlException, IOException {
        List<IBodyElement> bodyElements = module.getBodyElements();
        for(int i=0;i<3;i++){
            for (IBodyElement bodyElement : bodyElements) {
                if(bodyElement instanceof XWPFParagraph){
                    XWPFParagraph paragraph=(XWPFParagraph)bodyElement;
                    CTP ctp = CTP.Factory.parse(paragraph.getCTP().newInputStream());
                    CTBody body = merge.getDocument().getBody();
                    body.addNewP();
                    body.setPArray(body.getPList().size()-1,ctp);
//                    CTP ctp1 = body.insertNewP(body.getPList().size());//先新建段落关联body
//                    body.setPArray(body.getPList().size()-1,ctp);
//                    XWPFParagraph paragraph1 = new XWPFParagraph(ctp1, merge);
                    merge.createParagraph();
//                    merge.setParagraph(paragraph1,merge.getBodyElements().size()-1);

                }else if(bodyElement instanceof XWPFTable){
                    XWPFTable table=(XWPFTable)bodyElement;
                    CTTbl ctTbl = CTTbl.Factory.parse(table.getCTTbl().newInputStream());
                    CTBody body = merge.getDocument().getBody();
                    CTTbl ctTbl1 = body.insertNewTbl(body.getTblList().size());//先新建表格关联body
                    body.setTblArray(body.getTblList().size()-1,ctTbl);
                    XWPFTable xwpfTable = new XWPFTable(ctTbl1, merge);
                    merge.insertTable(merge.getBodyElements().size(),xwpfTable);//关联表格

                }else if(bodyElement instanceof XWPFSDT){
                    XWPFSDT sdt=(XWPFSDT)bodyElement;
//                    CTSdtBlock.Factory.parse();
                    CTBody body = merge.getDocument().getBody();
//                    body.insertNewSdt(body.getSdtList().size()-1);
//                    body.setSdtArray(body.getSdtList().size()-1,ctTbl1);
//                    body.insertNewSdt()
                }
            }
            //下一页
//            XWPFParagraph paragraph = merge.createParagraph();
//            XWPFRun run1 = paragraph.createRun();
//            run1.addBreak(BreakType.PAGE);
        }
    }
}

参考:

https://stackoverflow/questions/70648296/merge-documents-by-inserting-content-into-a-new-page-using-poi/70700519#70700519

SpringBoot操作XWPFTable(2)创建行和列 – 每天进步一点点

POI:对Word的基本操作_poi操作word-CSDN博客

java通过poi解析word入门_java poi word-CSDN博客

本文标签: 操作 Word poi