Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

Анализ PDF отчетов


(Sergey Korol) #1

Доброго времени суток!

Стоит задача верификации PDF-отчетов. Данную тему читал, предложенное решение не совсем подходит. Собственно, на текущий момент подготовлен модуль, работающий с itextpdf либой. Для анализа элементов формируется xml модель на основе pdf исходника. Проблема в том, что сама либа дробит текст на множество блоков, которые порой очень непросто найти. К примеру, строка "Какой чудесный день" может в xml модели быть представлена в виде:

        <text id="3" pageNumber="1">
            <ascentLineRectangle>
                <height>1.0</height>
                <width>25.0</width>
                <x>164.0</x>
                <y>754.0</y>
            </ascentLineRectangle>
            <baseLineRectangle>
                <height>1.0</height>
                <width>25.0</width>
                <x>164.0</x>
                <y>744.0</y>
            </baseLineRectangle>
            <descentLineRectangle>
                <height>1.0</height>
                <width>25.0</width>
                <x>164.0</x>
                <y>741.0</y>
            </descentLineRectangle>
            <fontName>Helvetica-Bold</fontName>
            <renderMode>0</renderMode>
            <singleSpaceWidth>3.6139984</singleSpaceWidth>
            <text>Како</text>
        </text>
 
        <text id="5" pageNumber="1">
            ...
            <text>й</text>
        </text>
 
        <text id="7" pageNumber="1">
            ...
            <text>чудесны</text>
        </text>
 
        <text id="11" pageNumber="1">
            ...
            <text>й</text>
        </text>
 
        <text id="17" pageNumber="1">
            ...
            <text>день</text>
        </text>
Причем, помимо разбиения самого слова, эти блоки могут идти еще и не подряд, более того - с отклонением в координатах на пару пикселей. Это все недостатки самой либы, но суть сейчас не в ней. Пользователь хочет верифицировать конкретные участки PDF репорта конкретными expected значениями. Т.е. вызовом метода
verifyText(PDFElement.Title, "Какой чудесный день");
я смог бы проверить тайтл документа на соответствие заданной строке.
Пока есть очень сложное и трудоемкое решение с привязкой к модели данных: создание в коде темплейта PDF документа, описание всех логических блоков и элементов с привязкой к набору ID, парсинг xml модели по заданным ID и сравнение с эталоном. Такой подход требует значительной подготовки и очень сильно завязан на модели данных. Т.е. если завтра формат репорта поменяется, придется писать новый темплейт.
Собственно, вопрос в том, сталкивался ли кто-либо с анализом PDF документов? Может существует более красивое решение? Или возможно у кого-то есть идеи, как можно распарсить xml таким образом, чтобы найти текст, разбитый на блоки в хаотичном порядке?
 
П.С. С картинами приблизительно то же самое, но value в xml представлено в виде encoded base64 string. Ну а вариант, предложенный в другом топике, не подходит, ибо там нет логической составляющей, а лишь банальная проверка мэпа на contains.

(Mykhailo Poliarush) #2

сразу скажу, что с этой библиотекой не работал 

пару соображений

1. погуглить и посмотреть, как разработчики работают с данной библиотекой

например, http://www.vogella.com/articles/JavaPDF/article.html

2. для того, чтобы сделать более красивое решение, надо полностью понимать, как строится модель

если элементы идут хаотично, значит есть какой-то критерий, который определяется как потом символы или строки будут выводиться

значит, так потом и нужно будет считывать строки и символы, например по id, хотя не уверен

3. на счет других библиотек, могу еще назвать http://pdfbox.apache.org/

но ее надо поробовать, я давно с ней работал и то поверхностно

тут скорее всего надо делать небольшое исследование, какие библиотеки есть и что они фактически могут (потому что не факт, что подойдут)

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

если вот такая модуль представления данных останется, то сильно красивого универсального решения, увы, мне кажется не построишь


(Sergey Korol) #3

