Поделиться через


Пользовательские наборы шрифтов

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

Введение

В большинстве случаев приложения используют шрифты, установленные локально в системе. DirectWrite предоставляет доступ к этим шрифтам с помощью методов IDWriteFactory3::GetSystemFontSet или IDWriteFactory::GetSystemFontCollection. В некоторых случаях приложениям также может потребоваться использовать шрифты, которые входят в состав Windows 10, но в настоящее время не установлены в текущей системе. Доступ к таким шрифтам можно получить из службы шрифтов Windows с помощью метода GetSystemFontSet или путем вызова МЕТОДА IDWriteFactory3::GetSystemFontCollection с параметром includeDownloadableFonts, для которых задано значение TRUE. 

Однако в некоторых сценариях приложений приложения должны использовать шрифты, которые не установлены в системе и не предоставляются службой шрифтов Windows. Ниже приведены примеры таких сценариев.

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

DirectWrite предоставляет API для работы с пользовательскими шрифтами в этих и других аналогичных сценариях. Данные настраиваемого шрифта могут поступать из файлов в локальной файловой системе; из удаленных облачных источников, доступных по протоколу HTTP; или из произвольных источников после загрузки в буфер памяти. 

Примечание

Хотя DirectWrite предоставляет API для работы с пользовательскими шрифтами, начиная с Windows 7, в Windows 10 и в Windows 10 Creators Update (предварительная версия сборки 15021 или более поздней версии) добавлены более новые API, которые упрощают реализацию нескольких упомянутых сценариев. В этом разделе рассматриваются API-интерфейсы, доступные в окне 10. Сведения о приложениях, которые должны работать в более ранних версиях Windows, см. в статье Пользовательские коллекции шрифтов (Windows 7/8). 

 

Сводка API-интерфейсов

В этом разделе рассматриваются функции, предоставляемые следующими API:

 

Основные понятия

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

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

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

Набор шрифтов можно использовать для получения соответствующего объекта коллекции шрифтов. Дополнительные сведения см. в разделе Наборы шрифтов и коллекции шрифтов ниже. 

Интерфейс IDWriteFontSet предоставляет методы, позволяющие запрашивать значения свойств, такие как имя семейства или насыщенность шрифта, а также ссылки на лица шрифтов, соответствующие определенным значениям свойств. После фильтрации до определенного выделенного фрагмента можно получить экземпляр интерфейса IDWriteFontFaceReference с методами скачивания (если фактические данные шрифта в настоящее время удалены) для получения соответствующего объекта IDWriteFontFace3 , который можно использовать для макета и отрисовки. 

Интерфейс IDWriteFontFile лежит в основе каждого лица шрифта или ссылки на шрифт. Он представляет расположение файла шрифта и содержит два компонента: загрузчик файлов шрифтов и ключ файла шрифта. Загрузчик файлов шрифтов (IDWriteFontFileLoader) при необходимости используется для открытия файла и возвращает поток с данными (IDWriteFontFileStream). В зависимости от загрузчика данные могут находиться в локальном пути к файлу, удаленный URL-адрес или в буфере памяти. Ключ — это определенное загрузчиком значение, которое однозначно идентифицирует файл в контексте загрузчика, позволяя загрузчику находить данные и создавать для него поток. 

Настраиваемые шрифты можно легко добавить в пользовательский набор шрифтов, который, в свою очередь, можно использовать для фильтрации или упорядочения сведений о шрифтах в таких целях, как создание пользовательского интерфейса средства выбора шрифтов. Набор шрифтов также можно использовать для создания коллекции шрифтов для использования в ИНТЕРФЕЙСАх API более высокого уровня, таких как IDWriteTextFormat и IDWriteTextLayout. Интерфейс IDWriteFontSetBuilder можно использовать для создания настраиваемого набора шрифтов, который включает несколько настраиваемых шрифтов. Его также можно использовать для создания настраиваемого набора шрифтов, который смешивает пользовательские шрифты и системные шрифты; или сочетание шрифтов с различными источниками фактических данных — локальным хранилищем, удаленными URL-адресами и памятью. 

