Очерк по поводу создания PDF-файлов. Добавляем в программу просмотр PDF с помощью ActiveX Создание pdf в delphi 7

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

Для этого нам понадобятся компоненты с вкладки Rave . Давайте же установим, необходимые нам, для работы компоненты, а это:

  • TRvNDRWriter
  • TRvRenderPdf
  • TButton

Компонент TRvNDRWriter предназначен для записи в pdf -файл информацию (текстовую, графическую), через поток.

Компонент TRvRenderPdf предназначен для создания, отрисовки информации (текстовой, графической и так далее). Ну и по нажатию на кнопку, мы будем что-то заносить в наш pdf -файл.

Из теории много писать ничего не будем, а сразу перейдем к программированию, но необходимо еще сперва создать обычный пустой pdf -файл и положить его в корень с программой. Дальше на событие OnClick нашей кнопки мы напишем следующий код:

procedure TForm1. Button1Click (Sender: TObject ) ; var Streams: TMemoryStream; begin Streams: = TMemoryStream. create ; RvNDrWriter1. Stream : = Streams; RvNDRWriter1. Execute ; RvRenderPdf1. PrintRender (Streams, "test.pdf" ) ; ShellExecute(Handle, nil , "test.pdf" , "" , "" , SW_SHOW) ; FreeAndNil (Streams) ; end ;

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

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

procedure TForm1. Print (Sender: TObject ) ; begin bmp: = TBitmap. Create ; bmp. LoadFromFile ("test.bmp" ) ; with RvNDRWriter1 do begin SetFont("Arial" , 16) ; FontColor: = clGreen; Print("Test Text" ) ; PrintHeader("Begin PDF File" , pjCenter) ; PrintFooter("End PDF File" , pjCenter) ; LineTo(10, 10) ; PrintBitmap(1, 1, 1, 1, bmp) ; end ; FreeAndNil (bmp) ; end ;

Ну, сперва, мы загружаем картинку *.bmp , естественно переменная bmp , у нас объект класса TBitmap . Загружаем мы для того, чтобы потом ее отрисовать в нашем pdf -документе.

А дальше, я думаю все понятно, процедура PrintHeader - выводит надпись в заголовок документа (страницы), процедура PrintFooter - выводит надпись в конец документа (страницы). Процедура PrintBitmap - выводит изображение в документ, процедура SetFont - устанавливает шрифт документа, процедура FontColor - устанавливает цвет шрифта документа.

Для этого примера нам будут необходимы следующие компоненты, которые находятся во вкладке Rave. И так, приступим к установке нужных нам компонентов, а именно:

  • TRvNDRWriter
  • TRvRenderPdf
  • TButton

Предлагаю немного остановиться на этих компонентах и понять для чего они служат. Кстати о них я вычил информацию из пару книг которые приобрел progbook.ru . Поэтому если хотите приобрести достойные книги по Delphi, то милости прошу в данный магазин.

Итак, первым у нас идет компонент TRvNDRWriter. Этот компонент необходим нам для записи в pdf-файл информации, не важно какой, текстовой или графической, через поток.

Procedure TForm1.Button1Click(Sender: TObject); var Streams:TMemoryStream; begin Streams:=TMemoryStream.create; RvNDrWriter1.Stream:=Streams; RvNDRWriter1.Execute; RvRenderPdf1.PrintRender(Streams, "test.pdf"); ShellExecute(Handle, nil, "test.pdf", "", "", SW_SHOW); FreeAndNil(Streams); end;

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

Но не будем сильно разглагольствовать, а перейдем сразу к интересующей нас теме, а именно к программированию:)

Для начала нам необходимо создать обычный пустой pdf-файл и разместить его в корневой папке нашей программы.

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

После выполнения данного когда, нам необходимо событие, которое возникает при прорисовке данных, в этом событии мы будем выводить нужную нам информацию.

Разберем код. Для начала я загружаю картинку *.bmp, создаем переменную bmp, и объект класса TBitmap. Загружаю я ее для того, чтобы потом рисовать в нашем pdf-файле.

