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

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

一、前言

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中即可,也可以自己放置对象。这么操作,已经满足使用

相关文章
|
19天前
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
46 4
|
19天前
|
Java
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
49 2
|
26天前
|
算法 Java Linux
java制作海报二:java使用Graphics2D 在图片上合成另一个照片,并将照片切割成头像,头像切割成圆形方法详解
这篇文章介绍了如何使用Java的Graphics2D类在图片上合成另一个照片,并将照片切割成圆形头像的方法。
43 1
java制作海报二:java使用Graphics2D 在图片上合成另一个照片,并将照片切割成头像,头像切割成圆形方法详解
|
19天前
|
Java 数据安全/隐私保护
Java ffmpeg 实现视频加文字/图片水印功能
【10月更文挑战第22天】在 Java 中使用 FFmpeg 实现视频加文字或图片水印功能,需先安装 FFmpeg 并添加依赖(如 JavaCV)。通过构建 FFmpeg 命令行参数,使用 `drawtext` 滤镜添加文字水印,或使用 `overlay` 滤镜添加图片水印。示例代码展示了如何使用 JavaCV 实现文字水印。
|
25天前
|
前端开发 小程序 Java
java基础:map遍历使用;java使用 Patten 和Matches 进行正则匹配;后端传到前端展示图片三种情况,并保存到手机
这篇文章介绍了Java中Map的遍历方法、使用Pattern和matches进行正则表达式匹配,以及后端向前端传输图片并保存到手机的三种情况。
17 1
|
26天前
|
算法 Java Linux
java制作海报四:java BufferedImage 转 InputStream 上传至OSS。png 图片合成到模板(另一个图片)上时,透明部分变成了黑色
这篇文章主要介绍了如何将Java中的BufferedImage对象转换为InputStream以上传至OSS,并解决了png图片合成时透明部分变黑的问题。
45 1
|
26天前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
91 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
2月前
|
Java
Java-FileInputStream和FileOutputStream的使用,txt文件及图片文件的拷贝
这篇文章介绍了Java中FileInputStream和FileOutputStream的使用,包括如何读取和写入txt文件以及如何拷贝图片文件。
Java-FileInputStream和FileOutputStream的使用,txt文件及图片文件的拷贝
|
2月前
|
存储 缓存 监控
Java——图片文件位于 bin 目录下,下载新图片会导致应用程序重启
【9月更文挑战第22天】在Java应用中,若图片位于bin目录下且下载新图片导致应用重启,可能是因为部署方式不当或资源监控机制过于敏感。解决方法包括:更改图片存储位置至独立目录;配置应用服务器减少资源监控敏感度;使用独立资源服务器托管静态资源;优化代码减少资源重复加载。具体方案需根据应用实际情况和技术栈调整。
|
26天前
|
算法 小程序 Java
java制作海报三:获取微信二维码详情,并改变大小,合成到海报(另一张图片)上
这篇文章介绍了如何使用Java获取微信小程序的二维码,并将其调整大小后合成到海报(另一张图片)上。
36 0