Как уже упоминалось, ссылка на шрифт может ссылаться на данные шрифта в удаленном источнике, но данные должны быть локальными, чтобы получить объект распознавания шрифта, который можно использовать для макета и отрисовки. Загрузка удаленных данных обрабатывается очередью загрузки шрифтов. Приложения могут использовать интерфейс IDWriteFontDownloadQueue для постановки в очередь запросов на скачивание удаленных шрифтов для запуска процесса загрузки, а также для регистрации объекта IDWriteFontDownloadListener для выполнения действий по завершении процесса загрузки. 

Для большинства интерфейсов, описанных здесь, DirectWrite предоставляет системные реализации. Исключением является интерфейс IDWriteFontDownloadListener , который приложение реализует для выполнения конкретных действий приложения при локальном скачивании удаленных шрифтов. Приложения могут иметь причину предоставлять собственные пользовательские реализации для некоторых других интерфейсов, хотя это потребуется только в конкретных, более сложных сценариях. Например, приложению потребуется предоставить пользовательскую реализацию интерфейса IDWriteFontFileLoader для обработки файлов шрифтов в локальном хранилище, использующее формат контейнера WOFF2. Дополнительные сведения будут представлены ниже. 

Шрифты и форматы файлов шрифтов

Еще одна ключевая концепция, которую полезно понять, — это связь между отдельными лицами шрифтов и файлами шрифтов, которые их содержат. Идея файла шрифта OpenType (TTF или OTF), содержащего один шрифт, знакома. Но формат шрифта OpenType также позволяет использовать коллекцию шрифтов OpenType (TTC или OTC), которая представляет собой один файл, содержащий несколько шрифтов. Файлы коллекции OpenType часто используются для больших шрифтов, которые тесно связаны и имеют одинаковые значения для определенных данных шрифтов. Объединяя шрифты в одном файле, общие данные можно удалить. По этой причине ссылка на шрифт или начертание шрифта должна ссылаться не только на файл шрифта (или эквивалентный источник данных), но и указывать индекс шрифта в этом файле для общего случая, когда файл может быть файлом коллекции. 

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

Некоторые API DirectWrite могут работать с отдельными шрифтами, в то время как другие API могут обрабатывать файлы, которые могут включать файлы коллекции OpenType с несколькими лицами. Аналогичным образом некоторые API обрабатывают только необработанные данные в формате OpenType, в то время как другие API могут обрабатывать упакованные форматы контейнеров WOFF и WOFF2. Эти сведения приведены в приведенном ниже обсуждении. 

Наборы шрифтов и коллекции шрифтов

Некоторые приложения могут быть реализованы для работы со шрифтами с помощью интерфейса IDWriteFontCollection . Существует прямое соответствие между коллекцией шрифтов и набором шрифтов. Каждый из них может содержать одни и те же шрифты, но они представляют их с другой организацией. Из любой коллекции шрифтов можно получить соответствующий набор шрифтов, и наоборот.

При работе с рядом настраиваемых шрифтов проще всего использовать интерфейс построителя наборов шрифтов, чтобы создать пользовательский набор шрифтов, а затем получить коллекцию шрифтов после создания набора шрифтов. Процесс создания настраиваемого набора шрифтов будет подробно описан ниже. Чтобы получить интерфейс IDWriteFontCollection1 из набора шрифтов, используется метод IDWriteFactory3::CreateFontCollectionFromFontSet .

Если приложение имеет объект коллекции и должно получить соответствующий набор шрифтов, это можно сделать с помощью метода IDWriteFontCollection1::GetFontSet

Распространенные сценарии

В этом разделе описываются некоторые из наиболее распространенных сценариев, связанных с пользовательскими наборами шрифтов:

  • Создание настраиваемого набора шрифтов с использованием произвольных шрифтов по путям в локальной файловой системе.
  • Создание настраиваемого набора шрифтов с помощью известных шрифтов (возможно, в составе приложения), которые хранятся в локальной файловой системе.
  • Создание настраиваемого набора шрифтов с помощью известных удаленных шрифтов в Интернете.
  • Создание настраиваемого набора шрифтов с использованием данных шрифта, загруженных в память.

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

Создание набора шрифтов с использованием произвольных шрифтов в локальной файловой системе

