java使用poi操作word, 支持動態的行(一個佔位符插入多條)和表格中動態行, 支持圖片)

模板圖

希望可以幫到大家,希望給個start
項目git源碼地址
在這裏插入圖片描述

效果圖

在這裏插入圖片描述

1,引入maven依賴

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

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

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

2,核心工具類

package per.qiao.utils.hutool.poi;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.*;

/**
 * Create by IntelliJ Idea 2018.2
 *
 * @author: qyp
 * Date: 2019-10-25 14:48
 */
public class DynWordUtils {

    private final Logger logger = LoggerFactory.getLogger(DynWordUtils.class);

    /**
     * 被list替換的段落 被替換的都是oldParagraph
     */
    private XWPFParagraph oldParagraph;

    /**
     * 參數
     */
    private Map<String, Object> paramMap;

    /**
     * 當前元素的位置
     */
    int n = 0;

    /**
     * 判斷當前是否是遍歷的表格
     */
    boolean isTable = false;

    /**
     * 模板對象
     */
    XWPFDocument templateDoc;

    /**
     * 默認字體的大小
     */
    final int DEFAULT_FONT_SIZE = 10;

    /**
     * 重複模式的佔位符所在的行索引
     */
    private int currentRowIndex;

    /**
     * 入口
     *
     * @param paramMap     模板中使用的參數
     * @param templatePaht 模板全路徑
     * @param outPath      生成的文件存放的本地全路徑
     */
    public static void process(Map<String, Object> paramMap, String templatePaht, String outPath) {
        DynWordUtils dynWordUtils = new DynWordUtils();
        dynWordUtils.setParamMap(paramMap);
        dynWordUtils.createWord(templatePaht, outPath);
    }

    /**
     * 生成動態的word
     * @param templatePath
     * @param outPath
     */
    public void createWord(String templatePath, String outPath) {
        File inFile = new File(templatePath);
        try (FileOutputStream outStream = new FileOutputStream(outPath)) {
            templateDoc = new XWPFDocument(OPCPackage.open(inFile));
            parseTemplateWord();
            templateDoc.write(outStream);
        } catch (Exception e) {
            StackTraceElement[] stackTrace = e.getStackTrace();

            String className = stackTrace[0].getClassName();
            String methodName = stackTrace[0].getMethodName();
            int lineNumber = stackTrace[0].getLineNumber();

            logger.error("錯誤:第:{}行, 類名:{}, 方法名:{}", lineNumber, className, methodName);
            throw new RuntimeException(e.getCause().getMessage());
        }
    }

    /**
     * 解析word模板
     */
    public void parseTemplateWord() throws Exception {

        List<IBodyElement> elements = templateDoc.getBodyElements();

        for (; n < elements.size(); n++) {
            IBodyElement element = elements.get(n);
            // 普通段落
            if (element instanceof XWPFParagraph) {

                XWPFParagraph paragraph = (XWPFParagraph) element;
                oldParagraph = paragraph;
                if (paragraph.getParagraphText().isEmpty()) {
                    continue;
                }

                delParagraph(paragraph);

            } else if (element instanceof XWPFTable) {
                // 表格
                isTable = true;
                XWPFTable table = (XWPFTable) element;

                delTable(table, paramMap);
                isTable = false;
            }
        }

    }

    /**
     * 處理段落
     */
    private void delParagraph(XWPFParagraph paragraph) throws Exception {
        List<XWPFRun> runs = oldParagraph.getRuns();
        StringBuilder sb = new StringBuilder();
        for (XWPFRun run : runs) {
            String text = run.getText(0);
            if (text == null) {
                continue;
            }
            sb.append(text);
            run.setText("", 0);
        }
        Placeholder(paragraph, runs, sb);
    }


    /**
     * 匹配傳入信息集合與模板
     *
     * @param placeholder 模板需要替換的區域()
     * @param paramMap    傳入信息集合
     * @return 模板需要替換區域信息集合對應值
     */
    public void changeValue(XWPFRun currRun, String placeholder, Map<String, Object> paramMap) throws Exception {

        String placeholderValue = placeholder;
        if (paramMap == null || paramMap.isEmpty()) {
            return;
        }

        Set<Map.Entry<String, Object>> textSets = paramMap.entrySet();
        for (Map.Entry<String, Object> textSet : textSets) {
            //匹配模板與替換值 格式${key}
            String mapKey = textSet.getKey();
            String docKey = PoiWordUtils.getDocKey(mapKey);

            if (placeholderValue.indexOf(docKey) != -1) {
                Object obj = textSet.getValue();
                // 需要添加一個list
                if (obj instanceof List) {
                    placeholderValue = delDynList(placeholder, (List) obj);
                } else {
                    placeholderValue = placeholderValue.replaceAll(
                            PoiWordUtils.getPlaceholderReg(mapKey)
                            , String.valueOf(obj));
                }
            }
        }

        currRun.setText(placeholderValue, 0);
    }