Я думаю дальше особо описывать не стоит все понятно и так, процедура PrintHeader - добавляет надпись в заголовок документа, процедура PrintFooter - добавляет надпись в конец документа. Процедура PrintBitmap - добавляет изображение в документ, процедура SetFont - задает необходимый шрифт документа, процедура FontColor - задает нужный цвет шрифта документа.

Все последующие графические функции и процедуры, точно такие же, как и при выводе графической информации на Canvas других компонентов.

Функцию NewPage я использую для создания нового листа в документе. Все что будет добавлено после нее на вывод информации, будет выводиться на новом листе документа.

Важно , что бы все модули: RpRender, RpRenderPDF, RpDefine, RpBase, RpFiler, RpRave, RpCon были подключены к проекту!


дата публикации 23-09-2005 07:00 Очерк по поводу создания PDF-файлов

В последнее время на просторах интернета обнаружилось очень много PDF converter"ов, reader"ов и write"ов. И подавляющее большинство из них предлагается за деньги. Сама программа от 10$ до 300$. А уж исходный код за гораздо большие деньги цена начинается от 200$ а в одном месте (заинтересовавшись этим полазил по инету) аж за 900 евро.

Данная проблемма меня заинтересовала в плане программирования и вот результаты довожу до вашего сведения. (Данные результаты получены мною при изучении внутренностей PDF файла, когда открываешь его в total commander через F3)

Обычный PDF файл состоит из четырех частей :=

Что такое такое

? Это обычное упоминание о версии PDF specification. Которое присутствует в первой строке PDF файла. Например "%PDF-1.3" В седьмой версии акробата которая вышла где то в начале лета этого года, этот номер "%PDF-1.7", но это не версия продукта, это версия именно спецификации. Второй строкой PDF идет небольшая аброкадабра (видимо предназначена для дальнейшего использования) " %вгПУ"

Все с первой частью PDF разобрались.

Что из себя представляет вторая часть которая называется ?

Ответ очень простой: это последовательность объектов, описание которых как и хедера представлены в текстовом виде.

Каждый объект это текстовой фрагмент с порядковым номером в имени например "4 0 obj"

  • 4 это порядковый номер объекта
  • 0 это номер (ре)генерации файла то есть когда файл обновляется (редактируется) то данный номер увеличивается
  • obj это кодовое слово означающее что в теле документа нам встретился объект

Все объекты делятся на косвенные и прямые. Все косвенные, и их большинство, после слова obj имеют в своем теле делиметер "<<", означающее начало данных объекта. И в конце данных закрывающий делиметер ">>" и кодовое слово endobj

