Cага о пакетном конвертировании pdf в text

В прошлом году была заказана, как казалось на первый взгляд, простая работа: создать систему пакетной обработки файлов — содержащих 12-ти колоночную таблицу, данные из которой экспортировать в БД. Все бы ничего — да вот файлы оказались документами в pdf, а заказчик утверждал что другого формата для обработки предоставить никак не может.

image
Образец того самого pdf-а — в файле сохранена структура, но подчищены все данные.

Чтож, несмотря на предупреждения знающих людей, а предупреждали они ой как не зря — я за работу взялся и пережил вот такое приключение:



Начался поиск софта для пакетной обработки pdf файлов. Работа производилась на linux — потому первым претендентом оказалась программа из пакета poppler — pdftotext, которую чаще всего в этих целях и используют.

pdftotext


функция легко вызывается из командной строки, и имеет следующий вид:

$ pdftotext ваш.pdf ваш.txt


Но вот результат конвертирования оказался весьма странным:

Выписка из реестра
сделок

по итогам торгов
ЗАО «Санкт — Петербургская
Международная
Товарно — сырьевая Биржа»

Форма СЭТ-ВРС

Дата торгов: 01/01/1900
Секция: Секция «Нефтепродукты» ЗАО «СПбМТСБ»
Участник: ООО
Код участника:000000000000
Наименование и код клиента: ОАО / 000000000000
Код инструмента: 0000000001
Описание инструмента: Бензин Премиум Евро-95 вид II класс B,
Hомер
сделки

Время
сделки

Hомер заявки

Покупка/ Цена сделки(за Количество
продажа¹
одну тонну)
лотов

01-01

01-02

01-03

S

01-05

5

02-01

02-02

02-03

S

02-05

1

Итого по 0000000001

и так далее…

Мало того: вывод еще менялся от файла к файлу — конечно в таком виде невозможно было написать парсер для выдергивания данных из таблиц.

Потому на горизонте возник второй, на сей раз проприетарный кандидат: Adobe Acrobat Reader for linux.

Acrobat Reader


В данном случае решил, для начала, испытать его возможность «File — Save as Text» в графическом режиме, не сильно углубляясь в командную строку. Как потом выяснилось, правильно сделал — результаты конвертирования моих pdf и в данной программе, не внушали оптимизма:

Код участника:
Секция:
Код инструмента:
Код инструмента:
000000000000
ООО
0000000001
0000000002
00,000,000.11 00,000.12
0,000,000.21 0,000.22
01-12
02-12
03-12
04-12
ЗАО «Санкт — Петербургская
Международная
Товарно — сырьевая Биржа»
Выписка из реестра
сделок
по итогам торгов
Дата торгов: 01/01/1900
Форма СЭТ-ВРС
Наименование и код клиента: ОАО / 000000000000
Участник:
Секция «Нефтепродукты» ЗАО «СПбМТСБ»
Описание инструмента: Бензин Премиум Евро-95 вид II класс B,
Код
трейдера
Время
сделки Объем сделки
Hомер
сделки
Hомер заявки
Итого по 0000000001
Описание инструмента: Дизельно топливо зимнее З-0.2-35.
Код
трейдера
Время
сделки Объем сделки
Hомер
сделки
Hомер заявки
Итого по 0000000002

и так далее…

Парсить такой вывод, тоже не очень хотелось.

Третьим и, как выяснилось, последним кандидатом оказался pdfedit.

pdfedit


Во всех репозиториях он представлен графической программой с возможностью выполнения скриптов из командной строки. Так же как у Acrobat Reader — первый запуск конвертирования pdf в text произвел из графического окружения «файл — сохранить как текст», результат сильно порадовал:

ЗАО "Санкт - Петербургская     Выписка из реестра                            Форма СЭТ-ВРС
Международная                        сделок
Товарно - сырьевая Биржа"       по итогам торгов
                             Дата торгов: 01/01/1900