    /**
     * 處理的動態的段落(參數爲list)
     *
     * @param placeholder 段落佔位符
     * @param obj
     * @return
     */
    private String delDynList(String placeholder, List obj) {
        String placeholderValue = placeholder;
        List dataList = obj;
        Collections.reverse(dataList);
        for (int i = 0, size = dataList.size(); i < size; i++) {
            Object text = dataList.get(i);
            // 佔位符的那行, 不用重新創建新的行
            if (i == 0) {
                placeholderValue = String.valueOf(text);
            } else {
                XWPFParagraph paragraph = createParagraph(String.valueOf(text));
                if (paragraph != null) {
                    oldParagraph = paragraph;
                }
                // 增加段落後doc文檔會的element的size會隨着增加(在當前行的上面添加),回退並解析新增的行(因爲可能新增的帶有佔位符,這裏爲了支持圖片和表格)
                if (!isTable) {
                    n--;
                }
            }
        }
        return placeholderValue;
    }

    /**
     * 創建段落 <p></p>
     *
     * @param texts
     */
    public XWPFParagraph createParagraph(String... texts) {

        // 使用遊標創建一個新行
        XmlCursor cursor = oldParagraph.getCTP().newCursor();
        XWPFParagraph newPar = templateDoc.insertNewParagraph(cursor);
        // 設置段落樣式
        newPar.getCTP().setPPr(oldParagraph.getCTP().getPPr());

        copyParagraph(oldParagraph, newPar, texts);

        return newPar;
    }

    /**
     * 處理表格(遍歷)
     *
     * @param table    表格
     * @param paramMap 需要替換的信息集合
     */
    public void delTable(XWPFTable table, Map<String, Object> paramMap) throws Exception {
        List<XWPFTableRow> rows = table.getRows();
        for (int i = 0, size = rows.size(); i < size; i++) {
            XWPFTableRow row = rows.get(i);
            currentRowIndex = i;
            // 如果是動態添加行 直接處理後返回
            if (delAndJudgeRow(table, paramMap, row)) {
                return;
            }
        }
    }

    /**
     * 判斷並且是否是動態行,並且處理表格佔位符
     * @param table 表格對象
     * @param paramMap 參數map
     * @param row 當前行
     * @return
     * @throws Exception
     */
    private boolean delAndJudgeRow(XWPFTable table, Map<String, Object> paramMap, XWPFTableRow row) throws Exception {
        // 當前行是動態行標誌
        if (PoiWordUtils.isAddRow(row)) {
            List<XWPFTableRow> xwpfTableRows = addAndGetRows(table, row, paramMap);
            // 回溯添加的行,這裏是試圖處理動態添加的圖片
            for (XWPFTableRow tbRow : xwpfTableRows) {
                delAndJudgeRow(table, paramMap, tbRow);
            }
            return true;
        }

        // 如果是重複添加的行
        if (PoiWordUtils.isAddRowRepeat(row)) {
            List<XWPFTableRow> xwpfTableRows = addAndGetRepeatRows(table, row, paramMap);
            // 回溯添加的行,這裏是試圖處理動態添加的圖片
            for (XWPFTableRow tbRow : xwpfTableRows) {
                delAndJudgeRow(table, paramMap, tbRow);
            }
            return true;
        }
        // 當前行非動態行標籤
        List<XWPFTableCell> cells = row.getTableCells();
        for (XWPFTableCell cell : cells) {
            //判斷單元格是否需要替換
            if (PoiWordUtils.checkText(cell.getText())) {
                List<XWPFParagraph> paragraphs = cell.getParagraphs();
                for (XWPFParagraph paragraph : paragraphs) {
                    List<XWPFRun> runs = paragraph.getRuns();
                    StringBuilder sb = new StringBuilder();
                    for (XWPFRun run : runs) {
                        sb.append(run.toString());
                        run.setText("", 0);
                    }
                    Placeholder(paragraph, runs, sb);
                }
            }
        }
        return false;
    }

