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方法,上代码:

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

      希望对大家有所帮助。

      心态放平,多试多思考。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值