Прямые объекты не должны иметь в своем теле открывающих и закрывающих делиметеров "<<", ">>" Все косвенные объекты доступны через cross-reference table . В ней представлены ссылки в виде смещения от начала файла до начала объекта (Данные (строки) в объекте разделяются #13#10 либо #13 )

Тип "самого главного" объекта в теле PDF файла носит гордое имя "/Catalog"

4 0 obj << /Type /Catalog /Pages 2 0 R /OpenAction [ 5 0 R /XYZ null 364 1 ] /PageMode /UseNone >> endobj

На самом деле в теле минимального PDF файла типа "Hello world" должно быть 3 "главных" объекта. Давайте я их перечислю по типам:

  • "/Catalog" содержит в себе ссылку: на дерево страниц (/Pages )
  • "/Pages" содержит в себе ссылку на группу страниц документа (Например 2 0 obj > endobj)
  • "/Page" содержит в себе ссылку на объекты относящиеся к конкретной странице. (Например 3 0 obj > /Rotate 0 >>)
И несколько "второстепенных"

Разберем объект страница:

  • /Rotate поле показывающее на сколько градусов изображение страницы должно быть повернуто при отображение в программе
  • /MediaBox и /CropBox поля описывающие размер страницы
  • /Parent ссылка на родительский объект "/Pages"
  • /Resources это поле описывает какой фонт должен быть использован при отображении страницы (фонт это отдельный объект) и установку ProcSet эта установка показывает какое содержимое потока данных данной страницы (тоже может быть определен как объект, а не как поле)
  • /Contents Самое интересное поле в объекте "страница", дает ссылку на объект содержимого данной страницы, причем: если это поле отсутствует в объекте "страница" значит страница пустая
Содержимое страницы:
Объект "stream" 4 0 obj > stream BT /F12 9 Tf 10 782 TD 0 -12.5 TD (Max Fokin) Tj 0 -12.5 TD (mnb) Tj 0 -12.5 TD () Tj 0 -12.5 TD (Max Privet) Tj 0 -12.5 TD (1) Tj 0 -12.5 TD (1) Tj 0 -12.5 TD (2) Tj 0 -12.5 TD (3) Tj 0 -12.5 TD (45) Tj ET endstream endobj /Length 305 это поле показыввающее сколько байт от слова stream до слова endstream

Самый простой вариант - это некодированный и несжатый поток данных в объекте stream . Он ограничивается операторами BT и ET

BT Begins a Text Object - характеризует начало текста ET Ends a Text Object. - характеризует конец текста /F12 9 Tf

  • /F12 это кодовое имя объекта который характеризует фонт используемый на данной странице
  • 9 это размер фонта
  • Tf это оператор который характеризует что данная строка в объекте steam есть установка фонта и размера
10 782 TD это цифры откуда начинается данная строка (отсчет производиться от левого верхнего угла) Tj это оператор перевода на новую строку Ну а в круглых скобках наш текст

Кодированный поток я сдесь не объясняю. Он основан на алгоритмах RC4, RC5, MD5.

Что такое объект Font 12 0 obj >

  • /Type /Font Естественно название типа
  • /Subtype /Type1 название подтипа
  • /Name /F7 F7 это кодовое имя

PDF поддерживает несколько видов фонтов. Они перечисленны ниже

  • Type 1, including subsets and Multiple Master "snapshots"
  • Type 3
  • TrueType, including subsets
  • Type 0
Честно говоря, я не разбирался с Type 3, TrueType, including subsets, Type 0 ничего по ним сказать не могу
А Type 1 - это следующие фонты Courier Courier-Bold Courier-BoldOblique Courier-Oblique Helvetica Helvetica-Bold Helvetica-BoldOblique Helvetica-Oblique Times-Roman Times-Bold Times-Italic Times-BoldItalic Symbol ZapfDingbats

20 0 obj > endobj Это объект с названиями кодовых имен для фонтов первого типа. По этому кодовому имени можно легко получить сам объект фонт . 6 0 obj >

ВСЕ: то есть минимальное Состоит из следующих объектов: "catalog" , "pages", "page", "Resources" (опиционально может присутствовать, как поле в объекте страница), нетипизированный объект "stream" , группа объектов "font"

Что такое ? На самом деле это обычная текстовая таблица, она начинается со слова xref и своем теле имеет ссылки на все косвенные объекты в документе. Вот пример xref 0 27 0000000021 65535 f 0000000016 00000 n 0000000105 00000 n 0000000169 00000 n 0000000356 00000 n 0000000713 00000 n 0000000892 00000 n 0000001006 00000 n 0000001125 00000 n 0000001247 00000 n 0000001373 00000 n 0000001486 00000 n 0000001604 00000 n 0000001725 00000 n 0000001850 00000 n 0000001967 00000 n 0000002084 00000 n 0000002203 00000 n 0000002326 00000 n 0000002439 00000 n 0000002558 00000 n 0000000024 00001 f 0000002751 00000 n 0000002831 00000 n 0000000000 00001 f 0000002915 00000 n 0000002955 00000 n 0 27 Эти цифры обозначают следующее:

  • 0 - первый object number в таблице
  • 27 - количество элементов таблицы

Первый элемент таблицы всегда иммет вид "XXXXXXXXXX 65535 f " где X это цифра, а 65535 это значение по умолчанию для первого элемента в таблице. Символ "f" обозначает "free ", то есть объект не используется

Разберем элемент данной таблицы.

  • Первые 10 цифр - это смещение от начала файла до начала объекта.
  • 0000000016 означает что через 16 байт от начала файла Вас встретит первое упоминание об объекте то есть, например, 4 0 obj

Вторые пять цифр - это номер генерации файла. Если файл только что создан, то они всегда нули. Если файл модифицируется, то это число увеличивается на единицу. То есть, 0000000024 00001 f

Канонический, только что созданный PDF файл, имеет только одну таблицу. Но, если файл редактируется, то таких таблиц может быть очень много.

Взаимосвязь таблиц осуществляется при помощи последнего элемента и кодового слова startxref

Канонический, только что созданный PDF файл, имеет только одну таблицу, после таблицы идет элемент trailer А после трайлера идет кодовое слово startxref , указывающее на смещение от начала файла до начала таблицы, вот пример. trailer > startxref 173 %%EOF Это значит, что через 173 байта от начала документа, будет присутствовать кодовое слово xref . Но, если файл был отредактирован, то последний в файле трайлер будет иметь вид: xref 0 3 0000000000 65535 f 0000003609 00000 n 0000003832 00000 n trailer ] >> startxref 173 %%EOF Но, если мы обратимся к таблице, куда указывает ссылка startxref 173 , то мы найдем следующую таблицу, а за ней трайлер, который будет иметь поле /Prev 3896 3 16 0000000016 00000 n 0000000664 00000 n 0000000936 00000 n 0000001106 00000 n 0000001133 00000 n 0000001250 00000 n 0000001395 00000 n 0000001811 00000 n 0000001992 00000 n 0000002180 00000 n 0000002360 00000 n 0000002760 00000 n 0000003438 00000 n 0000003516 00000 n 0000000776 00000 n 0000000916 00000 n trailer ] >> startxref 567 %%EOF