    /**
     * 處理佔位符
     * @param runs 當前段的runs
     * @param sb 當前段的內容
     * @throws Exception
     */
    private void Placeholder(XWPFParagraph currentPar, List<XWPFRun> runs, StringBuilder sb) throws Exception {
        if (runs.size() > 0) {
            String text = sb.toString();
            XWPFRun currRun = runs.get(0);
            if (PoiWordUtils.isPicture(text)) {
                // 該段落是圖片佔位符
                ImageEntity imageEntity = (ImageEntity) PoiWordUtils.getValueByPlaceholder(paramMap, text);
                int indentationFirstLine = currentPar.getIndentationFirstLine();
                // 清除段落的格式,否則圖片的縮進有問題
                currentPar.getCTP().setPPr(null);
                //設置縮進
                currentPar.setIndentationFirstLine(indentationFirstLine);
                addPicture(currRun, imageEntity);
            } else {
                changeValue(currRun, text, paramMap);
            }
        }
    }

    /**
     * 添加圖片
     * @param currRun 當前run
     * @param imageEntity 圖片對象
     * @throws InvalidFormatException
     * @throws FileNotFoundException
     */
    private void addPicture(XWPFRun currRun, ImageEntity imageEntity) throws InvalidFormatException, FileNotFoundException {
        Integer typeId = imageEntity.getTypeId().getTypeId();
        String picId = currRun.getDocument().addPictureData(new FileInputStream(imageEntity.getUrl()), typeId);
        ImageUtils.createPicture(currRun, picId, templateDoc.getNextPicNameNumber(typeId),
                imageEntity.getWidth(), imageEntity.getHeight());
    }

    /**
     * 添加行  標籤行不是新創建的
     *
     * @param table
     * @param flagRow  flagRow 表有標籤的行
     * @param paramMap 參數
     */
    private List<XWPFTableRow> addAndGetRows(XWPFTable table, XWPFTableRow flagRow, Map<String, Object> paramMap) throws Exception {
        List<XWPFTableCell> flagRowCells = flagRow.getTableCells();
        XWPFTableCell flagCell = flagRowCells.get(0);

        String text = flagCell.getText();
        List<List<String>> dataList = (List<List<String>>) PoiWordUtils.getValueByPlaceholder(paramMap, text);

        // 新添加的行
        List<XWPFTableRow> newRows = new ArrayList<>(dataList.size());
        if (dataList == null || dataList.size() <= 0) {
            return newRows;
        }

        XWPFTableRow currentRow = flagRow;
        int cellSize = flagRow.getTableCells().size();
        for (int i = 0, size = dataList.size(); i < size; i++) {
            if (i != 0) {
                currentRow = table.createRow();
                // 複製樣式
                if (flagRow.getCtRow() != null) {
                    currentRow.getCtRow().setTrPr(flagRow.getCtRow().getTrPr());
                }
            }
            addRow(flagCell, currentRow, cellSize, dataList.get(i));
            newRows.add(currentRow);
        }
        return newRows;
    }

    /**
     * 添加重複多行 動態行  每一行都是新創建的
     * @param table
     * @param flagRow
     * @param paramMap
     * @return
     * @throws Exception
     */
    private List<XWPFTableRow> addAndGetRepeatRows(XWPFTable table, XWPFTableRow flagRow, Map<String, Object> paramMap) throws Exception {
        List<XWPFTableCell> flagRowCells = flagRow.getTableCells();
        XWPFTableCell flagCell = flagRowCells.get(0);
        String text = flagCell.getText();
        List<List<String>> dataList = (List<List<String>>) PoiWordUtils.getValueByPlaceholder(paramMap, text);
        String tbRepeatMatrix = PoiWordUtils.getTbRepeatMatrix(text);
        Assert.assertNotNull("模板矩陣不能爲空", tbRepeatMatrix);

        // 新添加的行
        List<XWPFTableRow> newRows = new ArrayList<>(dataList.size());
        if (dataList == null || dataList.size() <= 0) {
            return newRows;
        }

        String[] split = tbRepeatMatrix.split(PoiWordUtils.tbRepeatMatrixSeparator);
        int startRow = Integer.parseInt(split[0]);
        int endRow = Integer.parseInt(split[1]);
        int startCell = Integer.parseInt(split[2]);
        int endCell = Integer.parseInt(split[3]);

        XWPFTableRow currentRow;
        for (int i = 0, size = dataList.size(); i < size; i++) {
            int flagRowIndex = i % (endRow - startRow + 1);
            XWPFTableRow repeatFlagRow = table.getRow(flagRowIndex);
            // 清除佔位符那行
            if (i == 0) {
                table.removeRow(currentRowIndex);
            }
            currentRow = table.createRow();
            // 複製樣式
            if (repeatFlagRow.getCtRow() != null) {
                currentRow.getCtRow().setTrPr(repeatFlagRow.getCtRow().getTrPr());
            }
            addRowRepeat(startCell, endCell, currentRow, repeatFlagRow, dataList.get(i));
            newRows.add(currentRow);
        }
        return newRows;
    }