При работе с произвольным набором файлов шрифтов в локальном хранилище метод IDWriteFontSetBuilder1::AddFontFile удобен, так как в одном вызове он может обрабатывать все грани шрифтов в файле коллекции шрифтов OpenType, а также все экземпляры шрифта переменной OpenType. Он доступен в Windows 10 Creators Update (предварительная версия сборки 15021 или более поздней версии) и рекомендуется всегда, когда он доступен. 

Чтобы использовать этот метод, используйте следующий процесс.

1. Начните с создания интерфейса IDWriteFactory5 :
IDWriteFactory5* pDWriteFactory; 
HRESULT hr = DWriteCreateFactory( 
  DWRITE_FACTORY_TYPE_SHARED, 
  __uuidof(IDWriteFactory5), 
  reinterpret_cast<IUnknown**>(&pDWriteFactory) 
); 

 
2. Используйте фабрику для получения интерфейса IDWriteFontSetBuilder1:

IDWriteFontSetBuilder1* pFontSetBuilder; 
if (SUCCEEDED(hr)) 
{ 
  hr = pDWriteFactory->CreateFontSetBuilder(&pFontSetBuilder); 
}  
                
  1. Для каждого файла шрифта в локальной файловой системе создайте idWriteFontFile , который ссылается на него:
IDWriteFontFile* pFontFile; 
if (SUCCEEDED(hr)) 
{ 
  hr = pDWriteFactory->CreateFontFileReference(pFilePath, /* lastWriteTime*/ nullptr, &pFontFile); 
} 

 
4. Добавьте объект IDWriteFontFile в построитель наборов шрифтов с помощью метода AddFontFile :

hr = pFontSetBuilder->AddFontFile(pFontFile); 

Если путь к файлу, указанный в вызове CreateFontFileReference , ссылается на нечто, отличное от поддерживаемого файла OpenType, то вызов AddFontFile вернет ошибку, DWRITE_E_FILEFORMAT.

  1. После добавления всех файлов в построитель наборов шрифтов можно создать пользовательский набор шрифтов:
IDWriteFontSet* pFontSet; 
hr = pFontSetBuilder->CreateFontSet(&pFontSet); 

 

Если приложение должно работать в версиях Windows 10, предшествующих Windows 10 Creators Update, метод AddFontFile будет недоступен. Доступность можно определить, создав интерфейс IDWriteFactory3 , а затем используя QueryInterface, чтобы попытаться получить интерфейс IDWriteFactory5 . Если это будет успешно, интерфейс IDWriteFontSetBuilder1 и метод AddFontFile также будут доступны.

Если метод AddFontFile недоступен, для добавления отдельных шрифтов необходимо использовать метод IDWriteFontSetBuilder::AddFontFaceReference . Чтобы разрешить файлы коллекции шрифтов OpenType, содержащие несколько лиц, можно использовать метод IDWriteFontFile::Analyze для определения количества лиц, содержащихся в файле. Процесс выглядит следующим образом.

1. Начните с создания интерфейса IDWriteFactory3 :
IDWriteFactory3* pDWriteFactory; 
HRESULT hr = DWriteCreateFactory( 
DWRITE_FACTORY_TYPE_SHARED, 
  __uuidof(IDWriteFactory5), 
  reinterpret_cast<IUnknown**>(&pDWriteFactory) 
); 
  1. Используйте фабрику для получения интерфейса IDWriteFontSetBuilder :
IDWriteFontSetBuilder* pFontSetBuilder; 
if (SUCCEEDED(hr)) 
{ 
  hr = pDWriteFactory->CreateFontSetBuilder(&pFontSetBuilder); 
} 
  1. Для каждого файла шрифта создайте IDWriteFontFile, как описано выше:
IDWriteFontFile* pFontFile; 
if (SUCCEEDED(hr)) 
{ 
  hr = pDWriteFactory->CreateFontFileReference(pFilePath, /* lastWriteTime*/ nullptr, &pFontFile); 
} 

Вместо добавления файла непосредственно в построитель наборов шрифтов необходимо определить количество лиц и создать отдельные объекты IDWriteFontFaceReference
4. Используйте метод Analyze , чтобы получить количество лиц в файле. 

BOOL isSupported; 
DWRITE_FONT_FILE_TYPE fileType; 
UINT32 numberOfFonts; 
hr = pFontFile->Analyze(&isSupported, &fileType, /* face type */ nullptr, &numberOfFonts); 

