用POI实现word读写操作并自动将标题编号

WORD文档的读写比较复杂,包括标题、段落、正文、表格等,此处只是简单的读写word文档(2007及以上版本)
注意:
1.目标文档标题结构都是相邻的,没有相差两级及以上的标题
2.只读取文本
3.能简单设置格式,如标题加粗、段落首行缩进

需要的jar包
这里写图片描述

public class DealWordByPoi {
private Map<String,Map<String,Object>> orderMap =new HashMap<String, Map<String,Object>>();

    public void init(String targetPath,String sourcePath){
        InputStream is = null;
        XWPFDocument doc=null;
        OutputStream out=null;
        try {
            XWPFDocument createDoc = new XWPFDocument();

            is = new FileInputStream(sourcePath);
            doc = new XWPFDocument(is);
            //获取段落
            List<XWPFParagraph> paras=doc.getParagraphs();

            for (XWPFParagraph para : paras){
//              System.out.println(para.getCTP());//得到xml格式
//              System.out.println(para.getStyleID());//段落级别
//              System.out.println(para.getParagraphText());//段落内容

                String titleLvl = getTitleLvl(doc,para);//获取段落级别
                if("a5".equals(titleLvl)||"HTML".equals(titleLvl)||"".equals(titleLvl)||null==titleLvl){
                    titleLvl = "8";
                }
//              System.out.println(titleLvl+"-----");//0,1,2
                if(!"8".equals(titleLvl)){
                    System.out.println(titleLvl+"===="+para.getParagraphText());
                }


                XWPFParagraph ctPara = createDoc.createParagraph();
                //一个XWPFRun代表具有相同属性的一个区域。
                XWPFRun ctRun = ctPara.createRun();
                String ctText = para.getParagraphText();
                ctRun.setFontFamily("宋体");//字体
                ctRun.setFontSize(12);

                if(null!=titleLvl&&!"".equals(titleLvl)&&!"8".equals(titleLvl)){
                    addCustomHeadingStyle(createDoc,titleLvl,Integer.parseInt(titleLvl));
                    String orderCode = getOrderCode(titleLvl);//获取编号
                    ctText = orderCode+" "+ctText;
                    ctRun.setBold(true);//标题加粗
                    ctRun.setFontSize(14);

                    ctPara.setStyle(titleLvl);

                }else{//正文
                    ctPara.setIndentationFirstLine(567);//首行缩进:567==1厘米
//                  ctRun.setTextPosition(6);//设置行间距
                }

                ctRun.setText(ctText);//内容
            }
            out=new FileOutputStream(targetPath);
            createDoc.write(out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            try {
                if(null!=out){
                    out.close();
                } 
                if(null!=is){
                    is.close();
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Word中的大纲级别,可以通过getPPr().getOutlineLvl()直接提取,但需要注意,Word中段落级别,通过如下三种方式定义: 
     *  1、直接对段落进行定义; 
     *  2、对段落的样式进行定义; 
     *  3、对段落样式的基础样式进行定义。 
     *  因此,在通过“getPPr().getOutlineLvl()”提取时,需要依次在如上三处读取。
     * @param doc
     * @param para
     * @return
     */
    private String getTitleLvl(XWPFDocument doc, XWPFParagraph para) {
        String titleLvl = "";
        try {
            //判断该段落是否设置了大纲级别
            if (para.getCTP().getPPr().getOutlineLvl() != null) {
                // System.out.println("getCTP()");
//              System.out.println(para.getParagraphText());
//              System.out.println(para.getCTP().getPPr().getOutlineLvl().getVal());

                return String.valueOf(para.getCTP().getPPr().getOutlineLvl().getVal());
            }
        } catch (Exception e) {

        }

        try {
            //判断该段落的样式是否设置了大纲级别
            if (doc.getStyles().getStyle(para.getStyle()).getCTStyle().getPPr().getOutlineLvl() != null) {

                // System.out.println("getStyle");
//              System.out.println(para.getParagraphText());
//              System.out.println(doc.getStyles().getStyle(para.getStyle()).getCTStyle().getPPr().getOutlineLvl().getVal());

                return String.valueOf(doc.getStyles().getStyle(para.getStyle()).getCTStyle().getPPr().getOutlineLvl().getVal());
            }
        } catch (Exception e) {

        }

        try {
            //判断该段落的样式的基础样式是否设置了大纲级别
            if (doc.getStyles().getStyle(doc.getStyles().getStyle(para.getStyle()).getCTStyle().getBasedOn().getVal())
                    .getCTStyle().getPPr().getOutlineLvl() != null) {
                // System.out.println("getBasedOn");
//              System.out.println(para.getParagraphText());
                String styleName = doc.getStyles().getStyle(para.getStyle()).getCTStyle().getBasedOn().getVal();
//              System.out.println(doc.getStyles().getStyle(styleName).getCTStyle().getPPr().getOutlineLvl().getVal());

                return String.valueOf(doc.getStyles().getStyle(styleName).getCTStyle().getPPr().getOutlineLvl().getVal());
            }
        } catch (Exception e) {

        }

        try {
            if(para.getStyleID()!=null){
                return para.getStyleID();
            }
        } catch (Exception e) {

        }

        return titleLvl;
    }

    /**
     * 增加自定义标题样式。这里用的是stackoverflow的源码
     * 
     * @param docxDocument 目标文档
     * @param strStyleId 样式名称
     * @param headingLevel 样式级别
     */
    private static void addCustomHeadingStyle(XWPFDocument docxDocument, String strStyleId, int headingLevel) {

        strStyleId = String.valueOf(Integer.parseInt(strStyleId)+1);
        CTStyle ctStyle = CTStyle.Factory.newInstance();
        ctStyle.setStyleId(strStyleId);

        CTString styleName = CTString.Factory.newInstance();
        styleName.setVal(strStyleId);
        ctStyle.setName(styleName);

        CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
        indentNumber.setVal(BigInteger.valueOf(headingLevel));

        // lower number > style is more prominent in the formats bar
        ctStyle.setUiPriority(indentNumber);

        CTOnOff onoffnull = CTOnOff.Factory.newInstance();
        ctStyle.setUnhideWhenUsed(onoffnull);

        // style shows up in the formats bar
        ctStyle.setQFormat(onoffnull);

        // style defines a heading of the given level
        CTPPr ppr = CTPPr.Factory.newInstance();
        ppr.setOutlineLvl(indentNumber);
        ctStyle.setPPr(ppr);

        XWPFStyle style = new XWPFStyle(ctStyle);

        // is a null op if already defined
        XWPFStyles styles = docxDocument.createStyles();

        style.setType(STStyleType.PARAGRAPH);
        styles.addStyle(style);

    }
    /**
     * 获取标题编号
     * @param titleLvl
     * @return
     */
    private String getOrderCode(String titleLvl) {
        String order = "";

        if("0".equals(titleLvl)||Integer.parseInt(titleLvl)==8){//文档标题||正文
            return "";
        }else if(Integer.parseInt(titleLvl)>0&&Integer.parseInt(titleLvl)<8){//段落标题

            //设置最高级别标题
            Map<String,Object> maxTitleMap = orderMap.get("maxTitleLvlMap");
            if(null==maxTitleMap){//没有,表示第一次进来
                //最高级别标题赋值
                maxTitleMap = new HashMap<String, Object>();
                maxTitleMap.put("lvl", titleLvl);
                orderMap.put("maxTitleLvlMap", maxTitleMap);
            }else{
                String maxTitleLvl = maxTitleMap.get("lvl")+"";//最上层标题级别(0,1,2,3)
                if(Integer.parseInt(titleLvl)<Integer.parseInt(maxTitleLvl)){//当前标题级别更高
                    maxTitleMap.put("lvl", titleLvl);//设置最高级别标题
                    orderMap.put("maxTitleLvlMap", maxTitleMap);
                }
            }

            //查父节点标题
            int parentTitleLvl = Integer.parseInt(titleLvl)-1;//父节点标题级别
            Map<String,Object> cMap = orderMap.get(titleLvl);//当前节点信息
            Map<String,Object> pMap = orderMap.get(parentTitleLvl+"");//父节点信息

            if(0==parentTitleLvl){//父节点为文档标题,表明当前节点为1级标题
                int count= 0;
                //最上层标题,没有父节点信息
                if(null==cMap){//没有当前节点信息
                    cMap = new HashMap<String, Object>();
                }else{
                    count = Integer.parseInt(String.valueOf(cMap.get("cCount")));//当前序个数
                }
                count++;
                order = count+"";
                cMap.put("cOrder", order);//当前序
                cMap.put("cCount", count);//当前序个数
                orderMap.put(titleLvl, cMap);

            }else{//父节点为非文档标题
                int count= 0;
                //如果没有相邻的父节点信息,当前标题级别自动升级
                if(null==pMap){
                    return getOrderCode(String.valueOf(parentTitleLvl));
                }else{
                    String pOrder = String.valueOf(pMap.get("cOrder"));//父节点序
                    if(null==cMap){//没有当前节点信息
                        cMap = new HashMap<String, Object>();
                    }else{
                        count = Integer.parseInt(String.valueOf(cMap.get("cCount")));//当前序个数
                    }
                    count++;
                    order = pOrder+"."+count;//当前序编号
                    cMap.put("cOrder", order);//当前序
                    cMap.put("cCount", count);//当前序个数
                    orderMap.put(titleLvl, cMap);
                }
            }

            //字节点标题计数清零
            int childTitleLvl = Integer.parseInt(titleLvl)+1;//子节点标题级别
            Map<String,Object> cdMap = orderMap.get(childTitleLvl+"");//
            if(null!=cdMap){
                cdMap.put("cCount", 0);//子节点序个数
                orderMap.get(childTitleLvl+"").put("cCount", 0);
            }
        }
        return order;
    }

    public static void main(String[] args) {
        DealWordByPoi cwd = new DealWordByPoi();
        cwd.init("E:/doc/d.docx", "E:/doc/a.docx");
    }
}

效果图:
源文件
源文件
处理后的文件
这里写图片描述

  • 15
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
如果你要在POI实现Word自动编号,可以按照以下步骤进行: 1. 创建一个新的Word文档并打开。 2. 添加一个段落样式,设置为“标题1”,包括要自动编号的文本。 3. 将光标移到要添加自动编号的段落中。 4. 通过调用XWPFParagraph对象的getCTP()方法获取CTP对象。 5. 调用addNewNumPr()方法添加一个新的编号属性。 6. 设置编号格式,可以是阿拉伯数字、罗马数字等。 7. 设置起始编号。 8. 保存并关闭文档。 下面是一个示例代码,可以帮助你实现Word自动编号: ```java // 创建一个新的Word文档 XWPFDocument document = new XWPFDocument(); // 添加一个段落样式,设置为“标题1”,包括要自动编号的文本 XWPFParagraph para = document.createParagraph(); para.setStyle("Heading1"); para.createRun().setText("自动编号标题"); // 将光标移到要添加自动编号的段落中 CTP ctp = para.getCTP(); // 添加一个新的编号属性 CTPPr ppr = ctp.getPPr(); if (ppr == null) { ppr = ctp.addNewPPr(); } CTNumPr numPr = ppr.addNewNumPr(); // 设置编号格式为阿拉伯数字 CTDecimalNumber numId = numPr.addNewNumId(); numId.setVal(BigInteger.valueOf(1)); CTNumFmt fmt = numPr.addNewFmt(); fmt.setVal(STNumberFormat.DECIMAL); // 设置起始编号 CTDecimalNumber lvlText = numPr.addNewLvl().addNewLvlText(); lvlText.setVal("1."); lvlText.setLvl(BigInteger.valueOf(0)); // 保存并关闭文档 FileOutputStream out = new FileOutputStream("自动编号的文档.docx"); document.write(out); out.close(); document.close(); ``` 请注意,上述代码中的“自动编号标题”将自动获得编号“1.”,下一个“自动编号标题”将获得编号“2.”,以此类推。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值