Показаны сообщения с ярлыком pdf. Показать все сообщения
Показаны сообщения с ярлыком pdf. Показать все сообщения

2008-12-18

PDF, Ubuntu, Python: история продолжается

Продолжая тему о работе с PDF в Ubuntu расскажу, с какими сложностями я столкнулся, когда стал решать задачу о внесении метаданных в PDF документ. Нужно всего лишь в определенные страницы сосканированного проекта добавить скрытые пометки, которые помогли бы в поиске этих страниц (а это, как правило, чертеж какой-нибудь).

Можно внести информацию о всем документе с помощью упомянутой ранее утилиты pdftk таким образом:
$ pdftk file.pdf dump_data output metainfo

В файл metainfo запишется информация следующего характера:
InfoKey: Title
InfoValue: untitled
InfoKey: Producer
InfoValue: ReportLab http://www.reportlab.com
InfoKey: Author
InfoValue: anonymous
InfoKey: Subject
InfoValue: unspecified
InfoKey: CreationDate
InfoValue: 20081217165436
PdfID0: ebd25ab8427fa1508e9924204f74b274
PdfID1: ebd25ab8427fa1508e9924204f74b274
NumberOfPages: 1


Соответственно, InfoKey - ключ, InfoValue - значение. Думаю, тут все понятно, двинем дальше. Теперь мы можем поменять значения ключей и сохранить файл, а потом изменить значение метаданных в нашем PDF файле:

$ cp file.pdf file.cp.pdf
$ pdftk file.cp.pdf update_info metainfo output file.pdf
$ rm file.cp.pdf


Зачем такой огород? А затем, что pdftk умеет сохранять метаданные только в другой файл, поэтому мы создаем временный, потом обновляем его данные, сохраняя в исходный, и удаляем временный. Вот петрушка... Но это еще цветочки. Ягодки в том, что если прописать в файле с метаданными текст на русском языке, то evince покажет его крякозябрями, а если попробовать извлечь инфо из обновленного файла, то увидим вместо наших русских буковок вот примерно такое: "￐ᅠ￑テ￑", что совсем не соответствует желаемому. К тому же, так задача пометок отдельных страниц не решается. Копаем дальше.

Эта же утилита умеет пристегивать к отдельным страницам PDF документа файлы. Во! Это уже что-то! Составляем файл с нужными данными (рекомендуется HTML или Excel файл, потому что тогда Adobe Reader адекватно среагирует, то есть откроет его). Пристегиваем файл с информацией к первой странице PDF:

$ cp file.pdf file.cp.pdf
$ pdftk file.cp.pdf attach_files to_page 1 output file.pdf
$ rm file.cp.pdf


Ну наконец-то. Неужели это все? Проблема решена? Щазз! Мы только подходим к трудностям. Ведь нам надо как-то извлечь оттуда прикрепленные файлы. А вот тут нас ждет 2 проблемы.

1) Не нашел (а я искал!) в pdftk возможности извлечения файлов выборочно, из определенных страниц, только все скопом:
$ pdftk file.pdf unpack_files output unpack/
Здесь все файлы извлекаются в папку unpack, если она конечно существует, иначе будет error. Одноименные файлы перезаписываются.

2) Все шло более-менее гладко, пока я экспериментировал с файлами PDF журнала LinuxFormat. И картина изменилась, когда дело дошло до настоящих чертежей, отсканированных на нашем широкоформатном сканере. Нет, записывать метаданные и даже прикреплять файлы к страницам получалось нормально, но вот когда я попытался извлечь прикрепленные файлы я увидел примерно вот что:
Unhandled Java Exception:
java.lang.NullPointerException
   at java.lang.String.toLowerCase(libgcj.so.81)
   at java.lang.String.toLowerCase(libgcj.so.81)
   at com.lowagie.text.pdf.PdfEncodings.convertToBytes(pdftk)

Все это в различных вариациях на тему некоего libgcj.so.81. Из всего этого я сделал пару выводов: а) pdftk написана на Java, хотя бы частично и б) проблема связана с текстовой информацией в файле.

Вот тут я был озадачен. Текстовой информации в файле не подразумевалось никакой, только одно-единственное изображение. Ладно бы там была какая-нибудь кириллица, и он на кодировках спотыкался. Но ведь неоткуда там тексту взяться! И тем не менее, файлы сторонние обрабатывались на ура, а мои сосканированные - ни в какую.
Сейчас поведаю, через какие извращения я прошел, чтобы превратить мой PDF в корректно обрабатываемый pdftk. Все это я делал на Python, о библиотеках, которые тут приводятся, я говорил чуть раньше.
Приготовились смеяться? Начали!

[файл workpdf.py]

#!/usr/bin/env python
# -*- coding: utf-8 -*-


# Допустим, что файл называется так:
filename="00.pdf"

import os
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from reportlab.pdfbase import pdfmetrics, ttfonts
from pyPdf import PdfFileReader


# Полчение размера страницы в пикселах
input = PdfFileReader(file(filename, "rb"))
p=input.getPage(0)
width = p.mediaBox[2]
height= p.mediaBox[3]


# Починка файла (чтобы convert не ругался)
os.system("pdftk "+filename+" output 01a.pdf")

# Конвертирование его в картинку
os.system("convert 01a.pdf  workpdf_temp_file.jpg")

# Удаление ненужной копии
os.system("rm 01a.pdf "+filename)

# Задаем настройки шрифта
MyFontObject = ttfonts.TTFont('Verdana', '/usr/share/fonts/truetype/msttcorefonts/tahoma.ttf')
pdfmetrics.registerFont(MyFontObject)


