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();
            }
        }
    }

}

POI-TL (Pandas Object Interface for Tables and Layout) 是一个基于Apache POI库的工具包,它提供了一种更高级的方式来操作Excel表格,包括插入、编辑和布局数据。如果你想在Excel单元格中将图片放在文字之上,你可以按照以下步骤操作: 1. 导入必要的库:首先确保你已经安装了`pandas`、`openpyxl`以及`drawSvg`等库,它们可以帮助处理图像和Excel操作。 ```python import pandas as pd from openpyxl import Workbook from PIL import Image from drawSvg import Drawing ``` 2. 创建工作簿和工作表:创建一个新的`Workbook`实例,并选择一个工作表。 ```python wb = Workbook() ws = wb.active ``` 3. 插入文本:设置文本的位置和内容,例如: ```python text_location = 'A1' ws[text_location] = "这是文字" # 如果需要调整字体样式,可以使用类似下面的代码: cell = ws[text_location] cell.font = openpyxl.styles.Font(name='Arial', size=12) ``` 4. 添加图片:加载图片并指定要在其上放置的文字所在的单元格位置。在`openpyxl`中,你需要先保存图片到工作簿内作为图像对象,然后移动图片到适当位置。 ```python image_path = 'path_to_your_image.png' image = Image.open(image_path) # 将图片转换为BytesIO对象以便插入工作簿 image_stream = io.BytesIO() image.save(image_stream, format='PNG') image_stream.seek(0) # 定义图片的绝对位置,假设图片将覆盖在文字上方 image_location = text_location img_obj = ws.add_image(image_stream, image_location) img_obj.anchor = openpyxl.drawing.image.ImageAnchor(top=text_location[1], left=text_location[0]) # 更新图片大小和位置使其正好位于文字之上 img_size = img_obj.width, img_obj.height ws.row_dimensions[text_location[1]].height = max(img_size[1], ws.row_dimensions[text_location[1]].height) ws.column_dimensions[text_location[0]].width = max(img_size[0], ws.column_dimensions[text_location[0]].width) ``` 5. 保存工作簿:完成所有操作后,别忘了保存修改后的Excel文件。 ```python wb.save('output.xlsx') ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值