【Poi-tl Documentation】自定义占位符来设置图片大小

前置说明:

yaml 复制代码
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl</artifactId>
    <version>1.12.1</version>
</dependency>

模板文件:
image_test.docx

java 复制代码
package run.siyuan.poi.tl.policy;

import cn.hutool.core.util.ReUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.converter.ObjectToPictureRenderDataConverter;
import com.deepoove.poi.converter.ToRenderDataConverter;
import com.deepoove.poi.data.Paragraphs;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.data.style.PictureStyle;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.policy.PictureRenderPolicy;
import com.deepoove.poi.render.RenderContext;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.util.BufferedImageUtils;
import com.deepoove.poi.util.RegexUtils;
import com.deepoove.poi.util.SVGConvertor;
import com.deepoove.poi.util.UnitUtils;
import com.deepoove.poi.xwpf.BodyContainer;
import com.deepoove.poi.xwpf.BodyContainerFactory;
import com.deepoove.poi.xwpf.WidthScalePattern;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;

import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @ClassName CustomPictureRenderPolicy
 * @Description TODO
 * @Author siyuan
 * @Date 2024/3/3 12:53
 */
public class CustomPictureRenderPolicy extends PictureRenderPolicy {

    public static void main(String[] args) throws IOException {
        // 读取模板文件
        FileInputStream fileInputStream = new FileInputStream("/Users/wuzhiqian/Desktop/HY/image_test.docx");
        // 创建模板配置
        ConfigureBuilder configureBuilder = Configure.builder();
//        configureBuilder.buildGrammerRegex(RegexUtils.createGeneral("{{", "}}"));
//        configureBuilder.buildGrammerRegex("((%)?[\\w\\u4e00-\\u9fa5]+(\\.[\\w\\u4e00-\\u9fa5]+)*(\\[\\d,\\d\\])?)?");
        configureBuilder.setValidErrorHandler(new Configure.DiscardHandler());
        configureBuilder.addPlugin('%', new CustomPictureRenderPolicy());
        // 创建模板上下文
        Map<String, Object> context = new HashMap<>();
        context.put("aaaa", Pictures.ofStream(Files.newInputStream(Paths.get("/Users/wuzhiqian/Desktop/HY/11111.png"))).create());
        context.put("name2", Pictures.ofStream(Files.newInputStream(Paths.get("/Users/wuzhiqian/Desktop/HY/11111.png"))).create());
        // 使用模板引擎替换文本标签
        XWPFTemplate compile = XWPFTemplate.compile(fileInputStream, configureBuilder.build());
        compile.render(context);
        // 保存生成的文档
        FileOutputStream outputStream = new FileOutputStream("/Users/wuzhiqian/Desktop/HY/image_test" + System.currentTimeMillis() + ".docx");
        compile.write(outputStream);
        outputStream.close();
        compile.close();
        fileInputStream.close();
    }


    private static ToRenderDataConverter<Object, PictureRenderData> converter = new ObjectToPictureRenderDataConverter();

    public CustomPictureRenderPolicy() {
    }

    public PictureRenderData cast(Object source) throws Exception {
        return (PictureRenderData)converter.convert(source);
    }

    protected boolean validate(PictureRenderData data) {
        return null != data;
    }

    protected void afterRender(RenderContext<PictureRenderData> context) {
        this.clearPlaceholder(context, false);
    }

    protected void reThrowException(RenderContext<PictureRenderData> context, Exception e) {
        this.logger.info("Render picture " + context.getEleTemplate() + " error: {}", e.getMessage());
        String alt = ((PictureRenderData)context.getData()).getAltMeta();
        context.getRun().setText(alt, 0);
    }

    public void beforeRender(RenderContext<PictureRenderData> context) {
        System.out.println("================");
        XWPFRun run = context.getRun();
        String source = context.getEleTemplate().getSource();
        String tagName = context.getEleTemplate().getTagName();
        System.out.println(source);
        Pattern pattern = Pattern.compile( "(.*)\\{\\{%"+tagName+"}}\\[\\d+,\\d+](.*)");
        XWPFParagraph parent = (XWPFParagraph) run.getParent();
        IBody body = parent.getBody();
        for (XWPFParagraph paragraph : body.getParagraphs()) {
            String text = paragraph.getText();
            System.out.println(text + "-------------------------------");

            if (text.contains(source) && ReUtil.isMatch(pattern,text) ) {
                String s = ReUtil.get("\\{\\{%"+tagName+"}}\\[\\d+,\\d+]", text, 0);
                System.out.println(s);
                s = ReUtil.get("\\[\\d+,\\d+]", s, 0);
                System.out.println(s);
                String[] split = s.replace("[", "").replace("]", "").split(",");
                Integer w = Integer.valueOf(split[0]);
                Integer h = Integer.valueOf(split[1]);
                PictureStyle pictureStyle = new PictureStyle();
                pictureStyle.setWidth(w);
                pictureStyle.setHeight(h);
                context.getData().setPictureStyle(pictureStyle);

                for (XWPFRun xwpfRun : paragraph.getRuns()) {
                    if (s.equals(xwpfRun.text())){
                        System.out.println(xwpfRun.text()+"   clean");
                        // 删除[200,214] 格式字符
                        BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(xwpfRun);
                        bodyContainer.clearPlaceholder(xwpfRun);
                    }

                }
            }
        }
    }

