| ||||||||||||||||
![]() | ||||||||||||||||
| ||||||||||||||||
![]() | ||||||||||||||||
| ||||||||||||||||
![]() |
Очерк по поводу создания PDF-файлов В последнее время на просторах интернета обнаружилось очень много PDF converter'ов, reader'ов и write'ов. И подавляющее большинство из них предлагается за деньги. Сама программа от 10$ до 300$. А уж исходный код за гораздо большие деньги цена начинается от 200$ а в одном месте (заинтересовавшись этим полазил по инету) аж за 900 евро. Данная проблемма меня заинтересовала в плане программирования и вот результаты довожу до вашего сведения. (Данные результаты получены мною при изучении внутренностей PDF файла, когда открываешь его в total commander через F3) Обычный PDF файл состоит из четырех частей <PDF file> := <header> <body> <cross-reference table> <trailer> Что такое такое <header>? Это обычное упоминание о версии PDF specification. Которое присутствует в первой строке PDF файла. Например "%PDF-1.3" В седьмой версии акробата которая вышла где то в начале лета этого года, этот номер "%PDF-1.7", но это не версия продукта, это версия именно спецификации. Второй строкой PDF идет небольшая аброкадабра (видимо предназначена для дальнейшего использования) " %вгПУ" Все с первой частью PDF разобрались. Что из себя представляет вторая часть которая называется <body>? Ответ очень простой: это последовательность объектов, описание которых как и хедера представлены в текстовом виде. Каждый объект это текстовой фрагмент с порядковым номером в имени например "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 "главных" объекта. Давайте я их перечислю по типам:
И несколько "второстепенных" Разберем объект страница:
Содержимое страницы: Объект "stream" 4 0 obj << /Length 305 >> 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 -
10 782 TD - это цифры откуда начинается данная строка (отсчет производиться от левого верхнего угла) Tj - это оператор перевода на новую строку Ну а в круглых скобках наш текст Кодированный поток я сдесь не объясняю. Он основан на алгоритмах RC4, RC5, MD5. Что такое объект Font 12 0 obj << /Type /Font /Subtype /Type1 /Name /F7 /BaseFont /Courier-Oblique /Encoding /WinAnsiEncoding >>
PDF поддерживает несколько видов фонтов. Они перечисленны ниже
Честно говоря, я не разбирался с Type 3, TrueType,
including subsets, Type 0 ничего по ним сказать не могу. 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 << /F1 6 0 R /F2 7 0 R /F3 8 0 R /F4 9 0 R /F5 10 0 R /F6 11 0 R /F7 12 0 R /F8 13 0 R /F9 14 0 R /F10 15 0 R /F11 16 0 R /F12 17 0 R /F13 18 0 R /F14 19 0 R >> endobj Это объект с названиями кодовых имен для фонтов первого типа. По этому кодовому имени можно легко получить сам объект фонт. 6 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> ВСЕ: то есть минимальное <Body> Состоит из следующих объектов: "catalog" , "pages", "page", "Resources" (опиционально может присутствовать, как поле в объекте страница), нетипизированный объект "stream", группа объектов "font" Что такое <cross-reference table>? 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 n0 27 Эти цифры обозначают следующее :
Первый элемент таблицы всегда иммет вид "XXXXXXXXXX 65535 f" где X это цифра, а 65535 это значение по умолчанию для первого элемента в таблице. Символ "f" обозначает "free", то есть объект не используется Ссылки на объекты, которые используются, в конце имеют символ "n" Разберем элемент данной таблицы.
Вторые пять цифр — это номер генерации файла. Если файл только что создан, то они всегда нули. Если файл модифицируется, то это число увеличивается на единицу. То есть, 0000000024 00001 f Канонический, только что созданный PDF файл, имеет только одну таблицу. Но, если файл редактируется, то таких таблиц может быть очень много. Взаимосвязь таблиц осуществляется при помощи последнего элемента <trailer> и кодового слова startxref Канонический, только что созданный PDF файл, имеет только одну таблицу, после таблицы идет элемент trailer А после трайлера идет кодовое слово startxref, указывающее на смещение от начала файла до начала таблицы, вот пример. trailer << /Size 3 >> startxref 173 %%EOF Это значит, что через 173 байта от начала документа, будет присутствовать кодовое слово xref. Но, если файл был отредактирован, то последний в файле трайлер будет иметь вид: xref 0 3 0000000000 65535 f 0000003609 00000 n 0000003832 00000 n trailer << /Size 3 /ID[<7a15ab3ed3999575ff2f3034104a82c1><7a15ab3ed3999575ff2f3034104a82c1>] >> 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 << /Size 19 /Info 1 0 R /Root 4 0 R /Prev 3896 /ID[<7a15ab3ed3999575ff2f3034104a82c1><7a15ab3ed3999575ff2f3034104a82c1>] >> startxref 567 %%EOF Данное поле /Prev 3896 указывает нам на предыдущую таблицу, а ссылка startxref 567 указывает на следующую таблицу и так практически до бесконечности, пока в очередном поле startxref мы не увидим 0. Это значит, мы прочитали все таблицы. |
|
![]() | ||||||||||||||||
| ||||||||||||||||
![]() | ||||||||||||||||
|