В том то и прикол, что ребята уже проводили ресерч, и из фришных либ самый удачный вариант - itextpdf. Со всеми остальными получаются еще более ужасные результаты.

Дело не в критерии. Преставим ситуацию, когда в PDF последняя буква строки текста смещена на 1 пиксель вниз. С виду вообще не заметишь. Но вот xml модель построится, как было описано выше, только блок текста с последней буквой слова уедет вообще куда-то вниз, ибо все упирается в координаты, которые представляются вот в таком виде:

           <ascentLineRectangle>
                <height>1.0</height>
                <width>25.0</width>
                <x>164.0</x>
                <y>754.0</y>
            </ascentLineRectangle>
            <baseLineRectangle>
                <height>1.0</height>
                <width>25.0</width>
                <x>164.0</x>
                <y>744.0</y>
            </baseLineRectangle>
            <descentLineRectangle>
                <height>1.0</height>
                <width>25.0</width>
                <x>164.0</x>
                <y>741.0</y>
            </descentLineRectangle>
И как потом найти эту букву? Тут даже вопрос не в том, как найти, а в том, что таких букв может быть множество, с разными координатами.

(Mykhailo Poliarush) #4

ну я думаю точно также как и вы в этом случае

я вижу, только вариант, прохождения по ID в направлении возростания и считывание тега text, а все остальные теги можно игнорировать

ведь тебе нужен текст? или тебе нужно еще проверять привязку к координатам?

других вариантов, пока что не вижу 

а можешь показать кода, для считывания текста, если конечно можешь поделиться


(Sergey Korol) #5

Дело в том, что тут писалась обертка для этого всего дела. Вряд ли участок кода сильно поможет. Но приблизительно это выглядит так:

            PdfTester tester = new PdfTester();
            PdfDocumentModel model = tester.readModelFromPdf("test.pdf"); // or Xml
            List<PdfPageModel> pagesList = model.getPagesList();
 
            for (PdfPageModel page : pagesList) {
                List<PdfText> textList = page.getTextList();
                for (PdfText text : textList) {
                    String value = text.getText();       
                }
            }
Дело не только в привязке к координатам, а в формате верификации. Если я хочу проверить, имеется ли в конкретной секции документа конкретная строка, мне нужно не просто пройтись по блокам текста. А еще и склеить все возможные комбинации предложений из найденных по ходу дела кусков.
 
Пример. В документе есть 2 строки:
"life is good or life is amazing"
"it's a good day to start working"
Представим, что некоторые буквы некоторых слов, или части слов сместились на несколько пикселей в любом направлении, а следовательно, отделились друг от друга.
"lif e is goo d or lif e is amaz ing"
"it's a goo d day to start work ing"

Что мы в итоге увидим в xml модели? Нечто вроде такого:

<text>lif</text>

<text>is goo</text>

<text>or lif</text>

<text>is</text>

<text>amaz</text>

<text>e</text>

<text>d</text>

<text>e</text>

<text>ing</text>

<text>it's goo</text>

<text>day to start work</text>

<text>d</text>

<text>ing</text>

Предположим теперь, что первая строка - это секция 1, вторая строка - секция 2. Каждый разбитый text блок имеет свой id. Как теперь из этого хаоса склеить предложения и сравнить с эталоном? Т.е. я хочу узнать, что текст секции 1 equals "life is good or life is amazing". Вот тут и начинаются танцы с бубном. Конечно на практике разбиения не столь ужасные, как в примере. Но задача ведь стоит не просто найти текст в документе, а провести верификацию конкретной секции. Посему, проводится анализ модели данных. Разбиения приблизительно одинаковые для конкретного типа отчета. Далее конкретные секции мапятся с конкретными ID и т.п.


(Mykhailo Poliarush) #6

да, согласен в таком случае получается не очень хорошо и более того, трудоемкий обход

но альтернативы, к сожелению, предложить не могу

могу только пожелать удачи :)

 

и если вдруг найдется какое-то решение, пиши сюда

интересно будет посмотреть