Что такое поток в PDF
Поток — объект, у которого кроме словаря есть тело: цепочка байтов между ключевыми словами stream и endstream. В потоках лежит всё тяжёлое — команды рисования страницы, файлы встроенных шрифтов, изображения, вложения, цветовые профили. Словарь описывает тело: сколько в нём байтов до распаковки и каким фильтром оно упаковано. Всё остальное в документе — числа, имена, словари и ссылки — весит на порядки меньше и лежит открытым текстом.
Коротко о цифрах
- Границы тела
- между ключевыми словами stream и endstream
- Обязательное поле словаря
- длина тела в байтах до распаковки
- Фильтры изображений
- DCTDecode, JPXDecode, CCITTFaxDecode, JBIG2Decode
- Худшая находка при починке
- поток заменён пустым — страница откроется без содержимого
- Что делает оптимизация
- перекладывает объекты, не перекодируя пиксели
Что именно хранится в потоках
- Содержимое страницы: последовательность команд «поставить глиф», «залить прямоугольник», «нарисовать кривую».
- Встроенный шрифт — файл TrueType или Type 1 целиком либо его сабсет из использованных глифов.
- Изображение: пиксели в том виде, в каком их упаковал фильтр, вместе с шириной, высотой и числом бит на канал.
- Цветовой профиль ICC, по которому просмотрщик понимает, что означают числа цвета.
- Вложенный файл — PDF разрешает носить внутри себя что угодно, от таблицы до архива.
- Служебные объекты: таблица перекрёстных ссылок нового образца и контейнеры для мелких объектов.
Фильтры: чем упаковано тело
- FlateDecode — тот же алгоритм, что в zip. Им упакованы команды страниц, шрифты и вообще всё, что не картинка.
- DCTDecode — внутри лежит готовый JPEG. Просмотрщик отдаёт его декодеру как есть, не переупаковывая.
- JPXDecode — JPEG 2000, обычная находка в медицинских выгрузках и цветных архивных сканах.
- CCITTFaxDecode — факсовое сжатие двухцветных страниц, наследство сканеров и МФУ.
- JBIG2Decode — двухцветный формат, который узнаёт повторяющиеся фрагменты и хранит их один раз.
- ASCIIHexDecode и ASCII85Decode — обёртки для передачи двоичных данных текстом, сегодня редкость.
Когда заявленная длина не совпадает с телом
Поле длины — это обещание: столько байтов нужно прочитать после ключевого слова stream. Если обещание нарушено, разбор сходит с рельсов, потому что следующий объект начинается не там, где его ждут. Восстановление умеет искать endstream вручную и вычислять настоящую длину — в отчёте это отражается как восстановленная длина потока.
Худший исход — когда данные внутри повреждены настолько, что распаковать их нечем. Тогда поток заменяется пустым: файл после этого открывается, но соответствующая страница выходит без содержимого. Такой результат хуже исходного по содержанию, и честный инструмент обязан сказать об этом отдельной строкой, а не спрятать среди прочих исправлений.
Что с потоками делает оптимизация
Оптимизация для веба переписывает документ так, чтобы объекты первой страницы легли в начало файла, а следом легли таблицы подсказок с диапазонами байтов для остальных страниц. Браузер, который запрашивает файл по частям, показывает первую страницу, не дождавшись конца загрузки. Признак того, что это уже сделано, ищется прямым просмотром первых двух килобайт файла.
Чего оптимизация не делает — так это не трогает пиксели. Потоки изображений переносятся байт в байт, поэтому подшивка сканов после неё весит примерно столько же. Уменьшить вес картинок можно только их перекодированием, а это уже сжатие с растеризацией, у которого своя цена — потеря текстового слоя.
Частые вопросы
Почему PDF нельзя просто открыть блокнотом и почитать?
Каркас документа действительно текстовый, но весь смысл лежит в потоках, а они упакованы. В блокноте вы увидите словари, имена объектов и двоичную кашу вместо текста страницы.
Сжимает ли веб-оптимизация сами изображения?
Нет. Она меняет порядок объектов в файле и добавляет таблицы подсказок, а потоки картинок копирует без изменений. Если документ состоит из сканов, его вес после оптимизации останется прежним.
Почему одинаковый на вид текст занимает то два, то двести килобайт?
Разница в шрифтах. Страница с невстроенным стандартным шрифтом — это пара килобайт команд, а тот же текст с полностью встроенной гарнитурой тащит за собой файл шрифта целиком, иногда во всех начертаниях сразу.
Можно ли вытащить картинку из потока без потери качества?
Если она упакована как JPEG, внутри лежит готовый файл, и грамотный извлекатель отдаст его байт в байт. Растеризация страницы вместо этого перерисует картинку заново и добавит к её артефактам свои.