Java POI-TL設(shè)置Word圖片浮于文字上方
需求描述
word導(dǎo)出時(shí)插入圖片,圖片浮于文字上方而不是嵌入的方式
poi-tl 原生的插入圖片
poi-tl渲染圖片,使用的是org.apache.poi.xwpf.usermodel.XWPFRun的addPicture方法,該方法中有一段代碼:CTInline inline = drawing.addNewInline();意思就是默認(rèn)將圖片轉(zhuǎn)為inline類型,即行內(nèi)元素
解決方式 自定義圖片渲染插件
1. 自定義MyPictureRenderPolicy
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.AbstractRenderPolicy; import com.deepoove.poi.render.RenderContext; import com.deepoove.poi.util.BufferedImageUtils; 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.IBodyElement; import org.apache.poi.xwpf.usermodel.ParagraphAlignment; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.function.Supplier; /** * 自定義圖片渲染 */ public class MyPictureRenderPolicy extends AbstractRenderPolicy<Object> { @Override protected boolean validate(Object data) { if (null == data) { return false; } else if (data instanceof PictureRenderData) { return null != ((PictureRenderData) data).getPictureSupplier(); } else { return true; } } @Override public void doRender(RenderContext<Object> context) throws Exception { Helper.renderPicture(context.getRun(), wrapper(context.getData())); } @Override protected void afterRender(RenderContext<Object> context) { this.clearPlaceholder(context, false); } @Override protected void reThrowException(RenderContext<Object> context, Exception e) { this.logger.info("Render picture " + context.getEleTemplate() + " error: {}", e.getMessage()); String alt = ""; if (context.getData() instanceof PictureRenderData) { alt = ((PictureRenderData) context.getData()).getAltMeta(); } context.getRun().setText(alt, 0); } private static PictureRenderData wrapper(Object object) { return object instanceof PictureRenderData ? (PictureRenderData) object : Pictures.of(object.toString()).fitSize().create(); } public static class Helper { public static void renderPicture(XWPFRun run, PictureRenderData picture) throws Exception { Supplier<byte[]> supplier = picture.getPictureSupplier(); byte[] imageBytes = (byte[]) supplier.get(); 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(((IBodyElement) run.getParent()).getBody()); 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); Throwable var25 = null; 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)); //XWPFRunWrapper wrapper = new XWPFRunWrapper(run, false); CTDrawing drawing = run.getCTR().getDrawingArray(0); CTGraphicalObject graphicalobject = drawing.getInlineArray(0).getGraphic(); //拿到新插入的圖片替換添加CTAnchor 設(shè)置浮動(dòng)屬性 刪除inline屬性 CTAnchor anchor = getAnchorWithGraphic(graphicalobject, "Generated", Units.toEMU(width), Units.toEMU(height),//圖片大小 Units.toEMU(270), Units.toEMU(-70), false);//相對(duì)當(dāng)前段落位置 需要計(jì)算段落已有內(nèi)容的左偏移 drawing.setAnchorArray(new CTAnchor[]{anchor});//添加浮動(dòng)屬性 drawing.removeInline(0);//刪除行內(nèi)屬性 } catch (Throwable var20) { var25 = var20; throw var20; } finally { if (stream != null) { if (var25 != null) { try { stream.close(); } catch (Throwable var19) { var25.addSuppressed(var19); } } else { stream.close(); } } } } } } } private static boolean isSetSize(PictureStyle style) { return (style.getWidth() != 0 || style.getHeight() != 0) && style.getScalePattern() == WidthScalePattern.NONE; } /** * @param ctGraphicalObject 圖片數(shù)據(jù) * @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=\"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=\"" + deskFileName + "\"/><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; } }
2. 測(cè)試
該內(nèi)容將圖片自動(dòng)添加到word尾部
import cn.hutool.core.collection.CollectionUtil; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.config.ConfigureBuilder; import com.deepoove.poi.data.Pictures; import com.word.img.demo.picture.MyPictureRenderPolicy; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 數(shù)據(jù)處理-word 文檔尾部添加圖片 * * @author: * @date: * @description: */ public class PoiWordTest3 { public static void main(String[] args) throws IOException { //要寫入模板的數(shù)據(jù) Map<String, Object> exampleData = new HashMap<>(); exampleData.put("image1", Pictures.ofLocal("src/main/resources/icecream.png").size(120, 120).create()); FileInputStream inputStream = new FileInputStream("src/main/resources/test3.docx"); XWPFDocument doc = new XWPFDocument(inputStream); List<XWPFParagraph> docParagraphs = doc.getParagraphs(); if (CollectionUtil.isNotEmpty(docParagraphs)) { //最后一個(gè)段落 XWPFParagraph paragraph = docParagraphs.get(docParagraphs.size() - 1); XWPFRun run = paragraph.createRun(); run.setText("{{%image1}} "); //poi-tl 所需模板 } else { //添加段落 XWPFParagraph paragraph = doc.createParagraph(); XWPFRun run = paragraph.createRun(); run.setText("{{%image1}} "); //poi-tl 所需模板 } //用于處理word文件,上傳 File tempFileUpload = null; //上傳臨時(shí)文件 FileOutputStream tempFileOutputStreamUpload = null; //上傳臨時(shí)文件輸出流 FileInputStream tempFileInputStreamUpload = null; //上傳臨時(shí)文件輸入流 //臨時(shí)文件輸出流 tempFileUpload = File.createTempFile("wordTempUpload", ".docx"); tempFileOutputStreamUpload = new FileOutputStream(tempFileUpload); doc.write(tempFileOutputStreamUpload); tempFileInputStreamUpload = new FileInputStream(tempFileUpload); /********************* 把插件注冊(cè)為新標(biāo)簽類型 ***********************************/ ConfigureBuilder builder = Configure.builder(); builder.addPlugin('%',new MyPictureRenderPolicy());//把插件注冊(cè)為新標(biāo)簽類型 XWPFTemplate template = XWPFTemplate.compile(tempFileInputStreamUpload,builder.build()).render(exampleData); //文件輸出流 FileOutputStream out = new FileOutputStream("src/main/resources/test31.docx"); template.write(out); out.flush(); out.close(); template.close(); } }
參考
Java使用poi-tl設(shè)置word圖片環(huán)繞方式為浮于在文字上方
java poi設(shè)置生成的word的圖片為上下型環(huán)繞以及其位置的實(shí)現(xiàn)
以上就是Java POI-TL設(shè)置Word圖片浮于文字上方的詳細(xì)內(nèi)容,更多關(guān)于Java POI-TL設(shè)置Word圖片的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MyBatis-Plus數(shù)據(jù)庫(kù)配置與數(shù)據(jù)源整合方案
本文詳細(xì)介紹了在MyBatis-Plus中進(jìn)行數(shù)據(jù)庫(kù)配置與數(shù)據(jù)源整合的常見(jiàn)方法,包括單數(shù)據(jù)源和多數(shù)據(jù)源的配置步驟,以及如何使用SpringBoot的自動(dòng)配置和手動(dòng)配置來(lái)管理數(shù)據(jù)源,通過(guò)合理的配置,開(kāi)發(fā)者可以簡(jiǎn)化數(shù)據(jù)庫(kù)操作,實(shí)現(xiàn)高效的數(shù)據(jù)庫(kù)管理和復(fù)雜的應(yīng)用架構(gòu)2025-02-02springboot如何獲取yml文件的自定義參數(shù)
這篇文章主要介紹了springboot如何獲取yml文件的自定義參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03SpringBoot異步實(shí)現(xiàn)的8種方式
異步執(zhí)行對(duì)于開(kāi)發(fā)者來(lái)說(shuō)并不陌生,在實(shí)際的開(kāi)發(fā)過(guò)程中,很多場(chǎng)景多會(huì)使用到異步,本文主要介紹了SpringBoot異步實(shí)現(xiàn)的8種方式,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09Quarkus的Spring擴(kuò)展快速改造Spring項(xiàng)目
這篇文章主要為大家介紹了Quarkus的Spring項(xiàng)目擴(kuò)展,帶大家快速改造Spring項(xiàng)目示例演繹,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-02-02Java用BigDecimal類解決Double類型精度丟失的問(wèn)題
這篇文章主要介紹了Java用BigDecimal類解決Double類型精度丟失的問(wèn)題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12Java自定義一個(gè)變長(zhǎng)數(shù)組的思路與代碼
有時(shí)我們希望將把數(shù)據(jù)保存在單個(gè)連續(xù)的數(shù)組中,以便快速、便捷地訪問(wèn)數(shù)據(jù),但這需要調(diào)整數(shù)組大小或者對(duì)其擴(kuò)展,下面這篇文章主要給大家介紹了關(guān)于Java自定義一個(gè)變長(zhǎng)數(shù)組的思路與代碼,需要的朋友可以參考下2022-12-12IDEA 2021.1 操作SVN 最新超詳細(xì)教程(圖文)
本教程將通過(guò)idea從svn服務(wù)器中的任意一個(gè)分支檢出代碼(本文采用branches),然后再idea中創(chuàng)建新的分支、提交代碼、拉取代碼、合并分支等操作進(jìn)行一一記錄,暫不包含代碼合并,對(duì)idea2021.1操作svn相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)下吧2021-05-05Java數(shù)據(jù)結(jié)構(gòu)與算法之插值查找解析
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)與算法之插值查找解析,插值查找算法類似于二分查找,不同的就是插值查找每次從自適應(yīng)mid處開(kāi)始查找,需要的朋友可以參考下2023-12-12