Pull to refresh
2510.55
RUVDS.com
VDS/VPS-хостинг. Скидка 15% по коду HABR15

Создание PDF размером с Германию

Level of difficultyEasy
Reading time7 min
Views25K
Original author: Alexwlchan

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

Подобное утверждение появилось на просторах интернета ещё в 2007 году. Этот твит является характерным примером постов с аналогичным заявлением, в которых оно преподносится как твёрдый факт без каких-либо подтверждающих свидетельств или объяснений. То есть мы должны просто принять, что один PDF может покрыть лишь около половины площади Германии, и нам никак не объясняют, почему его магический предел составляет 381 километр.

Тут мне стало интересно – а создавал ли кто-нибудь такой большой PDF? Насколько это сложно? А можно ли сделать документ ещё больше?

Несколько лет назад я из праздного любопытства немного поигралась с PostScript, предшественником PDF, и это оказалось очень увлекательным! Ранее мне не доводилось изучать внутреннее устройство PDF, так что здесь у меня возник для этого хороший повод.

Приступим!

▍ Откуда происходит это утверждение?


Такие посты зачастую сопровождаются репликами вроде «ну, вообще-то», когда люди в ответах поясняют, что это ограничение конкретного PDF-ридера, а не самого PDF. Обычно они ссылаются на что-нибудь вроде статьи Википедии, в которой сказано:

Сам по себе формат не ограничивает размера страниц. Однако Adobe Acrobat накладывает ограничение в 15,000,000 х 15,000,000 дюймов, то есть 225 триллионов дюймов2 (145,161 км2)[2].

По ссылке вы найдёте спецификацию для PDF 1.7, где в приложении даётся более подробное пояснение (выделение моё):

До версии PDF 1.6 размер базовой единицы пользовательского пространства был установлен равным 1/72 дюйма. В Acrobat Reader до версии 4.0 минимальный допустимый размер страницы составляет 72 х 72 единицы (1 х 1 дюйм); максимальный же размер равен 3240 х 3240 единиц (45 х 45 дюймов). В версиях Acrobat 5.0 и старше минимальный допустимый размер составляет уже 3 х 3 единицы (примерно 0,04 на 0,04 дюйма), а максимальный – 14,400 х 14,400 единиц (200 х 200 дюймов).

Начиная с PDF 1.6, размер базовой единицы пользовательского пространства можно установить через запись UserUnit словаря страницы. Acrobat 7.0 поддерживает максимальное значение UserUnit равное 75,000, обеспечивая максимальный размер страницы 15,000,000 дюймов (14,400 * 75,000 * 1/72). Минимальное же значение UserUnit равно 1.0 (по умолчанию).

Пятнадцать миллионов дюймов – это ровно 381 километр, что соответствует числу, указанному в оригинальном твите. И хотя этот лимит впервые появился в PDF 1.6, он относится к 7-й версии Adobe Acrobat. Похоже, что изначальное утверждение основывается именно на этом факте.
А что, если создать PDF, превышающий эти «максимальные» значения?

▍ Внутренняя структура PDF


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

Я нашёл отличную статью, объясняющую внутреннюю структуру PDF. Её содержание вместе с небольшим допросом ChatGPT помогло мне достаточно разобраться, чтобы начать писать простые файлы вручную.

Я знаю, что по факту возможности PDF очень обширны, так что сформировавшееся у меня представление, пожалуй, будет сильно упрощённым:


Начинаются PDF-документы всегда с номера версии(%PDF-1.6), а завершаются маркером конца файла (%%EOF).

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

За этим списком идёт xref, таблица перекрёстных ссылок. Это таблица поиска объектов, которая указывает на все объекты в файле, сообщая, что объект 1 находится в 10 байтах от начала, объект 2 – в 20 байтах, объект 3 – в 30 байтах и так далее. Просматривая эту таблицу, PDF-ридер понимает, сколько объектов находится в файле и где именно.

Сегмент trailer содержит метаданные документа, например, количество его страниц и отметку о наличии шифрования.

