Шрифты переменных OpenType

В этом разделе описываются шрифты переменных OpenType, их поддержка в DirectWrite и Direct2D, а также способы их использования в приложении. 

Что такое переменные шрифты OpenType?

В версии 1.8 спецификации формата шрифта OpenType появилось новое расширение формата, известного как Варианты шрифта OpenType. Шрифты, использующие эти расширения, называются переменными шрифтами OpenType. Шрифт переменной OpenType — это один шрифт, который может вести себя как несколько шрифтов, используя непрерывную интерполяцию между различными макетами, каждый из которых определен в одном шрифте.

Шрифт переменной OpenType может определять непрерывный вариант его структуры вдоль одной или нескольких независимых осей, таких как вес или ширина:

 

Показывает шрифт переменной OpenType с буквой

Разработчик шрифта определяет набор осей вариантов для использования в данном шрифте. Эти оси могут включать набор хорошо известных (или "зарегистрированных") осей вариации, таких как вес и ширина, но они также могут включать произвольные пользовательские оси вариации, определенные разработчиком шрифта.  

Выбирая набор осей вариантов для шрифта, разработчик шрифта определяет абстрактное n-мерное пространство вариантов оформления шрифта. Текстовые подсистемы могут указывать потенциально любую позицию или "экземпляр" в этом непрерывном пространстве для размещения и отрисовки текста. 

Разработчик шрифта также может выбирать и назначать имена определенным экземплярам в пространстве вариантов разработки; они называются именованными экземплярами. Например, шрифт с изменением веса может поддерживать непрерывное изменение между очень легкими и очень тяжелыми штрихами, в то время как разработчик шрифта выбрал определенные весовые коэффициенты вдоль этого континуума и назначил им имена, такие как "Light", "Regular" и "Semibold". 

Формат шрифта переменной OpenType использует таблицы данных, найденные в традиционных шрифтах OpenType, а также некоторые дополнительные таблицы, описывающие изменение значений различных элементов данных для разных экземпляров. Формат определяет один экземпляр варианта как "экземпляр по умолчанию", который использует традиционные таблицы для получения значений по умолчанию. Все остальные экземпляры зависят от данных по умолчанию и других разностных данных. Например, таблица "glyf" может иметь описание кривой Безье номинальной фигуры глифа, которая используется для экземпляра по умолчанию, в то время как таблица gvar будет описывать, как контрольные точки Безье для глифа корректируются для других экземпляров. Аналогичным образом, другие значения шрифта могут иметь номинальное значение и разностные данные, описывающие изменение этих значений для разных экземпляров; например, x-height и другие метрики на уровне шрифта или положения привязки к глифам и корректировки кернинга. 

Так как переменные шрифты могут поддерживать произвольный набор осей вариантов, им требуется расширяемая модель семейств шрифтов, которая более непосредственно отражает создание семейств шрифтов конструкторами шрифтов: семейство шрифтов определяется именем семейства и определенными характеристиками проектирования, которые являются постоянными, с произвольным числом (определяемым разработчиком шрифтов) способов изменения структуры. Одно семейство шрифтов может быть создано с вариантами для веса, но другое семейство шрифтов может быть создано с вариантами x-height, serif-size, "funkiness" или любым другим шрифтом, который хочет разработчик шрифтов. В этой модели выбор лица шрифта лучше всего описать с помощью общего или "предпочтительного" или "типографического" имени семейства, а также набора пар "ключ-значение", каждая из которых представляет разновидность и конкретное значение, причем типы вариаций в целом являются расширяемым набором. Это общее понятие семейства шрифтов может применяться к традиционным, не переменным шрифтам, а также к переменным шрифтам. Например, в этой общей типографической модели семейства семейство "Selawik VF" может иметь вариации по весу, оптическому размеру и дизайну засечек с такими экземплярами, как "Semilight Banner Sans". 

Однако некоторые существующие реализации программного обеспечения, включая существующие API DirectWrite, могут быть разработаны с учетом более ограниченной модели семейств шрифтов. Например, некоторые приложения могут предполагать, что семейство шрифтов может иметь не более вариантов Обычный, Полужирный, Курсив и Курсив полужирного шрифта. Существующие интерфейсы IDWriteFontCollection и IDWriteFontFamily предполагают модель семейства weight/stretch/style ("WSS"), что позволяет указать варианты в семействе с помощью DWRITE_FONT_WEIGHT, DWRITE_FONT_STRETCH или DWRITE_FONT_STYLE перечислений в качестве параметров. В предыдущем примере оптический размер и оси засечек не будут рассматриваться как внутренние семейные оси вариации в модели WSS. 

