POI4.0(或3.17) 替换word(docx)文字为图片时,如何设置图片格式浮于文字上方_xwpfpicture 调整偏移_eei238234324的博客-CSDN博客

      最近项目中遇到一个问题: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方法,上代码:

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

      希望对大家有所帮助。

      心态放平,多试多思考。


原网址: 访问
创建于: 2023-06-05 10:14:56
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共0条评论