    public void doRender(RenderContext<PictureRenderData> context) throws Exception {
        CustomPictureRenderPolicy.Helper.renderPicture(context.getRun(), (PictureRenderData) context.getData());
    }

    public static class Helper {
        public Helper() {
        }

        public static void renderPicture(XWPFRun run, PictureRenderData picture) throws Exception {
            byte[] imageBytes = picture.readPictureData();
            if (null == imageBytes) {
                throw new IllegalStateException("Can't read picture byte arrays!");
            } else {
                // 根据图片流 设置图片类型
                PictureType pictureType = picture.getPictureType();
                if (null == pictureType) {
                    pictureType = PictureType.suggestFileType(imageBytes);
                }
                // 图片类型为空,报错
                if (null == pictureType) {
                    throw new RenderException("PictureRenderData must set picture type!");
                } else {
                    PictureStyle style = picture.getPictureStyle();
                    if (null == style) {
                        style = new PictureStyle();
                    }

                    int width = style.getWidth();
                    int height = style.getHeight();
                    if (pictureType == PictureType.SVG) {
                        imageBytes = SVGConvertor.toPng(imageBytes, (float) width, (float) height);
                        pictureType = PictureType.PNG;
                    }

                    if (!isSetSize(style)) {
                        BufferedImage original = BufferedImageUtils.readBufferedImage(imageBytes);
                        width = original.getWidth();
                        height = original.getHeight();
                        if (style.getScalePattern() == WidthScalePattern.FIT) {
                            BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
                            int pageWidth = UnitUtils.twips2Pixel(bodyContainer.elementPageWidth((IBodyElement) run.getParent()));
                            if (width > pageWidth) {
                                double ratio = (double) pageWidth / (double) width;
                                width = pageWidth;
                                height = (int) ((double) height * ratio);
                            }
                        }
                    }

                    InputStream stream = new ByteArrayInputStream(imageBytes);

                    try {
                        PictureStyle.PictureAlign align = style.getAlign();
                        if (null != align && run.getParent() instanceof XWPFParagraph) {
                            ((XWPFParagraph) run.getParent()).setAlignment(ParagraphAlignment.valueOf(align.ordinal() + 1));
                        }

                        run.addPicture(stream, pictureType.type(), "Generated", Units.pixelToEMU(width), Units.pixelToEMU(height));
                    } catch (Throwable var13) {
                        try {
                            stream.close();
                        } catch (Throwable var12) {
                            var13.addSuppressed(var12);
                        }

                        throw var13;
                    }

                    stream.close();
                }
            }
        }

        private static boolean isSetSize(PictureStyle style) {
            return (style.getWidth() != 0 || style.getHeight() != 0) && style.getScalePattern() == WidthScalePattern.NONE;
        }
    }

}
相关推荐
萧瑟余晖4 分钟前
Spring Batch核心模型与快速上手详解
java·spring·batch
缓慢更新29 分钟前
AI 辅助生成单测:从接口定义到用例的流水线
java·服务器·前端·网络·数据库·人工智能
LMT流氓兔2 小时前
第五单元——第十六课:给书店助手加上网页界面
java·开发语言·jvm·人工智能·oracle·langchain·1024程序员节
wangbing11253 小时前
平台介绍-modbus设备调试
java
西索斯coding4 小时前
gpt-image-2.5-sunburst 和 gemini-3.1-flash-image 差多少?60 组项目素材的成功成本评测框架
java·linux·gpt·ai
wuminyu4 小时前
Java字节码学习总结:字节码如何加载、解析和执行
java·linux·c语言·jvm·c++
Flynn Liang5 小时前
大模型怎么自己调函数?Spring AI 2.0 Tool Calling 自动循环原理 + 完整时序图
java·人工智能·spring·agent·函数调用·springai·toolcalling
乌暮6 小时前
请求进门先安检:Spring 拦截器与统一功能实战指南
java·开发语言·后端·学习·spring
学长毕设6 小时前
基于SpringBoot的烘焙商品商品系统-附源码
java·spring boot·后端
金銀銅鐵6 小时前
[Java] 用纯 Swing 做一个《論語·學而》阅读器
java·ai编程·交互设计