Java POI Word07版插入图片并指定浮动位置

简介: 由于工作需要,又接触到了POI操作Word,以往只是简单的读取操作,这次是写入操作;

@TOC

一、前言

1.1 目标

在指定DOCX模板时,在模板指定位置插入文本或图片。

1.2 了解

拓展名为DOCX的Word07版文件加药后可以获得xml文件,也直接直接另存为xml。这里面就是特有的文件属性,一下就是图片属性与图片内容两个bean。POI3.17就是直接编写a:graphic,随后的版本就不需要这么麻烦了,直接操作接口就行。但是配置属性(例如浮动文字上方)还是需要编写wp:anchor才行。
在这里插入图片描述

二、开始

2.1 模板

本次模板必须是拓展名为DOCX的Word07版文件,03版的实现方法是完全不同的,这是POI团队的锅。
内容参数格式:${参数}
在这里插入图片描述

2.2 jar包

POI的一个问题就是jar包升级的问题,每次升级有的地方差异还是挺大的,所选好版本之后开始研究即可。我之前的帖子有用3.17,本次使用4.1.2,插入图片的是现实不一样的。

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.9</version>
</dependency>

2.3 代码

非表格样式处理直接读取段落即可

public void create(String filePath, JSONObject jsonObject) {
        try {
            InputStream template = new InputStream(new File(filePath));

            //获取docx解析对象
            XWPFDocument document = new XWPFDocument(template);
            //解析替换文本段落对象.
            List<XWPFParagraph> paragraphs = document.getParagraphs();
            for (XWPFParagraph paragraph : paragraphs) {
                //判断此段落时候需要进行替换
                String text = paragraph.getText().trim();
                if (WordUtil.checkText(text)) {
                    List<XWPFRun> runs = paragraph.getRuns();
                    for (XWPFRun run : runs) {
                        if (!" ".equals(run.toString().trim())) {
                            if (run.toString().indexOf("${jpeg}") != -1 && textMap.containsKey("jpeg")){
                                addPic(run,textMap, 1188000, 1728000,0, 0);
                                break;
                            }
                            //替换模板原来位置
                            String value = changeValue(run.toString(), jsonObject);
                            setWrap(value, run);
                        }
                    }
                }
            }
            // workBook写入输出流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            document.write(baos);
            baos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

表格内样式处理精确到表格内的单元格,然后再按照段落的方式处理

public void create(String filePath, JSONObject jsonObject) {
        try {
            InputStream template = new InputStream(new File(filePath));
            List<String[]> tableList = Lists.newArrayList();

            //解析替换表格对象
            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 (WordUtil.checkText(table.getText())) {
                        List<XWPFTableRow> rows = table.getRows();
                        //遍历表格,并替换模板
                        eachTable(rows, jsonObject);
                    }
                }
            }

            // workBook写入输出流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            document.write(baos);
            baos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
/**
     * 遍历表格
     *
     * @param rows    表格行对象
     * @param textMap 需要替换的信息集合
     */
    public static void eachTable(List<XWPFTableRow> rows, JSONObject 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);
                            //新增制证照片单独处理
                            if (run.toString().indexOf("${jpeg}") != -1 && textMap.containsKey("jpeg")){
                                addPic(run,textMap, 1188000, 1728000,0, 0);
                                break;
                            }
                            String value = changeValue(run.toString(), textMap);
                            setWrap(value, run);
                        }
                    }
                }
            }
        }
    }

一些对应的方法,可以放到工具类里面

