Что такое "эмбеддинг" шрифта
PDF документы часто содержат текст написанный разными шрифтами. Чтобы Acrobat Reader или другой просмотрщик pdf могли изобразить этот текст, ему нужен доступ к файлам шрифтов используемых в документе. Если на ОС в которой вы открываете pdf не будет нужных шрифтов, то текст может стать нечитаемым. Для решения этой проблемы в стандарте PDF есть возможность копировать файлы используемых шрифтов внутрь документа, таким образом гарантируя, что где бы вы ни открыли документ, шрифты всегда будут доступны и текст будет читаемым. Такое копирование шрифтов называется "эмбеддингом" (от англ. embedding). Здесь, конечно, возникает проблема: файлы шрифтов часто имеют большой размер и pdf файл может стать неприемлемо большим из за эмбеддинга. PDF экспорт в FR VCL 4 умеет делать эмбеддинг, но делает это самым простым способом - просто копирует все файлы шрифтов в документ. Иногда такое приводит к раздуванию pdf файла до десятков мегабайт.
Эмбеддинг шрифтов в FR VCL 5
В пятой версии было решено улучшить эмбеддинг, за счёт извлечения из нужных шрифтов только используемых символов. Обычно из каждого шрифта используется не более 50 символов - это связано с тем, что pdf документ создаётся на каком либо одном языке и потому использует символы только одного алфавита. Шрифты же, особенно универсальные как Arial Unicode MS и восточные, в которых изображены иероглифы, содержат от 3 до 50 тысяч символов.
Для примера возьмём простой отчёт в котором несколько абзацев текста написаны шрифтом Arial. Отчёт использует только 41 символ, тогда как в Arial их 3415. Поэтому, если извлечь эти 41 символ и вставить их в pdf файл, то можно сэкономить почти 700 кб - размер шрифта Arial. Ещё один показательный пример: экспортируйте этот отчёт в pdf с включённым эмбеддингом и посмотрите на размер получившегося файла - больше 30 мб; если этот же отчёт сохранить в pdf экспортом из 5-ой версии, то получится вот такой файл на 116 кб, который даже открывается в Acrobat Reader значительно быстрее.
В этой ветке нашего форума можно взять тестовую программу которая экспортирует в pdf с новой версией эмбеддинга.
Как устроен шрифт
Обычно шрифт представлен файлом формата TTF, реже - файлом формата TTC который просто содержит несколько TTF файлов. Шрифт состоит из двух важных частей:
- Массив глифов. Каждый из глифов является изображением некоторого символа или части символа. Глиф в TTF представлен в виде кривых Безье и, возможно, картинки показывающей как должен выглядеть глиф для очень маленького размера шрифта.
- Таблица "cmap" которая задаёт отображение 2-байтного юникода в индекс глифа. Такая таблица нужна потому что шрифт редко содержит глифы для всех возможных значений юникода и нужно как то указать, для каких именно юникодов в шрифте есть глифы.
Некоторые символы нарисованы при помощи одного глифа: обычно символы английского алфавита нарисованы именно так. Другие символы могут быть представлены несколькими глифами: например русская буква Ё во многих шрифтах состоит из глифа буквы Е и глифа изображающего две точки (точнее два квадрата - точки в шрифтах почему то рисуют в виде квадратов). Наконец, некоторые шрифты содержат глифы изображающие сразу несколько символов (т.н. лигатуры), глифы изображающие символ в зависимости от его положения в слове и некоторые другие особые глифы. Вообщем, алгоритм который узнает какими глифами изобразить слово не такой тривиальный как может показаться.
Реализация эмбеддинга в PDF экспорте
Суть эмбеддинга в том, что из массива глифов, из таблицы "cmap" и других таблиц в шрифте извлекается информация соответствующая используемым глифам, после чего создаются аналогичные таблицы куда записывается извлечённая информация. В итоге получается файл шрифта, но с меньшим количеством глифов. Пример такого укороченного файла здесь.
Далее, полученный шрифт записывается в pdf файл в виде нескольких pdf объектов. Рассмотрим это на примере отчёта с одной строчкой "Open Type Font" внутри, из которого получается вот такой pdf файл.
Код
%PDF-1.5 %ЂЂЂЂ % Этот pdf объект представляет TfrxMemoView с текстом "Open Type Font". % Он выбирает шрифт оператором Tf и рисует текст оператором Tj. 2 0 obj << /Length 257 /Length1 257 >> stream ... /F0 10 Tf ... <004F00700065006E0020005400790070006500200046006F006E0074> Tj ... endstream endobj % Общее описание шрифта % Поле /Encoding задаёт преобразование кодов символов в CID-ы. % Здесь это преобразование тождественно, т.е. CID символа равен его коду. 3 0 obj << /Type /Font /Subtype /Type0 /BaseFont /IJIVDA+Arial /Encoding /Identity-H /DescendantFonts [11 0 R] /ToUnicode 6 0 R >> endobj % Это параметры шрифта. % Здесь указываются параметры извлекаемые из файла шрифта. 9 0 obj << /Type /FontDescriptor /FontName /IJIVDA+Arial /FontFamily /IJIVDA+Arial /FontBBox [-1361 -665 4096 2060] /ItalicAngle 0 /Ascent 1854 /Descent -434 /CapHeight 0 /StemV 0 /Flags 32 /CIDSet 5 0 R /FontFile2 8 0 R >> endobj 11 0 obj << /Type /Font /Subtype /CIDFontType2 /CIDToGIDMap 10 0 R /BaseFont /IJIVDA+Arial /CIDSystemInfo 7 0 R /FontDescriptor 9 0 R /W [ 32 [277.8] 70 [610.8] 79 [777.8] 84 [610.8] 101 [556.2] 110 [556.2] 111 [556.2] 112 [556.2] 116 [277.8] 121 [500.0] ] >> endobj % А это сам TTF файл с шрифтом. % Между stream и endstream находится .ttf файл с нужными глифами. 8 0 obj << /Length 15148 /Length1 10856 /Filter [ /ASCIIHexDecode /FlateDecode ] >> stream 7801c57a7b...00175ac7e0 endstream endobj % Таблица преобразования CID-ов в индексы глифов. 10 0 obj << /Length 86 /Length1 244 /Filter [ /ASCIIHexDecode /FlateDecode ] >> stream 78016360a0103052a81f593b133207cc66868bb0c059b818ac18126c0cec0c1c50514eb82c1700078f0038 endstream endobj ... %%EOF |
Здесь все параметры шрифта очень просты. Интерес представляет то, как оператор Tj выводит текст.
- В качестве аргумента он принимает последовательность 2-байтных кодов. В примере это коды 4f 70 65 6e и т.д.
- Каждый код оператор преобразуется в CID, при помощи параметра шрифта /Encoding /Identity-H. В данном случае преобразование тривиальное - все коды не изменяются. Теперь у оператора Tj есть последовательность CID-ов (она та же самая: 4f 70 65 6e ...) и далее он работает с этими CID-ами.
- Каждый CID преобразуется в GID с помощью параметра /CIDToGIDMap. GID это индекс глифа. После этого у Tj есть индексы глифов и он может начать рисовать текст.
Здесь нужно заметить, что стандарт PDF не используется таблицу "cmap" которая отвечает как раз за поиск глифов по кодам символов. Вместо этого в pdf файл нужно записывать аналог "cmap".