Type1 и truetype шрифты в LaTeX. Версия для печати   


В тексте рассматривается вопрос об использовании произвольных type1 и truetype шрифтов в pdf файлах, полученных из latex исходников с помощью системы tetex. При этом предполагается, что pdf-файлы используются для электронных презентаций, т.е. рассматриваемый метод позволяет вставлять шрифты именно как векторные (outlined), а не растеризованные (bitmapped) шрифты (качество последних при просмотре на устройствах с низкими dpi - дисплеях, например, - как правило, хуже, в связи с чем и возникают частые жалобы в форумах - "Написал курсовую в LaTeX, сгенерировал pdf, а все шрифты какие-то корявые").
Довольно большой набор русских type1 шрифтов распространяется в пакете pscyr (ftp://mch5.chem.msu.su/pub/russian/psfonts/). Установка этого пакета подробно (и по русски!) описана в файле README. Однако, ничто не мешает вам использовать любые понравившиеся вам шрифты в latex. Единственные проблемы - это 1) сделать эти шрифты доступными для latexa, и 2) отучить latex растеризовать их. Как это сделать и будет описано в данном тексте.
Что нужно?
tetex версии >= 1.0.7, ghostscript версии >=6.5 и несколько фонтов. В качестве примера предлагаю использовать свободные фонты с сайта Vedi () - Quake (type1 версия) и Stonehenge, Nadejda, Coptic (ttf версии). Также нужен будет один файл из пакета pscyr t2a.enc (файл с вектором кодировки T2AAdobeEncoding), но вы можете просто установить у себя этот пакет
целиком, тем более, что в нем содержатся очень симпатичные русские шрифты (мне лично очень понравились Лазурский и Академия - шрифт используемый в старых изданиях 50х годов прошлого века). Тем кто хочет поглубже разобраться в структурах ps, pdf файлов, а также в том, как использовать фонты в этих файлах могу порекомендовать толстые английские мануалы с сайта Adobe ( ) - PostScript Language Reference, 3'd edition; PDF Reference, 3'd edition; Adobe Type 1 Font Format; Supporting Fonts in the PostScript Language Environment.
В чем проблема?
"Корявые" фонты - это type3 фонты, т.е. фонты реализованные с помощью графических команд postscript. С одной стороны это делает фонт более гибким (можно нарисовать любой символ), но с другой - это все же графика, и хотя pdf умеет сглаживать графику, но результат оказывается хуже чем у outlined type1 и truetype шрифтов. Однако просто взять и вставить outlined шрифт не получится (например отредактировав ps-файл ) - дело в том что эти шрифты должны быть правильно "реализованы" в postscript файле, иначе ps2pdf конвертор все равно будет растеризовать их, и в результате для некоторых символов (как правило русских букв) будет использован type3 шрифт.
Что значит правильно реализовать шрифт? Дело в том, что хотя в самом файле шрифта может содержаться любое количество глифов, но одновременно адресовать и отобразить с помощью постскрипт команды show в постскрипт файле можно только 256 символов. Если вы заглянете внутрь файла с type1 шрифтом (лучше брать файлы с расширением pfa), то вы найдете в начале список глифов реализованных по-умолчанию в этом фонте (строчка Encoding). Список может быть как стандартным, например, AdobeStandardEncoding, либо явно определяться как encoding-array. Например, в шрифте Quake он определен так:

/FontName /QuakeСyr def 
/Encoding 256 array 0 1 255 {1 index exch /.notdef put } for 
dup 32 /space put 
dup 33 /exclam put 
dup 34 /quotedbl put 

и т.д. - ровно 256 символов. Если же просмотреть его с помощью ghostscript-команд (как это сделать, описано в "Почему Мозилла печатает пустые квадраты вместо русских букв") можно убедится, что он дополнительно содержит еще 3 незакодированных символа. В postscript-language любой символ имеет уникальное имя - /space - пробел и т.д., в частности для русских букв зарезервированы имена /afii10017-afii10049; /afii10065-/afii10097 (можно убедится, что в шрифте Quake они уже закодированы). Вывод текста в постскрипт файле осуществляется с помощью команды show, которая принимает в качестве аргумента последовательность байт - индексов букв в текущем фонте (т.е. в фонте предварительно реализованном в постскрипт-файле с помощью команды selectfont). Чтобы отобразить символы, которые не содержатся в предопределенном encoding-array фонта, нужно в постскрипт файле "динамически" создать фонт (команда definefont), в котором бы содержались нужные символы. Причем сделать это можно двумя путями:
1. Заменив кодировку type1 фонта. Это я бы назвал "правильно" реализованным фонтом. Замена кодировки заключается в том, что задается последовательность имен символов (которые уникальны и стандартны), которые будут включены в логический динамический фонт. Последовательность, в которой эти имена указываются, задает индексирование шрифта. В принципе, из одного физического шрифта внутри одного постскрипт файл можно создать множество логических шрифтов, но в каждый момент активным (selected) будет только один. Когда шрифт подгружается в постскрипт файл, то автоматически создается логический фонт с кодировкой, указанной в самом фонте.
2. Создать фонт с "нуля". Нужные символы определяются в шрифте либо как битмапы, полученные растеризацией глифов исходного фонта, либо символы исходного фонта вставляются с помощью команд <имя символа> showglyph. В обеих этих случаях вы имеете type3 фонт.
Собственно, чтобы из latex файла получать красивые pdf - нужно научить dvips обрабатывать "нестандартные" символы согласно варианту 1, а не 2, что он склонен делать, так сказать, по умолчанию.
Фонты в LaTeX
Вообще говоря, LaTeX безразлично какие именно шрифты вы собираетесь использовать в своем документе (LaTeX не занимается отображением документа). Единственное, что ему нужно знать о шрифте - это где-какие символы расположены и их метрику, чтобы правильно распределять текст на странице. Эту информацию latex извлекает из tfm (tex font metric) файла и его наличие - непременное условие, чтобы любой фонт можно было бы использовать в latex документах. Т.е. первый шаг к подключению какого-либо шрифта к latex - это преобразовании метрики шрифта. У type1 шрифтов информация о начертании букв и их размерах содержится в двух различных файлах - afm (может также иметь расширение pfm) и pfa (или pfb) соответственно, для ttf данные о глифах и метрика содержатся в одном и том-же файле. Если же вы хотите классифицировать шрифты по стилям, чтобы LaTeX автоматически подбирал шрифты для bold, italic, serifed, sans-serifed и т.д. текста, то для этой цели вам понадобится еще fd-файл (font descriptor). Однако, этот файл не является обязательным, если вы собираетесь использовать фонта в своем документе "разово" - например для каких-нибудь декоративных надписей.
Прежде чем вставлять шрифты подготовьте временную директорию (~/latex-fonts к примеру), где будут собраны шрифты, копируйте в нее quake.pfa, quake.afm, coptic.ttf, stonhen.ttf и nadeb.ttf (предварительно скачав их с сайта Vedi), а также t2a.enc из пакета pscyr. Для генерации pdf файла будет использована команда pdflatex, напрямую создающая pdf-файл из исходников latex. Поэтому создайте в этой временной директории еще файл pdftex.cfg, скопировав системный pdftex.cfg файл (найти его можно командой
cd / ; kpsewhich -progname=pdftex pdftex.cfg

у меня это /usr/share/texmf/pdftex/config/pdftex.cfg). Где-нибудь в конце этого файла добавьте строчку
map +myfonts.map
и создайте в директории ~/latex-fonts файл myfonts.map (в него будем записывать описания сгенерированных фонтов). Теперь, сделав cd ~/latex-fonts, убедитесь, что tetex видит локальный конфигурационный файл
cd ~/latex-fonts; kpsewhich --progname=pdftex pdftex.cfg

(ответ должен быть ./pdftex.cfg). Таким образом вы можете как угодно экспериментировать со шрифтами, не затрагивая системную конфигурацию tetex.
Еще раз замечу, что метод, который будет приведен ниже, работает только в случае использования команды pdflatex, генерирующей pdf файл непосредственно из latex исходника. Если же вам для каких-то целей нужно сгенерировать постскрипт-файл (последовательностью команд latex...; dvips ...), то тут имеются проблемы связанные с тем, что ghostscript не умеет обрабатывать embedded true-type фонты (в конце я остановлюсь на этом).
type1 шрифты.
Поскольку технология type1 шрифтов, также как и формат pdf, разработы Adobe, то с type1 шрифтами проблем не возникает. Вначале изготавливаем tfm из afm файла (например из шрифта QuakeCyr)
>afm2tfm quake.afm -T t2a.enc 
quake QuakeCyr " T2AEncoding ReEncodeFont " < t2a.enc 

Команда afm2tfm (входит в состав tetex) конвертирует afm (adobe font metric) файл в quake.tfm файл, попутно перекодируя его в T2AAdobeEncoding (-T t2a.enc). Вывод этой команды - это примерно та строчка, которую теперь нужно добавить в файл myfonts.map. Вам только нужно еще указать файл, из которого pdflatex будет брать начертание шрифтов. Так что, в окончательном виде, вам нужно дописать в файл myfonts.map строку:
 quake QuakeCyr " T2AEncoding ReEncodeFont " < quake.pfa < t2a.enc 

(означает она следующее - latex имя шрифта, постскрипт имя шрифта, команда указывающая перекодировку фонта, файл где находятся глифы шрифта и файл с кодировкой).
ttf шрифты.
С ними ситуация хуже. Поскольку стандарт Adobe на true-type шрифты не распространяется, то кодировка символов в ttf шрифте может отличаться от стандартной адобовской, и в основном проблемы возникают именно с тем, что в шрифте не удается найти глиф с именем (к примеру) /afii10017, а большая русская буква А именуется как Agrave. Поэтому, прежде чем начать встраивать ttf шрифт в latex, рекомендуется вначале просмотреть его с помощью ghostscript, чтобы понять, какой способ наименования глифов выбрал автор при создании шрифта. Проще всего это сделать, добавив в ghostscript'овский Fontmap.GS файл строчку вида:
 /Test-Font (/path/to/some/font.ttf); 

и открыть в gv ps файл:
(prfont.ps) runlibfile 
/Test-Font DoFont
В результате в ghostviewer'e вы увидите таблицу со всеми глифами, содержащимися в шрифте, вместе с их адобовскими названиями.
Способов кодирования символов может быть три (the good, the bad and the ugly):
1 Автор шрифта следует адобовскому стандарту именования символов. Самый лучший вариант и (к счастью) самый распространенный - если вы имеете дело с профессионально сделанным шрифтом (например с Monotype'овским), то можно быть уверенным, что шрифт закодирован именно так.
2 В шрифте сбита кодировка (случайно или умышленно). Некоторый глифы имеют странные названия (если вы смотрите шрифт с помощью DoFоnt команды) или не отображаются, когда вы пытаетесь использовать шрифт в latex-документе.
3 Шрифт закодирован с помощью грубого хака - национальные символы отображаются на верхнюю половину ISOLatin1Charset.
Последние два случая лечению, тем не менее, поддаются.
Начнем с самого простого случая. В первую очередь вам нужно извлечь метрику шрифта из ttf файла (напомню, что в отличии от type1 шрифтов, где метрика и глифы хранятся в разных файлах, true-type шрифты cостоят из одного файла). Для этого воспользуемся командой ttf2afm из пакета ghostscript:
ttf2afm -e t2a.enc -o dscopt.afm dscoptic.ttf

Единственная неочевидная опция - это -e t2a которая указывает, что в dscoptic.afm файл будут помещена информация только о символах содержащихся в файле t2a.enc. Далее следуем уже известным путем:
>afm2tfm dscopt.afm -T t2a.enc 
dscopt DSCoptic " T2AEncoding ReEncodeFont " < t2a.enc 

и добавляем эту строчку в файл myfonts.map так же как и раньше, опять же дописывая имя шрифта:
 dscopt DSCoptic " T2AEncoding ReEncodeFont " < dscoptic.ttf < t2a.enc

DSCoptic - это вариант 1, шрифт закодированный согласно адобовскому стандарту. Вариант 2 - шрифт Надежда, в котором латинская буква /H именуется как /FL0048h. Так что, если вы попытаетесь конвертировать его аналогично случаю приведеному выше, то большой латинской буквы H в текстах вы не увидите. Лечится это подкручиванием кодировки. Копируете файл t2a.enc в файл t2a_nade.enc и в последнем вместо строчки /H вставляете /FL0048h. После чего конвертируете файл nadeb2.ttf
ttf2afm -e t2a_nade.enc -o nade.afm nadeb2.ttf
afm2tfm nade.afm -T t2a_nade.enc
echo 'nade Nadejda-Bold " T2AAdobeEncoding ReEncodeFont" < nadeb2.ttf < t2a_nade.enc ' >> myfonts.map 

Шрифт Stonhenge - это третий случай. В принципе, можно исправить кодировку в нем точно также как и в шрифте Надежда (исправлений вам, правда, придется вносить гораздо больше), но есть и более простой вариант. А именно - позиции русских символов в T2A кодировке совпадают с позициями символов в шрифте Stonehenge. Поэтому можно вытащить кодировку из этого шрифта и затем с ее помощью создать tfm файл.
 ttf2afm -с stone -o stone.afm stone.ttf
ttf2afm -e stone.e00 -o stone.afm stone.ttf
afm2tfm stone.afm -T stone.e00
echo 'stone Stonehenge " Encoding1 ReEncodeFont" < stone.ttf < stone.e00 ' >> myfonts.map 

Комментарии: ttf2afm -с stone ... извлекает кодировки из шрифта и сохраняет их в нескольких файлах с названиями stone.e. Мы используем кодировку из файла stone.e00 для перекодировки фонта.
Тестируем шрифты.
Простой пример. В данном примере переопределяются стили для roman, sans-serif и typewriter текста. Четвертый шрифт определяется in-place и используется разово.
\documentclass[a4]{article} 
\usepackage[koi8-r]{inputenc} 
\usepackage[russian]{babel}
\renewcommand{\rmdefault}{stone} 
\renewcommand{\sfdefault}{nade}
\renewcommand{\ttdefault}{dscoptic}
\title{Использование type1 и tt шрифтов в LaTeX.} 
\author{Проект Vedi} 
\sloppy 
\begin{document} 
\maketitle 
Дурацкий тест включения true-type и type1 шрифтов в LaTeX. 

\sf Внимание! \tt Специалистам в полиграфии и издательском деле просьба этот текст не компилировать и не смотреть! 

Это может повлечь за собой 

\font\Scary quake at20pt \Scary обострение профессиональных заболеваний - аллергию, тошноту и рвоту !
\end{document} 

В скомпилированном командой pdflatex test.tex (запускаем ее в каталоге latex-fonts) документе, outlined окажется только шрифт Scary (т.е. QuakeCyr), вместо остальных tetex подставит растеризованные шрифты. Если вы посмотрите на ошибки, которые выдаст tetex, то вы заметите, что он не находит шрифты T2A/dscoptic/m/n и др. и честно сообщает, что вместо них он использует шрифты по-умолчанию.
Ошибка связана с тем обстоятельством, что LaTeX является WYSIWYM ситемой и кое-что пытается проделать за вас. А именно, в качестве /Scary фонта вы ему однозначно дали понять, что нужно использовать шрифт QuakeCyr, а с помощью \renewcommand{\rmdefault... вы ему указали, что для надписей, выполняемых roman шрифтами, он должен использовать семейство шрифтов stone, при этом самостоятельно (точнее в соответствии с используемым стилем) подбирая какой текст выделять italic-ом, bold-ом или normal. Таким образом нам понадобятся еще файлы с описаниями семейств шрифтов, т.е. fd-файлы. Писать их придется вручную, но все они достаточно однотипны.
t2adscoptic.fd

\ProvidesFile{t2adscoptic.fd}[DSCoptic Font]
\DeclareFontFamily{T2A}{dscoptic}{}
\DeclareFontShape{T2A}{dscoptic}{m}{n}{ <-> dscoptic}{}

t2anade.fd

\ProvidesFile{t2anade.fd}[Nadejda Font]
\DeclareFontFamily{T2A}{nade}{}
\DeclareFontShape{T2A}{nade}{m}{n}{ <-> nade}{}

t2astone.fd

\ProvidesFile{t2astone.fd}[Stonehenge Font]
\DeclareFontFamily{T2A}{stone}{}
\DeclareFontShape{T2A}{stone}{m}{n}{ <-> stone}{}

Ну и для полноты картины еще файл для QuakeCyr:
t2aquake.fd

\ProvidesFile{t2aquake.fd}[QuakeCyr Font]
\DeclareFontFamily{T2A}{quake}{}
\DeclareFontShape{T2A}{quake}{m}{n}{ <-> quake}{}

Т.е. мы в данном случае определили 4 семейства в кодировке T2A , каждое из которых содержит по одному medium normal (обычный прямой) шрифту. После того как все 4 fd-файла окажутся в директории latex-fonts, осталось только перезапустить команду pdflatex test.tex.
В итоге, в сгенерированном pdf файле все шрифты окажутся или Type1 или TrueType, Embedded в том и другом случае - в чем можно убедиться командой:
$pdffonts test.pdf 
name type emb sub uni object ID 
--------------------------------
Stonehenge TrueType yes no no 6 0 
Nadejda-Bold TrueType yes no no 9 0 
DSCoptic TrueType yes no no 12 0 
OXCCSV+QuakeCyr Type 1 yes yes no 15 0

Наводим порядок.
Если результат тестирования вас удовлетворил, то можно заняться раскладыванием шрифтов и конфигурационных файлов по их привычным местам.
сp *.afm /usr/share/texmf/fonts/afm/public/local
cp *.fd  /usr/share/texmf/tex/latex/local
cp *.pfa *.pfb /usr/share/texmf/fonts/type1/public/local
cp *.ttf /usr/share/texmf/fonts/truetype/public/local
cp *.enc /usr/share/texmf/dvips/base
cat ./myfonts.map >> /usr/share/texmf/dvips/config/pdftex.map

Проверьте также, что файл pdftex.map указан в конфигурационном файле pdflatex'а /usr/share/texmf/pdftex/config/pdftex.cfg. Если нет допишите в него строчку map +pdftex.map (или map pdftex.map). Теперь остается только синхронизировать базу данных tetex (команда mktexlsr) и шрифты готовы к использованию.
Почему же нельзя использовать ttf шрифты с dvips ?
На самом деле можно, но ;) ...
Для чего вообще может понадобится использование dvips: pdflatex имеет ограничение - он не умеет обрабатывать встроенную в latex-документ postscript графику (обойти это ограничение можно, если преобразовать рисунки в pdf-формат, а в latex вставлять уже сгенерированные pdf файлы). Но, в то же время, вместо использования pdflatex напрямую можно пойти длинным путем - конвертировать latex в dvi-файл (latex test.tex), затем полученный dvi преобразовать в постскрипт (командой из пакета tetex - dvips test.dvi), и потом уже конвертировать его в pdf программой из пакета ghostscript ps2pdf test.ps. Программа dvips конвертирует latex шрифты в постскриптовские по правилам, записанным в ее конфигурационном файле /usr/share/texmf/dvips/config/psfonts.map. Его формат такой-же как и pdftex.map, поэтому строки, соответствующие type1 шрифтам, вы можете не задумываясь переносить в этот файл. С ttf шрифтами (в отличии от type 1) возникает следующая проблема: если в строке описания шрифта упоминается файл *.ttf, то dvips встраивает этот шрифт в постскрипт файл. А ghostscript (по крайней мере вплоть до версии 7.05) не умеет работать со встроенными в постскрипт файл true-type шрифтами. Соответственно любые программы, использующие при работе ghostscript (gv, gs и ps2pdf в том числе), вылетатют с cообщением об ошибке в постскрипт файле (если у кого имеется интерпретатор постскрипта, отличный от gs, или постскрипт-принтер, то можете проверить, как они реагируют на встроенные ttf).
Очевидное решение - не встраивать в постскрипт true-type шрифты - тоже не проходит (замечу, что постскрипт без встроенных "экзотических" шрифтов - вещь крайне непортабельная, и отсылать его своим сотрудникам или знакомым не стоит - все равно они там ничего не увидят). А именно: предположим вы добавляете в psfonts.map строчку
dscopt DSCoptic " T2AEncoding ReEncodeFont " < t2a.enc

и генерируете постскрипт файл. Чтобы ghostscript смог его отобразить, нужно указать ghostscript'у, где именно нужно брать глифы для шрифта DSCoptic, для чего в файл /usr/share/ghostscript//lib/Fontmap.GS вставляете путь к файлу шрифта
/DSCoptic  (/usr/share/texmf/fonts/type1/public/local/dscoptic.ttf); 

Ghostviewer после этого должен правильно и без ошибок отобразить документ. Однако, если вы запустите ps2pdf, то в сгенерированном pdf файле вы увидите все те же кривые type3 шрифты, отдаленно напоминающие DSCoptic. Это также ограничение ghostscript'a, о чем его авторы пишут в документации :
ps2pdf will sometimes convert text to high-resolution bitmapped fonts rather than to 
embedded outline fonts. This will occur when the PostScript file uses Type 3, CIDFontType 1, or 
CIDFontType 4 fonts, or Type 0 fonts that reference any of these; it may also occur in some 
cases if the input file uses fonts with non-standard encodings, or in some other rare cases

Ну, это для них они "rare cases" - а для нас суровые будни. Так что, если вы хотите использовать графику в latex-докуменетах, то либо используйте исключительно type1 шрифты, postscript графику и команду dvips, либо все же предварительно конвертируйте постскрипт рисунки в pdf и пользуйтесь командой pdflatex (заметьте, что для надписей на рисунках вам, тем не менее, придется использовать type1 шрифты, поскольку конвертировать ps->pdf вам придется все той-же командой ps2pdf).
Всякое-разное
1. Как настроить поиск и копирование текста в pdf файлах сгенерированных с помощью pdflatex?
Вообще-то это зависит от приложения, в котором вы просматриваете pdf-файлы. Вот как это можно сделать в acrobat reader версии 4.0 под Linux (не думаю что в 5.0 имеются какие-то отличия).
Устанавливаете системную локаль cp1251 и генерируете truetype шрифты в кодировке cp1251 (как сделать это описано в статье Русский в X-ах). После этого правите шелл-скрипт acroread и меняете там первую строчку, где определяется переменная окружения LC_ALL на
export LANG=ru_RU.cp1251
export LC_CTYPE="ru_RU.cp1251" 

После этого вам нужно поменять шрифт интерфейса acrobat readera на любой шрифт в подходящей кодировке. Проще всего это сделать добавив строку в файл ~/.Xdefaults вида:
*FontList:-monotype-*-*-*-*-*-*-*-*-*-*-*-*-cp1251

В таком случае текстовый поиск работает, а с копированием текста нужно иметь ввиду, что Xselection будет вам возвращать текст в кодировке cp1251, так что вклеивание текста будет правильно работать в приложениях запущенных в той-же локали.
2. Я вставляю в latex файл postscript-графику, а после того как я делаю latex-dvips в получившемся постскрипт файле все надписи на рисунках куда-то пропадают?
Значит вы используете одинаковые фонты как в рисунках, так и в latex документе, и кроме этого в рисунках фонты не embedded. Еще одна причина не использовать dvips. Проблема вот в чем: когда вы создаете ps файл, все шрифты в нем получаются embedded с custom кодировкой, причем название шрифтов остается тем же самым. Если в графическом файле используется шрифт с тем же названием, то при отображении графики будет использован именно embedded шрифт, а не тот что определен в Fontmap.GS. Кодировка этих двух логических шрифтов (встроенного и ghostscript-овского) в общем случае не обязана совпадать, поэтому надписи на рисунках окажутся не теми, на что вы расчитываете. Что делать? Самое правильное - конвертировать ps-графику в pdf и пользоваться pdflatex (как и было сказано ранее). Если по каким-то причинам этого сделать нельзя - используйте в графических файлах шрифты, постскриптовские имена которых не конфликтуют с именами шрифтов, применямых в документе.
Благодарности.
Автор благодарит Evgeny Adishchev за полезные замечания.

Опубликовал: geekkoo
Дата: 03.05.2004
постоянный адрес статьи: http://linuxportal.ru/entry.php/P1003_0_3_0/