Наконец, значение startxref представляет указатель на начало таблицы xref. Отсюда PDF-ридер начинает чтение документа: он проходит от конца файла, пока не находит значение startxref, после чего считывает таблицу xref, узнавая всё о содержащихся в нём объектах.

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

%PDF-1.6

% Первый объект.  Начало каждого объекта обозначено так:
%
%     <номер объекта> <номер поколения> obj
%
% (Номер поколения используется для версионирования и обычно представлен 0).
%
% Первым идёт объект 1 – он начинается как `1 0 obj`. Второй объект
% будет начинаться с `2 0 obj`, затем идёт `3 0 obj`и так далее. 
% Конец каждого объекта обозначается как `endobj`.
%
% Это объект «stream», отрисовывающий фигуру. Сначала я указываю
% его длину (54 байта).  Затем выбираю цвет в виде RGB-значения
% (`1 0 0 RG` = красный). После устанавливаю толщину линии (`5 w`) 
% и, наконец, задаю для отрисовки этого квадрата координаты:
%
%     (100, 100) ----> (200, 100)
%                          |
%     [s = start]          |
%         ^                |
%         |                |
%         |                v
%     (100, 200) <---- (200, 200)
%
1 0 obj
<<
	/Length 54
>>
stream
1 0 0 RG
5 w
100 100 m
200 100 l
200 200 l
100 200 l
s
endstream
endobj

% Второй объект.
%
% Это будет объект «Page», определяющий одну страницу. Он содержит
% один объект: объект 1, то есть красный квадрат. Это строка `1 0 R`.
%
% «R» означает «Reference» (ссылка), а `1 0 R` гласит «Смотри на   
% объект 1 с номером поколения 0» -- а объект 1 – это красный 
% квадрат.
% Он также указывает на объект «Pages», содержащий информацию обо
% всех страницах в PDF-документе – это ссылка `3 0 R`.
2 0 obj
<<
	/Type /Page
	/Parent 3 0 R
	/MediaBox [0 0 300 300]
	/Contents 1 0 R
>>
endobj

% Третий объект.
%
% Это объект «Pages», который содержит информацию о разных страницах. 
% `2 0 R` является ссылкой на объект «Page», определённый выше.
3 0 obj
<<
	/Type /Pages
	/Kids [2 0 R ]
	/Count 1
>>
endobj

% Четвёртый объект.
%
% Это объект «Catalog», формирующий основную структуру документа.
% Он указывает на объект «Pages», содержащий информацию о разных
% страницах – это ссылка `3 0 R`.
4 0 obj
<<
	/Type /Catalog
	/Pages 3 0 R
>>
endobj

% Таблица xref. Это таблица поиска для всех объектов. 
%
% Не уверена, для чего нужна первая запись, но она, похоже, важна.
% Остальные записи соответствуют созданным мной объектам.
xref
0 4
0000000000 65535 f
0000000851 00000 n
0000001396 00000 n
0000001655 00000 n
0000001934 00000 n

% Хвост (trailer). Он содержит метаданные PDF-документа. В данном
% случае в нём находятся две записи, сообщающие нам, что:
%
%   - В таблице ‘xref’ присутствует 4 записи.
%   - Корнем документа является объект 4, то есть «Catalog».
%
trailer
<<
	/Size 4
	/Root 4 0 R
>>

% Маркер startxref сообщает, что таблица xref находится в 2196 байтах 
% от начала файла.
startxref
2196

% Маркер конца файла.
%%EOF

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

Довольно быстро стало ясно, почему никто не пишет PDF-файлы вручную – очень много волокиты с переделкой таблиц поиска. Но я рада, что проделала этот эксперимент. Управление всеми объектами документа и их ссылками позволило мне понять основную модель PDF. Я открыла несколько «реальных» документов, созданных другими приложениями, где обнаружила намного больше типов объектов – но сейчас я хотя бы могла отчасти разобраться, что там к чему.

