Java实现word文档替换,以及插入图片

Java实现word文档替换,以及在指定插入图片
在开发过程中 我们可能会遇到这样的需求,给你一个word文档,可能是合同等其他,需要你把文档内容补充完整 ,把字段赋值进去或者在指定位置插入一张图片

如果有需要完成品的工具类直接拉倒最后,开箱即用

文章目录

  • 教学开始
    • pom.xml
    • 文档字段替换
    • 插入图片
    • 替换表格里的字段
    • 图片悬浮
    • 完整的POIUtil工具类
    • 重写后的 XWPFRun
    • 一些问题
  • 补充内容
    • word转图片或pdf
    • Linux系统Word转换图片或PDF,字体出现乱码
    • 表格嵌套问题
  • 总结

教学开始

pom.xml

这里采用的是apache poi 的包 开源免费放心食用

        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poiartifactId>
            <version>4.1.0version>
        dependency>
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poi-ooxmlartifactId>
            <version>4.1.0version>
        dependency>
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poi-ooxml-schemasartifactId>
            <version>4.1.0version>
        dependency>
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poi-scratchpadartifactId>
            <version>4.1.0version>
        dependency>
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>ooxml-schemasartifactId>
            <version>1.4version>
        dependency>

下面开始实操给大家解决

文档字段替换

前后效果图对比
替换前:

Java实现word文档替换,以及插入图片_第1张图片
替换后:

Java实现word文档替换,以及插入图片_第2张图片
名字成功变成PG_L