Секция: Секция "Нефтепродукты" ЗАО "СПбМТСБ"
Участник: ООО
Код участника:000000000000
Наименование и код клиента : ОАО / 000000000000
Код инструмента: 0000000001
Описание инструмента: Бензин Премиум Евро-95 вид II класс B,
  Hомер  Время   Hомер заявки
                    Покупка/ Цена сделки(за Количество
                                                  HДС         Биржевой   Hаименование
                                                                                Код
                                Объем сделки                 КПР
 сделки  сделки     продажа¹
                      одну тонну)
                             лотов               (18%)           сбор     контрагента
                                                                             трейдера
01-01   01-02 01-03 S  01-05  5  01-07        01-08    01-09   01-10    ПеСее
                                                                           01-12
02-01   02-02 02-03 S  02-05  1   02-07         02-08  02-09    02-10   ПбСее
                                                                           02-12
 Итого по 0000000001            00,000,000.11                 00,000.12
Код инструмента: 0000000002
Описание инструмента: Дизельно топливо зимнее З-0.2-35.
  Hомер  Время   Hомер заявки
                    Покупка/ Цена сделки(за Количество
                                                  HДС         Биржевой   Hаименование
                                                                                Код
                                Объем сделки                 КПР
 сделки  сделки     продажа¹
                      одну тонну)
                             лотов               (18%)           сбор     контрагента
                                                                             трейдера
03-01   03-02 03-03 S  03-05  1   03-07         03-08  03-09    03-10   ПуСее
                                                                           03-12
04-01   04-02 04-03 S  04-05  1   04-07         04-08  04-09    04-10   ПгСее
                                                                           04-12
 Итого по 0000000002             0,000,000.21                  0,000.22
Маклер СПбМТСБ                Копия электронного документа
      ¹ B - покупка, S - продажа.       1/3



Получился очень корректный вывод — парсить который легко и приятно. Но не тут то было, путь к завершению эпопеи оказался тернист. Вызвать функцию «сохранить как текст» из командной строки, в стандартно собранном pdfedit, было невозможно:

$ pdfedit -console
PDFedit 0.4.5
Использование:
 pdfedit -console [имя функции] [параметр(ы) функции]
First parameter is name of function to invoke (case insensitive) or its unambiguous part.
Остальные параметры передаются вызванной функции.
Доступные функции:
 Delinearizator
  Описание: Delinearize input file
  Параметры: [input file] [output file]
 Flattener
  Описание: Flatten input file (remove all revisions except the last one)
  Параметры: [input file] [output file] 



вывод консольного хелпа давал две утилиты, никак не связанные с конвертированием в текст. Дальнейшее изучение документации на wiki привело к готовому скрипту конвертирования savealltext.qs, но беда в том, что он должен был исполнятся из графического меню. В результате пришлось подробно изучать матчасть:

описание API для создания скриптов в pdfedit
wiki создания скрипта для pdfedit
примеры готовых скриптов

«Курение» уже этого материала + поэтапный разбор представленного в стандартной сборке консольного скрипта /usr/share/pdfedit/delinearize.qs — привело меня к созданию своего savealltext.qs:

/** Print help for savealltext */
function savealltext_help() {
 print(tr("Usage:"));
 print("savealltext ["+tr("input file")+"] ["+tr("output file")+"]");
 print(" "+tr("Input file must exist"));
 print(" "+tr("Output file must not exist"));
 exit(1);
}

function savealltext_fail(err) {
 print(tr("savealltext failed!"));
 print(err);
 exit(2);
}

function saveAsText_save(p,f) {
 document=loadPdf(p)
 qs="";
 pages=document.getPageCount();
 for (i=1;i<=pages;i++) {
  pg=document.getPage(i);
  text=pg.getText();
  qs+=text;
  qs+="\n";
 }
 saveFile(f,qs);
}

p=parameters();
if (p.length!=2) {
 savealltext_help("savealltext "+tr("is expecting two parameters"));
}
inFile=p[0];
outFile=p[1];
if (!exists(inFile)) savealltext_fail(tr("Input file '%1' does not exist").arg(inFile));
if (exists(outFile)) savealltext_fail(tr("Output file '%1' already exist").arg(outFile));
if (inFile==outFile) savealltext_fail(tr("Input and output files must be different"));
if (saveAsText_save(inFile,outFile)) {
} else {
 print(tr("savealltext")+" :"+inFile+" -> "+outFile);
}