Данное поле /Prev 3896 указывает нам на предыдущую таблицу, а ссылка startxref 567 указывает на следующую таблицу и так практически до бесконечности, пока в очередном поле startxref мы не увидим 0 . Это значит, мы прочитали все таблицы.

В данном очерке, конечно, не хватает исходного кода. Вот и он: представлены два модуля основной "PDFDocument " и вспомогательный "PDFBaseFonts "

К материалу прилагаются файлы:

  • Исходный код модулей PDFDocument.pas и PDFBaseFonts.pas (16 K) обновление от 9/23/2005 7:02:00 AM

Обсуждение материала [ 31-07-2006 06:33 ] 7 сообщений

Возможности компонента, заявленные разработчиками:

  • Поддержка векторной и растровой графики в PDF-документах
  • Защита PDF-документов паролем
  • Поддержки AcroForms/PDF-форм
  • Водяные знаки
  • Генерация PDF документов с внутренними и внешними ссылками, веб-ссылками и закладками
  • Полная поддержка Unicode
  • Поддержка различных шрифтов, форматирование текста, расположение текста в несколько столбцов
  • Поддержка TCanvas
  • Встроенный архиватор для сжатия текстовой и графической информации в документе
  • Преобразование TIFF в PDF

В качестве примера, я решил собрать демонстрационное приложение, идущее в комплекте вместе с компонентом THotPDF . Приложение довольно простенькое — генерирует PDF-ку с двумя ссылками и простым текстом. Код тоже простой и понятный:

procedure TForm1. HelloWorldButtonClick (Sender: TObject ) ; begin HPDF. BeginDoc ; HPDF. CurrentPage . PrintHyperlink (20 , 35 , "Website: " + MainEdit. Text , MainEdit. Text ) ; HPDF. CurrentPage . SetFont ( "Times New Roman" , , 16 ) ; HPDF. CurrentPage . SetRGBHyperlinkColor (clRed) ; HPDF. CurrentPage . PrintHyperlink (20 , 50 , "Order page: " + BuyEdit. Text , BuyEdit. Text ) ; HPDF. CurrentPage . SetFont ( "Times New Roman" , , 14 ) ; HPDF. CurrentPage . TextOut (20 , 75 , 0 , "Click the link to navigate" ) ; HPDF. EndDoc ; end ;