Метод Analyze также задает значения для параметров isSupported и fileType. Если файл не является поддерживаемым форматом, то параметр isSupported будет иметь значение FALSE, и можно выполнить соответствующие действия, такие как игнорирование файла. 
5. Перебор количества шрифтов, заданных в параметре numberOfFonts. В цикле создайте idWriteFontFaceReference для каждой пары "файл-индекс" и добавьте его в построитель наборов шрифтов. 

for (uint32_t fontIndex = 0; fontIndex < numberOfFonts; fontIndex++) 
{ 
  IDWriteFontFaceReference* pFontFaceReference;
  hr = pDWriteFactory->CreateFontFaceReference(pFontFile, fontIndex, DWRITE_FONT_SIMULATIONS_NONE, &pFontFaceReference);

  if (SUCCEEDED(hr))
  {
    hr = pFontSetBuilder->AddFontFaceReference(pFontFaceReference);
  }
} 
  1. После добавления всех лиц в построитель наборов шрифтов создайте пользовательский набор шрифтов, как показано выше.

Приложение можно сконструировать таким образом, чтобы оно использовало предпочтительный метод AddFontFile при запуске в Windows 10 Creators Update, но при запуске в более ранних версиях Windows 10 оно будет использовать метод AddFontFaceReference . Проверьте доступность интерфейса IDWriteFactory5 , как описано выше, и затем ветвь соответствующим образом. Этот подход показан в примере DirectWrite пользовательских наборов шрифтов

Создание набора шрифтов с использованием известных шрифтов в локальной файловой системе

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

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

Пользовательские значения информационных свойств можно назначать при вызове метода IDWriteFontSetBuilder::AddFontFaceReference . Метод для этого выглядит следующим образом. его можно использовать в любой версии Windows 10. 

Как показано выше, начните с получения интерфейсов IDWriteFactory3 и IDWriteFontSet . Для каждого добавляемого пользовательского шрифта создайте idWriteFontFaceReference, как показано выше. Однако перед добавлением в построитель наборов шрифтов (в цикле на шаге 5, показанном выше), приложение определяет пользовательские значения свойств, которые будут использоваться. 

Набор значений настраиваемых свойств определяется с помощью массива DWRITE_FONT_PROPERTY структур. Каждый из них идентифицирует определенное свойство из перечисления DWRITE_FONT_PROPERTY_ID и соответствующее значение свойства, которое будет использоваться.  

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

В следующем примере определяются пользовательские значения для трех информационных свойств: имя семейства, полное имя и вес шрифта. 

DWRITE_FONT_PROPERTY props[] = 
{ 
  { DWRITE_FONT_PROPERTY_ID_FAMILY_NAME, L"My Icon Font", L"en-US" }, 
  { DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"My Icon Font", L"en-US" }, 
  { DWRITE_FONT_PROPERTY_ID_WEIGHT, L"400", nullptr } 
}; 
               
            

Определив нужный массив значений свойств для шрифта, вызовите Метод AddFontFaceRefence, передав массив свойств, а также ссылку на лицо шрифта. 

hr = pFontSetBuilder->AddFontFaceReference(pFontFaceReference, props, ARRAYSIZE(props)); 

 

После добавления всех настраиваемых лиц шрифтов в построитель наборов шрифтов вместе с их настраиваемыми свойствами создайте настраиваемый набор шрифтов, как показано выше. 

Создание настраиваемого набора шрифтов с помощью известных удаленных шрифтов в Интернете

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

Последовательность вызовов API для добавления удаленных шрифтов в набор шрифтов аналогична последовательности, описанной в предыдущем сценарии. Поскольку данные шрифта являются удаленными, операции, связанные с чтением фактических данных шрифта, будут отличаться от операций с файлами в локальном хранилище. В этом случае в Windows 10 Creators Update добавлен новый интерфейс более низкого уровня IDWriteRemoteFontFileLoader

Чтобы использовать удаленный загрузчик файлов шрифтов, его необходимо сначала зарегистрировать в фабрике DirectWrite. Загрузчик должен находиться в приложении до тех пор, пока используются связанные с ним шрифты. После того как шрифты больше не используются и в какой-то момент до уничтожения фабрики загрузчик необходимо отменить регистрацию. Это можно сделать в деструкторе для класса, которому принадлежит объект загрузчика. Эти действия будут показаны ниже. 

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

