写图片到word中

前言

  在上一篇中已经生成了Jpg的echarts图片,在很久之前写了一个根据模板写word的工具代码,

  工具代码,https://www.cnblogs.com/weiyanei/p/17040015.html

  下面代码是定义工具代码中表格的行为,

//每个表格多少行表头
public static int[] headCount = new int[]{1,1,1};
//每个表格列数
public static int[] cellsCount = new int[]{6,7,8};

工具类修改

  将以前的工具类替换下面的方法,增加了插入图片的代码。

/**
     * 替换段落文本
     * @param document docx解析对象
     * @param textMap 需要替换的信息集合
     */
public static void changeText(XWPFDocument document, Map<String, String> textMap){
    //获取段落集合
    List<XWPFParagraph> paragraphs = document.getParagraphs();

    for (XWPFParagraph paragraph : paragraphs) {
        //判断此段落时候需要进行替换
        String text = paragraph.getText();
        if(checkText(text)){
            List<XWPFRun> runs = paragraph.getRuns();
            for (XWPFRun run : runs) {
                //替换模板原来位置
                String value = changeValue(run.toString(), textMap);
                if(value.indexOf("img|")>=0){
                    value = value.split("\\|")[1];
                    //插入图片
                    try {
                        run.setText("",0);
                        run.addPicture(new FileInputStream(value),XWPFDocument.PICTURE_TYPE_JPEG,"", Units.toEMU(400),Units.toEMU(200));
                    } catch (InvalidFormatException|IOException e) {
                        log.error("",e);
                    }
                }else{
                    run.setText(value,0);
                }
            }
        }
    }

}




/**
     * 匹配传入信息集合与模板
     * @param value 模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
public static String changeValue(String value, Map<String, String> textMap){
    Set<Entry<String, String>> textSets = textMap.entrySet();
    for (Entry<String, String> textSet : textSets) {
        //匹配模板与替换值 格式${key}
        String keyName = textSet.getKey();
        String key = "${"+keyName+"}";
        if(value.indexOf(key)!= -1){
            //增加特殊处理,判断是否哟啊插入图片
            if(keyName.indexOf("img")>=0){//说明此处要插入图片
                String picPath = textSet.getValue();//图片路径
                value = "img|"+picPath;
            }else{
                value = textSet.getValue();
            }
        }
    }
    //模板未匹配到区域替换为空
    if(checkText(value)){
        value = "";
    }
    return value;
}

  word中插入图片使用,

run.addPicture(new FileInputStream(value),XWPFDocument.PICTURE_TYPE_JPEG,"", Units.toEMU(400),Units.toEMU(200))

  根据上述代码可以知道,主要是识别是否有${img}这个key,所以如果有多张图片,就直接使用 ${img01},${img02},只要里面是img开头就行,所以img就是图片的特定字段。

  img对应的value就是图片的文件地址,即上一篇中返回的图片地址。

  模板也放在resources下的files即可,如下图

使用

  生成图片的方法是异步的,因为可能会耗点时,所以在生成图片时用来干点其他,不至于阻塞了。

/**
     * 生成图表图片
     */
private FutureTask<PageData> createPic(Map<String,FactoryInfo> factoryMap){
    FutureTask<PageData> future =
        new FutureTask<>(new Callable<PageData>() {
            public PageData call() {
                try {
                    return echartsService.generateBarImage(packageBarOption(factoryMap));
                } catch (Exception e) {
                    logger.error("", e);
                    return RespUtil.getErrorResp(RespUtil.SYSTEM_ERROR_CODE,RespUtil.SYSTEM_ERROR_MSG);
                }
            }
        });

    executorService.execute(new Runnable() {
        @Override
        public void run() {
            future.run();
        }
    });
    return future;
}

  

  在主方法中调用

//创建图表
boolean hasPic = false;
FutureTask<PageData> future = this.createPic(factoryMap);



String picPath = this.getClass().getClassLoader().getResource("files").getPath()+File.separator+"bar.jpg";
try {
    PageData pageData = future.get();
    if(pageData.getString("code").equals("0")){
        hasPic = true;
    }
    if(hasPic){//图片已经产生
        map.put("desc_image","本月各工厂样品管理信息,如下图:");
        picPath = pageData.getString("picPath"); //获取图片路径
        map.put("img",picPath);
    }
} catch (InterruptedException | ExecutionException e) {
    logger.error("",e);
}


String inputUrl = "model.docx";

//创建word
boolean hasWord = false;
String reportName = "ats_report_"+huibaoMonthStr+"-"+UUID.randomUUID().toString().replaceAll("-","")+".docx";
try{
    String path = this.getClass().getClassLoader().getResource("files/"+inputUrl).getPath();
    String filePath = URLDecoder.decode(path, "UTF-8");
    logger.info(filePath);
    inputUrl = filePath;

    String outPath = this.getClass().getClassLoader().getResource("files").getPath();
    outPath += File.separator+reportName;
    WordToNewWordUtils.changWord(inputUrl, outPath, map, list);
    hasWord = true;
}catch (IOException e){
    logger.error("",e);
}

  执行创建word后就可以在工程下看到有docx文件生成,

  

  如果不想生成docx文件,直接下载的话,那么只需要重载工具类的方法,把文件输出改为输出文件流就行,如下是重载方法,

