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)文章
Spring?Boot指標(biāo)監(jiān)控及日志管理示例詳解
Spring Boot Actuator可以幫助程序員監(jiān)控和管理SpringBoot應(yīng)用,比如健康檢查、內(nèi)存使用情況統(tǒng)計(jì)、線程使用情況統(tǒng)計(jì)等,這篇文章主要介紹了Spring?Boot指標(biāo)監(jiān)控及日志管理,需要的朋友可以參考下2023-11-11
關(guān)于Java從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳
這篇文章主要介紹了關(guān)于Java從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳,File?和?IO?流其實(shí)是很相似的,都是將文件從一個(gè)地方轉(zhuǎn)移到另一個(gè)地方,這也是流的特點(diǎn)之一,需要的朋友可以參考下2023-04-04
java生成excel并導(dǎo)出到對(duì)應(yīng)位置的方式
這篇文章主要介紹了java生成excel并導(dǎo)出到對(duì)應(yīng)位置的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
IDEA2023.3.4開啟SpringBoot項(xiàng)目的熱部署(圖文)
本文使用的開發(fā)工具是idea,使用的是springboot框架開發(fā)的項(xiàng)目,配置熱部署,可以提高開發(fā)效率,文中通過圖文介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02
基于spring 方法級(jí)緩存的多種實(shí)現(xiàn)
下面小編就為大家?guī)硪黄趕pring 方法級(jí)緩存的多種實(shí)現(xiàn)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
java工具類實(shí)現(xiàn)文件壓縮zip以及解壓縮功能
這篇文章主要給大家介紹了關(guān)于java工具類實(shí)現(xiàn)文件壓縮zip以及解壓縮功能的相關(guān)資料,文中主要使用使用的是hutool工具類,Hutool是一個(gè)Java工具類庫,由國內(nèi)的程序員loolly開發(fā),目的是提供一些方便、快捷、實(shí)用的工具類和工具方法,需要的朋友可以參考下2024-02-02

