Apache POI: Как перезапустить нумерацию в нумерованном списке в текстовом документе?

Я пытаюсь использовать библиотеку 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();
}

Таким образом, это правильно применяет стиль, который содержит числовой формат, но есть только одна последовательность (1 ... для любого количества элементов списка, выходящих в документе). Например:

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 ответа

Решение

С некоторой помощью keil. Я разобрался с решением. Я разместил полный рабочий образец здесь: https://github.com/jimklo/apache-poi-sample

Хитрость заключается в том, что вам нужно ссылаться на AbstractNum стиля нумерации, определенного в документе, при создании нового Num, который перезапускает нумерацию.

Вот основные моменты, однако ключ должен был определить, что представляет собой идентификатор AbstractNum для стиля внутри документа. Кажется прискорбным, что, учитывая, что это просто документ 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, мы можем создать новый Num, который перезапускается через LvlOverride и StartOverride.

/**
 * 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;
}

И теперь вы можете просто применить этот Num ID к списку, который вы создаете.

/**
 * 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.

Единственный способ, который я нашел, это переопределить уровень в 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);
    }
}
Другие вопросы по тегам