public static boolean changWord(String inputUrl, OutputStream fileOutputStream,
                                    Map<String, String> textMap, List<List<List<String>>> tableList) {

    //模板转换默认成功
    boolean changeFlag = true;
    nowTableIndex = -1;
    try {
        //获取docx解析对象
        XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
        //解析替换文本段落对象
        WordToNewWordUtils.changeText(document, textMap);
        //解析替换表格对象
        WordToNewWordUtils.changeTable(document, textMap, tableList);

        //生成新的word
        document.write(fileOutputStream);
    } catch (IOException e) {
        log.error("生成word文档:",e);
        changeFlag = false;
    }

    return changeFlag;

}

  使用的话如下所示,response是 HttpServletResponse,就是http的请求输出流,这样就实现文件下载了。

try{
    String path = this.getClass().getClassLoader().getResource("files/"+inputUrl).getPath();
    String filePath = URLDecoder.decode(path, "UTF-8");
    log.info(filePath);
    inputUrl = filePath;

    response.setContentType("application/msword;charset=utf-8");
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outputFileName, "UTF-8"));
    OutputStream outputStream = response.getOutputStream();
    WordToNewWordUtils.changWord(inputUrl, outputStream, modelMap, list);
}catch (IOException e){
    log.error("",e);
}

 

完整的工具代码

package com.staryea.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;

import java.io.*;
import java.util.*;
import java.util.Map.Entry;

public class WordToNewWordUtils {

    protected static Log log = LogFactory.getLog(WordToNewWordUtils.class);

    //每个表格多少行表头
    public static int[] headCount = new int[]{1,1,1};
    //每个表格列数
    public static int[] cellsCount = new int[]{6,7,8};
    private static int nowTableIndex = -1;

    /**
     * 根据模板生成新word文档
     * 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
     * @param inputUrl 模板存放地址
     * @param outputUrl 新文档存放地址
     * @param textMap 需要替换的信息集合
     * @param tableList 需要插入的表格信息集合
     * @return 成功返回true,失败返回false
     */
    public static boolean changWord(String inputUrl, String outputUrl,
                                    Map<String, String> textMap, List<List<List<String>>> tableList) {

        //模板转换默认成功
        boolean changeFlag = true;
        nowTableIndex = -1;
        try {
            //获取docx解析对象
            XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
            //解析替换文本段落对象
            WordToNewWordUtils.changeText(document, textMap);
            //解析替换表格对象
            WordToNewWordUtils.changeTable(document, textMap, tableList);

            //生成新的word
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();

        } catch (IOException e) {
            log.error("生成word文档:",e);
            changeFlag = false;
        }

        return changeFlag;

    }

    public static boolean changWord(String inputUrl, OutputStream fileOutputStream,
                                    Map<String, String> textMap, List<List<List<String>>> tableList) {

        //模板转换默认成功
        boolean changeFlag = true;
        nowTableIndex = -1;
        try {
            //获取docx解析对象
            XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
            //解析替换文本段落对象
            WordToNewWordUtils.changeText(document, textMap);
            //解析替换表格对象
            WordToNewWordUtils.changeTable(document, textMap, tableList);

            //生成新的word
            document.write(fileOutputStream);
        } catch (IOException e) {
            log.error("生成word文档:",e);
            changeFlag = false;
        }

        return changeFlag;

    }

