java poi设置生成的word的图片为上下型环绕以及其位置的实现
作者:十三 发布时间:2023-05-23 03:12:08
问题描述
在使用poi-tl word模版工具时,发现生成的文档中,图片格式为嵌入型,有的图片甚至被表格遮挡一半。而自己想要的图片格式为上下型环绕,并且图片需要居中。
问题分析
poi-tl渲染图片,使用的是org.apache.poi.xwpf.usermodel.XWPFRun的addPicture方法,该方法中有一段代码:CTInline inline = drawing.addNewInline();意思就是默认将图片转为inline类型,即行内元素。
然后我们把生成的嵌入型图片的文档转换成xml文件,然后再新建一个文档,插入图片后,设置图片为上下型环绕,保存为另一个xml,比较下两个xml的区别。嵌入型图片的xml是:
上下型环绕的图片的xml是
我们看到两种格式的图片标签分别为inline和anchor。所以如果我们想把图片设置为上下型环绕,需要重写poi的addPicture方法,把图片转为anchor类型。
我们仿照org.apache.poi.xwpf.usermodel.XWPFRun的addPicture方法,将CTInline inline = drawing.addNewInline();换成 CTAnchor anchor = drawing.addNewAnchor();,然后对比着xml,依次对anchor的字段进行赋值。结果发现生成的word无法正常打开,查了很多资料,都说poi的CTAnchor有问题,使用后无法正常打开生成的word。
此路不通,那我们就尝试另一种思路,我们不通过CTAnchor来生成anchor标签,而是直接使用xml,将xml赋给poi的drawing。具体的处理方式在后面。
xml标签和图片格式解析
在word中,在图片上右键,选择大小和位置,就可以看到如下界面:
图中的上下型对应的是xml中的<wp:wrapTopAndBottom/>标签,不同环绕方式该标签值不一样。如果需要其他格式,可以设置好后,把文档保存为xml,找到对应的标签。
图中的距正文上下左右距离,对应的是<wp:anchor distT="71755" distB="71755" distL="114300" distR="114300" ...>中的disT、disB、disL、disR属性。
图中位置一栏,水平对齐方式居中、相对于栏对应的是xml中的<wp:positionH relativeFrom="column"><wp:align>center</wp:align></wp:positionH>。
垂直-绝对位置0.1cm,下侧段落对应的是xml中的<wp:positionV relativeFrom="paragraph"><wp:posOffset>36195</wp:posOffset></wp:positionV>。
我们可以根据不同的需要来设置不同的xml。
我使用的xml是
String xml = "<wp:anchor allowOverlap=\"0\" layoutInCell=\"1\" locked=\"0\" behindDoc=\"0\" relativeHeight=\"0\"
simplePos=\"0\" distR=\"0\" distL=\"0\" distB=\"0\" distT=\"0\" " +
" xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\"" +
" xmlns:wp14=\"http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing\"" +
" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" >" +
"<wp:simplePos y=\"0\" x=\"0\"/>" +
"<wp:positionH relativeFrom=\"column\">" +
"<wp:align>center</wp:align>" +
"</wp:positionH>" +
"<wp:positionV relativeFrom=\"paragraph\">" +
"<wp:posOffset>0</wp:posOffset>" +
"</wp:positionV>" +
"<wp:extent cy=\""+height+"\" cx=\""+width+"\"/>" +
"<wp:effectExtent b=\"0\" r=\"0\" t=\"0\" l=\"0\"/>" +
"<wp:wrapTopAndBottom/>" +
"<wp:docPr descr=\"Picture Alt\" name=\"Picture Hit\" id=\"0\"/>" +
"<wp:cNvGraphicFramePr>" +
"<a:graphicFrameLocks noChangeAspect=\"true\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" />" +
"</wp:cNvGraphicFramePr>" +
"<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
"<a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
"<pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
"<pic:nvPicPr>" +
"<pic:cNvPr name=\"Picture Hit\" id=\"1\"/>" +
"<pic:cNvPicPr/>" +
"</pic:nvPicPr>" +
"<pic:blipFill>" +
"<a:blip r:embed=\""+relationId+"\"/>" +
"<a:stretch>" +
"<a:fillRect/>" +
"</a:stretch>" +
"</pic:blipFill>" +
"<pic:spPr>" +
"<a:xfrm>" +
"<a:off y=\"0\" x=\"0\"/>" +
"<a:ext cy=\""+height+"\" cx=\""+width+"\"/>" +
"</a:xfrm>" +
"<a:prstGeom prst=\"rect\">" +
"<a:avLst/>" +
"</a:prstGeom>" +
"</pic:spPr>" +
"</pic:pic>" +
"</a:graphicData>" +
"</a:graphic>" +
"<wp14:sizeRelH relativeFrom=\"margin\">" +
"<wp14:pctWidth>0</wp14:pctWidth>" +
"</wp14:sizeRelH>" +
"<wp14:sizeRelV relativeFrom=\"margin\">" +
"<wp14:pctHeight>0</wp14:pctHeight>" +
"</wp14:sizeRelV>" +
"</wp:anchor>";
其中width和height是图片的宽度和高度,relationId是图片的id。
解决方案
1,首先定义一个poi-tl的图片渲染器,使得其不再调用poi默认的图片渲染器,而是使用我们自己定义的。
public class MyPictureRenderPolicy extends AbstractRenderPolicy<PictureRenderData> {
@Override
protected boolean validate(PictureRenderData data) {
return (null != data.getData() || null != data.getPath());
}
@Override
public void doRender(RunTemplate runTemplate, PictureRenderData picture, XWPFTemplate template)
throws Exception {
XWPFRun run = runTemplate.getRun();
MyPictureRenderPolicy.Helper.renderPicture(run, picture);
}
@Override
protected void afterRender(RenderContext context) {
clearPlaceholder(context, false);
}
@Override
protected void doRenderException(RunTemplate runTemplate, PictureRenderData data, Exception e) {
logger.info("Render picture " + runTemplate + " error: {}", e.getMessage());
runTemplate.getRun().setText(data.getAltMeta(), 0);
}
public static class Helper {
public static void renderPicture(XWPFRun run, PictureRenderData picture) throws Exception {
int suggestFileType = suggestFileType(picture.getPath());
InputStream ins = null == picture.getData() ? new FileInputStream(picture.getPath())
: new ByteArrayInputStream(picture.getData());
String relationId = run.getDocument().addPictureData(ins, suggestFileType);
long width = Units.toEMU(picture.getWidth());
long height = Units.toEMU(picture.getHeight());
CTDrawing drawing = run.getCTR().addNewDrawing();
String xml = "<wp:anchor allowOverlap=\"0\" layoutInCell=\"1\" locked=\"0\" behindDoc=\"0\" relativeHeight=\"0\" simplePos=\"0\" distR=\"0\" distL=\"0\" distB=\"0\" distT=\"0\" " +
" xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\"" +
" xmlns:wp14=\"http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing\"" +
" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" >" +
"<wp:simplePos y=\"0\" x=\"0\"/>" +
"<wp:positionH relativeFrom=\"column\">" +
"<wp:align>center</wp:align>" +
"</wp:positionH>" +
"<wp:positionV relativeFrom=\"paragraph\">" +
"<wp:posOffset>0</wp:posOffset>" +
"</wp:positionV>" +
"<wp:extent cy=\""+height+"\" cx=\""+width+"\"/>" +
"<wp:effectExtent b=\"0\" r=\"0\" t=\"0\" l=\"0\"/>" +
"<wp:wrapTopAndBottom/>" +
"<wp:docPr descr=\"Picture Alt\" name=\"Picture Hit\" id=\"0\"/>" +
"<wp:cNvGraphicFramePr>" +
"<a:graphicFrameLocks noChangeAspect=\"true\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" />" +
"</wp:cNvGraphicFramePr>" +
"<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
"<a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
"<pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
"<pic:nvPicPr>" +
"<pic:cNvPr name=\"Picture Hit\" id=\"1\"/>" +
"<pic:cNvPicPr/>" +
"</pic:nvPicPr>" +
"<pic:blipFill>" +
"<a:blip r:embed=\""+relationId+"\"/>" +
"<a:stretch>" +
"<a:fillRect/>" +
"</a:stretch>" +
"</pic:blipFill>" +
"<pic:spPr>" +
"<a:xfrm>" +
"<a:off y=\"0\" x=\"0\"/>" +
"<a:ext cy=\""+height+"\" cx=\""+width+"\"/>" +
"</a:xfrm>" +
"<a:prstGeom prst=\"rect\">" +
"<a:avLst/>" +
"</a:prstGeom>" +
"</pic:spPr>" +
"</pic:pic>" +
"</a:graphicData>" +
"</a:graphic>" +
"<wp14:sizeRelH relativeFrom=\"margin\">" +
"<wp14:pctWidth>0</wp14:pctWidth>" +
"</wp14:sizeRelH>" +
"<wp14:sizeRelV relativeFrom=\"margin\">" +
"<wp14:pctHeight>0</wp14:pctHeight>" +
"</wp14:sizeRelV>" +
"</wp:anchor>";
drawing.set(XmlToken.Factory.parse(xml, DEFAULT_XML_OPTIONS));
CTPicture pic = getCTPictures(drawing).get(0);
XWPFPicture xwpfPicture = new XWPFPicture(pic, run);
run.getEmbeddedPictures().add(xwpfPicture);
}
public static List<CTPicture> getCTPictures(XmlObject o) {
List<CTPicture> pictures = new ArrayList<>();
XmlObject[] picts = o.selectPath("declare namespace pic='"
+ CTPicture.type.getName().getNamespaceURI() + "' .//pic:pic");
for (XmlObject pict : picts) {
if (pict instanceof XmlAnyTypeImpl) {
// Pesky XmlBeans bug - see Bugzilla #49934
try {
pict = CTPicture.Factory.parse(pict.toString(),
DEFAULT_XML_OPTIONS);
} catch (XmlException e) {
throw new POIXMLException(e);
}
}
if (pict instanceof CTPicture) {
pictures.add((CTPicture) pict);
}
}
return pictures;
}
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;
}
}
}
然后在渲染模板的时候,配置我们自己定义的图片渲染器
public static void main(String[] args) throws Exception{
String path = "1.docx";
InputStream templateFile = Demo.class.getClassLoader().getResourceAsStream(path);
Map map = new HashMap();
map.put("pic", new PictureRenderData(120, 80, ".png", Demo.class.getClassLoader().getResourceAsStream("1.png")));
// 将数据整合到模板中去
Configure.ConfigureBuilder builder = Configure.newBuilder();
builder.supportGrammerRegexForAll();
builder.addPlugin('@', new MyPictureRenderPolicy());
XWPFTemplate template = XWPFTemplate.compile(templateFile, builder.build()).render(map);
String docPath = "C:\\Users\\csdc01\\Desktop\\out.docx";
FileOutputStream outputStream1 = new FileOutputStream(docPath);
template.write(outputStream1);
outputStream1.flush();
outputStream1.close();
}
源码:https://github.com/ksyzz/poi-tl-demo.git
来源:https://segmentfault.com/a/1190000020302512