1. Создайте интерфейс IDWriteFactory5, как показано выше.  2. Создайте интерфейс IDWriteFontSetBuilder , как показано выше.  3. Используйте фабрику для получения IDWriteRemoteFontFileLoader
IDWriteRemoteFontFileLoader* pRemoteFontFileLoader; 
if (SUCCEEDED(hr)) 
{ 
    hr = pDWriteFactory->CreateHttpFontFileLoader( 
        /* referrerURL */ nullptr, 
        /* extraHeaders */ nullptr, 
        &pRemoteFontFileLoader 
    ); 
} 

Это возвращает предоставленную системой реализацию интерфейса удаленного загрузчика файлов шрифтов, который может обрабатывать HTTP-взаимодействия для скачивания данных шрифта от имени приложения. URL-адрес ссылающегося объекта или дополнительные заголовки можно указать, если это требуется для службы шрифтов или служб, которые являются источником шрифтов.  

Важно!

Примечание по безопасности. При попытке получить удаленный шрифт злоумышленник может подделать предполагаемый сервер, который будет вызываться. В этом случае злоумышленнику будут раскрыты URL-адреса целевого объекта и ссылки, а также сведения о заголовке. Разработчики приложений несут ответственность за снижение этого риска. Рекомендуется использовать протокол HTTPS, а не HTTP. 

 

Один удаленный загрузчик файлов шрифтов можно использовать для нескольких шрифтов, хотя разные загрузчики можно использовать, если шрифты получены из нескольких служб с разными требованиями к URL-адресу ссылочного объекта или дополнительным заголовкам. 
4. Зарегистрируйте удаленный загрузчик файлов шрифтов в фабрике. 

 if (SUCCEEDED(hr)) 
 { 
     hr = pDWriteFactory->RegisterFontFileLoader(pRemoteFontFileLoader); 
 } 

С этого момента действия по созданию настраиваемого набора шрифтов аналогичны описанным для известных локальных файлов шрифтов с двумя важными исключениями. Во-первых, объект IDWriteFontFile создается с помощью интерфейса удаленного загрузчика файлов шрифтов, а не с помощью фабрики. Во-вторых, нельзя использовать метод Analyze, так как данные шрифта не являются локальными. Вместо этого приложение должно знать, является ли удаленный файл шрифта файлом коллекции шрифтов OpenType, и если да, то оно должно знать, какой из шрифтов в коллекции будет использоваться, и индекс для каждого из них. Следовательно, оставшиеся шаги приведены ниже. 
5. Для каждого удаленного файла шрифта используйте интерфейс удаленного загрузчика файлов шрифтов, чтобы создать IDWriteFontFile, указав URL-адрес, необходимый для доступа к файлу шрифта. 

 IDWriteFontFile* pFontFile; 
 hr = pRemoteFontFileLoader->CreateFontFileReferenceFromUrl( 
     pDWriteFactory, 
     /* baseUrl */ L"https://github.com/", 
     /* fontFileUrl */ L"winjs/winjs/blob/master/src/fonts/Symbols.ttf?raw=true", 
     &pFontFile 
 ); 

Обратите внимание, что полный URL-адрес можно указать в параметре fontFileUrl или разделить на базовую и относительную части. Если указан базовый URL-адрес, объединение значений baseUrl и fontFileUrl должно содержать полный URL-адрес, DirectWrite не будет предоставлять дополнительных разделителей.

Важно!

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

 

Также обратите внимание, что URL-адрес может указывать на необработанный файл шрифта OpenType (TTF, OTF, TTC, OTC), но он также может указывать на шрифты в файле контейнера WOFF или WOFF2. Если имеется ссылка на файл WOFF или WOFF2, то DirectWrite реализации удаленного загрузчика файлов шрифтов автоматически распаковыт данные шрифта из файла контейнера. 
6. Для каждого индекса распознавания шрифта в удаленном файле шрифта, который будет использоваться, создайте IDWriteFontFaceReference

 IDWriteFontFaceReference* pFontFaceReference; 
 hr = pDWriteFactory->CreateFontFaceReference(pFontFile, /* faceIndex */ 0, DWRITE_FONT_SIMULATIONS_NONE, &pFontFaceReference);
  1. Определите пользовательские свойства для лица шрифта, как показано выше. 
  2. Добавьте ссылку на лицо шрифта вместе с пользовательскими свойствами в построитель наборов шрифтов, как показано выше. 
  3. После добавления всех шрифтов в построитель наборов шрифтов создайте набор шрифтов, как показано выше. 
  4. В какой-то момент, когда удаленные шрифты больше не будут использоваться, отмените регистрацию удаленного загрузчика файлов шрифтов. 