Полная поддержка шрифтов переменных потребует api, которые позволяют указать член семьи с потенциально несколькими параметрами, как определено шрифтом. Но существующие проекты API могут обеспечить частичную поддержку переменных шрифтов путем проецирования именованных экземпляров, определенных в шрифте переменной, в более ограниченные модели семейства шрифтов. В предыдущем примере "Selawik VF Semilight Banner Sans" можно проецировать в модель WSS как семейство "Selawik VF Banner Sans" с "Semilight" в качестве весового варианта. 

В качестве другого примера рассмотрим семейство шрифтов, например Sitka, с вариантами веса и оптического размера. Именованные варианты в семействе включают Sitka Text Regular и Sitka Banner Bold (а также многие другие). Типографическое имя семейства — "Sitka", а имена лиц для этих вариантов в типографической модели семейства будут "Обычный текст" и "Баннер полужирным". Модели семейства из четырех членов и WSS не допускают варианты оптического размера в пределах семейства, поэтому различия оптического размера должны рассматриваться как различия на уровне семьи. В следующей таблице показано, как в модели семейства WSS будет обрабатываться выбор шрифтов из семейства Sitka.

Типографическая модель семейства

Модель семейства WSS

Семейство

Распознавание лиц

Семейство

Распознавание лиц

Ситка

Обычный текст

Текст sitka

Регулярно

Ситка

Баннер полужирный

Знамя Ситка

Полужирный шрифт

Ситка

Курсив заголовка

Подпись к ситка

Курсив

 

Проекция имен из типографической модели семейства в модель семейства WSS может применяться к шрифтам без переменных и к именованным экземплярам переменных шрифтов. Однако это невозможно сделать для других неимованных экземпляров из непрерывного пространства вариантов разработки шрифта переменной. По этой причине для поддержки полной функциональности переменных шрифтов потребуются ИНТЕРФЕЙСы API, предназначенные для ссылки на лица в семействе типографов с точки зрения неограниченного набора вариантов осей и значений осей. 

Поддержка шрифтов переменных OpenType в DirectWrite

С выпуска Windows 10 Creators Update формат переменной шрифта OpenType по-прежнему является очень новым, а поставщики шрифтов, платформы и приложения все еще находятся в процессе реализации нового формата. Это обновление обеспечивает начальную реализацию этого формата в DirectWrite. 

DirectWrite были обновлены для поддержки шрифтов переменных OpenType. Использование текущих API обеспечивает поддержку любых именованных экземпляров шрифта переменной. Эту поддержку можно использовать для полных рабочих процессов — от перечисления именованных экземпляров, выбора именованного экземпляра, использования в макете и формировании до отрисовки и печати. Для приложений, которые также используют текстовое взаимодействие GDI для определенных операций, аналогичная поддержка была также добавлена в существующие API GDI. 

В Windows 10 Creators Update DirectWrite не поддерживает произвольные экземпляры, использующие возможность непрерывного изменения шрифтов переменных.

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

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

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

Так как существующие API-интерфейсы DirectWrite поддерживают выбор лиц с использованием модели веса, растяжения и стиля, именованные экземпляры шрифтов, использующих другие оси вариации, будут проецироваться из общей типографической модели семейства в модель WSS, как описано выше. Для этого используется переменный шрифт, включающий таблицу "атрибуты стиля" ('STAT') с таблицами значений оси, которые DWrite использует для различения маркеров имен лиц, которые обозначают атрибуты веса, растяжения или стиля, от маркеров, относящихся к другим осям вариации.  

Если переменный шрифт не содержит таблицу STAT, как это требуется для переменных шрифтов в спецификации OpenType, то DirectWrite будет рассматривать шрифт как неперемечаемый шрифт, содержащий только экземпляр по умолчанию.  

Если шрифт содержит таблицу "STAT", но не содержит соответствующих вложенных таблиц оси и значений, это может привести к непредвиденным результатам, таким как наличие нескольких лиц с одинаковыми именами лиц. Такие шрифты в настоящее время не поддерживаются. 