    /**
     * 根據模板cell添加新行
     *
     * @param flagCell    模板列(標記佔位符的那個cell)
     * @param row         新增的行
     * @param cellSize    每行的列數量(用來補列補足的情況)
     * @param rowDataList 每行的數據
     */
    private void addRow(XWPFTableCell flagCell, XWPFTableRow row, int cellSize, List<String> rowDataList) {
        for (int i = 0; i < cellSize; i++) {
            XWPFTableCell cell = row.getCell(i);
            cell = cell == null ? row.createCell() : row.getCell(i);
            if (i < rowDataList.size()) {
                PoiWordUtils.copyCellAndSetValue(flagCell, cell, rowDataList.get(i));
            } else {
                // 數據不滿整行時,添加空列
                PoiWordUtils.copyCellAndSetValue(flagCell, cell, "");
            }
        }
    }

    /**
     * 根據模板cell  添加重複行
     * @param startCell 模板列的開始位置
     * @param endCell 模板列的結束位置
     * @param currentRow 創建的新行
     * @param repeatFlagRow 模板列所在的行
     * @param rowDataList 每行的數據
     */
    private void addRowRepeat(int startCell, int endCell, XWPFTableRow currentRow, XWPFTableRow repeatFlagRow, List<String> rowDataList) {
        int cellSize = repeatFlagRow.getTableCells().size();
        for (int i = 0; i < cellSize; i++) {
            XWPFTableCell cell = currentRow.getCell(i);
            cell = cell == null ? currentRow.createCell() : currentRow.getCell(i);
            int flagCellIndex = i % (endCell - startCell + 1);
            XWPFTableCell repeatFlagCell = repeatFlagRow.getCell(flagCellIndex);
            if (i < rowDataList.size()) {
                PoiWordUtils.copyCellAndSetValue(repeatFlagCell, cell, rowDataList.get(i));
            } else {
                // 數據不滿整行時,添加空列
                PoiWordUtils.copyCellAndSetValue(repeatFlagCell, cell, "");
            }
        }
    }

    /**
     * 複製段落
     *
     * @param sourcePar 原段落
     * @param targetPar
     * @param texts
     */
    private void copyParagraph(XWPFParagraph sourcePar, XWPFParagraph targetPar, String... texts) {

        targetPar.setAlignment(sourcePar.getAlignment());
        targetPar.setVerticalAlignment(sourcePar.getVerticalAlignment());

        // 設置佈局
        targetPar.setAlignment(sourcePar.getAlignment());
        targetPar.setVerticalAlignment(sourcePar.getVerticalAlignment());

        if (texts != null && texts.length > 0) {
            String[] arr = texts;
            XWPFRun xwpfRun = sourcePar.getRuns().size() > 0 ? sourcePar.getRuns().get(0) : null;

            for (int i = 0, len = texts.length; i < len; i++) {
                String text = arr[i];
                XWPFRun run = targetPar.createRun();

                run.setText(text);

                run.setFontFamily(xwpfRun.getFontFamily());
                int fontSize = xwpfRun.getFontSize();
                run.setFontSize((fontSize == -1) ? DEFAULT_FONT_SIZE : fontSize);
                run.setBold(xwpfRun.isBold());
                run.setItalic(xwpfRun.isItalic());
            }
        }
    }

    public void setParamMap(Map<String, Object> paramMap) {
        this.paramMap = paramMap;
    }
}

poi工具類PoiWordUtils

package per.qiao.utils.hutool.poi;

import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.junit.Assert;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcBorders;

import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Create by IntelliJ Idea 2018.2
 *
 * @author: qyp
 * Date: 2019-10-26 2:12
 */
public class PoiWordUtils {

    /**
     * 佔位符第一個字符
     */
    public static final String PREFIX_FIRST = "$";

    /**
     * 佔位符第二個字符
     */
    public static final String PREFIX_SECOND = "{";

    /**
     * 佔位符的前綴
     */
    public static final String PLACEHOLDER_PREFIX = PREFIX_FIRST + PREFIX_SECOND;