hr = pDWriteFactory->UnregisterFontFileLoader(pRemoteFontFileLoader); 

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

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

Чтобы запросить скачивание всего шрифта перед его использованием, можно использовать метод IDWriteFontFaceReference::EnqueueFontDownloadRequest . Если шрифт очень большой, для обработки определенных строк может потребоваться только часть данных. DirectWrite предоставляет дополнительные методы, которые можно использовать для запроса частей данных шрифта, необходимых для определенного содержимого, EnqueueCharacterDownloadRequest и EnqueueGlyphDownloadRequest.  

Предположим, что в приложении используется подход, позволяющий изначально выполнять обработку с использованием локальных, альтернативных или резервных шрифтов. Метод IDWriteFontFallback::MapCharacters можно использовать для определения локальных резервных шрифтов, а также автоматически помещает в очередь запрос на скачивание предпочтительного шрифта. Кроме того, если используется IDWriteTextLayout и некоторые или весь текст в макете отформатирован с помощью удаленной ссылки на шрифт, то DirectWrite будет автоматически использовать метод MapCharacters для получения локальных резервных шрифтов и постановки в очередь запроса на скачивание данных удаленного шрифта. 

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

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

После добавления удаленных запросов шрифтов в очередь необходимо инициировать процесс скачивания. Если в IDWriteTextLayout используются удаленные шрифты, скачивание будет инициировано автоматически, когда приложение вызывает методы IDWriteTextLayout , которые выполняют операции макета или отрисовки, такие как Методы GetLineMetrics или Draw. В других сценариях приложение должно инициировать скачивание напрямую, вызвав IDWriteFontDownloadQueue::BeginDownload.  

После завершения скачивания приложение должно выполнить соответствующие действия— продолжить с ожидающими операциями или повторять операции, которые были изначально выполнены с помощью резервных шрифтов. (Если используется макет текста DirectWrite, то idWriteTextLayout3::InvalidateLayout можно использовать для очистки временных результатов, вычисляемых с помощью резервных шрифтов.) Чтобы приложение оповестило о завершении процесса загрузки и соответствующих действий, оно должно предоставить реализацию интерфейса IDWriteFontDownloadListener и передать его в вызов BeginDownload. 

Важно!

Примечание о безопасности и производительности. При попытке получить удаленный шрифт нет никакой гарантии, что Windows получит ответ от сервера. Если сервер не отвечает, Windows в конечном итоге истечет время ожидания, хотя это может занять несколько минут, если несколько удаленных шрифтов извлекаются, но не удается. Вызов BeginDownload возвращается немедленно. Приложения не должны блокировать пользовательский интерфейс при ожидании вызова IDWriteFontDownloadListener::D ownloadCompleted

 

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

Создание настраиваемого набора шрифтов с использованием данных шрифта, загруженных в память

Так же, как низкоуровневые операции чтения данных из файла шрифта отличаются для файлов на локальном диске и удаленных файлов в Интернете, то же самое справедливо и для данных шрифтов, загруженных в буфер памяти. В Windows 10 Creators Update IDWriteInMemoryFontFileLoader добавлен новый низкоуровневый интерфейс для обработки данных шрифтов в памяти. 

Как и в случае с удаленным загрузчиком файлов шрифтов, загрузчик файлов шрифтов в памяти необходимо сначала зарегистрировать в фабрике DirectWrite. Загрузчик должен храниться приложением до тех пор, пока используются связанные с ним шрифты. После того как шрифты больше не используются и в какой-то момент перед уничтожением фабрики загрузчик необходимо отменить регистрацию. Это можно сделать в деструкторе для класса, которому принадлежит объект загрузчика. Эти действия будут показаны ниже. 

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

