Как извлечь фактический пронумерованный многоуровневый список из docx?

У меня задача распарсить WORD файл *.docx. Это протокол совещания. Он собирается из нескольких файлов путем копипаста в результате чего где список пунктов это просто текст в виде 1.1.1. и его можно разложить с помощью регуляторного выражения, а где-то это пронумерованный многоуровневый список. Библиотека apache.poi.xwpf некорректно извлекает фактическую нумерацию из такого списка, обращаясь к xml файла docx, где эта нумерация кривая. Как можно получить фактическую нумерацию которую видно в документе? Может есть другие варианты?

package org.example;

import org.apache.poi.xwpf.usermodel.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WordParser {
    private final static String 
NUMBER_POINT_PATTERN = "\\s*(\\d+(?:\\.\\d+){2,3})\\.\\s*(.*)";

    // Метод для обработки Word-файла
    public List<String[]> 
processWordFile(Path wordFilePath, int 
startingSerialNumber) throws IOException {
    List<String[]> data = new ArrayList<>();
    boolean shouldParseNextParagraph = false;

    Pattern pattern = Pattern.compile(NUMBER_POINT_PATTERN);
    int serialNumber = startingSerialNumber;

    // Открываем документ Word
    try (FileInputStream fis = new FileInputStream(wordFilePath.toFile());
         XWPFDocument document = new XWPFDocument(fis)) {

        List<XWPFParagraph> paragraphs = document.getParagraphs();

        for (XWPFParagraph para : paragraphs) {
            String paraNumber = getParagraphNumbering(para); // Нумерация через форматирование
            String paraText = para.getText().trim(); // Текст параграфа

            // Если форматированная нумерация отсутствует, проверяем текст с помощью регулярного выражения
            if (paraNumber.isEmpty()) {
                Matcher matcher = pattern.matcher(paraText);
                if (matcher.matches()) {
                    paraNumber = matcher.group(1); // Присваиваем номер из текста, если совпадает
                    paraText = matcher.group(2); // Остальная часть текста
                }
            }

            String fullText = (paraNumber.isEmpty() ? "" : paraNumber + ". ") + paraText;

            if (fullText.isEmpty()) {
                continue;  // Пропускаем пустые параграфы
            }

            // Если в предыдущем параграфе найдена ключевая фраза, ищем пункты с нумерацией
            if (shouldParseNextParagraph) {
                Matcher matcher = pattern.matcher(fullText);
                if (matcher.matches()) {
                    String taskNumber = matcher.group(1);
                    String activity = matcher.group(2);
                    data.add(new String[]{String.valueOf(serialNumber++), taskNumber, activity});
                } else {
                    shouldParseNextParagraph = false;
                }
            }

            // Ищем ключевые фразы, указывающие на начало следующего пункта
            if (fullText.contains("Поручить:") || fullText.contains("Для выполнения:")) {
                shouldParseNextParagraph = true;
            }
        }
    } catch (IOException e) {
        System.err.println("Ошибка при обработке файла: " + wordFilePath);
        throw e;
    }
    return data;
}

// Метод для получения нумерации параграфа
public String getParagraphNumbering(XWPFParagraph para) {
    if (para.getNumID() != null) {
        XWPFNumbering numbering = para.getDocument().getNumbering();
        if (numbering != null) {
            XWPFNum num = numbering.getNum(para.getNumID());
            if (num != null) {
                XWPFAbstractNum absNum = numbering.getAbstractNum(num.getCTNum().getAbstractNumId().getVal());
                if (absNum != null) {
                    int ilvl = para.getNumIlvl().intValue();
                    String lvlText = absNum.getCTAbstractNum().getLvlArray(ilvl).getLvlText().getVal();

                    String[] currentNumbers = new String[ilvl + 1];
                    for (int i = 0; i <= ilvl; i++) {
                        currentNumbers[i] = getCurrentLevelNumber(para, numbering, i);
                    }

                    for (int i = 0; i <= ilvl; i++) {

Ответы (0 шт):