    /**
     * 佔位符後綴
     */
    public static final String PLACEHOLDER_END = "}";

    /**
     * 表格中需要動態添加行的獨特標記
     */
    public static final String addRowText = "tbAddRow:";

    public static final String addRowRepeatText = "tbAddRowRepeat:";

    /**
     * 表格中佔位符的開頭 ${tbAddRow:  例如${tbAddRow:tb1}
     */
    public static final String addRowFlag = PLACEHOLDER_PREFIX + addRowText;

    /**
     * 表格中佔位符的開頭 ${tbAddRowRepeat:  例如 ${tbAddRowRepeat:0,2,0,1} 第0行到第2行,第0列到第1列 爲模板樣式
     */
    public static final String addRowRepeatFlag = PLACEHOLDER_PREFIX + addRowRepeatText;

    /**
     * 重複矩陣的分隔符  比如:${tbAddRowRepeat:0,2,0,1} 分隔符爲 ,
     */
    public static final String tbRepeatMatrixSeparator = ",";

    /**
     * 佔位符的後綴
     */
    public static final String PLACEHOLDER_SUFFIX = "}";

    /**
     * 圖片佔位符的前綴
     */
    public static final String PICTURE_PREFIX = PLACEHOLDER_PREFIX + "image:";

    /**
     * 判斷當前行是不是標誌表格中需要添加行
     *
     * @param row
     * @return
     */
    public static boolean isAddRow(XWPFTableRow row) {
        return isDynRow(row, addRowFlag);
    }

    /**
     * 添加重複模板動態行(以多行爲模板)
     * @param row
     * @return
     */
    public static boolean isAddRowRepeat(XWPFTableRow row) {
        return isDynRow(row, addRowRepeatFlag);
    }