Спецификация OpenType позволяет представить данные структуры глифов в одном из двух форматов: с помощью таблицы "glyf", которая использует структуру TrueType и формат указания, или с помощью таблицы "CFF", в которой используется формат компактного шрифта ("CFF"). В шрифте переменной с контурами TrueType по-прежнему используется таблица glyf и дополняется таблицей gvar, которая предоставляет данные вариантов для контуров. Это означает, что экземпляр по умолчанию для шрифта переменной с контурами TrueType использует только традиционные таблицы OpenType, которые будут поддерживаться в более старых версиях программного обеспечения без поддержки шрифтов переменных. Однако в шрифте переменной с контурами CFF таблица CFF заменяема таблицей CFF2, которая инкапсулирует данные структуры по умолчанию и связанные с ними данные вариантов в одной таблице. Данные CFF обрабатываются отдельным растеризатором, который используется для данных TrueType, а для таблицы "CFF2" требуется обновленный растеризатор CFF с поддержкой "CFF2". Таблица "CFF2" не может быть обработана более старыми растеризаторами CFF. Для шрифта переменной с данными структуры CFF это означает, что даже экземпляр по умолчанию не будет работать в более старых версиях программного обеспечения. 

В Windows 10 Creators Update DirectWrite не поддерживает переменные шрифты с данными структуры CFF с помощью таблицы CFF2. 

Использование шрифтов переменных OpenType

Шрифты переменных OpenType могут быть простыми в использовании с учетом текущих ограничений, указанных выше:

  • В настоящее время поддерживаются только именованные экземпляры шрифта переменной.
  • В настоящее время поддерживаются только переменные шрифты, использующие данные структуры глифа TrueType (а не структуры CFF). 
  • Для шрифтов, использующих оси вариантов оформления, отличных от веса, растяжения или стиля, именованные экземпляры будут проецироваться в модель семейства WSS, что может привести к тому, что некоторые именованные экземпляры будут отображаться в виде отдельных семейств (как это было в прошлом для шрифтов без переменных). Для поддержки этого переменные шрифты должны иметь таблицу "STAT", содержащую соответствующие подтаблицы значений оси.
  • Именованные экземпляры переменных шрифтов поддерживаются в DirectWrite API, но если некоторые операции выполняются в старых реализациях, которые не поддерживают переменные шрифты, они могут привести к неверным результатам. 
  • Некоторые API DirectWrite используют перечисления DWRITE_FONT_WEIGHT, DWRITE_FONT_STRETCH и DWRITE_FONT_STYLE для указания атрибутов веса, растяжения и стиля при выборе лиц. Если шрифт переменной использует соответствующие вариантные оси, но имеет много именованных экземпляров, требующих более детальной детализации, не все именованные экземпляры будут доступны для выбора в этих API.

Шрифты переменных OpenType, соответствующие этим требованиям, можно установить из оболочки Windows так же, как и другие шрифты OpenType, а также использовать в пользовательских наборах шрифтов, созданных приложением.  

При установке в системе все именованные экземпляры переменной шрифта будут включены в набор шрифтов, возвращаемый путем вызова метода IDWriteFontFamily3::GetSystemFontSet . Обратите внимание, что набор шрифтов — это плоский список без иерархии с группировкой по семействам, но каждый элемент в наборе имеет свойство family-name на основе модели семейства WSS. Набор шрифтов можно отфильтровать для конкретного именованного экземпляра шрифта переменной с помощью методов IDWriteFontSet::GetMatchingFonts . Однако при использовании перегрузки GetMatchingFonts , принимающей familyName, указанное имя familyName должно использовать имя, соответствующее модели семейства шрифтов WSS. Полный список имен семейств, совместимых с WSS, в наборе шрифтов можно получить с помощью методов IDWriteFontSet::GetPropertyValues с помощью DWRITE_FONT_PROPERTY_ID_FAMILY_NAME.  

Аналогичным образом все именованные экземпляры переменной шрифта будут представлены в коллекции шрифтов, возвращаемой методом IDWriteFactory::GetSystemFontCollection . Поскольку элементы коллекции шрифтов являются семействами шрифтов на основе модели WSS, именованные экземпляры шрифта переменной могут быть представлены в коллекции как члены двух или более семейств шрифтов. Если используется метод IDWriteFontCollection::FindFamilyName , параметр familyName должен быть именем семейства, совместимым с WSS. Чтобы найти все имена семейств, совместимые с WSS, из коллекции шрифтов приложение может перебирать каждое семейство и вызывать IDWriteFontFamily::GetFamilyNames, хотя может быть проще получить соответствующий набор шрифтов и использовать метод GetPropertyValues , как описано выше. 

