Технічна стаття

Структура файлу PDF: Як насправді працює формат

PDF не є форматом документа в тому ж сенсі, що й Word або RTF. Ті формати зберігають послідовність вмісту, який рендерер інтерпретує в момент відображення, тому вивід залежить від того, які шрифти та механізм верстки виявляться присутніми. PDF зберігає результат цього процесу: точні інструкції рендерингу, програми шрифтів, стиснуті потоки зображень і граф об'єктів, який об'єднує їх у самодостатній опис кожної сторінки. Файл містить достатньо інформації, щоб ідентично відтворити кожну сторінку на будь-якому сумісному рендерері, що є водночас його головною метою проєктування та джерелом більшості складнощів, з якими ви стикаєтеся, коли намагаєтеся генерувати, розбирати або модифікувати його програмно

Об'єктна модель

Кожен PDF — це колекція пронумерованих об'єктів. Об'єкт може бути логічним (boolean), цілим (integer), дійсним числом (real), ім'ям (name), рядком (string), масивом (array), словником (dictionary), потоком (stream) або нульовим (null). Майже все цікаве є словником, який є набором пар ключ-значення, де ключі — це імена, а значення — це будь-які інші типи об'єктів, зокрема посилання на інші об'єкти за номером і лічильником генерації. Потік — це словник, за яким слідує послідовність байтів, зазвичай стиснута

Словник каталогу є коренем. Він вказує на дерево сторінок (page tree), яке організовує словники сторінок у збалансовану деревоподібну структуру, а не у плоский список, тому перехід на 5 000-ну сторінку 10 000-сторінкового документа не вимагає обходу кожного попереднього дескриптора сторінки. Кожен словник сторінки посилається на свої потоки вмісту (одну або кілька послідовностей операторів опису сторінки), свій словник ресурсів (який своєю чергою посилається на дескриптори шрифтів, колірні простори та XObjects зображень) і свій медіабокс (media box — координатний простір, у якому знаходиться сторінка). Початок координат (origin) знаходиться в лівому нижньому куті, а позитивна вісь Y спрямована вгору, в одиницях 1/72 дюйма

Наприкінці файлу розміщена таблиця перехресних посилань (cross-reference table), яка зіставляє кожен номер об'єкта з його зміщенням у байтах (byte offset) у файлі. Саме це забезпечує прямий доступ (random access): програма перегляду спочатку зчитує таблицю перехресних посилань, а потім переходить безпосередньо до тих об'єктів, які їй потрібні. У PDF 1.5 з'явилися потоки перехресних посилань, які стискають таблицю в потоковий об'єкт і пакують пов'язані об'єкти в потоки об'єктів, що помітно зменшує розмір файлу для документів із великою кількістю дрібних об'єктів

Потоки вмісту (Content streams) та графічна модель

Візуальний вміст сторінки знаходиться в одному або кількох потоках вмісту. Кожен потік — це послідовність операторів PDF упереміш із їхніми операндами. Текстовий оператор BT починає текстовий об'єкт, Tf вибирає шрифт і розмір зі словника ресурсів, Td розміщує текстовий курсор, Tj або TJ малює рядок, а ET закриває текстовий об'єкт. Векторна графіка дотримується подібного шаблону: m встановлює початкову точку контуру, l додає сегмент лінії, c додає криву Безьє, а f або S заповнює або обводить (strokes) контур

Графічний стан (graphics state) керує всім, що відбувається між операторами: поточною матрицею трансформації (current transformation matrix), шириною лінії, колірним простором, кольором заповнення, кольором обведення (stroke) та контуром обрізання (clipping path). Оператори, як-от q і Q, поміщають (push) і витягують (pop) графічний стан зі стека, і саме так PDF реалізує локальні перетворення координат та тимчасові перевизначення (overrides) стану без впливу на контекст навколо них. Форми XObject узагальнюють це: самодостатній потік вмісту зі своїм власним словником ресурсів, який можна намалювати на сторінці в довільних позиціях і масштабах за допомогою одного оператора Do

Вбудовування шрифтів та вилучення тексту (text extraction)

PDF може посилатися на шрифти за назвою та покладатися на те, що програма перегляду замінить їх чимось, але на практиці будь-який документ, яким ви маєте намір поділитися, має містити вбудовані дані шрифтів. Шрифт Type 1 або TrueType/OpenType, вбудований у PDF, містить словник дескриптора шрифту, що вказує на потік файлу шрифту. Для шрифтів TrueType цей потік містить двійкову програму шрифту (binary font program); для Type 1 — це дані PFB. Підмножини (Subsetting), що робить кожен серйозний генератор PDF, видаляє гліфи, на які не посилається документ, зберігаючи керовані розміри файлу навіть для великих шрифтів Unicode

