PDFDomTree не обнаруживает пробелы при преобразовании файла PDF в HTML

Я использую PDFDomTree с pdfbox-2.0.9 в своем приложении Java для преобразования файла PDF в файл HTML. Следующий код, который я использовал для преобразования pdf.

try {   
    PDDocument document = PDDocument.load(new File("some path"));
    PDFDomTree parser = new PDFDomTree(PDFDomTreeConfig.createDefaultConfig());
    Writer output = new PrintWriter(new File("some output path"), "utf-8");

    parser.writeText(document, output);
    output.close();
    document.close();
} catch (IOException | ParserConfigurationException e) {
    throw e;
}

Теперь моя проблема заключается в том, что когда я пытался проанализировать выходной html, я понял, что преобразователь не смог обнаружить пробел между двумя словами, из-за чего я получил конкатенацию некоторых слов.

Посмотрите сравнение ниже: введите здесь описание изображения

Соответствующий PDF-файл можно найти здесь, если это необходимо.

Кто-нибудь может помочь мне с этим?


person vsbehere    schedule 03.08.2018    source источник
comment
Я не очень хорошо знаком с этой библиотекой, но мне нужно отследить ошибку пробела до точки происхождения? Например, если проблема возникает, когда вы создаете переменную document, будут другие, у которых есть ваша проблема, и вы можете найти ее. Если проблема с пробелами отсутствует в переменной PDDocument document, просто замените белый текст случайным набором символов и явно установите обратно пробел при вызове parser.writeText. (документ, вывод);   -  person ViaTech    schedule 03.08.2018
comment
пожалуйста, поделитесь документом в формате PDF. И обновитесь до последней версии (не решит эту проблему, но всегда полезно использовать последнюю версию).   -  person Tilman Hausherr    schedule 03.08.2018
comment
Я бы предположил, что в исходном файле нет пробелов, только пробелы, но эти пробелы слишком малы, чтобы их можно было распознать как межсловное пространство. (Если вы посмотрите на свои скриншоты, то увидите, что отсутствующие места — это очень маленькие промежутки.) Если вы поделитесь PDF-файлом, мы сможем проверить, верно ли это предположение. В этом случае можно настроить извлечение текста для создания пробелов.   -  person mkl    schedule 03.08.2018
comment
@TilmanHausherr Я добавил ссылку на рассматриваемый PDF-файл. Пожалуйста, проверьте.   -  person vsbehere    schedule 03.08.2018
comment
@mkl Я также проверил этот случай. На самом деле это пробелы, а не пробелы. Когда я скопировал текст из pdf и вставил его в простой текстовый редактор, текст появился с соответствующими пробелами.   -  person vsbehere    schedule 03.08.2018
comment
PDFDomTree не из PDFBox, это из стороннего проекта. Утилита PDFBox ExtractText работает нормально. (И он также может конвертировать в HTML)   -  person Tilman Hausherr    schedule 03.08.2018
comment
vsbehere - мое предположение было основано на другом предположении: вы использовали классы извлечения текста pdfbox. Как отметил @Tilman, это не так. Я разъяснил этот факт как в заголовке вашего вопроса, так и в тексте вашего вопроса.   -  person mkl    schedule 04.08.2018
comment
vsbehere - На самом деле это пробелы, а не пробелы. - вы правы, кроме того, spetto al mese precente, per effetto (включая пробелы) на самом деле рисуется одной инструкцией в PDF. Удаление пробелов в таком случае показывает неожиданный недостаток в экстракторе текста. Как уже указывал @TilmanHausherr, извлечение текста PDFBox здесь отлично работает.   -  person mkl    schedule 06.08.2018
comment
Я только что посмотрел на фактически извлеченные данные. Похоже, что pdf2dom на самом деле считает всю строку одним словом, потому что пробелы слишком малы. А, я только что нашел if (!text.getUnicode().trim().isEmpty()) в переопределении processTextPosition. Это удалит любой символ пробела, потому что символы идут один за другим. Таким образом, эти пробелы в дальнейшем больше не учитываются.   -  person mkl    schedule 06.08.2018
comment
Да, я понял это. Я работаю, чтобы найти способ или решение для этого. Между тем, если вы найдете или предложите что-нибудь, это будет полезно. Спасибо.   -  person vsbehere    schedule 06.08.2018
comment
По крайней мере, вы должны сообщить о проблеме github.com/radkovo/Pdf2Dom/issues. .   -  person Tilman Hausherr    schedule 06.08.2018
comment
@TilmanHausherr, не могли бы вы рассказать мне, как использовать утилиту PDFBox ExtractText для преобразования pdf в html, и сохранит ли она также всю информацию о стилях?   -  person vsbehere    schedule 07.08.2018
comment
См. pdfbox.apache.org/2.0/commandline.html , команда java - jar pdfbox-app-2.0.11.jar ExtractText -html файл.pdf . Он сохранит некоторую информацию о стилях, но не всю.   -  person Tilman Hausherr    schedule 07.08.2018
comment
@TilmanHausherr Недавно я наткнулся на другой pdf-файл, в котором утилита extractText дает сбой и слова сливаются. Слово «че нель» в 3-м ряду складывается. Вы можете получить доступ к PDF-файлу по адресу drive.google.com/open?id=1Mokm8HbiS22YMhVOXTae9IjNfBvCJqdI.   -  person vsbehere    schedule 17.05.2019
comment
Вероятно, это трудное решение… пробелы настолько малы, что PDFBox принимает неправильное решение, является ли пробел пробелом или нет. Вы можете попробовать поиграть с spacingTolerance и averageCharTolerance стриптизерши. Он недоступен в утилите командной строки, но если вы хотите, я добавлю его, если вы создадите задачу в PDFBox JIRA.   -  person Tilman Hausherr    schedule 18.05.2019


Ответы (1)


Подручный инструмент для извлечения текста, 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