    private static boolean isDynRow(XWPFTableRow row, String dynFlag) {
        if (row == null) {
            return false;
        }
        List<XWPFTableCell> tableCells = row.getTableCells();
        if (tableCells != null) {
            XWPFTableCell cell = tableCells.get(0);
            if (cell != null) {
                String text = cell.getText();
                if (text != null && text.startsWith(dynFlag)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 從參數map中獲取佔位符對應的值
     *
     * @param paramMap
     * @param key
     * @return
     */
    public static Object getValueByPlaceholder(Map<String, Object> paramMap, String key) {
        if (paramMap != null) {
            if (key != null) {
                return paramMap.get(getKeyFromPlaceholder(key));
            }
        }
        return null;
    }

    /**
     * 後去佔位符的重複行列矩陣
     * @param key 佔位符
     * @return {0,2,0,1}
     */
    public static String getTbRepeatMatrix(String key) {
        Assert.assertNotNull("佔位符爲空", key);
        String $1 = key.replaceAll("\\" + PREFIX_FIRST + "\\" + PREFIX_SECOND + addRowRepeatText + "(.*)" + "\\" + PLACEHOLDER_SUFFIX, "$1");
        return $1;
    }

    /**
     * 從佔位符中獲取key
     *
     * @return
     */
    public static String getKeyFromPlaceholder(String placeholder) {
        return Optional.ofNullable(placeholder).map(p -> p.replaceAll("[\\$\\{\\}]", "")).get();
    }

    public static void main(String[] args) {
        String s = "${aa}";
        s = s.replaceAll(PLACEHOLDER_PREFIX + PLACEHOLDER_SUFFIX , "");
        System.out.println(s);
//        String keyFromPlaceholder = getKeyFromPlaceholder("${tbAddRow:tb1}");
//        System.out.println(keyFromPlaceholder);
    }

    /**
     * 複製列的樣式,並且設置值
     * @param sourceCell
     * @param targetCell
     * @param text
     */
    public static void copyCellAndSetValue(XWPFTableCell sourceCell, XWPFTableCell targetCell, String text) {
        //段落屬性
        List<XWPFParagraph> sourceCellParagraphs = sourceCell.getParagraphs();
        if (sourceCellParagraphs == null || sourceCellParagraphs.size() <= 0) {
            return;
        }
        XWPFParagraph sourcePar = sourceCellParagraphs.get(0);
        XWPFParagraph targetPar = targetCell.getParagraphs().get(0);

        // 設置段落的樣式
        targetPar.getCTP().setPPr(sourcePar.getCTP().getPPr());
//        CTTcBorders tcBorders = sourceCell.getCTTc().getTcPr().getTcBorders();
//        targetCell.getCTTc().getTcPr().setTcBorders(tcBorders);

        List<XWPFRun> sourceParRuns = sourcePar.getRuns();
        if (sourceParRuns != null && sourceParRuns.size() > 0) {
            // 如果當前cell中有run
            List<XWPFRun> runs = targetPar.getRuns();
            Optional.ofNullable(runs).ifPresent(rs -> rs.stream().forEach(r -> r.setText("", 0)));
            if (runs != null && runs.size() > 0) {
                runs.get(0).setText(text, 0);
            } else {
                XWPFRun cellR = targetPar.createRun();
                cellR.setText(text, 0);
                // 設置列的樣式位模板的樣式
                targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
            }
            setTypeface(sourcePar, targetPar);
        } else {
            targetCell.setText(text);
        }
    }
    /**
     * 複製字體
     */
    private static void setTypeface(XWPFParagraph sourcePar, XWPFParagraph targetPar) {
        XWPFRun sourceRun = sourcePar.getRuns().get(0);
        String fontFamily = sourceRun.getFontFamily();
        //int fontSize = sourceRun.getFontSize();
        String color = sourceRun.getColor();
//        String fontName = sourceRun.getFontName();
        boolean bold = sourceRun.isBold();
        boolean italic = sourceRun.isItalic();
        int kerning = sourceRun.getKerning();
//        String style = sourcePar.getStyle();
        UnderlinePatterns underline = sourceRun.getUnderline();

        XWPFRun targetRun = targetPar.getRuns().get(0);
        targetRun.setFontFamily(fontFamily);
//        targetRun.setFontSize(fontSize == -1 ? 10 : fontSize);
        targetRun.setBold(bold);
        targetRun.setColor(color);
        targetRun.setItalic(italic);
        targetRun.setKerning(kerning);
        targetRun.setUnderline(underline);
        //targetRun.setFontSize(fontSize);
    }
    /**
     * 判斷文本中時候包含$
     * @param text 文本
     * @return 包含返回true,不包含返回false
     */
    public static boolean checkText(String text){
        boolean check  =  false;
        if(text.indexOf(PLACEHOLDER_PREFIX)!= -1){
            check = true;
        }
        return check;
    }

    /**
     * 獲得佔位符替換的正則表達式
     * @return
     */
    public static String getPlaceholderReg(String text) {
        return "\\" + PREFIX_FIRST + "\\" + PREFIX_SECOND + text + "\\" + PLACEHOLDER_SUFFIX;
    }

    public static String getDocKey(String mapKey) {
        return PLACEHOLDER_PREFIX + mapKey + PLACEHOLDER_SUFFIX;
    }

    /**
     * 判斷當前佔位符是不是一個圖片佔位符
     * @param text
     * @return
     */
    public static boolean isPicture(String text) {
        return text.startsWith(PICTURE_PREFIX);
    }

    /**
     * 刪除一行的列
     * @param row
     */
    public static void removeCells(XWPFTableRow row) {
        int size = row.getTableCells().size();
        try {
            for (int i = 0; i < size; i++) {
                row.removeCell(i);
            }
        } catch (Exception e) {

        }
    }
}

對圖片的支持ImageEntity

package per.qiao.utils.hutool.poi;

import static per.qiao.utils.hutool.poi.ImageUtils.ImageType.PNG;

/**
 * Create by IntelliJ Idea 2018.2
 *
 * 圖片實體對象
 *
 * @author: qyp
 * Date: 2019-10-26 21:52
 */
public class ImageEntity {

    /**
     * 圖片寬度
     */
    private int width = 400;

    /**
     * 圖片高度
     */
    private int height = 300;

    /**
     * 圖片地址
     */
    private String url;

    /**
     * 圖片類型
     * @see ImageUtils.ImageType
     */
    private ImageUtils.ImageType typeId = PNG;

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public ImageUtils.ImageType getTypeId() {
        return typeId;
    }

    public void setTypeId(ImageUtils.ImageType typeId) {
        this.typeId = typeId;
    }
}

圖片工具類ImageUtils

package per.qiao.utils.hutool.poi;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;

/**
 * Create by IntelliJ Idea 2018.2
 *
 * @author: qyp
 * Date: 2019-10-26 21:55
 */
public class ImageUtils {

    /**
     * 圖片類型枚舉
     */
    enum ImageType {

        /**
         * 支持四種類型 JPG/JPEG, GIT, BMP, PNG
         */
        JPG("JPG", XWPFDocument.PICTURE_TYPE_JPEG),
        JPEG("JPEG", XWPFDocument.PICTURE_TYPE_JPEG),
        GIF("GIF", XWPFDocument.PICTURE_TYPE_GIF),
        BMP("BMP", XWPFDocument.PICTURE_TYPE_GIF),
        PNG("PNG", XWPFDocument.PICTURE_TYPE_PNG)
        ;
        private String name;
        private Integer typeId;
        ImageType(String name, Integer type) {
            this.name = name;
            this.typeId = type;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getTypeId() {
            return typeId;
        }

        public void setTypeId(Integer typeId) {
            this.typeId = typeId;
        }
    }


    public static void createPicture(XWPFRun run, String blipId, int id, int width, int height) {
        final int EMU = 9525;
        width *= EMU;
        height *= EMU;
        CTInline inline = run.getCTR().addNewDrawing().addNewInline();

        String picXml = "" +
                "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
                "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                "         <pic:nvPicPr>" +
                "            <pic:cNvPr id=\"" + id + "\" name=\"Generated\"/>" +
                "            <pic:cNvPicPr/>" +
                "         </pic:nvPicPr>" +
                "         <pic:blipFill>" +
                "            <a:blip r:embed=\"" + blipId + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" +
                "            <a:stretch>" +
                "               <a:fillRect/>" +
                "            </a:stretch>" +
                "         </pic:blipFill>" +
                "         <pic:spPr>" +
                "            <a:xfrm>" +
                "               <a:off x=\"0\" y=\"0\"/>" +
                "               <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" +
                "            </a:xfrm>" +
                "            <a:prstGeom prst=\"rect\">" +
                "               <a:avLst/>" +
                "            </a:prstGeom>" +
                "         </pic:spPr>" +
                "      </pic:pic>" +
                "   </a:graphicData>" +
                "</a:graphic>";

        XmlToken xmlToken = null;
        try {
            xmlToken = XmlToken.Factory.parse(picXml);
        } catch(XmlException xe) {
            xe.printStackTrace();
        }
        inline.set(xmlToken);

        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);

        CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);

        CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("Picture " + id);
        docPr.setDescr("Generated");
    }

}

測試類

package per.qiao.utils.hutool.poi;

import org.junit.Test;

import java.util.*;

/**
 * Create by IntelliJ Idea 2018.2
 *
 * @author: qyp
 * Date: 2019-10-26 17:34
 */
public class DynWordUtilsTest {

    /**
     * 說明 普通佔位符位${field}格式
     * 表格中的佔位符爲${tbAddRow:tb1}  tb1爲唯一標識符
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) {

        // 模板全的路徑
        String templatePaht = "E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\審查報告模板1023體檢表.docx";
        // 輸出位置
        String outPath = "e:\\22.docx";

        Map<String, Object> paramMap = new HashMap<>(16);
        // 普通的佔位符示例 參數數據結構 {str,str}
        paramMap.put("title", "德瑪西亞");
        paramMap.put("startYear", "2010");
        paramMap.put("endYear", "2020");
        paramMap.put("currentYear", "2019");
        paramMap.put("currentMonth", "10");
        paramMap.put("currentDate", "26");
        paramMap.put("name", "黑色玫瑰");

        // 段落中的動態段示例 [str], 支持動態行中添加圖片
        List<Object> list1 = new ArrayList<>(Arrays.asList("2、list1_11111", "3、list1_2222", "${image:image0}"));
        ImageEntity imgEntity = new ImageEntity();
        imgEntity.setHeight(200);
        imgEntity.setWidth(300);
        imgEntity.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
        imgEntity.setTypeId(ImageUtils.ImageType.JPG);

        paramMap.put("image:image0", imgEntity);
        paramMap.put("list1", list1);

        List<String> list2 = new ArrayList<>(Arrays.asList("2、list2_11111", "3、list2_2222"));
        paramMap.put("list2", list2);

        // 表格中的參數示例 參數數據結構 [[str]]
        List<List<String>> tbRow1 = new ArrayList<>();
        List<String> tbRow1_row1 = new ArrayList<>(Arrays.asList("1、模塊一", "分類1"));
        List<String> tbRow1_row2 = new ArrayList<>(Arrays.asList("2、模塊二", "分類2"));
        tbRow1.add(tbRow1_row1);
        tbRow1.add(tbRow1_row2);
        paramMap.put(PoiWordUtils.addRowText + "tb1", tbRow1);

        List<List<String>> tbRow2 = new ArrayList<>();
        List<String> tbRow2_row1 = new ArrayList<>(Arrays.asList("指標c", "指標c的意見"));
        List<String> tbRow2_row2 = new ArrayList<>(Arrays.asList("指標d", "指標d的意見"));
        tbRow2.add(tbRow2_row1);
        tbRow2.add(tbRow2_row2);
        paramMap.put(PoiWordUtils.addRowText + "tb2", tbRow2);

        List<List<String>> tbRow3 = new ArrayList<>();
        List<String> tbRow3_row1 = new ArrayList<>(Arrays.asList("3", "耕地估值"));
        List<String> tbRow3_row2 = new ArrayList<>(Arrays.asList("4", "耕地歸屬", "平方公里"));
        tbRow3.add(tbRow3_row1);
        tbRow3.add(tbRow3_row2);
        paramMap.put(PoiWordUtils.addRowText + "tb3", tbRow3);

        // 支持在表格中動態添加圖片
        List<List<String>> tbRow4 = new ArrayList<>();
        List<String> tbRow4_row1 = new ArrayList<>(Arrays.asList("03", "旅遊用地", "18.8m2"));
        List<String> tbRow4_row2 = new ArrayList<>(Arrays.asList("04", "建築用地"));
        List<String> tbRow4_row3 = new ArrayList<>(Arrays.asList("04", "${image:image3}"));
        tbRow4.add(tbRow4_row3);
        tbRow4.add(tbRow4_row1);
        tbRow4.add(tbRow4_row2);

        // 支持在表格中添加重複模板的行
        List<List<String>> tbRow5 = new ArrayList<>();
        List<String> tbRow5_row1 = new ArrayList<>(Arrays.asList("歡樂喜劇人"));
        List<String> tbRow5_row2 = new ArrayList<>(Arrays.asList("常遠", "艾倫"));
        List<String> tbRow5_row3 = new ArrayList<>(Arrays.asList("岳雲鵬", "孫越"));

        List<String> tbRow5_row4 = new ArrayList<>(Arrays.asList("諾克薩斯"));
        List<String> tbRow5_row5 = new ArrayList<>(Arrays.asList("德萊文", "諾手"));
        List<String> tbRow5_row6 = new ArrayList<>(Arrays.asList("男槍", "卡特琳娜"));

        tbRow5.add(tbRow5_row1);
        tbRow5.add(tbRow5_row2);
        tbRow5.add(tbRow5_row3);
        tbRow5.add(tbRow5_row4);
        tbRow5.add(tbRow5_row5);
        tbRow5.add(tbRow5_row6);
        paramMap.put("tbAddRowRepeat:0,2,0,1", tbRow5);

        ImageEntity imgEntity3 = new ImageEntity();
        imgEntity3.setHeight(100);
        imgEntity3.setWidth(100);
        imgEntity3.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
        imgEntity3.setTypeId(ImageUtils.ImageType.JPG);

        paramMap.put(PoiWordUtils.addRowText + "tb4", tbRow4);
        paramMap.put("image:image3", imgEntity3);

        // 圖片佔位符示例 ${image:imageid} 比如 ${image:image1}, ImageEntity中的值就爲image:image1
        // 段落中的圖片
        ImageEntity imgEntity1 = new ImageEntity();
        imgEntity1.setHeight(500);
        imgEntity1.setWidth(400);
        imgEntity1.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
        imgEntity1.setTypeId(ImageUtils.ImageType.JPG);
        paramMap.put("image:image1", imgEntity1);

        // 表格中的圖片
        ImageEntity imgEntity2 = new ImageEntity();
        imgEntity2.setHeight(200);
        imgEntity2.setWidth(100);
        imgEntity2.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
        imgEntity2.setTypeId(ImageUtils.ImageType.JPG);
        paramMap.put("image:image2", imgEntity2);

        DynWordUtils.process(paramMap, templatePaht, outPath);
    }


    @Test
    public void testImage() {

        Map<String, Object> paramMap = new HashMap<>(16);
        String templatePaht = "E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\11.docx";
        String outPath = "e:\\3.docx";
        ImageEntity imgEntity1 = new ImageEntity();
        imgEntity1.setHeight(500);
        imgEntity1.setWidth(400);
        imgEntity1.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
        imgEntity1.setTypeId(ImageUtils.ImageType.JPG);

        paramMap.put("image:img1", imgEntity1);
        DynWordUtils.process(paramMap, templatePaht, outPath);
    }
}

模板圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章