Вилучення тексту (Text extraction) — це те, де вбудовування шрифтів завдає зворотного удару. Візуальне представлення символу визначається гліфом у вбудованій програмі шрифту. Значення Unicode цього символу визначається потоком ToUnicode CMap, прикріпленим до словника шрифту. Якщо ToUnicode CMap відсутній або неправильний, програма перегляду PDF може відобразити розбірливий текст, але не може витягти його як осмислений Unicode, і саме тому копіювання/вставка з деяких PDF-файлів дає сміття. Тегований PDF (Tagged PDF) (ISO 32000 §14.8) додає другий шар: дерево логічної структури (logical structure tree), яке зіставляє вміст сторінки з семантичними ролями документа, як-от абзаци, заголовки та комірки таблиці. Програми зчитування з екрана (screen readers) і механізми переформатування (reflow engines) використовують дерево структури, а не необроблений порядок потоку вмісту, що пояснює, чому візуально добре зверстаний PDF все ще може бути недоступним, якщо тегування відсутнє або помилкове

Поступові оновлення (Incremental updates) та цифрові підписи

Коли ви зберігаєте зміни в наявному PDF-файлі без переписування його з нуля, нові об'єкти додаються після тіла оригінального файлу разом із новим розділом перехресних посилань і новим словником трейлера. Оновлений трейлер вказує на нові дані перехресних посилань, а замінені об'єкти залишаються у файлі, але на них просто не посилається новий ланцюжок перехресних посилань. Це і є поступове оновлення, і воно має два важливі наслідки

По-перше, файл збільшується з кожним циклом збереження. Документ, який неодноразово редагувався і зберігався, накопичує шари застарілих об'єктів. Інструменти на зразок QPDF можуть лінеаризувати або стиснути-і-переписати файл, щоб повернути цей простір, але за замовчуванням відбувається накопичення. По-друге, цифрові підписи залежать від поступових оновлень для своєї моделі цілісності (integrity model). Підпис за стандартом ISO 32000 охоплює діапазон байтів файлу, як правило, все, крім місця для самого значення підпису. Будь-які зміни після підписання, які з'являються як додаткові поступові оновлення, видно програмі зчитування, що виконує валідацію (validating reader), як модифікації, зроблені після підписання, і це саме той контрольний слід (audit trail), який вам потрібен. Однак це також означає, що певні модифікації, наприклад, додавання підпису погодження або заповнення полів форми, чітко дозволені стандартом без визнання оригінального підпису недійсним, за умови, що зміни відповідають налаштуванням дозволів документа (ISO 32000-2 §12.7.6). Зміна, яка виходить за межі цих дозволів, позначається як несанкціонована (unauthorized). Правильне розуміння цієї відмінності має значення, коли ви генеруєте документи, які будуть зустрічно підписані на наступних етапах (downstream)

Рівні відповідності (Conformance levels) та лінія ISO 32000

PDF розпочався як пропрієтарний формат Adobe у 1993 році, увібрав у себе модель формування зображень (imaging model) PostScript і протягом п'ятнадцяти версій накопичував функції: шифрування у 1.1, інтерактивні форми у 1.2, цифрові підписи та логічну структуру у 1.3, прозорість у 1.4, потоки об'єктів у 1.5, AES-шифрування у 1.6. Adobe передала PDF 1.7 до ISO у 2007 році, і результатом став ISO 32000-1:2008. ISO 32000-2:2020 охоплює PDF 2.0, який посилив кілька недостатньо визначених (underspecified) областей, переглянув формування ключа AES-256 (ревізія 6 замінила ревізію 5) і додав чітку підтримку асоційованих файлів і мультимедіа (rich media)

Підстандарти (substandards) походять із тієї ж бази. PDF/A (ISO 19005) обмінює функції на архівну стабільність: жодного шифрування, жодних залежностей від зовнішнього вмісту, усі шрифти вбудовано, колірні простори незалежні від пристрою (device-independent), необхідні метадані XMP. PDF/A-1 базується на PDF 1.4, PDF/A-2 — на PDF 1.7, PDF/A-3 дозволяє вбудовувати файли будь-якого формату. PDF/X (ISO 15930) — це підмножина для друку на виробництві (print production subset): наміри виведення (output intents), обрізи та межі обрізання (bleed and trim boxes), жодної прозорості у старих рівнях відповідності. PDF/UA (ISO 14289) вимагає теговану структуру, зіставлення Unicode (Unicode mappings) і метадані про мову для доступності (accessibility). Це не конкуруючі формати; вони є набором додаткових обмежень поверх базового PDF, і один файл може одночасно відповідати більш ніж одному, якщо ці обмеження не конфліктують

Для кожного, хто пише код для створення або обробки PDF, практичною основою (baseline) є ISO 32000-2 з уважним ставленням до розділів, що охоплюють модель перехресних посилань (§7.5), графічний стан (§8.4), оператори стану тексту (§9.3), дескриптори шрифтів і ToUnicode (§9.6 та §9.10), інтерактивні форми (§12.7) і цифрові підписи (§12.8). Стандарт довгий, але більшість програмної роботи з PDF-файлами знову і знову зачіпає вузьку його частину. Розуміння об'єктної моделі (object model) та механізму перехресних посилань (cross-reference mechanism) є відправною точкою; усе інше — це спеціалізація від неї