DirectWrite предполагается, что данные шрифта в необработанном формате OpenType эквивалентны файлу OpenType (.ttf, .otf, .ttc, .otc), но в памяти, а не на диске. Данные не могут быть в формате контейнера WOFF или WOFF2. Данные могут представлять коллекцию шрифтов OpenType. Если пользовательские свойства не используются, то метод IDWriteFontSetBuilder1::AddFontFile можно использовать для добавления всех шрифтов в данные в одном вызове. 

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

Ниже приведен метод создания настраиваемого набора шрифтов с использованием данных шрифтов в памяти. для этого требуется Windows 10 Creators Update. При этом предполагается объект data-owner, реализованный приложением, который реализует IUnknown, а также методы, возвращающие указатель на буфер памяти и размер буфера. 

1. Создайте интерфейс IDWriteFactory5, как показано выше. 2. Создайте интерфейс [**IDWriteFontSetBuilder1**](/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontsetbuilder1), как показано выше. 3. Используйте фабрику для получения IDWriteInMemoryFontFileLoader. 
 IDWriteInMemoryFontFileLoader* pInMemoryFontFileLoader; 
if (SUCCEEDED(hr)) 
{ 
    hr = pDWriteFactory->CreateInMemoryFontFileLoader(&pInMemoryFontFileLoader); 
}

Это возвращает предоставленную системой реализацию интерфейса загрузчика файлов шрифтов в памяти. 
4. Зарегистрируйте загрузчик файлов шрифтов в памяти в фабрике. 

if (SUCCEEDED(hr)) 
{ 
    hr = pDWriteFactory->RegisterFontFileLoader(pInMemoryFontFileLoader); 
}

 
5. Для каждого файла шрифта в памяти используйте загрузчик файлов шрифтов в памяти, чтобы создать IDWriteFontFile

IDWriteFontFile* pFontFile; 
hr = pInMemoryFontFileLoader->CreateInMemoryFontFileReference( 
    pDWriteFactory, 
    pFontDataOwner->fontData /* returns void* */, 
    pFontDataOwner->fontDataSize /* returns UINT32 */, 
    pFontDataOwner /* ownerObject, owns the memory with font data and implements IUnknown */, 
    &pFontFile 
); 

 
6. Добавьте объект IDWriteFontFile в построитель наборов шрифтов с помощью метода AddFontFile , как показано выше.  При необходимости приложение может вместо этого создать отдельные объекты IDWriteFontFaceReference на основе IDWriteFontFile, при необходимости определить пользовательские свойства для каждой ссылки на лицо шрифта, а затем добавить ссылку на шрифт с пользовательскими свойствами в набор шрифтов с помощью метода AddFontFaceReference , как показано выше. 
7. После добавления всех шрифтов в построитель наборов шрифтов создайте настраиваемый набор шрифтов, как показано выше. 
8. В какой-то момент, когда шрифты в памяти больше не будут использоваться, отмените регистрацию загрузчика файлов шрифтов в памяти. 

hr = pDWriteFactory->UnregisterFontFileLoader(pInMemoryFontFileLoader);

 

Расширенные сценарии

Некоторые приложения могут предъявлять особые требования, требующие более сложной обработки, чем описано выше. 

Объединение наборов шрифтов

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

Чтобы объединить два или более наборов шрифтов, метод IDWriteFontSetBuilder::AddFontSet добавляет все шрифты в заданном наборе шрифтов для добавления в построитель наборов шрифтов в одном вызове. Если в новом наборе шрифтов нужны только определенные шрифты из существующего набора шрифтов, метод IDWriteFontSet::GetMatchingFonts можно использовать для получения нового объекта набора шрифтов, отфильтрованного для включения только шрифтов, соответствующих указанным свойствам. Эти методы предоставляют простой способ создания настраиваемого набора шрифтов, объединяющего шрифты из двух или более существующих наборов шрифтов.

Использование локальных данных шрифта WOFF или WOFF2

Если приложение содержит файлы шрифтов в локальной файловой системе или буфере памяти, но использует форматы контейнеров WOFF или WOFF2, DirectWrite (Windows 10 Creator Update или более поздней версии) предоставляет метод для распаковки формата контейнера IDWriteFactory5::UnpackFontFile, который возвращает IDWriteFontFileStream