/**
     * 匹配传入信息集合与模板
     *
     * @param value   模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
    public static String changeValue(String value, JSONObject textMap) {

       boolean flag = false;
        Set<Map.Entry<String, Object>> textSets = textMap.entrySet();
        for (Map.Entry<String, Object> textSet : textSets) {
            //匹配模板与替换值 格式${key}
            String key = "${" + textSet.getKey() + "}";
            if (value.indexOf(key) != -1) {
                value = textSet.getValue() == null ? "" : ("" + textSet.getValue());
                flag = true;
                break;
            }
        }
        //模板未匹配到区域替换为空
        if (checkText(value)) {
            value = "";
        }
        return value;
    }
    public static void setWrap(String value, XWPFRun run) {
        if ( value.indexOf("\n") > 0) {
            //设置换行
            String[] text = value.split("\n");
            for (int f = 0; f < text.length; f++) {
                if (f == 0) {
                    run.setText(text[f].trim(),0);
                } else {
//                    run.addCarriageReturn();//硬回车
                    // 换行
                    run.addBreak();
                    run.setText(text[f]);
                }
            }
        } else {
            run.setText((String) value,0);
        }
    }
    
/**
     * @param ctGraphicalObject 图片数据
     * @param deskFileName      图片描述
     * @param width             宽
     * @param height            高
     * @param leftOffset        水平偏移 left
     * @param topOffset         垂直偏移 top
     * @param behind            文字上方,文字下方
     * @return
     * @throws Exception
     */
    public static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject, String deskFileName, int width, int height, int leftOffset, int topOffset, boolean behind) {
        String anchorXML =""
                +"<wp:anchor xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"" + ((behind) ? 1 : 0) + "\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"
                +"   <wp:simplePos x=\"0\" y=\"0\"/>"
                +"   <wp:positionH relativeFrom=\"column\">"
                +"      <wp:posOffset>"+ leftOffset + "</wp:posOffset>"
                +"   </wp:positionH>"
                +"   <wp:positionV relativeFrom=\"line\">"
                +"      <wp:posOffset>"+ topOffset +"</wp:posOffset>"
                +"   </wp:positionV>"
                +"   <wp:extent cx=\"" + width + "\" cy=\"" + height + "\"/>"
                +"   <wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>"
                +"   <wp:wrapNone/>"
                +"   <wp:docPr id=\"1\" name=\"Drawing 0\" descr=\"" +deskFileName+ "\"/><wp:cNvGraphicFramePr/>"
                +"</wp:anchor>";
        try {
            CTDrawing drawing = CTDrawing.Factory.parse(anchorXML);
            CTAnchor anchor = drawing.getAnchorArray(0);
            anchor.setGraphic(ctGraphicalObject);
            return anchor;
        } catch (XmlException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 新增制证照片单独处理
     * @param run
     * @param textMap
     */
    public static void addPic(XWPFRun run, JSONObject textMap, int width, int height, int leftOffset, int topOffset){
        String runText = run.toString().trim();
        byte[] zjzp = Base64.decode(textMap.getString("jpeg"));
        try(ByteArrayInputStream byteInputStream = new ByteArrayInputStream(zjzp)) {
            //1、添加图片
            run.addPicture(byteInputStream,XWPFDocument.PICTURE_TYPE_JPEG,"照片", width, height);
            //2、获取图片
            CTDrawing cTDrawing = run.getCTR().getDrawingArray(0);
            CTGraphicalObject cTGraphicalObject = cTDrawing.getInlineArray(0).getGraphic();
            //3、设置属性
            CTAnchor ctAnchor = getAnchorWithGraphic(cTGraphicalObject,"照片", width, height,leftOffset, topOffset,false);
            cTDrawing.setAnchorArray(new CTAnchor[]{ctAnchor});
            cTDrawing.removeInline(0);
            run.setText(runText.replace("${jpeg}",""), 0);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

总结

效果就不展示了,JSON使用的是fastJSON,需要修改的内容放到JSON中即可,也可以自己放置对象。这么操作,已经满足使用

相关文章
|
2月前
|
Java
java通过commons-fileupload实现多张图片的上传(servlet)
java通过commons-fileupload实现多张图片的上传(servlet)
23 2
|
2月前
|
存储 Java 计算机视觉
Java代码居然可以做出如此高级的图片编辑系统
Java代码居然可以做出如此高级的图片编辑系统
38 0
|
2月前
|
前端开发 Java
java通过commons-fileupload实现多张图片的上传(jsp页面)
java通过commons-fileupload实现多张图片的上传(jsp页面)
16 2
|
5月前
|
Java Linux Windows
java 图片上绘制文字Graphics2D linux 中文乱码
java 图片上绘制文字Graphics2D linux 中文乱码
100 0
|
7月前
|
Java Maven
【Java用法】使用poi写Java代码导出Excel文档的解决方案
【Java用法】使用poi写Java代码导出Excel文档的解决方案
59 0
|
8月前
|
Java
Java实现图片转化为pdf
Java实现图片转化为pdf
284 0
|
2月前
|
Java
java中复制图片
java中复制图片
10 2
|
2月前
|
存储 Java Apache
java 读取图片成二进制流
【2月更文挑战第10天】
|
3月前
|
Java
POI上传excel的java后台逻辑
POI上传excel的java后台逻辑
|
3月前
|
文字识别 Java
Java使用Tess4j识别图片里的文字
Java使用Tess4j识别图片里的文字