Теперь оставалось понять, как применить этот новообретённый навык ручного редактирования PDF-документов для создания их гигантских образцов?

▍ Изменение размера страницы: /MediaBox и /UserUnit


Внутри PDF-документа размер каждой страницы устанавливается в отдельных объектах «Page», что позволяет создавать страницы разных размеров. Мы это уже видели:

<<
	/Type /Page
	/Parent 3 0 R
	/MediaBox [0 0 300 300]
	/Contents 1 0 R
>>

Здесь параметр MediaBox задаёт ширину и высоту страницы – в данном случае квадрат 300 х 300 единиц. По умолчанию размер единицы составляет 1/72 дюйма, значит, одна сторона страницы будет равна 300 × 1/72 = 4,17 дюйма. И если открыть документ в Adobe Acrobat, то эти расчёты подтвердятся:


Изменив значение MediaBox, мы можем увеличить страницу. Например, если изменить его на 600 600, Acrobat сообщит, что теперь размер страницы равен 8,33 x 8,33 in. Прекрасно!

Это значение можно увеличить вплоть до 14400 14400, максимума, который допускает Acrobat. Теперь в свойствах документа мы видим размер страницы 200.00 x 200.00 in. (При попытке превысить этот лимит Acrobat выдаёт предупреждение).

Но 200 дюймов это далеко не 381 километр – всё дело в том, что мы используем предустановленный размер единицы 1/72 дюйма. Можно увеличить этот размер, подключив значение /UserUnit. К примеру, установка этого значения на 2 удвоит размер страницы по обоим направлениям.

<<
	/Type /Page
	/Parent 3 0 R
	/MediaBox [0 0 14400 14400]
	/UserUnit 2
	/Contents 1 0 R
>>

Теперь Acrobat сообщает, что размер страницы равен 400.00 x 400.00 in.

Если задрать UserUnit до максимума в 75 000, Acrobat покажет, что размер страницы составляет 15,000,000.00 x 15,000,000.00 in – 381 км вдоль каждой из сторон, и это уже будет соответствовать изначальному заявлению. Если вам любопытно, можете скачать этот PDF.

При попытке создать страницу большего размера путём увеличения значения MediaBox или UserUnit, Acrobat просто это проигнорирует. Он продолжит говорить, что размер страницы равен 15 миллионам дюймов, даже если метаданные будут сообщать иное. (И если вы установите UserUnit больше 75000, это произойдёт втихую – без какого-либо предупреждения или ошибки, указывающей на превышение максимального размера страницы).

Но это вряд ли можно считать проблемой – не думаю, что значение UserUnit широко используется на практике. Я нашла один ответ на Stack Overflow, подтверждающий эту догадку, и ни одного реального примера онлайн. Встроенное в macOS приложение Preview даже не поддерживает это значение – просто его игнорирует и рассматривает все PDF-документы так, будто единица пользовательского пространства равна 1/72 дюйма.

Но, в отличие от Acrobat, приложение Preview не имеет максимального предела для значения MediaBox. Я без проблем смогла указать ширину в виде 1, сопровождаемой 12 нулями:


Если вам интересно, эта ширина примерно соответствует расстоянию между Землёй и Луной. Мне понадобится линейка, чтобы это проверить, но я уверена, что такой документ точно шире Германии.

Эксперимент можно было продолжить, что я и сделала. В конечном итоге я получила документ, размер которого по описанию Preview оказался больше всей Вселенной – примерно 37 триллионов световых лет в квадрате. Конечно, он преимущественно пустой, но и Вселенная тоже. Если вы захотите поиграться с этим файлом – он к вашим услугам здесь.

Только не пытайтесь его распечатать.

Скидки, итоги розыгрышей и новости о спутнике RUVDS — в нашем Telegram-канале ?
Tags:
Hubs:
Total votes 93: ↑90 and ↓3+120
Comments57

Articles

Information

Website
ruvds.com
Registered
Founded
Employees
11–30 employees
Location
Россия
Representative
ruvds