Однако приложению потребуется способ получить IDWriteFontFileStream в объект загрузчика файла шрифта. Один из способов сделать это — создать пользовательскую реализацию IDWriteFontFileLoader , которая создает оболочку для потока. Как и в случае с другими загрузчиками файлов шрифтов, его необходимо зарегистрировать перед использованием и отменить регистрацию, прежде чем фабрика выйдет из область.  

Если пользовательский загрузчик также будет использоваться с необработанными (не упакованными) файлами шрифтов, то приложению также потребуется предоставить пользовательскую реализацию интерфейса IDWriteFontFileStream для обработки этих файлов. Однако существуют более простые способы использования API, описанные выше для обработки необработанных файлов шрифтов. Потребность в реализации настраиваемого потока можно избежать, используя отдельные пути кода для упакованных файлов шрифтов и необработанных файлов шрифтов. 

После создания объекта пользовательского загрузчика файлов шрифтов данные упакованных файлов шрифтов добавляются в загрузчик средствами конкретного приложения. Загрузчик может обрабатывать несколько файлов шрифтов, каждый из которых определяется с помощью ключа, определяемого приложением, который непрозрачн для DirectWrite. После добавления упакованного файла шрифта в загрузчик используется метод IDWriteFactory::CreateCustomFontFileReference для получения IDWriteFontFile на основе этого загрузчика для данных шрифта, определенных заданным ключом.  

Фактическое распаковка данных шрифта может выполняться при добавлении шрифтов в загрузчик, но также может обрабатываться в методе IDWriteFontFileLoader::CreateStreamFromKey, который DirectWrite будет вызываться при первом чтении данных шрифта. 

После создания объекта IDWriteFontFile оставшиеся действия по добавлению шрифтов в пользовательский набор шрифтов будут выполняться, как описано выше. 

Реализация этого подхода показана в примере DirectWrite пользовательских наборов шрифтов

Использование DirectWrite механизмов удаленных шрифтов с пользовательской низкоуровневой сетевой реализацией

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

В этой ситуации приложению потребуется создать пользовательскую реализацию интерфейса IDWriteRemoteFontFileLoader , которая взаимодействует с другими интерфейсами более низкого уровня необходимыми способами. Приложению также потребуется предоставить пользовательские реализации этих интерфейсов более низкого уровня: IDWriteRemoteFontFileStream и IDWriteAsyncResult. Эти три интерфейса имеют методы обратного вызова, которые DirectWrite будут вызываться во время операций загрузки. 

При вызове IDWriteFontDownloadQueue::BeginDownload DirectWrite будет выполнять запросы к удаленному загрузчику файлов шрифтов о расположении данных и запрашивать удаленный поток. Если данные не являются локальными, будет вызываться метод BeginDownload потока. Реализация потока не должна блокировать этот вызов, но должна немедленно возвращаться, передавая объект IDWriteAsyncResult, который предоставляет дескриптор ожидания, DirectWrite будет использовать для ожидания асинхронной операции загрузки. Реализация пользовательского потока отвечает за обработку удаленного взаимодействия. После возникновения события завершения DirectWrite вызовет IDWriteAsyncResult::GetResult, чтобы определить результат операции. Если результат будет успешным, ожидается, что последующие вызовы ReadFragment в поток для скачанных диапазонов будут успешно завершены. 

Важно!

Примечание по безопасности и производительности. При попытке получить удаленный шрифт злоумышленник может подделать вызываемый сервер или не ответить на него. При реализации пользовательских сетевых взаимодействий вы можете иметь больший контроль над устранением рисков, чем при работе со сторонними серверами. Тем не менее, вы должны рассмотреть соответствующие меры по устранению рисков, чтобы избежать раскрытия информации или отказа в обслуживании. Рекомендуется использовать безопасные протоколы, такие как HTTPS. Кроме того, необходимо выполнить сборку в течение некоторого времени ожидания, чтобы дескриптор событий, возвращенный в DirectWrite в конечном итоге был задан. 

 

Вспомогательные сценарии в более ранних версиях Windows

Описанные сценарии могут поддерживаться в DirectWrite в более ранних версиях Windows, но потребует гораздо больше пользовательской реализации со стороны приложения с использованием более ограниченных API, которые были доступны до Windows 10. Дополнительные сведения см. в статье Пользовательские коллекции шрифтов (Windows 7/8).