будучи помещенным в каталог /usr/share/pdfedit/, скрипт вызывается из командной строки и производит требуемое конвертирование:

$ pdfedit -console
PDFedit 0.4.5-20111014140242
Использование:
 pdfedit -console [имя функции] [параметр(ы) функции]
First parameter is name of function to invoke (case insensitive) or its unambiguous part.
Остальные параметры передаются вызванной функции.
Доступные функции:
 Delinearizator
  Описание: Delinearize input file
  Параметры: [input file] [output file]
 Flattener
  Описание: Flatten input file (remove all revisions except the last one)
  Параметры: [input file] [output file]
 savealltext
  Описание: savealltext input file
  Параметры: [input file] [output file]



$pdfedit -console savealltext ваш.pdf ваш.txt

Вроде бы все отлично, но подводные камни продолжали всплывать: выяснилось что pdfedit, в консольном режиме, забирает текст с области по умолчанию 612x792 pixels — в разрешении 72 dpi это соответствует А4 листу альбомной ориентации. Поворачивать область сканирования программа никак не хотела, несмотря на присутствие в коде соответствующих инструкций с патча rotate_text_fix.patch.
Поиски определения этой триклятой «дефолтной области» завели меня в исходный код проекта, к файлу: src/kernel/displayparams.h — вот ведь, нашли куда засунуть!

static const int DEFAULT_PAGE_RX = 612; /**< Default A4 width on a device with 72 horizontal dpi. */
static const int DEFAULT_PAGE_RY = 792; /**< Default A4 height on a device with 72 vertical dpi. */

Заменил оба значения на 1584 точек (что соответствует половине ватмана в том же разрешении) и прересобрал проект.

На этом, можно сказать, мучения закончились — pdf-ки обрабатывались из терминала, во всю свою А4-ую дущу альбомной ориентации.
По результатам своих мучений сделал правила для сборки пакетов pdfedit с savealltext для ArchLinux и выложил в AUR:
pdfedit релиз
pdfedit версия из csv

п.с.:


Скоро сказка сказывается, да не скоро дело делается — мне сильно не нравилось что pdfedit для запуска, даже в консольном режиме, требовал наличия в системе qt3:

$ pdfedit -console
pdfedit: error while loading shared libraries: libqt-mt.so.3: cannot open shared object file: No such file or directory

Что привело к рассмотрению ключей конфигурирования программы при сборке из исходников и тут был найден следующий рецепт:

configure --disable-gui --enable-pdfedit-core-dev --enable-tools

В результате бинарник графического pdfedit не собирается вовсе, зато создается целая куча полезных утилит, среди которых pdf_to_text — производящая необходимую мне конвертацию, применяя те же, что и скрипт savealltext.qs, алгоритмы:

$pdf_to_txt -file ваш.pdf >ваш.txt

Результаты такой сборки проекта, тоже превратил в PKGBUILD для ArchLinux и выложил в AUR.

upd1:


Спасибо всем за консультации! Благодаря обсуждению было выявлено еще несколько интересных способов конвертирования pdf в text:
zeliboba предложил применить к pdftotext опцию -layout
$ pdftotext -layout ваш.pdf ваш.txt

Можно без преувеличивания сказать что, файлы проекта из статьи, такой способ конвертирует лучше чем pdfedit:

ЗАО "Санкт - Петербургская                                                     Выписка из реестра                                                              Форма СЭТ-ВРС
Международная                                                                        сделок
Товарно - сырьевая Биржа"                                                       по итогам торгов

                                                                        Дата торгов: 01/01/1900

Секция: Секция "Нефтепродукты" ЗАО "СПбМТСБ"
Участник: ООО
Код участника:000000000000
Наименование и код клиента : ОАО / 000000000000