Даже не вдаваясь в тонкости работы компонента можно понять, что и как происходит в этом демонстрационном примере. Запускаем, смотрим на внешний вид сгенерированного документа:

Вот эти два синих прямоугольника в документе — ссылки на две страниц…а простого текста «Click the link to navigate» вообще не наблюдается нигде. Хотя, надо отдать должное, при клике по прямоугольнику ссылка действительно открывается. В общем, то ли разработчики поторопились заявить поддержку Delphi вплоть до XE3, то ли это так интересно работает trial-версия, но после запуска этого демонстрационного примера у меня как-то отпало желание использовать THotPDF даже за бесплатно. Двигаемся далее.

4. PDF Creator Pilot

Стоимость: от 450$ без исходников до 9950$ (!!!) с исходниками
5-XE4
http://www.colorpilot.com/pdflibrary.html#download

Библиотека стоимостью почти как Delphi XE3 Professional…Ну да ладно, посмотрим, что представляет из себя эта библиотечка.

Возможности, заявленные разработчиками:

  • Расширенный набор методов и свойств для легкого создания PDF;
  • Чтение и слияние существующих PDF-документов;
  • Добавление и удаление страниц PDF-документа;
  • Поддержка юникода;
  • Создание водяных знаков для каждой страницы;
  • Добавление эскизов для PDF-документа;
  • Использование и встраивание шрифтов (TrueType, OpenType, Type1 и т.д.);
  • Создание интерактивных PDF-документов, используя JavaScript и гиперссылки;
  • Поддержка интерактивных элементов AcroForm: текстовые поля ввода, кнопки, радио-кнопки, выпадающие списки, флажки;
  • шифрование и защита паролем созданных PDF-документов;
  • Создания и управление содержанием документа;
  • Доступ к HDC для рисования на PDF-страницах с помощью WinAPI функций.
  • Использование изображений в различных форматах (JPEG, TIFF, PNG, BMP, GIF);
  • Создание и использование аннотаций;
  • Создание PDF-документов на диске или в памяти;
  • Извлечения текста из PDF документов;

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

После установки запускаем Delphi (в моем случае — это Delphi XE3) и переходим в меню:

Component — Import Component — Import Type Library

В списке ищем библиотеку

Импортируем, создаем новый проект и подключаем в uses модуль PDFCreatorPilotLib_TLB.

Теперь можем протестировать работу библиотеки на каком-нибудь живом примере. Вначале попробуем сгенерировать документ с простым текстом:

procedure TForm1. Button1Click (Sender: TObject ) ; var fnt: integer ; begin { initialization } PDF : = TPDFDocument4. Create (nil ) ; PDF. SetLicenseData ("demo" , "demo" ) ; fnt : = pdf. AddFont ("Verdana" , false , false , false , false , fcANSI) ; PDF. UseFont (fnt, 14 ) ; PDF. ShowTextAt (20 , 40 , "HELLO, PDF!" ) ; { save } PDF. SaveToFile ("HelloPDF.pdf" , true ) ; PDF. Destroy ; end ;

Запускаем приложение и смотрим на созданный PDF-документ:

Теперь попробуем записать русский текст в файл:

procedure TForm1. Button1Click (Sender: TObject ) ; begin { initialization } {...} PDF. ShowTextAt (20 , 40 , "Привет, PDF!" ) ; { save } {...} end ;


Может где-то в свойствах класса надо что-то настроить, вызвать какой-нибудь метод, который включит-таки поддержку юникода, НО за такие бабки хотелось бы получить библиотеку, которая заработает сразу «из коробки» без всяких заморочек с настройками и подкрутками…Кстати, метод для вставки ссылки (AddHyperLink) тоже не сработал — документ остался девственно чист несмотря на то, что ссылка якобы вставилась. Но, надо отдать должное, попытка вставить в новый документ уже ранее созданную PDF-ку — сработал на ура — документ вставился на новую страничку как надо, без косяков.

