Как извлечь фактический пронумерованный многоуровневый список из 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++) {