Какое-то время назад мне по работе пришлось провести небольшое исследование. Суть его состояла в поиске наилучшего pdf-парсера реализованного на java.
Немного о проекте. В нем реализована система пересылки внутренних сообщений, к которым могут быть прикреплены файлы. Также есть поиск, который должен осуществляться по содержимому аттачментов. Большую часть подобных аттачментов составляют pdf-ки.
Собственно работа механизма довольно проста: при отсылке сообщения данные аттачмента парсятся и по ним стороится индекс.
Долгое время документы парсились при помощи библиотеки PDFBOX, работа которой не вызвала ни у кого радости: долго и со сбоями.
В итоге были выбраны 4 библиотеки, сравнением которых я занялся: PDFBOX, JPod, iText и Acrobat.
Акробат был исключен почти сразу, т.к. оказалось, что эта библиотека не поддерживается уже в течение нескольких лет, но статистика по ней осталась, поэтому я ее тоже опубликую.
Сравнивать библиотеки пришлось по двум критериям – скорости работы и качеству полученных результатов.
Предупрежу сразу: библиотеки тестировались на внутренних документах, и они подпадают под определенную секьюрность. Так что я даже названий указать не могу. Скажу лишь одно – контент файлов был самым разнообразным: текст, таблицы картинки, сканы и тп. Размеры файлов тоже довольно сильно различаются, так что можно ожидать объективных оценок.
Красным и зеленым цветами выделены худшее и лучшее время соответственно. Буква «Е» обозначает состояние перманентного коллапса, настигшее процесс вследствие переполнения буфера либо каких-либо иных ошибок.
В сравнении времени объективным победителем стал JPod. Порадовало отсутствие ошибок при парсинге.
Оценка качества была довольно субъективна и делилась всего на 3 категории: Best, Middle и Worst. Также еще есть оценка Empty, которая выставлялась, если в процессе парсинга наступал коллапс или просто парсер не находил текста внутри документа.
Оценивалось сходство полученного текста с оригиналом, но не очень критично, т.к. текст нужен был для индекса, а не для вывода.
Характерные особенности парсинга:
Acrobat очень часто парсит текст в одну строку. Пробелы между словами и предложениями сохраняются, поэтому в принципе это не критично для индексации.
iText не понимает «не-английские» символы. В тестировании применялись документы на английском, немецком и французском языках. Поэтому все их умляуты пошли лесом. Даже не просто пошли лесом – вместо подобных символов я получил вопросики. Возможно, это где-то и настраивается, но остальные понимали подобные символы без плясок с бубном.
PDFBOX по качеству нареканий не вызвал.
JPod – тоже все ок. За исключением одной особенности, которая заставила повозиться с ним довольно долго. В некоторых случаях документ полностью или частями парсился по одной букве в строку – для индекса такой парсинг бесполезен.
В итоге, победителем был объявлен JPod, несмотря на его особенность парсить по букве в строку.
Предстояло разобраться с этим.
На ковыряние исходников JPod ушло немало времени. Письма, форум проекта. В итоге было установлено, что подобное поведение парсера вызвано тем, какую ориентацию имеют страницы документа. Книжная ориентация проходит нормально, а альбомная – нет. Попытки поковырять параметры ничего не дали. Свойства класса, ответственные за ориентацию страницы были бесполезны.
В общем, в один из моментов я решил просто удалить из классов всяческую работу со шрифтами. Все равно для индексации текста они не нужны. Это помогло, т.к. блоки текста рассчитывались неверно, и это было вызвано именно шрифтами.
Тут бы я и остановился, но Egiptyanin настоял на том, что необходимо все-таки дойти до конца. Дальше я почти не участвовал.
Решение было найдено, и в этом виде используется: была переопределена матрица аффинных преобразований. Вместо динамической матрицы была установлена статическая. Класс CSPlainTextExtractor использовался вместо CSTextExtractor. Новый класс имеет такой вид:
Конечно, это не панацея и очень редко парсер не добавляет переносы в необходимые места, но для индексации это не важно.
Собственно, все. Спасибо за внимание =)
P.S. Это моя первая более менее серьезная статья, надеюсь на объективную критику.
Upd Особо внимательные читатели нашли в таблицах несоответствия — пофиксил.
Немного о проекте. В нем реализована система пересылки внутренних сообщений, к которым могут быть прикреплены файлы. Также есть поиск, который должен осуществляться по содержимому аттачментов. Большую часть подобных аттачментов составляют pdf-ки.
Собственно работа механизма довольно проста: при отсылке сообщения данные аттачмента парсятся и по ним стороится индекс.
Долгое время документы парсились при помощи библиотеки PDFBOX, работа которой не вызвала ни у кого радости: долго и со сбоями.
В итоге были выбраны 4 библиотеки, сравнением которых я занялся: PDFBOX, JPod, iText и Acrobat.
Акробат был исключен почти сразу, т.к. оказалось, что эта библиотека не поддерживается уже в течение нескольких лет, но статистика по ней осталась, поэтому я ее тоже опубликую.
Сравнивать библиотеки пришлось по двум критериям – скорости работы и качеству полученных результатов.
Предупрежу сразу: библиотеки тестировались на внутренних документах, и они подпадают под определенную секьюрность. Так что я даже названий указать не могу. Скажу лишь одно – контент файлов был самым разнообразным: текст, таблицы картинки, сканы и тп. Размеры файлов тоже довольно сильно различаются, так что можно ожидать объективных оценок.
Оценка времени:
Размер файла |
PDFBOX |
Acrobat |
JPOD |
iText |
74,1 KB | 00:02.591 | 00:03.155 | 00:01.683 | 00:00.963 |
257,5 KB | 00:01.680 | 00:03.191 | 00:00.116 | 00:00.78 |
1,6 MB | 00:05.805 | 00:02.884 | 00:02.532 | 00:02.79 |
28,1 MB | E | 01:10.983 | 00:43.815 | E |
13,6 MB | 00:05.218 | 00:04.331 | 00:00.599 | 00:00.77 |
1,9 MB | 00:02.782 | 00:14.506 | 00:00.608 | 00:00.707 |
1,6 MB | 00:06.182 | 00:02.98 | 00:00.906 | 00:02.413 |
8,9 MB | 00:05.98 | 00:03.894 | 00:00.680 | 00:00.647 |
2,4 MB | 00:14.15 | 00:07.893 | 00:02.826 | E |
604,7 KB | 00:03.342 | 00:04.721 | 00:00.551 | 00:01.222 |
100,6 KB | 00:01.819 | 00:04.212 | 00:00.84 | 00:00.456 |
1,6 MB | 00:05.633 | 00:03.99 | 00:00.883 | 00:02.18 |
10,3 MB | 00:22.311 | 00:22.145 | 00:27.663 | E |
1,9 MB | 00:06.943 | 00:14.736 | 00:01.200 | E |
2,1 MB | 00:02.573 | E | 00:00.498 | 00:00.475 |
111,0 KB | 00:01.956 | 00:02.846 | 00:00.705 | 00:00.300 |
814,3 KB | 00:02.552 | 00:04.221 | 00:00.306 | 00:00.900 |
2,0 MB | 00:06.319 | 00:07.128 | 00:01.821 | 00:02.796 |
338,7 KB | 00:01.950 | 00:03.684 | 00:00.79 | 00:00.415 |
12,9 MB | 00:15.932 | 00:13.628 | 00:04.989 | E |
7,3 MB | E | 00:17.275 | 00:16.377 | E |
97,2 MB | 00:27.291 | 00:01.994 | 00:05.739 | E |
5,2 MB | 00:07.773 | 00:11.108 | 00:01.964 | E |
Total: | ||||
Best | 0 | 2 | 14 | 7 |
Middle | 12 | 7 | 8 | 8 |
Worst (including errors) | 11 | 14 | 1 | 8 |
Errors | 2 | 1 | 0 | 8 |
Красным и зеленым цветами выделены худшее и лучшее время соответственно. Буква «Е» обозначает состояние перманентного коллапса, настигшее процесс вследствие переполнения буфера либо каких-либо иных ошибок.
В сравнении времени объективным победителем стал JPod. Порадовало отсутствие ошибок при парсинге.
Оценка качества:
Оценка качества была довольно субъективна и делилась всего на 3 категории: Best, Middle и Worst. Также еще есть оценка Empty, которая выставлялась, если в процессе парсинга наступал коллапс или просто парсер не находил текста внутри документа.
Оценивалось сходство полученного текста с оригиналом, но не очень критично, т.к. текст нужен был для индекса, а не для вывода.
Размер файла |
PDFBOX |
Acrobat |
JPOD |
iText |
74,1 KB | Best | Middle | Best | Best |
257,5 KB | Best | Middle | Best | Empty |
1,6 MB | Best | Empty | Empty | Worst |
28,1 MB | Empty | Middle | Best | Empty |
13,6 MB | Empty | Empty | Empty | Empty |
1,9 MB | Best | Middle | Worst | Middle |
1,6 MB | Best | Empty | Worst | Worst |
8,9 MB | Best | Middle | Middle | Best |
2,4 MB | Best | Middle | Worst | Empty |
604,7 KB | Best | Middle | Middle | Middle |
100,6 KB | Best | Middle | Best | Empty |
1,6 MB | Best | Empty | Worst | Worst |
10,3 MB | Best | Best | Best | Empty |
1,9 MB | Best | Best | Best | Empty |
2,1 MB | Best | Best | Best | Empty |
111,0 KB | Best | Best | Best | Best |
814,3 KB | Best | Worst | Best | Best |
2,0 MB | Best | Middle | Best | Best |
338,7 KB | Middle | Middle | Best | Empty |
12,9 MB | Best | Best | Best | Empty |
7,3 MB | Empty | Best | Best | Empty |
97,2 MB | Best | Best | Middle | Empty |
5,2 MB | Best | Best | Best | Empty |
Total: | ||||
Best | 19 | 8 | 14 | 5 |
Middle | 1 | 10 | 3 | 2 |
Worst (including empty) | 3 | 5 | 6 | 16 |
Empty | 3 | 4 | 2 | 13 |
Характерные особенности парсинга:
Acrobat очень часто парсит текст в одну строку. Пробелы между словами и предложениями сохраняются, поэтому в принципе это не критично для индексации.
iText не понимает «не-английские» символы. В тестировании применялись документы на английском, немецком и французском языках. Поэтому все их умляуты пошли лесом. Даже не просто пошли лесом – вместо подобных символов я получил вопросики. Возможно, это где-то и настраивается, но остальные понимали подобные символы без плясок с бубном.
PDFBOX по качеству нареканий не вызвал.
JPod – тоже все ок. За исключением одной особенности, которая заставила повозиться с ним довольно долго. В некоторых случаях документ полностью или частями парсился по одной букве в строку – для индекса такой парсинг бесполезен.
В итоге, победителем был объявлен JPod, несмотря на его особенность парсить по букве в строку.
Предстояло разобраться с этим.
Часть вторая. Внутри JPod.
На ковыряние исходников JPod ушло немало времени. Письма, форум проекта. В итоге было установлено, что подобное поведение парсера вызвано тем, какую ориентацию имеют страницы документа. Книжная ориентация проходит нормально, а альбомная – нет. Попытки поковырять параметры ничего не дали. Свойства класса, ответственные за ориентацию страницы были бесполезны.
В общем, в один из моментов я решил просто удалить из классов всяческую работу со шрифтами. Все равно для индексации текста они не нужны. Это помогло, т.к. блоки текста рассчитывались неверно, и это было вызвано именно шрифтами.
Тут бы я и остановился, но Egiptyanin настоял на том, что необходимо все-таки дойти до конца. Дальше я почти не участвовал.
Решение было найдено, и в этом виде используется: была переопределена матрица аффинных преобразований. Вместо динамической матрицы была установлена статическая. Класс CSPlainTextExtractor использовался вместо CSTextExtractor. Новый класс имеет такой вид:
public class CSPlainTextExtractor extends CSTextExtractor {
public void textSetTransform(float a, float b, float c, float d, float e, float f) {
super.textSetTransform(1, 0, 0, 1, 0, 0);
}
}
Конечно, это не панацея и очень редко парсер не добавляет переносы в необходимые места, но для индексации это не важно.
Собственно, все. Спасибо за внимание =)
P.S. Это моя первая более менее серьезная статья, надеюсь на объективную критику.
Upd Особо внимательные читатели нашли в таблицах несоответствия — пофиксил.