5. PDFtoolkit VCL v4.0.1.293

Стоимость: от 349$ без исходников до 499$ с исходниками
Поддерживаемые версии Delphi: 6-XE4
Страница загрузки trial-версии: http://www.gnostice.com/PDFtoolkit_VCL.asp?show=downloads

Про эту библиотеку компонентов от Gnostice имеется довольно много положительных отзывов в Сети.

По возможностям PDFtoolkit практически 1 в 1 соответствует PDF Creator Pilot, т.е. умеет «склеивать» PDF-ки, распознавать текст, вставлять ссылки, проводить поиск в документах и т.д. Но, в отличие от PDF Creator Pilot, PDFtoolkit if gtPDFDocument1. IsLoaded then begin gtPDFDocument1. TextOut ( "Привет, Мир!" , IntToStr (gtPDFDocument1. PageCount ) , //диапазон страниц в который будет вставлен текст gtPDFDocument1. GetPageSize (gtPDFDocument1. PageCount , muPixels) . Width / 2 , //вставляем текст в центр страницы gtPDFDocument1. GetPagesize (gtPDFDocument1. PageCount , muPixels) . Height / 2 ) ; {сохраняем документ} gtPDFDocument1. SaveToFile ("modified_doc.pdf" ) ; end ; finally gtPDFDocument1. Free end ;

Конечно, применительно к моей задаче PDFtoolkit оказывается практически бесполезным, т.к. меня интересует именно создание с нуля PDF-ки, но для тех, кто пишет собственный вьювер PDF-ок, думаю, эта библиотека должна подойти более чем.

Итак, что имеем в итоге. Есть 5 различных решений для создания и работы с PDF-документами в Delphi. Каждое решение имеет как свои достоинства (бесплатность, «навороченность»), так и недостатки (конская стоимость, проблемы с юникодом и т.д.). Применительно к моей задаче надо всеми решениями придётся «работать напильником». С другой стороны, в Сети есть куча платных и бесплатных сервисов для генерации PDF, но, памятуя о том, что такие сервисы имеют свойство вдруг брать и умирать, то как-то не тянет с ними связываться. Есть, конечно, ещё одно решение — самописное и не совсем в тему Delphi, но об этом как-нибудь в следующий раз, а пока пойду подумаю что же делать с клиентом для DelphiFeeds


До встречи в онлайне!

Существует два основных способа организовать в программе просмотр документов в формате PDF.

  • Использование технологии ActiveX;
  • Использование специальных компонентов.

Компоненты для работы с PDF есть не во всех выпусках Delphi и чаще всего их нужно искать и устанавливать дополнительно. В тоже время, для использования ActiveX необходимо всего лишь наличие на компьютере пользователя соответствующего программного обеспечения. В данном случае Adobe Acrobat Reader (бесплатная) или Adobe Acrobat.

Работу с ActiveX можно условно разделить на два этапа.

  • Импорт библиотеки типов или компонентов;
  • Собственно, использование импортированных средств в приложении.
Импорт компонентов ActiveX

Для импорта компонентов используется команда «Component» – «Import Component» в главном меню Delphi. Сам процесс импорта реализован в виде мастера.

На первой странице мастера выбираем «Import ActiveX Control».

После этого необходимо решить, как поступить с импортируемыми компонентами. Так как планируется их использование в виде компонентов Delphi, выбираем «Install to New Package».

Теперь остаётся только указать имя вновь создаваемого пакета.

После нажатия на кнопку «Finish», компоненты ActiveX для просмотра PDF будут импортированы в Delphi.

Просмотр PDF в приложении

Для создания просмотра PDFфайлов в приложении средствами ActiveX необходим компонент TAcroPDF. Процесс его импорта подробно описан выше.

В качестве примера создадим следующее приложение. Поместим на форму компоненты TAcroPDF, TOpenDialog и TButton как показано на скриншоте ниже.

При нажатии на кнопку вызываем диалог открытия файла для выбора файла PDF для просмотра.