Poi-Tl 自定义插件实现图片浮动于文字上

最近项目中使用了word合成,使用了poi-tl 进行处理。但是遇到要渲染一个图片,就一言难尽了。
官网默认的图片渲染结果会占住word的空间大小,导致整体间距会被拉大。
迫不得已,只能实现一个图片渲染的插件来实现图片浮动效果。

1.渲染类

CustomPictureRenderData 是对原poi-tl图片渲染类的扩展,添加了左偏移和上偏移属性。
这两属性用于图片位置的调整。

public class CustomPictureRenderData implements RenderData {
    
    /**
     * 图片宽度
     */
    private int width;
    /**
     * 图片高度
     */
    private int height;
    /**
     * 图片路径
     */
    private String path;

    /**
     * 图片二进制数据
     */
    private transient byte[] data;

    /**
     * 当图片不存在时,显示的文字
     */
    private String altMeta = "";

    /**
     * 左偏量
     */
    private int leftOffset;
    /**
     * 上偏移量
     */
    private int topOffset;
	
	// 省略get/set/构造等方法
 }
2. 自定义渲染策略

PoiPicPolicy定义了具体的渲染方式,在poi-tl的默认渲染方式基础上添加了对样式的处理。

public class PoiPicPolicy extends AbstractRenderPolicy<CustomPictureRenderData> {

    @Override
    protected boolean validate(CustomPictureRenderData data) {
        return (null != data && (null != data.getData() || null != data.getPath()));
    }

    @Override
    protected void afterRender(RenderContext<CustomPictureRenderData> context) {
        clearPlaceholder(context, false);
    }

    @Override
    protected void reThrowException(RenderContext<CustomPictureRenderData> context, Exception e) {
        logger.info("Render picture {}, error: {}",  context.getEleTemplate(),e.getMessage());
        context.getRun().setText(context.getData().getAltMeta(), 0);

    }

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

    public static class Helper {
        public static final int EMU = 9525;

        public static void renderPicture(XWPFRun run, CustomPictureRenderData picture) throws Exception {

            int suggestFileType = suggestFileType(picture.getPath());
            InputStream ins = null;
            try {
                ins = null == picture.getData() ? new FileInputStream(picture.getPath())
                        : new ByteArrayInputStream(picture.getData());
                String numberCode = RandomUtil.createNumberCode(4);
                run.addPicture(ins, suggestFileType, "Generated"+numberCode, picture.getWidth() * EMU,
                        picture.getHeight() * EMU);
                CTDrawing drawing = run.getCTR().getDrawingArray(0);
                CTGraphicalObject graphicalobject = drawing.getInlineArray(0).getGraphic();

                //拿到新插入的图片替换添加CTAnchor 设置浮动属性 删除inline属性
                CTAnchor anchor = getAnchorWithGraphic(graphicalobject,
                        Units.toEMU(picture.getWidth()), Units.toEMU(picture.getHeight()),
                        Units.toEMU(picture.getLeftOffset()), Units.toEMU(picture.getTopOffset()));
                //添加浮动属性
                drawing.setAnchorArray(new CTAnchor[]{anchor});
                //删除行内属性
                drawing.removeInline(0);
            }
            finally {
                IOUtils.closeQuietly(ins);
            }
        }

        /**
         * @param ctGraphicalObject 图片数据
         * @param width             宽
         * @param height            高
         * @param leftOffset        水平偏移 left
         * @param topOffset         垂直偏移 top
         * @return
         * @throws Exception
         */
        private static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject, int width, int height,
                                                     int leftOffset, int topOffset) {
            String anchorXML =
                    "<wp:anchor xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" "
                            + "simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"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=\"paragraph\">"
                            + "<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=\"G:/11.png\"/><wp:cNvGraphicFramePr/>"
                            + "</wp:anchor>";

            CTDrawing drawing = null;
            try {
                drawing = CTDrawing.Factory.parse(anchorXML);
            } catch (XmlException e) {
                e.printStackTrace();
            }
            CTAnchor anchor = drawing.getAnchorArray(0);
            anchor.setGraphic(ctGraphicalObject);
            return anchor;
        }

        public static int suggestFileType(String imgFile) {
            int format = 0;

            if (imgFile.endsWith(".emf")) format = XWPFDocument.PICTURE_TYPE_EMF;
            else if (imgFile.endsWith(".wmf")) format = XWPFDocument.PICTURE_TYPE_WMF;
            else if (imgFile.endsWith(".pict")) format = XWPFDocument.PICTURE_TYPE_PICT;
            else if (imgFile.endsWith(".jpeg") || imgFile.endsWith(".jpg"))
                format = XWPFDocument.PICTURE_TYPE_JPEG;
            else if (imgFile.endsWith(".png")) format = XWPFDocument.PICTURE_TYPE_PNG;
            else if (imgFile.endsWith(".dib")) format = XWPFDocument.PICTURE_TYPE_DIB;
            else if (imgFile.endsWith(".gif")) format = XWPFDocument.PICTURE_TYPE_GIF;
            else if (imgFile.endsWith(".tiff")) format = XWPFDocument.PICTURE_TYPE_TIFF;
            else if (imgFile.endsWith(".eps")) format = XWPFDocument.PICTURE_TYPE_EPS;
            else if (imgFile.endsWith(".bmp")) format = XWPFDocument.PICTURE_TYPE_BMP;
            else if (imgFile.endsWith(".wpg")) format = XWPFDocument.PICTURE_TYPE_WPG;
            else {
                throw new RenderException("Unsupported picture: " + imgFile
                        + ". Expected emf|wmf|pict|jpeg|png|dib|gif|tiff|eps|bmp|wpg");
            }
            return format;
        }

    }
}
3. 装配插件

将自定义策略以插件形式,加入默认配置中。
这里使用%,实际使用中替换类似{{%var}}的格式,当然也可以采用其他的标示符。

public class WordUtil {

    /**
     * 添加自定义配置
     */
    public static final Configure config = Configure.createDefault()
                                                    .plugin('%', new PoiPicPolicy());


    public static void replaceWord(OutputStream response, InputStream in,  Map<String, Object> params) throws Exception {
        XWPFTemplate template = null;
        try {
            template = XWPFTemplate.compile(in,config).render(params);
            template.write(response);
        }finally {
            if (template != null) {
                template.close();
            }
        }
    }

}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值