# Создаем основу нового PDF
MyCanvas = canvas.Canvas("test.pdf")

# Задаем размеры, равные исходному файлу, добавляем шрифты и тестовый текст
# Не знаю зачем, но видимо pdftk очень хочет найти текст в файле
MyCanvas.setPageSize((width, height))
MyCanvas.setFont("Verdana", 40)
MyCanvas.drawString(-100, -100, "test")


# Добавляем картинку из исходного файла
MyCanvas.drawImage("workpdf_temp_file.jpg",0,0)

# Завершаем и сохраняем файл
MyCanvas.showPage()
MyCanvas.save()


# А теперь барабанная дробь: прикрепляем файл и открепляем его без ошибок!
print os.system("pdftk test.pdf attach_files info to_page 1 output "+filename)
print os.system("pdftk "+filename+" unpack_files output unp/")


# И удаляем никому не нужные теперь файлы
os.system("rm test.pdf workpdf_temp_file.jpg")

Ну как? Животики надорвали? А главной проблемы - извлечения отдельно взятого файла от отдельно взятой страницы многостраничного PDF документа - я так и не решил. Пока не решил. А вы?

2008-12-15

Python и PDF

О том, с помощью чего и как можно работать с документами PDF в консоли я уже упоминал раньше. Несомненно, с помощью pdftk можно кое-чего достичь, и очень часто этого будет вполне достаточно.
Теперь передо мной встала другая задача: динамически вносить измененния в мета-информацию PDF файлов, и очень желательно - автоматически обрезать белые поля, остающиеся после сканирования документов. И оказалось, это не так просто.
С помошью того же pdftk можно получить информацию документа, записать ее в файл, изменить, а потом сделать update этой информации в другой файл. Что-ж, неплохо. Только кириллица там не прижилась, и слишком много телодвижений, да еще вручную. Это не наш метод. Хотя, если придется, то никуда не денешься.
К делу был привлечен Python, к которому я подбираю библиотеки для работы в PDF.
Кандидат номер 1:
pyPdf, ставится из стандартного репозитария Ubuntu (python-pypdf). Его описание, документация и несколько вполне ясных примеров с комментариями есть на сайте производителя. Вот что этот модуль умеет: извлекать информацию из документа, разбивать документ на страницы по отдельным файлам, собирать их в одно, обрезать, собирать многостраничные документы в одну страницу, шифровать и дешифровывать PDF. Минимум необходимого. Но я так и не нашел возможности вносить изменения в мета-информацию, а это то мне и нужно было.
Кандидат номер 2:
ReportLab, тоже есть в репозитарии Ubuntu. Документация обширная, я только начал вникать, но уже понятно, что эта библиотека - для создания документов, а что насчет правки уже мозоль натершей на языке мета-информации? Пока не знаю, возможно я найду возможности все-таки...
У кого есть опыт в этой сфере - не жадничайте, поделитесь! Неужели я один такой?
По поводу обрезки белых полей - глухо, как в танке. Пробовал использовать convert из комплекта ImageMagic и его опции -trim, а также -fuzzy, вроде бы специально предназначенных для такой задачи. Но результат нулевой. Цвета конечно не идеальные, но про -fuzz написано на сайте разработчика, что он как-раз для такого случая! Ан нет, не сработало. Откликнитесь, коллеги, у кого получилось?

2008-09-04

PDF в Ubuntu: несколько рецептов

На работе время от времени требуется производить манипуляции с файлами PDF. В результате проведенных поисков и тестов получены следующие результаты:
Использование пакета pdftk:
sudo apt-get install pdftk
1. Разбор файла на страницы:
pdftk input.pdf burst output page%03d.pdf
Получится пачка файлов с именами page001.pdf, page002.pdf.... page021.pdf и т.п.
2. Извлечение одной или нескольких страниц в отдельный файл:
pdftk input.pdf cat 1 output page1.pdf
pdftk input.pdf cat 5-9 output page5-9.pdf
3. Сборка одного файла из многих:
pdftk *.pdf cat output combined.pdf
pdftk page1.pdf page2.pdf cat output combined.pdf
На этой странице есть еще примеры. Примечание: при обработке файлов с символами кириллицы в названии выдает ошибку, собирает страницы в один файл быстрее, чем ghostscript.

Использование пакета ghostscript:
sudo apt-get install ghostscript
1. Сборка одного файла из множества:
gs -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=combinedpdf.pdf -dBATCH *.pdf

Облегчим себе жизнь скриптами и алиасами!
В папку с пользовательскими скриптами (у меня это ~/bin) поместились файлы:

# Скрипт pdfX. Для работы нужна утилита pdftk
if [ $1 ]; then
if [ $2 ]; then
pdftk $1 cat $2 output page$2.pdf;
else echo "Нужен обязательный параметр: извлекаемые страницы. Указывать или число (например 12) или промежуток чисел (например 2-4)";
fi;
else echo "Нужен обязательный параметр: имя файла pdf";
fi;

Использовать так:
pdfX input.pdf 4-5
pdfX input.pdf 43

# Скрипт pdfXall. Для работы нужна утилита pdftk
if [ $1 ]; then
pdftk $1 burst output page%03d.pdf
else echo "Нужен обязательный параметр: имя файла pdf";
fi;

Использовать так:
pdfXall input.pdf

В файл ~/.bashrc добавлена строчка:
alias pdf2one='gs -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=combinedpdf.pdf -dBATCH'

После перечитывания файла
~$ . .bashrc
в каталоге с кучей файлов pdf можно использовать команду:
pdf2one *.pdf