При работе с пользовательскими шрифтами для создания набора шрифтов можно использовать различные подходы, описанные в разделе Настраиваемые наборы шрифтов. Чтобы добавить переменный шрифт в настраиваемый набор шрифтов, рекомендуется использовать метод IDWriteFontSetBuilder1::AddFontFile , так как он поддерживает переменные шрифты и добавляет все именованные экземпляры переменной шрифта в одном вызове. В настоящее время невозможно добавить отдельные именованные экземпляры настраиваемого шрифта переменной в набор шрифтов с помощью методов IDWriteFontSetBuilder::AddFontFaceReference , так как невозможно создать ссылку на лицо шрифта, указывающую, какой из именованных экземпляров из файла шрифта переменной предназначен. Это означает, что в настоящее время невозможно добавить именованные экземпляры настраиваемого шрифта в пользовательский набор шрифтов с назначенными настраиваемыми свойствами. Это, в свою очередь, означает, что настраиваемые шрифты переменных в настоящее время трудно использовать в сочетании с API DirectWrite для удаленных шрифтов. Однако если именованные экземпляры шрифта переменной включены в системный набор шрифтов, ссылки на лица шрифтов для каждого именованного экземпляра уже будут существовать, и их можно добавить в настраиваемые наборы шрифтов, в том числе с использованием значений настраиваемых свойств. Дополнительные сведения см. в разделе Настраиваемые наборы шрифтов. 

При работе с переменными шрифтами перечисления DirectWrite DWRITE_FONT_WEIGHT и DWRITE_FONT_STRETCH тесно связаны с осями изменения веса и ширины, определенными в спецификации OpenType, но не совпадают. Во-первых, числовой масштаб для любой оси вариантов всегда поддерживает дробные значения, а fontWeight и fontStretch используют целые числа. Шкала весовой оси OpenType использует значения в диапазоне от 1 до 1000, которые также поддерживаются fontWeight. Таким образом, изменение значения оси вариативности на fontWeight является относительно незначительным: fontWeight, сообщаемый для именованного экземпляра, может быть округлен по сравнению с точным значением, используемым для определения именованного экземпляра в шрифте. Различие между DirectWrite fontStretch и шкалой оси ширины OpenType больше: DirectWrite использует значения от 1 до 9, следуя значениям usWidthClass таблицы OpenType OS/2, в то время как шкала ширины OpenType использует положительные значения, представляющие процент от нормальной ширины. Документация по usWidthClass в спецификации OpenType предоставляет сопоставление значений от 1 до 9 и процентов от нормальных значений. Значение fontStretch, сообщаемое для именованного экземпляра, может включать округление при преобразовании значений оси ширины. 

При создании IDWriteTextFormat необходимо указать коллекцию шрифтов и свойства шрифтов, совместимые с WSS (имя семейства, вес, растяжение и стиль). Это также применяется при настройке свойств форматирования шрифта в текстовом диапазоне IDWriteTextLayout . Свойства можно получить из объекта IDWriteFontFace3 или объектов IDWriteFont и IDWriteFontFamily , представляющих конкретный именованный экземпляр. Как отмечалось выше, значения, возвращаемые методами GetWeight и GetStretch, могут быть округленными аппроксимациями для фактических значений оси, используемых для определения именованного экземпляра, но DirectWrite сопоставляет сочетание свойств с нужным именованным экземпляром. 

Аналогичным образом, если приложение использует IDWriteFontFallbackBuilder для создания пользовательских резервных данных шрифта, семейства указываются для сопоставлений диапазонов символов с помощью имен семейств, совместимых с WSS. Резервный шрифт в DirectWrite основан на семействах с DirectWrite выборе варианта в резервном семействе, который является ближайшим совпадением для варианта начального семейства. Для вариантов, которые включают измерения, отличные от веса, растяжения и стиля, DirectWrite в настоящее время не сможет выбрать такие варианты в резервном семействе, если только пользовательские резервные данные не были созданы специально для предоставления резервных сопоставлений для семейств с определенными атрибутами, не имеющими WSS, таких как варианты оптического размера "Caption".