POI4.0(或3.17) 替换word(docx)文字为图片时,如何设置图片格式浮于文字上方
最近项目中遇到一个问题:POI 根据模板文件生成 word时,需要进行文字替换和图片替换,其中文字替换不必多说,只是图片替换时需要格式为浮于文字上方,网上找不到合适的资料,没办法 ,只好穷折腾了一天。
以WORD文件为模板,我用的是DOCX格式 的,至于DOC格式的话我想下面可能帮助不大,建议终止阅读。
一、文字替换
根据word模板进行文字替换比较简单,主要是将word中的目标位置预先填写好要插入的标记,定义的特殊一点,最好有前缀和后缀,防止poi解析时解析成多个段落,方便拼接,比如写成#{abc};在循环读取时设置好变量,检索到以 “#{” 开头的字符,再继续找到以 “}”结束的字符,拼好再替换。这里代码就不详细贴了,网上也能找到很多。
另外 特殊字符如果是两个相临的话最好中间加个空格一类的 #{u1} #{u2}。
二、图片替换
图片替换时,是以图片替换模板中的文本,还是上文说的类似 #{abc}类的特殊标记,其中POI官方给出的文档比较简单,并且默认是以嵌入式插入的,并不能满足需要,所以需要做一些改动,以poi4.0.0,3.17为例(我用的是4.0.0版本)。
说说解决思路过程:操作docx也可以看作是处理xml文件。docx用压缩文件打开就能看到word/document.xml,此文件是关键,我开始的时候怎么试也不行,最后想到直接用xml方式来处理。我先用CTInline插入图片到一个名叫a.docx的文件,此效果是图片嵌入。将此word另存一份新的b.docx,用office程序打开b.docx,然后只做一个操作,将图片调节为浮于文字上方,关闭保存。现在得到的a.docx和b.docx一个是嵌入一个是浮于文字上方,用压缩文件打开a.docx,b.docx两个文档 ,然后找到刚才说的word/document.xml,用文本编辑器打开,比较在关键文档 处的不同,就是标签<wp:inline>和<wp:anchor>的不同,具体代码不贴了,然后找到这个不同后基本问题就解决了,剩下的就是代码实现了。
代码实现上遇到了一个小问题,就是此关键要是重写org.apache.poi.xwpf.usermodel.XWPFRun.java类,开始用到的版本是3.17,在把源码拷出来重写时,找不到名称为CTSignedTwipsMeasure的类,很奇怪,应该是在poi-ooxml-schemas.jar的包里,最后我是升级到了4.0.0,又把poi-ooxml-schemas.jar包替换为ooxml-schemas-1.4.jar,其实直接替换为ooxml-schemas-1.4.jar就行,不过已经升级了就不再回退版本了,只用到一小部分,没什么大事。
言归正传,下一步就是重写XWPFRun.java(重写操作是先在项目源文件中增加一个org.apache.poi.xwpf.usermodel的包,然后再新建个XWPFRun.java的类,源代码拷上去)。主要是重写XWPFRun.java中的addPicture方法,上代码:
[code]public XWPFPicture addPicture(InputStream pictureData, int pictureType, String filename, int width, int height) throws InvalidFormatException, IOException { String relationId; XWPFPictureData picData; // Work out what to add the picture to, then add both the // picture and the relationship for it // TODO Should we have an interface for this sort of thing? if (parent.getPart() instanceof XWPFHeaderFooter) { XWPFHeaderFooter headerFooter = (XWPFHeaderFooter) parent.getPart(); relationId = headerFooter.addPictureData(pictureData, pictureType); picData = (XWPFPictureData) headerFooter.getRelationById(relationId); } else { @SuppressWarnings("resource") XWPFDocument doc = parent.getDocument(); relationId = doc.addPictureData(pictureData, pictureType); picData = (XWPFPictureData) doc.getRelationById(relationId); } // Create the drawing entry for it try { CTDrawing drawing = run.addNewDrawing(); CTAnchor inline = drawing.addNewAnchor(); // Do the fiddly namespace bits on the inline // (We need full control of what goes where and as what) String xml = "<a:graphic xmlns:a=\"" + CTGraphicalObject.type.getName().getNamespaceURI() + "\">" + "<a:graphicData uri=\"" + CTPicture.type.getName().getNamespaceURI() + "\">" + "<pic:pic xmlns:pic=\"" + CTPicture.type.getName().getNamespaceURI() + "\" />" + "</a:graphicData>" + "</a:graphic>"; InputSource is = new InputSource(new StringReader(xml)); org.w3c.dom.Document doc = DocumentHelper.readDocument(is); inline.set(XmlToken.Factory.parse(doc.getDocumentElement(), DEFAULT_XML_OPTIONS)); System.out.println("*********************"+inline.xmlText()); // Setup the inline inline.setDistT(0); inline.setDistR(0); inline.setDistB(0); inline.setDistL(0); inline.setSimplePos2(false); inline.setRelativeHeight(251658240);//查了文档 ,也试过,没找到作用位置,先放着 inline.setBehindDoc(false);//重点,浮于文字上方 inline.setLocked(false); inline.setLayoutInCell(true); inline.setAllowOverlap(true); CTPoint2D simplePost = inline.addNewSimplePos(); simplePost.setX(0); simplePost.setY(0); CTPosH ph = inline.addNewPositionH(); ph.setRelativeFrom(STRelFromH.PAGE);//以整个页面为准的定位 ph.setPosOffset(396049);//水平位置,针对RelativeFrom的一个偏移 1厘米=360045 ,1.1*360045 CTPosV pv = inline.addNewPositionV(); pv.setRelativeFrom(STRelFromV.PAGE); pv.setPosOffset(468058);//竖直位置,针对RelativeFrom的一个偏移 1.3*360045 /** * 这个是此元素指定的其他程度应添加到每个边缘 (顶部、 底部、 左、 右) * 以补偿应用于 DrawingML 对象的任何图形效果的图像 * 但目前好像用不到 */ CTEffectExtent efextent = inline.addNewEffectExtent(); efextent.setL(19050); efextent.setT(0); efextent.setR(0); efextent.setB(0); CTWrapNone wn = inline.addNewWrapNone(); CTNonVisualGraphicFrameProperties fp = inline.addNewCNvGraphicFramePr(); CTNonVisualDrawingProps docPr = inline.addNewDocPr(); long id = getParent().getDocument().getDrawingIdManager().reserveNew(); docPr.setId(id); /* This name is not visible in Word 2010 anywhere. */ docPr.setName("Drawing " + id); docPr.setDescr(filename); CTPositiveSize2D extent = inline.addNewExtent(); extent.setCx(width); extent.setCy(height); // Grab the picture object CTGraphicalObject graphic = inline.getGraphic(); CTGraphicalObjectData graphicData = graphic.getGraphicData(); CTPicture pic = getCTPictures(graphicData).get(0); // Set it up CTPictureNonVisual nvPicPr = pic.addNewNvPicPr(); CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr(); /* use "0" for the id. See ECM-576, 20.2.2.3 */ cNvPr.setId(0L); /* This name is not visible in Word 2010 anywhere */ cNvPr.setName("Picture " + id); cNvPr.setDescr(filename); CTNonVisualPictureProperties cNvPicPr = nvPicPr.addNewCNvPicPr(); cNvPicPr.addNewPicLocks().setNoChangeAspect(true); CTBlipFillProperties blipFill = pic.addNewBlipFill(); CTBlip blip = blipFill.addNewBlip(); blip.setEmbed(parent.getPart().getRelationId(picData)); /******** 增加 ***********/ blip.setCstate(STBlipCompression.PRINT);//压缩状态 blipFill.addNewStretch().addNewFillRect(); CTShapeProperties spPr = pic.addNewSpPr(); CTTransform2D xfrm = spPr.addNewXfrm(); CTPoint2D off = xfrm.addNewOff(); off.setX(0); off.setY(0); CTPositiveSize2D ext = xfrm.addNewExt(); ext.setCx(width); ext.setCy(height); CTPresetGeometry2D prstGeom = spPr.addNewPrstGeom(); prstGeom.setPrst(STShapeType.RECT); prstGeom.addNewAvLst(); // Finish up XWPFPicture xwpfPicture = new XWPFPicture(pic, this); pictures.add(xwpfPicture); return xwpfPicture; } catch (XmlException | SAXException e) { throw new IllegalStateException(e); } }
其他代码就不贴了,网上很多,浮于文字上方的关键就是要用CTAnchor。
如果大家有时间的话仔细对比office的xml文件及官方文档的话,office的问题基本都能迎刃而解,附其中一个属性的官方文档说明https://docs.microsoft.com/zh-cn/dotnet/api/documentformat.openxml.drawing.wordprocessing.horizontalposition?view=openxml-2.8.1
希望对大家有所帮助。
心态放平,多试多思考。
- 如何在C#下利用RichTextBox打开一个有文字格式和图片的Word文档
- 将剪切板中的图片粘贴至Word,浮动于文字上方,设置图片背景透明
- aswing学习笔记3-在JPanel中,如何将.png格式的图片设置为背景?
- 如何快速清除word(或网页)复制过来的文字背景色及格式
- 如何设置Matlab输出到Word中图片的大小
- 如何在WORD页脚中设置这种页码格式
- java导出word、pdf之添加页眉----页眉(指定格式,包括图片和文字)
- 怎样将图片格式的PDF文档变成word 文字识别
- Mac上如何把图片中的文字转换成word/pdf文字
- 如何将JPEG图片上的文字转换成word
- 使用poi替换word中的文字和图片实现打印
- 如何设置图片、文字在容器(div)中垂直居中的显示?
- ios 开发中,如何设置 uitabbar 里面 tabbaritem 不显示文字,只显示图片,图片垂直居中?
- iOS如何设置按钮左文字右图片
- Word控件Spire.Doc 教程:如何在C#中设置word 脚注的位置和数字格式
- C#word(2007)操作类--新建文档、添加页眉页脚、设置格式、添加文本和超链接、添加图片、表格处理、文档格式转化
- python复制word中的内容,包括格式、图片、文字
- 巧用正则表达式将Word文字替换为图片
- Using POI to replace elements in WORD(.docx/.doc)(使用POI替换word中的特定字符/文字)【改进】
- Java 替换word文档文字并指定位置插入图片