Бортовой журнал Ктулху

Какой максимальный размер может быть у PDF-файла? Больше Германии.

Говорят, когда коту нечем заняться, он лижет себе яйца. Когда программисту нечем заняться, он делает самый большой в мире PDF-файл. Мне попался в интернете пост, где утверждалось, что самый большой PDF-файл размером с половину Германии. Один человек доказал, что он может быть намного больше.

Однажды пользователь с псевдонимом Alexwlchan сидел дома и пил чай с печеньками. Я, конечно, немного добавил художественного вымысла, не знаю чем он занимался, возможно кодил на питоне и было скучно.

Дальше перевод от первого лица.

 

18942555

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

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

Я начал задумываться: кто-нибудь уже создавал такой большой PDF? Насколько это сложно? Можно ли создать PDF еще большего размера?

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

Fz4iNokXwAAv2hD

 

Давайте разберемся откуда взялось это утверждение?

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

Размеры страниц не ограничены самим форматом. Однако Adobe Acrobat налагает ограничение в 15 миллионов на 15 миллионов дюймов, или 225 триллионов кв. дюймов (145 161 км²).

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

В версиях PDF ранее PDF 1.6 размер единицы пользовательского пространства по умолчанию фиксирован и составляет 1/72 дюйма. В версиях Acrobat ранее 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 (по умолчанию).

15 миллионов дюймов — это ровно 381 километр, что соответствует числу в оригинальном твите. И хотя этот предел впервые появился в PDF 1.6, это "версия 7" Adobe Acrobat. Вероятно, именно отсюда и взялось первоначальное утверждение.

Что, если мы создадим PDF, который превышает эти "максимальные" значения?

unnamed 1

 

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

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

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

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

2024 07 09 21.09.13

Картинка, которую он представил

Начало и конец PDF-файла всегда одинаковы: номер версии (%PDF-1.6) и маркер конца файла (%%EOF).

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

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

Трейлер содержит некоторую метаинформацию о документе в целом, например, количество страниц и информацию о шифровании.

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

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

 

%PDF-1.6

% The first object. The start of every object is marked by:
%
% <object number> <generation number> obj
%
% (The generation number is used for versioning, and is usually 0.)
%
% This is object 1, so it starts as `1 0 obj`. The second object will
% start with `2 0 obj`, then `3 0 obj`, and so on. The end of each object
% is marked by `endobj`.
%
% This is a "stream" object that draws a shape. First I specify the
% length of the stream (54 bytes). Then I select a colour as an
% RGB value (`1 0 0 RG` = red), then I set a line width (`5 w`) and
% finally I give it a series of coordinates for drawing the square:
%
% (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

% The second object.
%
% This is a "Page" object that defines a single page. It contains a
% single object: object 1, the red square. This is the line `1 0 R`.
%
% The "R" means "Reference", and `1 0 R` is saying "look at object number 1
% with generation number 0" -- and object 1 is the red square.
%
% It also points to a "Pages" object that contains the information about
% all the pages in the PDF -- this is the reference `3 0 R`.
2 0 obj
<<
/Type /Page
/Parent 3 0 R
/MediaBox [0 0 300 300]
/Contents 1 0 R
>>
endobj

% The third object.
%
% This is a "Pages" object that contains information about the different
% pages. The `2 0 R` is reference to the "Page" object, defined above.
3 0 obj
<<
/Type /Pages
/Kids [2 0 R ]
/Count 1
>>
endobj

% The fourth object.
%
% This is a "Catalog" object that provides the main structure of the PDF.
% It points to a "Pages" object that contains information about the
% different pages -- this is the reference `3 0 R`.
4 0 obj
<<
/Type /Catalog
/Pages 3 0 R
>>
endobj

% The xref table. This is a lookup table for all the objects.
%
% I'm not entirely sure what the first entry is for, but it seems to be
% important. The remaining entries correspond to the objects I created.
xref
0 4
0000000000 65535 f
0000000851 00000 n
0000001396 00000 n
0000001655 00000 n
0000001934 00000 n

% The trailer. This contains some metadata about the PDF. Here there
% are two entries, which tell us that:
%
% - There are 4 entries in the `xref` table.
% - The root of the document is object 4 (the "Catalog" object)
%
trailer
<<
/Size 4
/Root 4 0 R
>>

% The startxref marker tells us that we can find the xref table 2196 bytes
% after the start of the file.
startxref
2196

% The end-of-file marker.
%%EOF

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

Быстро стало очевидно, почему никто не пишет PDF-файлы вручную – очень сложно было переделывать все таблицы поиска! Но я рад, что сделал это; манипуляции со всеми объектами PDF и их ссылками действительно помогли мне понять базовую модель 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 × 72 = 4,17 дюйма. И действительно, если я открою этот PDF в Adobe Acrobat, то это именно то, что будет указано.

adobe acrobat pdf 4in 1x

 

Изменяя значение MediaBox, мы можем увеличить страницу. Например, если мы изменим значение на 600 600, Acrobat сообщит, что теперь размеры страницы 8,33 x 8,33 дюйма. Отлично!

Мы можем увеличить его до максимума, разрешенного Acrobat, 14400 14400, и тогда он скажет, что страница теперь 200,00 x 200,00 дюйма. (Вы получите предупреждение, если попытаетесь превысить этот предел.)

Но 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 дюйма.

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

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

[Исправление, 1 февраля 2024 года: в первоначальной версии этого поста было несколько лишних нулей – это миллион дюймов, а не миллиард. Спасибо mrb на Hacker News за обнаружение ошибки!]

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

Но в отличие от Acrobat, приложение Preview не имеет верхнего предела для того, что мы можем установить в MediaBox. Оно спокойно позволяет мне задать ширину, которая является 1, за которой следуют двенадцать нулей.

preview megawide 1x

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

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

Пожалуйста, не пытайтесь его распечатать.

Источник: alexwlchan.net/2024/big-pdf

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