Уменьшаем размер шрифтов для TCPDF

Аватар пользователя seaji seaji 20 ноября 2009 в 18:08

TCPDF - php библиотека для создания PDF файлов.
www.tcpdf.org
В Друпале используется в модуле print (возможно еще в некоторых других).
Я использовал эту библиотеку в своем модуле.

Главная проблема с TCPDF состоит в том, что при использовании юникода и кирилицы размеры PDF файлов получаются просто огромными. К примеру, я использовал freesans и freeserif в документе.
При этом шестикилобайтный текст в PDF занимал около полутора мегабайт.
После применения описанной здесь методики я получил 100 кб файл.

Эта проблема является для меня критичной, т.к. мне нужно каждый месяц сохранять порядка 1000 файлов отчетов в PDF. Если каждый файл будет 1.5 мБ, то мне потребуется около 1.5 гигабайт на диске. Если каждый файл будет 100 кБ, то мне потребуется около 100 Мб на диске.

Все это происходит потому, что в PDF включаются файлы шрифтов, которые имеют приличный вес.

Я рассмотрю метод сжатия шрифтов описанный здесь:
http://www.tecnick.com/public/code/cp_dpage.php?aiocp_dp=tcpdf_fonts
в последнем разделе "Reducing the Size of TrueType Fonts"
Суть этого метода состоит в смене типа шрифта с TrueType на Type1 с указанием только одной кодировки, которой мы будем пользоваться. Я делал для cp1251.

При этом возникнут некоторые проблемы связанные с несоответствием кодировок Drupal (utf-8) и нашей кодировки шрифтов (cp1251).
Я покажу как эти проблемы обойти.

Преобразование типа шрифта

Итак, начинаем с преобразования типа шрифта
Это происходило примерно так:
используется утилитка ttf2ufm из папки tcpdf/fonts/utils
а командной строке делаем так:

ttf2ufm -b -L enc/cp1251.map times.ttf times

Таким образом мы возьмем из шрифта times.ttf только то, что нам необходимо.
в папке tcpdf/fonts/utils появятся три файла
times.pfb - файл шрифта типа Type1
times.afm и times.ufm - метаописания шрифта.

Создаете в этой папке файл convert.php
туда пишете

<?php
require('makefont.php');
MakeFont('times.pfb','times.afm'true'cp1251');
?>

После того, как вы дернете convert.php из строки браузера, в папке tcpdf/fonts/utils появятся еще два файла: times.php и times.z - эти файлы нужно поместить в папку tcpdf/fonts - это и есть шрифты.

Хочу сразу отметить одну тонкость. Если вы будете использовать arial.php и arial.z, то tcpdf их не воспримет, он будет лепить свою helveti(ca) - ку, которая у него используется по умолчанию.
Если хотите использовать arial, то придется переименовать файлы во что то другое. При этом незабудьте исправить файл arial.php. Нужно исправить в нем переменную $file и внести в нее новое имя файла.

Перекодировка

Теперь нам нужно подружить Drupal, который работает в UTF-8 со шрифтами в кодировке cp1251
Придется кое-что, кое-где немножечко хакнуть, надеюсь котята нас простят Smile

Во первых, производим некоторые изменения в файлах tcpdf

в файле tcpdf/config/lang/rus.php нужно сделать перекодировку:

<?php
// кодировка
$l['a_meta_charset'] = 'cp1251';
// надпись в футере
$l['w_page'] = iconv("utf-8","cp1251"'страница');
?>

подключаем библиотеку простой командой:

<?php
require_once('tcpdf/config/lang/rus.php');
require_once(
'tcpdf/tcpdf.php');
?>

Во вторых, мета данные придется указывать в латинском алфавите.
Я это не смог победить.
Сами мета данные устанавливаются нормально в кодировке cp1251, но отображаются они, видимо с использованием какой то другой кодировки и поэтому получаются всякие загагульки.

Видимо потребуется модуль транслитерации. Если он у вас стоит, то используйте следующую команду:

<?php
require_once(drupal_get_path('module''transliteration') .'/transliteration.inc');
?>

далее отдельно транслителируйте метаданные:

<?php
$pdf_title 
transliteration_process('Заголовок');
$pdf_subject transliteration_process('Тема');
$pdf_creator transliteration_process('Название вашего приложения');
$pdf_author transliteration_process('Автор');
$pdf_header_1 iconv("utf-8","cp1251"'Первая строка заголовка');
$pdf_header_2 iconv("utf-8","cp1251"'Вторая строка заголовка');
?>

в файле tcpdf/config/lang/rus.php нужно сделать перекодировку:

<?php
$l
['a_meta_charset'] = 'cp1251';
$l['w_page'] = iconv("utf-8","cp1251"'страница');
?>

создаем новый pdf:

<?php
$pdf 
= new TCPDF('L'PDF_UNITPDF_PAGE_FORMATfalse'cp1251'true);
?>

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

Указываем метаданные:

<?php
$pdf
->SetCreator($pdf_creator);
$pdf->SetAuthor($pdf_author);
$pdf->SetTitle($pdf_title);
$pdf->SetSubject($pdf_subject);

$pdf->setLanguageArray($l);
$pdf->SetFont('times'''12);
$pdf->SetHeaderData(''''$pdf_header_1$pdf_header_2);
$pdf->setHeaderFont(Array('times'''7));
$pdf->setFooterFont(Array('times'''PDF_FONT_SIZE_DATA));
$pdf->SetHeaderMargin(20);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);

$pdf->SetMargins(203015);
$pdf->SetAutoPageBreak(TRUE38);
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
?>

Тут вариаций много. Я использую хедер и футер, вы можете их отключить.
Подробнее обо всех настройках можно прочитать в документации.

Добавляем страницу:

<?php
$pdf
->AddPage();
ob_start();
print 
'здесь печатаем свой HTML код, который будет преобразован в PDF';
$page ob_get_clean();
// преобразуем кодировку
$page iconv("utf-8","cp1251"$page);
// пишем PDF
$pdf->writeHTML($pagetrue0true0);
// закрываем файл
$pdf->Close();
// передаем его в браузер
$pdf->Output($filename_pdf'I');
?>

Итак, подведем итоги, что и где придется хакать:

1. файл tcpdf/config/lang/rus.php:

<?php
$l
['a_meta_charset'] = 'cp1251';
$l['w_page'] = iconv("utf-8","cp1251"'страница');
?>

2. модуль (например print):

- создание нового объекта $pdf

<?php
$pdf 
= new TCPDF('L'PDF_UNITPDF_PAGE_FORMATfalse'cp1251'true);
?>

четвертый аргумент - отказ от юникода
пятый аргумент - установка кодировки документа

- установка метаданных:

<?php
$pdf
->SetCreator($pdf_creator);
$pdf->SetAuthor($pdf_author);
$pdf->SetTitle($pdf_title);
$pdf->SetSubject($pdf_subject);
?>

- запись HTML в объект $pdf:

<?php
$page 
iconv("utf-8","cp1251"$page);
$pdf->writeHTML($pagetrue0true0);
?>

Как видите хаков не так уж много.

Комментарии

Аватар пользователя seaji seaji 21 ноября 2009 в 2:23

Столкнувшись с реальной проблемой я сначала начал рыскать по интернету.
Не найдя подходящего решения я начал копать сам. Потратил больше суток на раскопку этой проблемы я нашел решение и решил записать его в блог (а вось кому пригодится).
Теперь еще за это и минусы хватаю на Хабре.
Ну что сказать? Спасибо всем за минусы Smile

Аватар пользователя seaji seaji 28 ноября 2009 в 1:55

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

Аватар пользователя zman zman 30 ноября 2010 в 23:25

> Главная проблема с TCPDF состоит в том, что при использовании юникода и кирилицы размеры PDF
> файлов получаются просто огромными. К примеру, я использовал freesans и freeserif в документе.

> При этом шестикилобайтный текст в PDF занимал около полутора мегабайт.

freesans около мегабайта
freeserif чтото около двух мегабайт

стандартый размер Trebuchet-ms около 500кило
при конвертации для использования в TCPDF чуток уменьшается до примерно 340kb

итого
если использовать шрифты поменьше размером, то получаются вроде не особо громадные пдф файлы:
92кб чистый текст - пдф 178кило

п.с.
самый мелкий UTF шрифт LiberationMono около 443кб, но не смог что-то он нормально заработать - косячил с пробелами,
еще есть не особо большая Verdana 569kb