Подручный инструмент для извлечения текста, PDFDomTree
от Pdf2Dom, основан на PDFTextStripper
от PDFBox, но использует его только для разбора инструкций по рисованию PDF в символы со стилем и положением, в то время как он сам выполняет весь анализ этих богатых символов.
В частности, он игнорирует все входящие пробельные символы в родительском классе PDFBoxTree
:
protected void processTextPosition(TextPosition text)
{
if (text.isDiacritic())
{
lastDia = text;
}
else if (!text.getUnicode().trim().isEmpty())
{
[...process character...]
}
}
(org.fit.pdfdom.PDFBoxTree
переопределить processTextPosition
)
В этом блоке [...process character...]
он пытается распознать пробелы в словах по жестко запрограммированным расстояниям:
//should we split the boxes?
boolean split = lastText == null || distx > 1.0f || distx < -6.0f || Math.abs(disty) > 1.0f
|| isReversed(getTextDirectionality(text)) != isReversed(getTextDirectionality(lastText));
(внутри блока [...process character...]
выше)
Поскольку текст в вашем PDF изначально небольшой (9pt определяется Pdf2Dom) и во многих строках очень плотно расположен, промежутки между словами обычно меньше, чем 1.0
, как предполагалось выше (distx > 1.0f
).
На мой взгляд, здесь есть 2 проблемы:
удаление пробелов означает удаление информации; (В некоторых ситуациях это может быть выгодно, я видел PDF-файлы с одной и той же линией, нарисованной дважды, с любым строковым аргументом рисования, содержащим пробелы, где другой содержит видимые символы, но это исключения.)
с жестко закодированными ограничениями расстояния distx > 1.0f
, distx < -6.0f
и т. д., даже несмотря на то, что размеры шрифта (а вместе с ними и размеры промежутков) могут сильно различаться.
Эти проблемы должны быть исправлены в коде. Два возможных обходных пути для PDF-файлов, таких как ваш demo.pdf:
Выбор различных ограничений расстояния
Настоящее исправление должно попытаться сделать ограничения расстояния динамическими, в зависимости от размера шрифта и, возможно, даже среднего расстояния между символами в текущей строке до текущей позиции. Обходным решением для вашего PDF может быть замена жестко закодированного расстояния на меньшее жестко закодированное расстояние.
Например. используя .5f
вместо 1.0f
в качестве расстояния до слова, т.е. заменив приведенный выше тест на
//should we split the boxes?
boolean split = lastText == null || distx > .5f || distx < -6.0f || Math.abs(disty) > 1.0f
Это приводит к тому, что Pdf2Dom распознает пропуски слов в вашем документе (или, по крайней мере, многие другие, я не проверял их все).
Интерпретация пробелов как разбиений
Вместо того, чтобы игнорировать пробелы, вы можете явно интерпретировать их как пробелы в словах, например. путем улучшения переопределения processTextPosition
, как это
protected void processTextPosition(TextPosition text)
{
if (text.isDiacritic())
{
lastDia = text;
}
else if (!text.getUnicode().trim().isEmpty())
{
[...process character...]
} else {
//!! process white spaces here
//finish current box (if any)
if (lastText != null)
{
finishBox();
}
//start a new box
curstyle = new BoxStyle(style);
lastText = null;
}
}
Я не анализировал код глубоко, поэтому я могу назвать это только обходным путем. Чтобы сделать это реальным исправлением, вы должны протестировать его на наличие побочных эффектов, а также расширить его, чтобы изучить точную природу пробела: есть другие символы пробела, чем обычный пробел, некоторые из них имеют нулевую ширину, некоторые не -разбивка и т. д. Все эти различные типы пустого пространства заслуживают особого внимания.
PS: Поскольку многие члены PDFBoxTree
защищены (а не закрыты), легко можно применить второй обходной путь без необходимости исправления Pdf2Dom:
PDDocument document = PDDocument.load(SOURCE);
PDFDomTree parser = new PDFDomTree(PDFDomTreeConfig.createDefaultConfig()) {
@Override
protected void processTextPosition(TextPosition text) {
if (text.getUnicode().trim().isEmpty()) {
//finish current box (if any)
if (lastText != null)
{
finishBox();
}
//start a new box
curstyle = new BoxStyle(style);
lastText = null;
} else {
super.processTextPosition(text);
}
}
};
Writer output = new PrintWriter(TARGET, "utf-8");
parser.writeText(document, output);
output.close();
(ExtractText тест testDemoImproved
)
person
mkl
schedule
06.08.2018
if (!text.getUnicode().trim().isEmpty())
в переопределенииprocessTextPosition
. Это удалит любой символ пробела, потому что символы идут один за другим. Таким образом, эти пробелы в дальнейшем больше не учитываются. - person mkl   schedule 06.08.2018spacingTolerance
иaverageCharTolerance
стриптизерши. Он недоступен в утилите командной строки, но если вы хотите, я добавлю его, если вы создадите задачу в PDFBox JIRA. - person Tilman Hausherr   schedule 18.05.2019