    /**
     * 替换段落文本
     * @param document docx解析对象
     * @param textMap 需要替换的信息集合
     */
    public static void changeText(XWPFDocument document, Map<String, String> textMap){
        //获取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();

        for (XWPFParagraph paragraph : paragraphs) {
            //判断此段落时候需要进行替换
            String text = paragraph.getText();
            if(checkText(text)){
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    //替换模板原来位置
                    String value = changeValue(run.toString(), textMap);
                    if(value.indexOf("img|")>=0){
                        value = value.split("\\|")[1];
                        //插入图片
                        try {
                            run.setText("",0);
                            run.addPicture(new FileInputStream(value),XWPFDocument.PICTURE_TYPE_JPEG,"", Units.toEMU(400),Units.toEMU(200));
                        } catch (InvalidFormatException|IOException e) {
                            log.error("",e);
                        }
                    }else{
                        run.setText(value,0);
                    }
                }
            }
        }

    }

    /**
     * 替换表格对象方法
     * @param document docx解析对象
     * @param textMap 需要替换的信息集合
     * @param tableList 需要插入的表格信息集合
     */
    public static void changeTable(XWPFDocument document, Map<String, String> textMap,
                                   List<List<List<String>>> tableList){
        //获取表格对象集合
        List<XWPFTable> tables = document.getTables();
        for (int i = 0; i < tables.size(); i++) {
            //只处理行数大于等于2的表格,且不循环表头
            XWPFTable table = tables.get(i);
            if(table.getRows().size()>1){
                //判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
                if(checkText(table.getText())){
                    List<XWPFTableRow> rows = table.getRows();
                    //遍历表格,并替换模板
                    eachTable(rows, textMap);
                }else{
//                  System.out.println("插入"+table.getText());
                    List<List<String>> oneTableContent = tableList.get(i);
                    nowTableIndex++;
                    insertTable(table, oneTableContent);
                }
            }
        }
    }





    /**
     * 遍历表格
     * @param rows 表格行对象
     * @param textMap 需要替换的信息集合
     */
    public static void eachTable(List<XWPFTableRow> rows ,Map<String, String> textMap){
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                //判断单元格是否需要替换
                if(checkText(cell.getText())){
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (XWPFRun run : runs) {
                            run.setText(changeValue(run.toString(), textMap),0);
                        }
                    }
                }
            }
        }
    }

    /**
     * 为表格插入数据,行数不够添加新行
     * @param table 需要插入数据的表格
     * @param tableList 插入数据集合
     */
    public static void insertTable(XWPFTable table, List<List<String>> tableList){
        if(tableList==null||tableList.size()==0){
            return;
        }

        XWPFTableRow modelRow = table.getRow(headCount[nowTableIndex]);

        //创建行,根据需要插入的数据添加新行,不处理表头
        for(int i = 1; i < tableList.size(); i++){
            XWPFTableRow row =table.createRow();
//            XWPFTableRow row = table.insertNewTableRow(headCount[nowTableIndex]+1);
            //创建行,根据第一行创建的,导致表头合并单位格影响了列数
            List<XWPFTableCell> cells = row.getTableCells();
            int minusCell = cellsCount[nowTableIndex]-cells.size();
            for (int j = 0; j < minusCell; j++) {
                row.addNewTableCell();
            }
            //行属性复制
            row.getCtRow().setTrPr(modelRow.getCtRow().getTrPr());
            cells = row.getTableCells();
            for (int i1 = 0; i1 < cells.size(); i1++) {
                XWPFTableCell modelCell = modelRow.getCell(i1);
                //列属性复制
                cells.get(i1).getCTTc().setTcPr(modelCell.getCTTc().getTcPr());
                //段落属性复制
                if(modelCell.getParagraphs()!=null&&modelCell.getParagraphs().size()>0){
                    cells.get(i1).getParagraphs().get(0).getCTP().setPPr(modelCell.getParagraphs().get(0).getCTP().getPPr());
                }
            }
        }
        //遍历表格插入数据
        List<XWPFTableRow> rows = table.getRows();

        int contentIndex = 0;
        for(int i = headCount[nowTableIndex]; i < rows.size(); i++,contentIndex++){
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for(int j = 0; j < cells.size(); j++){
                XWPFTableCell cell = cells.get(j);
                try{
                    cell.setText(tableList.get(contentIndex).get(j));
                }catch (Exception e){
                    log.info(contentIndex+":"+nowTableIndex+":"+rows.size()+":"+cells.size());
                }
            }
        }

    }



    /**
     * 判断文本中时候包含$
     * @param text 文本
     * @return 包含返回true,不包含返回false
     */
    public static boolean checkText(String text){
        boolean check  =  false;
        if(text.indexOf("$")!= -1){
            check = true;
        }
        return check;

    }

    /**
     * 匹配传入信息集合与模板
     * @param value 模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
    public static String changeValue(String value, Map<String, String> textMap){
        Set<Entry<String, String>> textSets = textMap.entrySet();
        for (Entry<String, String> textSet : textSets) {
            //匹配模板与替换值 格式${key}
            String keyName = textSet.getKey();
            String key = "${"+keyName+"}";
            if(value.indexOf(key)!= -1){
                //增加特殊处理,判断是否哟啊插入图片
                if(keyName.indexOf("img")>=0){//说明此处要插入图片
                    String picPath = textSet.getValue();//图片路径
                    value = "img|"+picPath;
                }else{
                    value = textSet.getValue();
                }
            }
        }
        //模板未匹配到区域替换为空
        if(checkText(value)){
            value = "";
        }
        return value;
    }

    public static void main(String[] args) {
        //模板文件地址
        String inputUrl = "E:\\xyhj\\document\\2023-02\\ATS开发\\pic.docx";
        //新生产的模板文件
        Random random = new Random();
        String outputUrl = "E:\\xyhj\\document\\2023-02\\ATS开发\\test"+random.nextInt(20)+".docx";

        Map<String, String> testMap = new HashMap<String, String>();
        testMap.put("img", "E:\\xyhj\\document\\2023-02\\ATS开发\\pic.jpg");

        List<List<List<String>>> testList = new ArrayList<>();
        WordToNewWordUtils.changWord(inputUrl, outputUrl, testMap, testList);
    }
}

 

posted @ 2023-03-01 14:12  伟衙内  阅读(152)  评论(0编辑  收藏  举报