猜你喜欢
- 1.实例1(主要看到[2])1.1.系统功能: 开发一个计算器服务CalculateService,这个服务包含加(plus)、减(minu
- Android6.0蓝牙出现无法扫描设备或闪退问题解决办法前言:目前待的这家公司是做智能家居的,最近客户那边有反馈说为什么我的手机蓝牙也打开
- 使用GroupingSearch对搜索结果进行分组Package org.apache.lucene.search.grouping Des
- if语句使用布尔表达式或布尔值作为分支条件来进行分支控制,其中if语句有如下三种形式:第一种形式:if ( logic expression
- 界面效果图如下:报表界面说下关键代码 需要开启 Windows Management Instrumentation服务(默认已经
- WebView是Android中一个非常实用的组件,它和Safai、Chrome一样都是基于Webkit网页渲染引擎,可以通过加载HTML数
- 今天看到一个银行的APP上面的loadingview 挺好的,就尝试着自己实现,觉得很简单,但自己实现起来还是发现了一些问题。Loading
- 垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法(你可以 写程序验证这个结论),一般的纯Java编写的Class不需要重新
- Android 调用系统应用的方法总结1、调用系统拍照Intent intent = new Intent("andr
- 简介Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同
- 本文实例为大家分享了C++实现连连看游戏的具体代码,供大家参考,具体内容如下这个项目还是挺不错的,运行后也比较有意思,可以看看。#inclu
- 常量是固定值,程序执行期间不会改变。常量可以是任何基本数据类型,比如整数常量、浮点常量、字符常量或者字符串常量,还有枚举常量。常量可以被当作
- 先来看看效果图先分析饼状图的构成,非常明显,饼状图就是一个又一个的扇形构成的,每个扇形都有不同的颜色,对应的有名字,数据和百分比。经以上信息
- Android Notification使用方法总结一. 基本使用1.构造notification NotificationCompat.B
- 这篇文章主要介绍了java property配置文件管理工具框架过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 本文实例讲述了C#中数组初始化与数组元素复制的方法。分享给大家供大家参考。具体如下:下面的代码演示如何创建和初始化数组,以及C#中如何将数组
- 本文实例为大家分享了Java读取并下载网络文件的具体代码,供大家参考,具体内容如下import java.io.ByteArrayOutpu
- 曾经遇到过这样的问题,在我的代码中使用了通知栏,一切都正常,但是就是正在进行的通知栏中属于我的程序的那一条总是上下跳来跳去,一闪一闪的。感觉
- 我发现网上许多讲解javaweb 项目 SSM(Spring,SpringMVC,Mybatis)配置的时候有些重点没有提到,一下我会贴上一
- SSM(Spring+SpringMVC+Mybatis),目前较为主流的企业级架构方案。标准的MVC设计模式,将整个系统划分为显示层、Co