附上代码实例

   
    public static void main(String[] args) {
        //这个map用于存储要替换的字段
        Map<String, Object> params = new HashMap<>();
        params.put("name", "PG_L");
        //模板的路径
        String wordUrl = "D:\\TuTu\\测试.docx";
        //要存储的文件的路径
        String newWordUrl = "D:\\TuTu\\测试2.docx";
        InputStream is = null;
        try {
            //读取文件
            is = new FileInputStream(wordUrl);
            XWPFDocument doc = new XWPFDocument(is);
            //替换段落里面的变量
            POIUtil.replaceInPara(doc, params);
            //输出到测试2文件中
            OutputStream os = new FileOutputStream(newWordUrl);
            doc.write(os);
            is.close();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

// POIUtil工具类
    /**
     * 替换段落里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    public static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
        for(XWPFParagraph para : doc.getParagraphs()) {
            replaceInPara(para, params);
        }
    }
    /**
     * 替换段落里面的变量
     *
     * @param paragraph   要替换的段落
     * @param params 参数
     */
    private static void replaceInPara(XWPFParagraph paragraph, Map<String, Object> params) {
        List<XWPFRun> runs = paragraph.getRuns();
        for (int i = 0; i < runs.size(); i++) {
            //获取字符
            String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
            if (text0 != null && text0.contains("$")) {
                //包含占位符的字符缓存
                StringBuilder cache = new StringBuilder(text0);
                int endIndex = 0;
                boolean contains = text0.contains("}");
                //同一个run中是否包含占位符
                if (!contains) {
                    int j = i + 1;
                    for (; j < runs.size(); j++) {
                        String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
                        if (text1 == null) {
                            continue;
                        }
                        cache.append(text1);
                        if (text1.contains("}")) {
                            endIndex = j;
                            break;
                        }
                    }
                }
                if (contains || endIndex != 0) {
                    //处理替换
                    String s = cache.toString();
                    for (Map.Entry<String, Object> entry : params.entrySet()) {
                        String k = entry.getKey();
                        String v = entry.getValue().toString();
                        if (s.contains("${" + k +"}")) {
                            String replace = s.replace("${" + k +"}", v);
                            runs.get(i).setText(replace, 0);
                            for (int j = endIndex; j > i; j--) {
                                runs.get(j).setText("", 0);
                            }


                            break;
                        }
                    }
                }
            }
        }
    }


有小伙伴可能要问,屁股刘(PG_L),那我想给替换的字段指定样式怎么办?当…当然可以了,但没必要,我的建议是在创建模板的时候就预定义好字段样式属性。如果实在有这方面的诉求,我后期可以单独开一篇文章来讲。

插入图片

插入图片我的建议是用书签来做
打开你的word文档,选择插入,选择书签,并命名
Java实现word文档替换,以及插入图片_第3张图片
替换后的图片:
Java实现word文档替换,以及插入图片_第4张图片
代码实例:

    public static void main(String[] args) {
        Map<String, Object> params = new HashMap<>();
        params.put("name", "PG_L");

        String wordUrl = "D:\\TuTu\\测试.docx";
        String newWordUrl = "D:\\TuTu\\测试2.docx";
        InputStream is = null;
        try {
            InputStream picStream = new FileInputStream("D:\\TuTu\\tutu.jpg");
            params.put("mypic", picStream);
            is = new FileInputStream(wordUrl);
            XWPFDocument doc = new XWPFDocument(is);
            //替换段落里面的变量
            POIUtil.replaceInPara(doc, params);
            OutputStream os = new FileOutputStream(newWordUrl);
            doc.write(os);
            is.close();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//POIUtil
    /**
     * 替换段落里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    public static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
        for(XWPFParagraph para : doc.getParagraphs()) {
            replaceInBook(para, params);
        }
    }
        /**
     * 替换段落里面的书签
     *
     * @param paragraph   要替换的段落
     * @param params 参数
     */
    private static void replaceInBook(XWPFParagraph paragraph, Map<String, Object> params) {
        CTP ctp = paragraph.getCTP();
        for (int dwI = 0; dwI < ctp.sizeOfBookmarkStartArray(); dwI++) {
            try {
                CTBookmark bookmark = ctp.getBookmarkStartArray(dwI);
                if(!params.containsKey(bookmark.getName())) continue;
                InputStream ins = (InputStream) params.get(bookmark.getName());
                XWPFRun run = paragraph.createRun();
                //bus.png为鼠标在word里选择图片时,图片显示的名字,100,100则为像素单元,根据实际需要的大小进行调整即可。
                run.addPicture(ins,XWPFDocument.PICTURE_TYPE_PNG,"tutu.png,", Units.toEMU(100), Units.toEMU(100));
            } catch (InvalidFormatException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

替换表格里的字段

前面讲了怎么替换字段 但是那是段落里的字段,表格里的其实也类似 无非多了一段步骤:找到表格、找到行、找到单元格、再找到段落(替换表格里的图片也类似操作,不再赘述)下面看案例:
替换前:
Java实现word文档替换,以及插入图片_第5张图片
替换后:
Java实现word文档替换,以及插入图片_第6张图片

    public static void main(String[] args) {
        Map<String, Object> params = new HashMap<>();
        params.put("name", "PG_L");
        params.put("henshuai","比坤坤都要帅");

        String wordUrl = "D:\\TuTu\\测试.docx";
        String newWordUrl = "D:\\TuTu\\测试2.docx";
        InputStream is = null;
        try {
            InputStream picStream = new FileInputStream("D:\\TuTu\\tutu.jpg");
            params.put("mypic", picStream);
            is = new FileInputStream(wordUrl);
            XWPFDocument doc = new XWPFDocument(is);
            //替换段落里面的变量
            POIUtil.replaceInTable(doc, params);
            OutputStream os = new FileOutputStream(newWordUrl);
            doc.write(os);
            is.close();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//POIUtil
    /**
     * 替换表格里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    private static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
        for(XWPFTable table : doc.getTables()){
            replaceInTable(table, params);
        }
    }
    /**
     * 替换表格里面的变量
     * @param table 要替换的表格
     * @param params 参数
     */
    private static void replaceInTable(XWPFTable table, Map<String, Object> params) {
        for(XWPFTableRow row : table.getRows()){
            for(XWPFTableCell cell : row.getTableCells()){
                for(XWPFTable secTable : cell.getTables()){
                    replaceInTable(secTable,params);
                }
                for (XWPFParagraph para : cell.getParagraphs()){
                    replaceInPara(para, params);
                    replaceInBook(para, params);
                }
            }
        }
    }

    /**
     * 替换段落里面的变量  这个和之前的一样 用就完事了
     *
     * @param paragraph   要替换的段落
     * @param params 参数
     */
    private static void replaceInPara(XWPFParagraph paragraph, Map<String, Object> params) {
        List<XWPFRun> runs = paragraph.getRuns();
        for (int i = 0; i < runs.size(); i++) {
            //获取字符
            String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
            if (text0 != null && text0.contains("$")) {
                //包含占位符的字符缓存
                StringBuilder cache = new StringBuilder(text0);
                int endIndex = 0;
                boolean contains = text0.contains("}");
                //同一个run中是否包含占位符
                if (!contains) {
                    int j = i + 1;
                    for (; j < runs.size(); j++) {
                        String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
                        if (text1 == null) {
                            continue;
                        }
                        cache.append(text1);
                        if (text1.contains("}")) {
                            endIndex = j;
                            break;
                        }
                    }
                }
                if (contains || endIndex != 0) {
                    //处理替换
                    String s = cache.toString();
                    for (Map.Entry<String, Object> entry : params.entrySet()) {
                        String k = entry.getKey();
                        String v = entry.getValue().toString();
                        if (s.contains("${" + k +"}")) {
                            String replace = s.replace("${" + k +"}", v);
                            runs.get(i).setText(replace, 0);
                            for (int j = endIndex; j > i; j--) {
                                runs.get(j).setText("", 0);
                            }


                            break;
                        }
                    }
                }
            }
        }
    }

图片悬浮

正常插入的图片会影响插入位置行的样式 ,但是如果设置了图片悬浮,就可以完美解决,不过需要自定义好图片位置,设置偏移量
在我屁股刘(PG_L)经过n轮尝试后 发现 重写apache poi下的包实现起来最最简单拉

先在自己的项目里 建一个包
org.apache.poi.xwpf.usermodel
然后把源码的XWPFRun拷贝到包里,然加上我的方法搞定

这里代码就直接展示具体插入图片的方法

    //原方法
    run.addPicture(ins,XWPFDocument.PICTURE_TYPE_PNG,"bus.png,", Units.toEMU(100), Units.toEMU(100));
    



//新方法
    /**
     *
     *  复制原本的方法,加入了是否悬浮图片;在加入图片的偏移位置参数
     *  x和y的取值:1厘米=360045
     * @param pictureData
     * @param pictureType
     * @param filename
     * @param width
     * @param height
     * @param isXuanfu
     * @param x  水平偏移位置
     * @param y  垂直偏移位置
     * @return
     * @throws InvalidFormatException
     * @throws IOException
     */
    public XWPFPicture addPictureV2(InputStream pictureData, int pictureType, String filename, int width, int height,boolean isXuanfu,int x,int y)
            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();
//            CTInline inline = drawing.addNewInline();

            //*************修改 zhengmeng
            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 =
                    " + CTGraphicalObject.type.getName().getNamespaceURI() + "\">" +
                            " + CTPicture.type.getName().getNamespaceURI() + "\">" +
                            " + CTPicture.type.getName().getNamespaceURI() + "\" />" +
                            "" +
                            "";
            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));

            // Setup the inline
            inline.setDistT(0);
            inline.setDistR(0);
            inline.setDistB(0);
            inline.setDistL(0);

            //*********************修改zhengmeng**********************************
            inline.setSimplePos2(false);
            inline.setRelativeHeight(251658240);//查了文档 ,也试过,没找到作用位置,先放着
            inline.setBehindDoc(isXuanfu);//重点,浮于文字上方
            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(x);//水平位置,针对RelativeFrom的一个偏移 1厘米=360045 ,1.1*360045
            CTPosV pv = inline.addNewPositionV();
            pv.setRelativeFrom(STRelFromV.PAGE);
            pv.setPosOffset(y);//竖直位置,针对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));

            /********   增加  zhengmeng***********/
            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);
        }
    }

这样基本的word替换就实现完了 接下来附上工具类

完整的POIUtil工具类


public class POIUtil {

    /**
     * 用一个docx文档作为模板,然后替换其中的内容,再写入目标文档中。
     * @throws Exception
     */
    public static ByteArrayOutputStream templateWrite(String filePath/*, String outFilePath*/, Map<String, Object> params) throws Exception {
        InputStream is = new FileInputStream(filePath);
        XWPFDocument doc = new XWPFDocument(is);
        //替换段落里面的变量
        replaceInPara(doc, params);
        //替换表格里面的变量
        replaceInTable(doc, params);
        //OutputStream os = new FileOutputStream(outFilePath);

        ByteArrayOutputStream os = new ByteArrayOutputStream();//二进制OutputStream
        doc.write(os);
        close(os);
        close(is);
        return os;
    }

    /**
     * 替换段落里面的书签
     *
     * @param paragraph   要替换的段落
     * @param params 参数
     */
    private static void replaceInBook(XWPFParagraph paragraph, Map<String, Object> params) {
        if(params == null){
            return;
        }
        CTP ctp = paragraph.getCTP();
        for (int dwI = 0; dwI < ctp.sizeOfBookmarkStartArray(); dwI++) {
            try {
                CTBookmark bookmark = ctp.getBookmarkStartArray(dwI);
                if(!params.containsKey(bookmark.getName())) continue;
                PicIuputStreamModel model = (PicIuputStreamModel) params.get(bookmark.getName());
                InputStream picIs = model.getIs();
                if(picIs == null) continue;
                XWPFRun run = paragraph.createRun();
                //bus.png为鼠标在word里选择图片时,图片显示的名字,400,400则为像素单元,根据实际需要的大小进行调整即可。
                run.addPictureV2(picIs,XWPFDocument.PICTURE_TYPE_PNG,"bus.png,", Units.toEMU(model.getWidth()), Units.toEMU(model.getHeight()),true,(int)(model.getLeftOffset()*360045),(int)(model.getTopOffset()*360045));
            } catch (InvalidFormatException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 替换段落里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    private static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
        for(XWPFParagraph para : doc.getParagraphs()) {
            replaceInPara(para, params);
            replaceInBook(para, params);
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param paragraph   要替换的段落
     * @param params 参数
     */
    private static void replaceInPara(XWPFParagraph paragraph, Map<String, Object> params) {
        if(params == null){
            return;
        }
        List<XWPFRun> runs = paragraph.getRuns();
        for (int i = 0; i < runs.size(); i++) {
            //获取字符
            String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
            if (text0 != null && text0.contains("$")) {
                //包含占位符的字符缓存
                StringBuilder cache = new StringBuilder(text0);
                int endIndex = 0;
                boolean contains = text0.contains("}");
                //同一个run中是否包含占位符
                if (!contains) {
                    int j = i + 1;
                    for (; j < runs.size(); j++) {
                        String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
                        if (text1 == null) {
                            continue;
                        }
                        cache.append(text1);
                        if (text1.contains("}")) {
                            endIndex = j;
                            break;
                        }
                    }
                }
                if (contains || endIndex != 0) {
                    //处理替换
                    String s = cache.toString();
                    //System.out.println("替换字符:"+s);
                    List<String> keys = get$Value(s);
                    for (String key : keys) {
                        String value = "";
                        if(params.containsKey(key)){
                            value = params.get(key).toString();
                        }
                        String replace = s.replace("${" + key +"}", value);
                        runs.get(i).setText(replace, 0);
                        for (int j = endIndex; j > i; j--) {
                            runs.get(j).setText("", 0);
                        }
                    }

//                    for (Map.Entry entry : params.entrySet()) {
//                        String k = entry.getKey();
//                        String v = entry.getValue().toString();
//                        if (s.contains("${" + k +"}")) {
//                            String replace = s.replace("${" + k +"}", v);
//                            runs.get(i).setText(replace, 0);
//                            for (int j = endIndex; j > i; j--) {
//                                runs.get(j).setText("", 0);
//                            }
//
//
//                            break;
//                        }
//                    }
                }
            }
        }
    }


    /**
     * 替换表格里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    private static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
        for(XWPFTable table : doc.getTables()){
            replaceInTable(table, params);
        }
    }
    /**
     * 替换表格里面的变量
     * @param table 要替换的表格
     * @param params 参数
     */
    private static void replaceInTable(XWPFTable table, Map<String, Object> params) {
        for(XWPFTableRow row : table.getRows()){
            for(XWPFTableCell cell : row.getTableCells()){
                for(XWPFTable secTable : cell.getTables()){
                    replaceInTable(secTable,params);
                }
                for (XWPFParagraph para : cell.getParagraphs()){
                    replaceInPara(para, params);
                    replaceInBook(para, params);
                }
            }
        }
    }

    public static List<String> get$Value(String str) {
        List<String> variables = new ArrayList<>();
        Pattern pattern = Pattern.compile("\\$\\{([^}]+)}");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            variables.add(matcher.group(1));
        }
        return variables;
    }

    /**
     * 关闭输入流
     * @param is
     */
    private static void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭输出流
     * @param os
     */
    private static void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }




}

重写后的 XWPFRun

public class XWPFRun implements ISDTContents, IRunElement, CharacterRun {
    private CTR run;
    private String pictureText;
    private IRunBody parent;
    private List<XWPFPicture> pictures;

    /**
     * @param r the CTR bean which holds the run attributes
     * @param p the parent paragraph
     */
    public XWPFRun(CTR r, IRunBody p) {
        this.run = r;
        this.parent = p;

        /*
         * reserve already occupied drawing ids, so reserving new ids later will
         * not corrupt the document
         */
        for (CTDrawing ctDrawing : r.getDrawingArray()) {
            for (CTAnchor anchor : ctDrawing.getAnchorArray()) {
                if (anchor.getDocPr() != null) {
                    getDocument().getDrawingIdManager().reserve(anchor.getDocPr().getId());
                }
            }
            for (CTInline inline : ctDrawing.getInlineArray()) {
                if (inline.getDocPr() != null) {
                    getDocument().getDrawingIdManager().reserve(inline.getDocPr().getId());
                }
            }
        }

        // Look for any text in any of our pictures or drawings
        StringBuilder text = new StringBuilder();
        List<XmlObject> pictTextObjs = new ArrayList<>();
        pictTextObjs.addAll(Arrays.asList(r.getPictArray()));
        pictTextObjs.addAll(Arrays.asList(r.getDrawingArray()));
        for (XmlObject o : pictTextObjs) {
            XmlObject[] ts = o.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//w:t");
            for (XmlObject t : ts) {
                NodeList kids = t.getDomNode().getChildNodes();
                for (int n = 0; n < kids.getLength(); n++) {
                    if (kids.item(n) instanceof Text) {
                        if (text.length() > 0) {
                            text.append("\n");
                        }
                        text.append(kids.item(n).getNodeValue());
                    }
                }
            }
        }
        pictureText = text.toString();

        // Do we have any embedded pictures?
        // (They're a different CTPicture, under the drawingml namespace)
        pictures = new ArrayList<>();
        for (XmlObject o : pictTextObjs) {
            for (CTPicture pict : getCTPictures(o)) {
                XWPFPicture picture = new XWPFPicture(pict, this);
                pictures.add(picture);
            }
        }
    }

    /**
     * @deprecated Use {@link XWPFRun#XWPFRun(CTR, IRunBody)}
     */
    @Deprecated
    public XWPFRun(CTR r, XWPFParagraph p) {
        this(r, (IRunBody) p);
    }

    /**
     * Add the xml:spaces="preserve" attribute if the string has leading or trailing white spaces
     *
     * @param xs the string to check
     */
    static void preserveSpaces(XmlString xs) {
        String text = xs.getStringValue();
        if (text != null && (text.startsWith(" ") || text.endsWith(" "))) {
            XmlCursor c = xs.newCursor();
            c.toNextToken();
            c.insertAttributeWithValue(new QName("http://www.w3.org/XML/1998/namespace", "space"), "preserve");
            c.dispose();
        }
    }

    private List<CTPicture> getCTPictures(XmlObject o) {
        List<CTPicture> pics = 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) {
                pics.add((CTPicture) pict);
            }
        }
        return pics;
    }

    /**
     * Get the currently used CTR object
     *
     * @return ctr object
     */
    @Internal
    public CTR getCTR() {
        return run;
    }

    /**
     * Get the currently referenced paragraph/SDT object
     *
     * @return current parent
     */
    public IRunBody getParent() {
        return parent;
    }

    /**
     * Get the currently referenced paragraph, or null if a SDT object
     *
     * @deprecated use {@link XWPFRun#getParent()} instead
     */
    @Deprecated
    public XWPFParagraph getParagraph() {
        if (parent instanceof XWPFParagraph) {
            return (XWPFParagraph) parent;
        }
        return null;
    }

    /**
     * @return The {@link XWPFDocument} instance, this run belongs to, or
     * null if parent structure (paragraph > document) is not properly set.
     */
    public XWPFDocument getDocument() {
        if (parent != null) {
            return parent.getDocument();
        }
        return null;
    }

    /**
     * For isBold, isItalic etc
     */
    private static boolean isCTOnOff(CTOnOff onoff) {
        if (!onoff.isSetVal()) {
            return true;
        }
        final STOnOff.Enum val = onoff.getVal();
        return (
                (STOnOff.TRUE == val) ||
                        (STOnOff.X_1 == val) ||
                        (STOnOff.ON == val)
        );
    }

    /**
     * Get the language tag associated with this run, if any.
     *
     * @return the language tag associated with this run, if any
     */
    public String getLang() {
        CTRPr pr = getRunProperties(false);
        Object lang = pr == null || !pr.isSetLang() ? null : pr.getLang().getVal();
        return (String) lang;
    }

    /**
     * Set the language tag associated with this run.
     *
     * @param lang the language tag associated with this run
     * @since 4.1.0
     */
    public void setLang(String lang) {
        CTRPr pr = getRunProperties(true);
        CTLanguage ctLang = pr.isSetLang() ? pr.getLang() : pr.addNewLang();
        ctLang.setVal(lang);
    }

    /**
     * Whether the bold property shall be applied to all non-complex script
     * characters in the contents of this run when displayed in a document
     *
     * @return true if the bold property is applied
     */
    @Override
    public boolean isBold() {
        CTRPr pr = getRunProperties(false);
        return pr != null && pr.isSetB() && isCTOnOff(pr.getB());
    }

    /**
     * Whether the bold property shall be applied to all non-complex script
     * characters in the contents of this run when displayed in a document.
     * 

* This formatting property is a toggle property, which specifies that its * behavior differs between its use within a style definition and its use as * direct formatting. When used as part of a style definition, setting this * property shall toggle the current state of that property as specified up * to this point in the hierarchy (i.e. applied to not applied, and vice * versa). Setting it to false (or an equivalent) shall * result in the current setting remaining unchanged. However, when used as * direct formatting, setting this property to true or false shall set the * absolute state of the resulting property. *

*

* If this element is not present, the default value is to leave the * formatting applied at previous level in the style hierarchy. If this * element is never applied in the style hierarchy, then bold shall not be * applied to non-complex script characters. *

* * @param value true if the bold property is applied to * this run */
@Override public void setBold(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff bold = pr.isSetB() ? pr.getB() : pr.addNewB(); bold.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } /** * Get text color. The returned value is a string in the hex form "RRGGBB". */ public String getColor() { String color = null; if (run.isSetRPr()) { CTRPr pr = getRunProperties(false); if (pr != null && pr.isSetColor()) { CTColor clr = pr.getColor(); color = clr.xgetVal().getStringValue(); } } return color; } /** * Set text color. * * @param rgbStr - the desired color, in the hex form "RRGGBB". */ public void setColor(String rgbStr) { CTRPr pr = getRunProperties(true); CTColor color = pr.isSetColor() ? pr.getColor() : pr.addNewColor(); color.setVal(rgbStr); } /** * Return the string content of this text run * * @return the text of this text run or null if not set */ public String getText(int pos) { return run.sizeOfTArray() == 0 ? null : run.getTArray(pos) .getStringValue(); } /** * Returns text embedded in pictures */ public String getPictureText() { return pictureText; } /** * Sets the text of this text run * * @param value the literal text which shall be displayed in the document */ public void setText(String value) { setText(value, run.sizeOfTArray()); } /** * Sets the text of this text run in the * * @param value the literal text which shall be displayed in the document * @param pos - position in the text array (NB: 0 based) */ public void setText(String value, int pos) { if (pos > run.sizeOfTArray()) { throw new ArrayIndexOutOfBoundsException("Value too large for the parameter position in XWPFRun.setText(String value,int pos)"); } CTText t = (pos < run.sizeOfTArray() && pos >= 0) ? run.getTArray(pos) : run.addNewT(); t.setStringValue(value); preserveSpaces(t); } /** * Whether the italic property should be applied to all non-complex script * characters in the contents of this run when displayed in a document. * * @return true if the italic property is applied */ @Override public boolean isItalic() { CTRPr pr = getRunProperties(false); return pr != null && pr.isSetI() && isCTOnOff(pr.getI()); } /** * Whether the bold property shall be applied to all non-complex script * characters in the contents of this run when displayed in a document *

*

* This formatting property is a toggle property, which specifies that its * behavior differs between its use within a style definition and its use as * direct formatting. When used as part of a style definition, setting this * property shall toggle the current state of that property as specified up * to this point in the hierarchy (i.e. applied to not applied, and vice * versa). Setting it to false (or an equivalent) shall * result in the current setting remaining unchanged. However, when used as * direct formatting, setting this property to true or false shall set the * absolute state of the resulting property. *

*

* If this element is not present, the default value is to leave the * formatting applied at previous level in the style hierarchy. If this * element is never applied in the style hierarchy, then bold shall not be * applied to non-complex script characters. *

* * @param value true if the italic property is applied to * this run */
@Override public void setItalic(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff italic = pr.isSetI() ? pr.getI() : pr.addNewI(); italic.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } /** * Get the underline setting for the run. * * @return the Underline pattern applied to this run * @see (@link UnderlinePatterns} */ public UnderlinePatterns getUnderline() { UnderlinePatterns value = UnderlinePatterns.NONE; CTUnderline underline = getCTUnderline(false); if (underline != null) { STUnderline.Enum baseValue = underline.getVal(); if (baseValue != null) { value = UnderlinePatterns.valueOf(baseValue.intValue()); } } return value; } /** * Specifies that the contents of this run should be displayed along with an * underline appearing directly below the character height. *

* If this element is not present, the default value is to leave the * formatting applied at previous level in the style hierarchy. If this * element is never applied in the style hierarchy, then an underline shall * not be applied to the contents of this run. *

* * @param value - * underline type * @see {@link UnderlinePatterns} : all possible patterns that could be applied */
public void setUnderline(UnderlinePatterns value) { CTUnderline underline = getCTUnderline(true); underline.setVal(STUnderline.Enum.forInt(value.getValue())); } /** * Get the CTUnderline for the run. * @param create Create a new underline if necessary * @return The underline, or null create is false and there is no underline. */ private CTUnderline getCTUnderline(boolean create) { CTRPr pr = getRunProperties(true); CTUnderline underline = pr.getU(); if (create && underline == null) { underline = pr.addNewU(); } return underline; } /** * Set the underline color for the run's underline, if any. * * @param color An RGB color value (e.g, "a0C6F3") or "auto". * @since 4.0.0 */ public void setUnderlineColor(String color) { CTUnderline underline = getCTUnderline(true); SimpleValue svColor = null; if (color.equals("auto")) { STHexColorAuto hexColor = STHexColorAuto.Factory.newInstance(); hexColor.set(STHexColorAuto.Enum.forString(color)); svColor = (SimpleValue) hexColor; } else { STHexColorRGB rgbColor = STHexColorRGB.Factory.newInstance(); rgbColor.setStringValue(color); svColor = (SimpleValue) rgbColor; } underline.setColor(svColor); } /** * Set the underline theme color for the run's underline, if any. * * @param themeColor A theme color name (see {@link STThemeColor.Enum}). * @since 4.0.0 */ public void setUnderlineThemeColor(String themeColor) { CTUnderline underline = getCTUnderline(true); STThemeColor.Enum val = STThemeColor.Enum.forString(themeColor); if (val != null) { underline.setThemeColor(val); } } /** * Get the underline theme color for the run's underline, if any. * * @return The {@link STThemeColor.Enum}. * @since 4.0.0 */ public STThemeColor.Enum getUnderlineThemeColor() { CTUnderline underline = getCTUnderline(false); STThemeColor.Enum color = STThemeColor.NONE; if (underline != null) { color = underline.getThemeColor(); } return color; } /** * Get the underline color for the run's underline, if any. * * @return The RGB color value as as a string of hexadecimal digits (e.g., "A0B2F1") or "auto". * @since 4.0.0 */ public String getUnderlineColor() { CTUnderline underline = getCTUnderline(true); String colorName = "auto"; Object rawValue = underline.getColor(); if (rawValue != null) { if (rawValue instanceof String) { colorName = (String)rawValue; } else { byte[] rgbColor = (byte[])rawValue; colorName = HexDump.toHex(rgbColor[0]) + HexDump.toHex(rgbColor[1]) + HexDump.toHex(rgbColor[2]); } } return colorName; } /** * Specifies that the contents of this run shall be displayed with a single * horizontal line through the center of the line. * * @return true if the strike property is applied */ @Override public boolean isStrikeThrough() { CTRPr pr = getRunProperties(false); return pr != null && pr.isSetStrike() && isCTOnOff(pr.getStrike()); } /** * Specifies that the contents of this run shall be displayed with a single * horizontal line through the center of the line. *

* This formatting property is a toggle property, which specifies that its * behaviour differs between its use within a style definition and its use as * direct formatting. When used as part of a style definition, setting this * property shall toggle the current state of that property as specified up * to this point in the hierarchy (i.e. applied to not applied, and vice * versa). Setting it to false (or an equivalent) shall result in the * current setting remaining unchanged. However, when used as direct * formatting, setting this property to true or false shall set the absolute * state of the resulting property. *

*

* If this element is not present, the default value is to leave the * formatting applied at previous level in the style hierarchy. If this * element is never applied in the style hierarchy, then strikethrough shall * not be applied to the contents of this run. *

* * @param value true if the strike property is applied to * this run */
@Override public void setStrikeThrough(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff strike = pr.isSetStrike() ? pr.getStrike() : pr.addNewStrike(); strike.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } @Deprecated public boolean isStrike() { return isStrikeThrough(); } @Deprecated public void setStrike(boolean value) { setStrikeThrough(value); } /** * Specifies that the contents of this run shall be displayed with a double * horizontal line through the center of the line. * * @return true if the double strike property is applied */ @Override public boolean isDoubleStrikeThrough() { CTRPr pr = getRunProperties(false); return pr != null && pr.isSetDstrike() && isCTOnOff(pr.getDstrike()); } /** * Specifies that the contents of this run shall be displayed with a * double horizontal line through the center of the line. * * @see #setStrikeThrough(boolean) for the rules about this */ @Override public void setDoubleStrikethrough(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff dstrike = pr.isSetDstrike() ? pr.getDstrike() : pr.addNewDstrike(); dstrike.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } @Override public boolean isSmallCaps() { CTRPr pr = getRunProperties(false); return pr != null && pr.isSetSmallCaps() && isCTOnOff(pr.getSmallCaps()); } @Override public void setSmallCaps(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff caps = pr.isSetSmallCaps() ? pr.getSmallCaps() : pr.addNewSmallCaps(); caps.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } @Override public boolean isCapitalized() { CTRPr pr = getRunProperties(false); return pr != null && pr.isSetCaps() && isCTOnOff(pr.getCaps()); } @Override public void setCapitalized(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff caps = pr.isSetCaps() ? pr.getCaps() : pr.addNewCaps(); caps.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } @Override public boolean isShadowed() { CTRPr pr = getRunProperties(false); return pr != null && pr.isSetShadow() && isCTOnOff(pr.getShadow()); } @Override public void setShadow(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff shadow = pr.isSetShadow() ? pr.getShadow() : pr.addNewShadow(); shadow.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } @Override public boolean isImprinted() { CTRPr pr = getRunProperties(false); return pr != null && pr.isSetImprint() && isCTOnOff(pr.getImprint()); } @Override public void setImprinted(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff imprinted = pr.isSetImprint() ? pr.getImprint() : pr.addNewImprint(); imprinted.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } @Override public boolean isEmbossed() { CTRPr pr = getRunProperties(false); return pr != null && pr.isSetEmboss() && isCTOnOff(pr.getEmboss()); } @Override public void setEmbossed(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff emboss = pr.isSetEmboss() ? pr.getEmboss() : pr.addNewEmboss(); emboss.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } /** * Specifies the alignment which shall be applied to the contents of this * run in relation to the default appearance of the run's text. * This allows the text to be repositioned as subscript or superscript without * altering the font size of the run properties. * * @return VerticalAlign * @see {@link VerticalAlign} all possible value that could be applyed to this run * @deprecated use {@link XWPFRun.getVerticalAlignment} */ @Removal(version = "4.2") public VerticalAlign getSubscript() { CTRPr pr = getRunProperties(false); return (pr != null && pr.isSetVertAlign()) ? VerticalAlign.valueOf(pr.getVertAlign().getVal().intValue()) : VerticalAlign.BASELINE; } /** * Specifies the alignment which shall be applied to the contents of this * run in relation to the default appearance of the run's text. This allows * the text to be repositioned as subscript or superscript without altering * the font size of the run properties. *

* If this element is not present, the default value is to leave the * formatting applied at previous level in the style hierarchy. If this * element is never applied in the style hierarchy, then the text shall not * be subscript or superscript relative to the default baseline location for * the contents of this run. *

* * @param valign Type of vertical align to apply * @see VerticalAlign */
public void setSubscript(VerticalAlign valign) { CTRPr pr = getRunProperties(true); CTVerticalAlignRun ctValign = pr.isSetVertAlign() ? pr.getVertAlign() : pr.addNewVertAlign(); ctValign.setVal(STVerticalAlignRun.Enum.forInt(valign.getValue())); } @Override public int getKerning() { CTRPr pr = getRunProperties(false); if (pr == null || !pr.isSetKern()) { return 0; } return pr.getKern().getVal().intValue(); } @Override public void setKerning(int kern) { CTRPr pr = getRunProperties(true); CTHpsMeasure kernmes = pr.isSetKern() ? pr.getKern() : pr.addNewKern(); kernmes.setVal(BigInteger.valueOf(kern)); } @Override public boolean isHighlighted() { CTRPr pr = getRunProperties(false); if (pr == null || !pr.isSetHighlight()) { return false; } STHighlightColor.Enum val = pr.getHighlight().getVal(); if (val == null || val == STHighlightColor.NONE) { return false; } return true; } // TODO Provide a wrapper round STHighlightColor, then expose getter/setter // for the highlight colour. Ideally also then add to CharacterRun interface @Override public int getCharacterSpacing() { CTRPr pr = getRunProperties(false); if (pr == null || !pr.isSetSpacing()) { return 0; } return pr.getSpacing().getVal().intValue(); } @Override public void setCharacterSpacing(int twips) { CTRPr pr = getRunProperties(true); CTSignedTwipsMeasure spc = pr.isSetSpacing() ? pr.getSpacing() : pr.addNewSpacing(); spc.setVal(BigInteger.valueOf(twips)); } /** * Gets the fonts which shall be used to display the text contents of * this run. Specifies a font which shall be used to format all characters * in the ASCII range (0 - 127) within the parent run * * @return a string representing the font family */ public String getFontFamily() { return getFontFamily(null); } /** * Specifies the fonts which shall be used to display the text contents of * this run. Specifies a font which shall be used to format all characters * in the ASCII range (0 - 127) within the parent run. *

* Also sets the other font ranges, if they haven't been set before * * @param fontFamily The font family to apply * @see FontCharRange */ public void setFontFamily(String fontFamily) { setFontFamily(fontFamily, null); } /** * Alias for {@link #getFontFamily()} */ @Override public String getFontName() { return getFontFamily(); } /** * Gets the font family for the specified font char range. * If fcr is null, the font char range "ascii" is used * * @param fcr the font char range, defaults to "ansi" * @return a string representing the font famil */ public String getFontFamily(FontCharRange fcr) { CTRPr pr = getRunProperties(false); if (pr == null || !pr.isSetRFonts()) { return null; } CTFonts fonts = pr.getRFonts(); switch (fcr == null ? FontCharRange.ascii : fcr) { default: case ascii: return fonts.getAscii(); case cs: return fonts.getCs(); case eastAsia: return fonts.getEastAsia(); case hAnsi: return fonts.getHAnsi(); } } /** * Specifies the fonts which shall be used to display the text contents of * this run. The default handling for fcr == null is to overwrite the * ascii font char range with the given font family and also set all not * specified font ranges * * @param fontFamily The font family to apply * @param fcr FontCharRange or null for default handling */ public void setFontFamily(String fontFamily, FontCharRange fcr) { CTRPr pr = getRunProperties(true); CTFonts fonts = pr.isSetRFonts() ? pr.getRFonts() : pr.addNewRFonts(); if (fcr == null) { fonts.setAscii(fontFamily); if (!fonts.isSetHAnsi()) { fonts.setHAnsi(fontFamily); } if (!fonts.isSetCs()) { fonts.setCs(fontFamily); } if (!fonts.isSetEastAsia()) { fonts.setEastAsia(fontFamily); } } else { switch (fcr) { case ascii: fonts.setAscii(fontFamily); break; case cs: fonts.setCs(fontFamily); break; case eastAsia: fonts.setEastAsia(fontFamily); break; case hAnsi: fonts.setHAnsi(fontFamily); break; } } } /** * Specifies the font size which shall be applied to all non complex script * characters in the contents of this run when displayed. * * @return value representing the font size */ @Override public int getFontSize() { CTRPr pr = getRunProperties(false); return (pr != null && pr.isSetSz()) ? pr.getSz().getVal().divide(new BigInteger("2")).intValue() : -1; } /** * Specifies the font size which shall be applied to all non complex script * characters in the contents of this run when displayed. *

* If this element is not present, the default value is to leave the value * applied at previous level in the style hierarchy. If this element is * never applied in the style hierarchy, then any appropriate font size may * be used for non complex script characters. *

* * @param size The font size as number of point measurements. */
@Override public void setFontSize(int size) { BigInteger bint = new BigInteger(Integer.toString(size)); CTRPr pr = getRunProperties(true); CTHpsMeasure ctSize = pr.isSetSz() ? pr.getSz() : pr.addNewSz(); ctSize.setVal(bint.multiply(new BigInteger("2"))); } /** * This element specifies the amount by which text shall be raised or * lowered for this run in relation to the default baseline of the * surrounding non-positioned text. This allows the text to be repositioned * without altering the font size of the contents. * * @return a big integer representing the amount of text shall be "moved" */ public int getTextPosition() { CTRPr pr = getRunProperties(false); return (pr != null && pr.isSetPosition()) ? pr.getPosition().getVal().intValue() : -1; } /** * This element specifies the amount by which text shall be raised or * lowered for this run in relation to the default baseline of the * surrounding non-positioned text. This allows the text to be repositioned * without altering the font size of the contents. *

* If the val attribute is positive, then the parent run shall be raised * above the baseline of the surrounding text by the specified number of * half-points. If the val attribute is negative, then the parent run shall * be lowered below the baseline of the surrounding text by the specified * number of half-points. *

*

* If this element is not present, the default value is to leave the * formatting applied at previous level in the style hierarchy. If this * element is never applied in the style hierarchy, then the text shall not * be raised or lowered relative to the default baseline location for the * contents of this run. *

* * @param val Positive values will raise the baseline of the text, negative * values will lower it. */
public void setTextPosition(int val) { BigInteger bint = new BigInteger(Integer.toString(val)); CTRPr pr = getRunProperties(true); CTSignedHpsMeasure position = pr.isSetPosition() ? pr.getPosition() : pr.addNewPosition(); position.setVal(bint); } /** * */ public void removeBreak() { // TODO } /** * Specifies that a break shall be placed at the current location in the run * content. * A break is a special character which is used to override the * normal line breaking that would be performed based on the normal layout * of the document's contents. * * @see #addCarriageReturn() */ public void addBreak() { run.addNewBr(); } /** * Specifies that a break shall be placed at the current location in the run * content. * A break is a special character which is used to override the * normal line breaking that would be performed based on the normal layout * of the document's contents. *

* The behavior of this break character (the * location where text shall be restarted after this break) shall be * determined by its type values. *

* * @see BreakType */
public void addBreak(BreakType type) { CTBr br = run.addNewBr(); br.setType(STBrType.Enum.forInt(type.getValue())); } /** * Specifies that a break shall be placed at the current location in the run * content. A break is a special character which is used to override the * normal line breaking that would be performed based on the normal layout * of the document's contents. *

* The behavior of this break character (the * location where text shall be restarted after this break) shall be * determined by its type (in this case is BreakType.TEXT_WRAPPING as default) and clear attribute values. *

* * @see BreakClear */
public void addBreak(BreakClear clear) { CTBr br = run.addNewBr(); br.setType(STBrType.Enum.forInt(BreakType.TEXT_WRAPPING.getValue())); br.setClear(STBrClear.Enum.forInt(clear.getValue())); } /** * Specifies that a tab shall be placed at the current location in * the run content. */ public void addTab() { run.addNewTab(); } public void removeTab() { //TODO } /** * Specifies that a carriage return shall be placed at the * current location in the run content. * A carriage return is used to end the current line of text in * Wordprocess. * The behavior of a carriage return in run content shall be * identical to a break character with null type and clear attributes, which * shall end the current line and find the next available line on which to * continue. * The carriage return character forced the following text to be * restarted on the next available line in the document. */ public void addCarriageReturn() { run.addNewCr(); } public void removeCarriageReturn() { //TODO } /** * * Adds a picture to the run. This method handles * attaching the picture data to the overall file. * 复制原本的方法,加入了是否悬浮图片;在加入图片的偏移位置参数 * x和y的取值:1厘米=360045 * @param pictureData * @param pictureType * @param filename * @param width * @param height * @param isXuanfu * @param x 水平偏移位置 * @param y 垂直偏移位置 * @return * @throws InvalidFormatException * @throws IOException */ public XWPFPicture addPictureV2(InputStream pictureData, int pictureType, String filename, int width, int height,boolean isXuanfu,int x,int y) 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(); // CTInline inline = drawing.addNewInline(); //*************修改 zhengmeng 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 = " + CTGraphicalObject.type.getName().getNamespaceURI() + "\">" + " + CTPicture.type.getName().getNamespaceURI() + "\">" + " + CTPicture.type.getName().getNamespaceURI() + "\" />" + "" + ""; 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)); // Setup the inline inline.setDistT(0); inline.setDistR(0); inline.setDistB(0); inline.setDistL(0); //*********************修改zhengmeng********************************** inline.setSimplePos2(false); inline.setRelativeHeight(251658240);//查了文档 ,也试过,没找到作用位置,先放着 inline.setBehindDoc(isXuanfu);//重点,浮于文字上方 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(x);//水平位置,针对RelativeFrom的一个偏移 1厘米=360045 ,1.1*360045 CTPosV pv = inline.addNewPositionV(); pv.setRelativeFrom(STRelFromV.PAGE); pv.setPosOffset(y);//竖直位置,针对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)); /******** 增加 zhengmeng***********/ 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); } } public XWPFPicture addPicture(InputStream pictureData, int pictureType, String filename, int width, int height) throws InvalidFormatException, IOException { String relationId; XWPFPictureData picData; if (this.parent.getPart() instanceof XWPFHeaderFooter) { XWPFHeaderFooter headerFooter = (XWPFHeaderFooter)this.parent.getPart(); relationId = headerFooter.addPictureData(pictureData, pictureType); picData = (XWPFPictureData)headerFooter.getRelationById(relationId); } else { XWPFDocument doc = this.parent.getDocument(); relationId = doc.addPictureData(pictureData, pictureType); picData = (XWPFPictureData)doc.getRelationById(relationId); } try { CTDrawing drawing = this.run.addNewDrawing(); CTInline inline = drawing.addNewInline(); String xml = " + CTGraphicalObject.type.getName().getNamespaceURI() + "\"> + CTPicture.type.getName().getNamespaceURI() + "\"> + CTPicture.type.getName().getNamespaceURI() + "\" />"; InputSource is = new InputSource(new StringReader(xml)); Document doc = DocumentHelper.readDocument(is); inline.set(org.apache.xmlbeans.XmlToken.Factory.parse(doc.getDocumentElement(), POIXMLTypeLoader.DEFAULT_XML_OPTIONS)); inline.setDistT(0L); inline.setDistR(0L); inline.setDistB(0L); inline.setDistL(0L); CTNonVisualDrawingProps docPr = inline.addNewDocPr(); long id = this.getParent().getDocument().getDrawingIdManager().reserveNew(); docPr.setId(id); docPr.setName("Drawing " + id); docPr.setDescr(filename); CTPositiveSize2D extent = inline.addNewExtent(); extent.setCx((long)width); extent.setCy((long)height); CTGraphicalObject graphic = inline.getGraphic(); CTGraphicalObjectData graphicData = graphic.getGraphicData(); CTPicture pic = (CTPicture)this.getCTPictures(graphicData).get(0); CTPictureNonVisual nvPicPr = pic.addNewNvPicPr(); CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr(); cNvPr.setId(0L); 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(this.parent.getPart().getRelationId(picData)); blipFill.addNewStretch().addNewFillRect(); CTShapeProperties spPr = pic.addNewSpPr(); CTTransform2D xfrm = spPr.addNewXfrm(); CTPoint2D off = xfrm.addNewOff(); off.setX(0L); off.setY(0L); CTPositiveSize2D ext = xfrm.addNewExt(); ext.setCx((long)width); ext.setCy((long)height); CTPresetGeometry2D prstGeom = spPr.addNewPrstGeom(); prstGeom.setPrst(STShapeType.RECT); prstGeom.addNewAvLst(); XWPFPicture xwpfPicture = new XWPFPicture(pic, this); this.pictures.add(xwpfPicture); return xwpfPicture; } catch (SAXException | XmlException var31) { throw new IllegalStateException(var31); } } /** * this method add chart template into document * * @param chartRelId relation id of chart in document relation file * @throws InvalidFormatException * @throws IOException * @since POI 4.0.0 */ @Internal public CTInline addChart(String chartRelId) throws InvalidFormatException, IOException { try { CTInline inline = run.addNewDrawing().addNewInline(); //xml part of chart in document String xml = " + CTGraphicalObject.type.getName().getNamespaceURI() + "\">" + " + CTChart.type.getName().getNamespaceURI() + "\">" + " + CTChart.type.getName().getNamespaceURI() + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:id=\"" + chartRelId + "\" />" + "" + ""; 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)); // Setup the inline with 0 margin inline.setDistT(0); inline.setDistR(0); inline.setDistB(0); inline.setDistL(0); CTNonVisualDrawingProps docPr = inline.addNewDocPr(); long id = getParent().getDocument().getDrawingIdManager().reserveNew(); docPr.setId(id); //This name is not visible in Word anywhere. docPr.setName("chart " + id); return inline; } catch (XmlException | SAXException e) { throw new IllegalStateException(e); } } /** * Returns the embedded pictures of the run. These * are pictures which reference an external, * embedded picture image such as a .png or .jpg */ public List<XWPFPicture> getEmbeddedPictures() { return pictures; } /** * Set the style ID for the run. * * @param styleId ID (not name) of the style to set for the run, e.g. "BoldItalic" (not "Bold Italic"). */ public void setStyle(String styleId) { CTRPr pr = getCTR().getRPr(); if (null == pr) { pr = getCTR().addNewRPr(); } CTString style = pr.getRStyle() != null ? pr.getRStyle() : pr.addNewRStyle(); style.setVal(styleId); } /** * Returns the string version of the text and the phonetic string */ @Override public String toString() { String phonetic = getPhonetic(); if (phonetic.length() > 0) { return text() + " (" + phonetic + ")"; } else { return text(); } } /** * Returns the string version of the text, with tabs and * carriage returns in place of their xml equivalents. */ @Override public String text() { StringBuilder text = new StringBuilder(64); // Grab the text and tabs of the text run // Do so in a way that preserves the ordering XmlCursor c = run.newCursor(); c.selectPath("./*"); while (c.toNextSelection()) { XmlObject o = c.getObject(); if (o instanceof CTRuby) { handleRuby(o, text, false); continue; } _getText(o, text); } c.dispose(); return text.toString(); } /** * @return the phonetic (ruby) string associated with this run or an empty String if none exists */ public String getPhonetic() { StringBuilder text = new StringBuilder(64); // Grab the text and tabs of the text run // Do so in a way that preserves the ordering XmlCursor c = run.newCursor(); c.selectPath("./*"); while (c.toNextSelection()) { XmlObject o = c.getObject(); if (o instanceof CTRuby) { handleRuby(o, text, true); } } // Any picture text? if (pictureText != null && pictureText.length() > 0) { text.append("\n").append(pictureText).append("\n"); } c.dispose(); return text.toString(); } /** * @param rubyObj rubyobject * @param text buffer to which to append the content * @param extractPhonetic extract the phonetic (rt) component or the base component */ private void handleRuby(XmlObject rubyObj, StringBuilder text, boolean extractPhonetic) { XmlCursor c = rubyObj.newCursor(); //according to the spec, a ruby object //has the phonetic (rt) first, then the actual text (base) //second. c.selectPath(".//*"); boolean inRT = false; boolean inBase = false; while (c.toNextSelection()) { XmlObject o = c.getObject(); if (o instanceof CTRubyContent) { String tagName = o.getDomNode().getNodeName(); if ("w:rt".equals(tagName)) { inRT = true; } else if ("w:rubyBase".equals(tagName)) { inRT = false; inBase = true; } } else { if (extractPhonetic && inRT) { _getText(o, text); } else if (!extractPhonetic && inBase) { _getText(o, text); } } } c.dispose(); } private void _getText(XmlObject o, StringBuilder text) { if (o instanceof CTText) { String tagName = o.getDomNode().getNodeName(); // Field Codes (w:instrText, defined in spec sec. 17.16.23) // come up as instances of CTText, but we don't want them // in the normal text output if (!"w:instrText".equals(tagName)) { text.append(((CTText) o).getStringValue()); } } // Complex type evaluation (currently only for extraction of check boxes) if (o instanceof CTFldChar) { CTFldChar ctfldChar = ((CTFldChar) o); if (ctfldChar.getFldCharType() == STFldCharType.BEGIN) { if (ctfldChar.getFfData() != null) { for (CTFFCheckBox checkBox : ctfldChar.getFfData().getCheckBoxList()) { if (checkBox.getDefault() != null && checkBox.getDefault().getVal() == STOnOff.X_1) { text.append("|X|"); } else { text.append("|_|"); } } } } } if (o instanceof CTPTab) { text.append('\t'); } if (o instanceof CTBr) { text.append('\n'); } if (o instanceof CTEmpty) { // Some inline text elements get returned not as // themselves, but as CTEmpty, owing to some odd // definitions around line 5642 of the XSDs // This bit works around it, and replicates the above // rules for that case String tagName = o.getDomNode().getNodeName(); if ("w:tab".equals(tagName) || "tab".equals(tagName)) { text.append('\t'); } if ("w:br".equals(tagName) || "br".equals(tagName)) { text.append('\n'); } if ("w:cr".equals(tagName) || "cr".equals(tagName)) { text.append('\n'); } } if (o instanceof CTFtnEdnRef) { CTFtnEdnRef ftn = (CTFtnEdnRef) o; String footnoteRef = ftn.getDomNode().getLocalName().equals("footnoteReference") ? "[footnoteRef:" + ftn.getId().intValue() + "]" : "[endnoteRef:" + ftn.getId().intValue() + "]"; text.append(footnoteRef); } } /** * @see [MS-OI29500] Run Fonts */ public static enum FontCharRange { ascii /* char 0-127 */, cs /* complex symbol */, eastAsia /* east asia */, hAnsi /* high ansi */ } /** * Set the text expand/collapse scale value. * * @param percentage The percentage to expand or compress the text * @since 4.0.0 */ public void setTextScale(int percentage) { CTRPr pr = getRunProperties(true); CTTextScale scale = pr.isSetW() ? pr.getW() : pr.addNewW(); scale.setVal(percentage); } /** * Gets the current text scale value. * * @return Value is an integer percentage * @since 4.0.0 */ public int getTextScale() { CTRPr pr = getRunProperties(true); CTTextScale scale = pr.isSetW() ? pr.getW() : pr.addNewW(); int value = scale.getVal(); if (value == 0) { value = 100; // 100% scaling, that is, no change. See 17.3.2.43 w (Expanded/Compressed Text) } return value; } /** * Set the highlight color for the run. Silently does nothing of colorName is not a recognized value. * * @param colorName The name of the color as defined in the ST_HighlightColor simple type ({@link STHightlightColor}) * @since 4.0.0 */ public void setTextHighlightColor(String colorName) { CTRPr pr = getRunProperties(true); CTHighlight highlight = pr.isSetHighlight() ? pr.getHighlight() : pr.addNewHighlight(); STHighlightColor color = highlight.xgetVal(); if (color == null) { color = STHighlightColor.Factory.newInstance(); } STHighlightColor.Enum val = STHighlightColor.Enum.forString(colorName); if (val != null) { color.setStringValue(val.toString()); highlight.xsetVal(color); } } /** * Gets the highlight color for the run * * @return {@link STHighlightColor} for the run. * @since 4.0.0 */ public STHighlightColor.Enum getTextHightlightColor() { CTRPr pr = getRunProperties(true); CTHighlight highlight = pr.isSetHighlight() ? pr.getHighlight() : pr.addNewHighlight(); STHighlightColor color = highlight.xgetVal(); if (color == null) { color = STHighlightColor.Factory.newInstance(); color.set(STHighlightColor.NONE); } return (STHighlightColor.Enum)(color.enumValue()); } /** * Get the vanish (hidden text) value * * @return True if the run is hidden text. * @since 4.0.0 */ public boolean isVanish() { CTRPr pr = getRunProperties(true); return pr != null && pr.isSetVanish() && isCTOnOff(pr.getVanish()); } /** * The vanish (hidden text) property for the run. * * @param value Set to true to make the run hidden text. * @since 4.0.0 */ public void setVanish(boolean value) { CTRPr pr = getRunProperties(true); CTOnOff vanish = pr.isSetVanish() ? pr.getVanish() : pr.addNewVanish(); vanish.setVal(value ? STOnOff.TRUE : STOnOff.FALSE); } /** * Get the vertical alignment value * * @return {@link STVerticalAlignRun.Enum} value (see 22.9.2.17 ST_VerticalAlignRun (Vertical Positioning Location)) * @since 4.0.0 */ public STVerticalAlignRun.Enum getVerticalAlignment() { CTRPr pr = getRunProperties(true); CTVerticalAlignRun vertAlign = pr.isSetVertAlign() ? pr.getVertAlign() : pr.addNewVertAlign(); STVerticalAlignRun.Enum val = vertAlign.getVal(); if (val == null) { val = STVerticalAlignRun.BASELINE; } return val; } /** * Set the vertical alignment of the run. * * @param verticalAlignment Vertical alignment value, one of "baseline", "superscript", or "subscript". * @since 4.0.0 */ public void setVerticalAlignment(String verticalAlignment) { CTRPr pr = getRunProperties(true); CTVerticalAlignRun vertAlign = pr.isSetVertAlign() ? pr.getVertAlign() : pr.addNewVertAlign(); STVerticalAlignRun align = vertAlign.xgetVal(); if (align == null) { align = STVerticalAlignRun.Factory.newInstance(); } STVerticalAlignRun.Enum val = STVerticalAlignRun.Enum.forString(verticalAlignment); if (val != null) { align.setStringValue(val.toString()); vertAlign.xsetVal(align); } } /** * Get the emphasis mark value for the run. * * @return {@link STEm.Enum} emphasis mark type enumeration. See 17.18.24 ST_Em (Emphasis Mark Type). * @since 4.0.0 */ public STEm.Enum getEmphasisMark() { CTRPr pr = getRunProperties(true); CTEm emphasis = pr.isSetEm() ? pr.getEm() : pr.addNewEm(); STEm.Enum val = emphasis.getVal(); if (val == null) { val = STEm.NONE; } return val; } /** * Set the emphasis mark for the run. The emphasis mark goes above or below the run * text. * * @param markType Emphasis mark type name, e.g., "dot" or "none". See 17.18.24 ST_Em (Emphasis Mark Type) * @since 4.0.0 */ public void setEmphasisMark(String markType) { CTRPr pr = getRunProperties(true); CTEm emphasisMark = pr.isSetEm() ? pr.getEm() : pr.addNewEm(); STEm mark = emphasisMark.xgetVal(); if (mark == null) { mark = STEm.Factory.newInstance(); } STEm.Enum val = STEm.Enum.forString(markType); if (val != null) { mark.setStringValue(val.toString()); emphasisMark.xsetVal(mark); } } /** * Get the run properties for the run. * * @param create If true, create the properties, if false, do not. * @return The run properties or null if there are no properties and create is false. */ protected CTRPr getRunProperties(boolean create) { CTRPr pr = run.isSetRPr() ? run.getRPr() : null; if (create && pr == null) { pr = run.addNewRPr(); } return pr; } }

一些问题

在实际开发中也遇到很多问题比如自定义的 不包含在一个 X W P F R u n 中,当然书签可以解决,但是为了展示字段直观,还是采用了字段替换,逐字段读取找到所有的 X W P F R u n 再替换,还有一个操作也能实现,但没必要,因为我提供的工具类完美解决了,感兴趣的可以按照如下操作试一下 1. 将 d o c x 后缀改成 z i p 2. 找到 w o r d / d o c u e n t . x m l 并打开 3. 修改或删除里面的 < w : t > {}不包含在一个XWPFRun中, 当然书签可以解决,但是为了展示字段直观,还是采用了字段替换, 逐字段读取找到所有的XWPFRun再替换,还有一个操作也能实现,但没必要,因为我提供的工具类完美解决了, 感兴趣的可以按照如下操作试一下 1.将docx后缀改成zip 2.找到 word/docuent.xml并打开 3.修改或删除里面的 不包含在一个XWPFRun中,当然书签可以解决,但是为了展示字段直观,还是采用了字段替换,逐字段读取找到所有的XWPFRun再替换,还有一个操作也能实现,但没必要,因为我提供的工具类完美解决了,感兴趣的可以按照如下操作试一下1.docx后缀改成zip2.找到word/docuent.xml并打开3.修改或删除里面的<w:t>{name}
docx文件的本质就是zip

补充内容

word转图片或pdf

点击此处有详细教程

Linux系统Word转换图片或PDF,字体出现乱码

点击此处有详细教程

表格嵌套问题

2023/12/11 21:19:00补充
表格中还存在表格的获取方式
通过XWPFTableCell中的getTables()方法获取表格
前文已经同步修改

总结

求个一键三连哦!!!!!!

你可能感兴趣的:(Apache,poi,java,java,word,开发语言)