Код инструмента: 0000000001
Описание инструмента: Бензин Премиум Евро-95 вид II класс B,

 Hомер        Время        Hомер заявки   Покупка/ Цена сделки(за Количество                         HДС                  Биржевой            Hаименование        Код
                                                                                 Объем сделки                       КПР
 сделки       сделки                      продажа¹  одну тонну)     лотов                           (18%)                   сбор               контрагента     трейдера

01-01         01-02      01-03               S           01-05             5       01-07          01-08     01-09         01-10       ПеСее                  01-12
02-01         02-02      02-03               S           02-05             1        02-07          02-08    02-09          02-10      ПбСее                  02-12

 Итого по 0000000001                                                             00,000,000.11                            00,000.12



С другой стороны — не со всеми pdf-ами это правило срабатывает:
«Сегодня экспериментировал с различными pdf файлами, получилась что между pdfedit и pdftotext — все равно нет абсолютного чемпиона, скажем:
Pdftotext — лучше сконвертировал ряд вытянутых в горизонтальном направлении таблиц и одноколоночных журналов.
Pdfedit — лучше работал с многоколоночными журналами(например:linuxformat) и вертикально ориентированными таблицами.
Гдето обе программы показали одинаковый результат (например: журнал Цейнгауз).»

kornerz предложил конвертировать pdf в svg, при помощи inkscape — предварительно разделив pdf-файл на листы (как вариант при помощи pdftk)

$pdftk file.pdf burst

