Apache POI:如何在word文档中的编号列表上重新开始编号?

时间:2016-10-24 06:38:10

标签: java apache-poi xwpf numbered-list

我尝试使用Apache POI XWPF库在Word docx文件中生成报告。

我的方法是使用现有的Word文档作为样式模板。在模板中,我定义了一个名为" SRINumberList"。

的样式

所以要加载模板并删除页眉或页脚中没有的所有内容:

protected void createDocFromTemplate() {
    try {
        document = new XWPFDocument(this.getClass().getResourceAsStream(styleTemplate));


        int pos = document.getBodyElements().size()-1;

        while (pos >= 0) {
            IBodyElement element = document.getBodyElements().get(pos);
            if (!EnumSet.of(BodyType.HEADER, BodyType.FOOTER).contains(element.getPartType())) {
                boolean success = document.removeBodyElement(pos);
                logger.log(Level.INFO, "Removed body element "+pos+": "+success);
            }
            pos--;
        }

    } catch (IOException e) {
        logger.log(Level.WARNING, "Not able to load style template", e);
        document = new XWPFDocument();
    }

}

现在,在我的文档中,有几个不同的部分包含编号列表。每个应该从1重新开始编号。这是我这样做的典型方式:

if (itemStem.getItems().size() > 0) {
        p = document.createParagraph();
        p.setStyle(ParaStyle.StemAndItemTitle.styleId);
        final BigInteger bulletNum = newBulletNumber();

        run = p.createRun();
        run.setText("Sub Items");

        itemStem.getItems().stream().forEach(item -> {
            XWPFParagraph p2 = document.createParagraph();
            p2.setStyle(ParaStyle.NumberList.styleId);

            XWPFRun run2 = p2.createRun();
            run2.setText(item.getSubItemText());
        });
        p = document.createParagraph();
        p.createRun();
}

所以这正确地应用了包含数字格式的Style,但是只有一个序列(1 ...在doc中退出了很多列表项)。例如:

Heading 1
1. item a
2. item b
3. item c

Heading 2
4. item a
5. item d
6. item g

但我想要的是:

Heading 1
1. item a
2. item b
3. item c

Heading 2
1. item a
2. item d
3. item g

所以基本上我试图弄清楚如何使用我所拥有的风格,但重新启动页面编号文档中的各个位置。有人可以提供一个如何工作的样本吗?

2 个答案:

答案 0 :(得分:0)

我找到的唯一方法是覆盖CTNum中的级别。另一种方法可能是创建大量新的抽象编号/样式,但是当您打开文档时,这将花费大量样式条目。

ArrayList<String> list = new ArrayList<String>();
list.add("SubItem 1");
list.add("SubItem 2");
list.add("SubItem 3");

XWPFNumbering numbering = document.getNumbering();
XWPFAbstractNum numAbstract =  numbering.getAbstractNum(BigInteger.ONE);

for (Integer nx = 1; nx < 3; nx++) {
    XWPFParagraph p = document.createParagraph();
    XWPFRun run = p.createRun();
    run.setText("Items " + nx.toString());
    //leveloverride (start the new numbering)
    BigInteger numId = numbering.addNum(numAbstract.getAbstractNum().getAbstractNumId());
    XWPFNum num = numbering.getNum(numId);
    CTNumLvl lvloverride = num.getCTNum().addNewLvlOverride();
    lvloverride.setIlvl(BigInteger.ZERO);
    CTDecimalNumber number = lvloverride.addNewStartOverride();
    number.setVal(BigInteger.ONE);

    for (String item : list) {
        XWPFParagraph p2 = document.createParagraph();
        p2.setNumID(num.getCTNum().getNumId());
        CTNumPr numProp = p2.getCTP().getPPr().getNumPr();
        numProp.addNewIlvl().setVal(BigInteger.ZERO);

        XWPFRun run2 = p2.createRun();
        run2.setText(item);
    }
}

答案 1 :(得分:0)

keil的帮助下。我找到了解决方案。我在这里发布了一个完整的工作示例:https://github.com/jimklo/apache-poi-sample

诀窍是,在创建重新开始编号的新Num时,需要引用文档中定义的Numbering样式的AbstractNum。

以下是重点,但关键是必须确定文档中Style的AbstractNum ID。这似乎是不幸的,因为这只是一个XML文档,没有一些方法可以枚举现有的Num和AbstractNum。如果有,我很想知道如何做到这一点。

/**
 * first discover all the numbering styles defined in the template.
 * a bit brute force since I can't find a way to just enumerate all the
 * abstractNum's inside the numbering.xml
 */
protected void initNumberingStyles() {
    numbering = document.getNumbering();

    BigInteger curIdx = BigInteger.ONE;
    XWPFAbstractNum abstractNum;

    while ((abstractNum = numbering.getAbstractNum(curIdx)) != null) {
        if (abstractNum != null) {
            CTString pStyle = abstractNum.getCTAbstractNum().getLvlArray(0).getPStyle();
            if (pStyle != null) {
                numberStyles.put(pStyle.getVal(), abstractNum);
            }
        }
        curIdx = curIdx.add(BigInteger.ONE);
    }

}

现在我们有了从Style到AbstractNum的映射,我们可以创建一个通过LvlOverride和StartOverride重新启动的新Num。

/**
 * This creates a new num based upon the specified numberStyle
 * @param numberStyle
 * @return
 */
private XWPFNum restartNumbering(String numberStyle) {
    XWPFAbstractNum abstractNum = numberStyles.get(numberStyle);
    BigInteger numId = numbering.addNum(abstractNum.getAbstractNum().getAbstractNumId());
    XWPFNum num = numbering.getNum(numId);
    CTNumLvl lvlOverride = num.getCTNum().addNewLvlOverride();
    lvlOverride.setIlvl(BigInteger.ZERO);
    CTDecimalNumber number = lvlOverride.addNewStartOverride();
    number.setVal(BigInteger.ONE);
    return num;
}

现在你可以将NumID应用到你正在创建的列表中。

/**
 * This creates a five item list with a simple heading, using the specified style..
 * @param index
 * @param styleName
 */
protected void createStyledNumberList(int index, String styleName) {
    XWPFParagraph p = document.createParagraph();
    XWPFRun run = p.createRun();
    run.setText(String.format("List %d: - %s", index, styleName));

    // restart numbering
    XWPFNum num = restartNumbering(styleName);

    for (int i=1; i<=5; i++) {
        XWPFParagraph p2 = document.createParagraph();

        // set the style for this paragraph
        p2.setStyle(styleName);

        // set numbering for paragraph
        p2.setNumID(num.getCTNum().getNumId());
        CTNumPr numProp = p2.getCTP().getPPr().getNumPr();
        numProp.addNewIlvl().setVal(BigInteger.ZERO);

        // set the text
        XWPFRun run2 = p2.createRun();
        run2.setText(String.format("Item #%d using '%s' style.", i, styleName));
    }

    // some whitespace
    p = document.createParagraph();
    p.createRun();

}

同样,总的来说,如果没有keil提供的指针,我就不会想到这一点。