$inkscape -z -f pg_0001.pdf -l output_page1.svg
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 23

    +1
    Вы не встречали простого способа преобразовать PDF/PPT файл в серию картинок?
      +6
      С помощью pdftk можно разбить PDF файл на страницы:
      pdftk file.pdf burst

      и потом inkscape-ом преобразовать в SVG получившиеся страницы:
      inkscape -z -f pg_0001.pdf -l output_page1.svg

      Обе утилиты доступны в большинстве современных linux дистрибутивов.
        0
        Однозначно в закладки!
        С pdftk знаком — утилита весьма известная. Единственное что не нравится — зависимость для сборки gcc-gcj(The GNU Compiler for Java)pdftk из AUR, хотя никто не мешает использовать уже собранную — бинарную версию pdftk-bin.
        За направление в сторону inkscape, большое спасибо — раньше не рассматривал этот векторный редактор с точки зрения пакетного конвертера. Конечно он есть в репозитории.
        +2
        convert из imagemagick.
          0
          Есть минимум несколько способов: pdf2ps + imagemagick (если я ничего не путаю), или например pdfdraw из замечательной библиотеки mupdf. Но, как я помню, во-первых, imagemagick жрет память и тормозит, во-вторых, я так и не решил проблему антиалиасинга, чтобы буковки отображались ровненькими и кругленькими. Так что если хотите этим заняться, придется экспериментировать.

          А ppt — тут наверно опенофис нужен, его вроде ничем больше не прочесть.
          +1
          Вот ради любопытства, попробуйте использовать FineReader =)
            +6
            Или, что лучше подойдет в вашей ситуации — FineReader Engine for Linux
              +1
              Спасибо за уточнение: заказал тестовую версию на сайте abbyy — в данном случае для себя(описанная в статье работа давно сдана), чтобы провести сравнительный анализ функциональности. И в дальнейшем иметь представление и об этой линейке продуктов.
                +1
                Если напишете статью не забудьте прилепить ссылку в эту.
                И спасибо за материал
              +2
              Когда делалась работа описанная в статье — в ТЗ не учитывались дополнительные расходы на приобретение коммерческого продукта(только за счет разработчика).
              Acrobat Reader я хотел использовать как эталон — для сравнения с другими конвертерами. Но вот эталон сам подкачал =)
              0
              Решение оригинальное, но не оптимальное, имхо :)
                0
                Отчет в таком виде дает биржа, т.к. он подписан цифровой подписью во избежание ;)
                  0
                  Да это документы с биржи — в первоначальном виде открывается редакторами в режиме read only.
                  Документ представленный на скриншоте, в начале статьи, — просто шаблон. Он был обработан delinearizator-ом и потом отредактирован в pdfedit — информация на оригинале не публичная.
                  • UFO just landed and posted this here
                      +1
                      1. Любопытно — либы в студию.
                      2. Название биржи есть на скриншоте: СПбМТСБ. Файлы предоставлял заказчик(о чем я тоже писал) — изменить формат «выписки из реестра сделок», он либо не смог, либо не захотел. ТЗ было составлено именно с учетом файлов в формате pdf.
                  0
                  Правильнее было бы назвать пакет headless.
                    +7
                    у pdftotext есть замечательная опция -layout которая старается сохранить исходное форматирование. работает отлично с весьмо сложными таблицами (не уверен, что вообще со всеми, но я проблем почти не встречал). имхо, вы слишком перемудрили pdfedit и скриптами.
                      0
                      Большое спасибо, с данной опцией вывод pdftotext — оказался просто отличным для файлов описанного в статье проекта.
                      Сегодня экспериментировал с различными pdf файлами, получилась что между pdfedit и pdftotext — все равно нет абсолютного чемпиона, скажем:
                      Pdftotext — лучше сконвертировал ряд вытянутых в горизонтальном направлении таблиц и одноколоночных журналов.
                      Pdfedit — лучше работал с многоколоночными журналами(например:linuxformat) и вертикально ориентированными таблицами.
                      Гдето обе программы показали одинаковый результат (например: журнал Цейнгауз).
                      Потому, наверно, проделанную работу нельзя назвать напрасной — получилось довести до ума еще один инструмент пакетной обработки pdf формата.
                      0
                      Супер. Правда у Вас более сложно получилось…
                      Я когда-то маялся с сфотографированными делами. Требовалось повернуть лист на 90 град. (если надо), разрезать пополам и записать 2 файла. Вроде тоже всё просто.
                      Потом оказалось, что пополам было не всегда пополам, т.к. дела фотографировались не со штатива, а «руками». То листы повёрнуты, то в одну половинку вклеен лист, который больше другой половинки. Нужно было обрезать чёрные поля вокруг фото, нужно было немного поворачивать лист, если он криво сфотографирован (а наких 99,9%). Потом нужно было найти центр и по нему резать.
                      Объём отсканированных книг был ближе к терабайту. Пришлось жертвовать качеством поворотов/обрезок в угоду скорости.
                      В итоге задачу решил (до приемлемого качества). Помог Linux, bash и ImageMagick (вот действительно магический пакет).
                      Клиент остался доволен, что главное!
                        0
                        Люблю такие статьи. Самому как бы не нужно что человек сделал, а приятно что доковырялся.
                          0
                          Попробовал добавить скрипт в /usr/share/pdfedit: (ubuntu 10.04 lts)

                          $ pdfedit -console savealltext

                          No function found matching 'savealltext'
                          Доступные функции:
                          Delinearizator
                          Flattener
                            0
                            лучше поздно, чем никогда

                            если посмотреть в /usr/share/pdfedit/delinearize.qs
                            то видно, что у него заголовок начинается с
                            // Console: Delinearizator
                            // Description: Delinearize input file
                            // Parameters: [input file] [output file]
                            /*
                             * PDFedit - free program for PDF document manipulation.

                            я вставил такой же в /usr/share/pdfedit/savealltext.qs:
                            // Console: SaveAllText
                            // Description: SaveAllText input file
                            // Parameters: [input file] [output file]
                            ...

                            и функция появилась:
                            $ pdfedit -console 
                            PDFedit 0.4.5-20140818011915
                            Usage:
                             pdfedit -console [function name] [function parameter(s)]
                            First parameter is name of function to invoke (case insensitive) or its unambiguous part.
                            Rest of parameters are passed to invoked function.
                            Available functions:
                             Delinearizator
                              Description: Delinearize input file
                              Parameters: [input file] [output file]
                             Flattener
                              Description: Flatten input file (remove all revisions except the last one)
                              Parameters: [input file] [output file]
                             SaveAllText
                              Description: SaveAllText input file
                              Parameters: [input file] [output file]
                            0
                            pdftotext работает гораздо лучше если прочитать документацию и найти там ключ -layout:

                            pdftotext -layout input.pdf output.txt

                            Only users with full accounts can post comments. Log in, please.