Conjuntos de Fontes Personalizados
Este tópico descreve várias maneiras pelas quais você pode usar fontes personalizadas em seu aplicativo.
- introdução
- Resumo de APIs
- principais conceitos
- fontes e formatos de arquivo de fonte
- conjuntos de fontes e coleções de fontes
-
cenários comuns
- Criar um conjunto de fontes usando fontes arbitrárias no sistema de arquivos local
- Criar um conjunto de fontes usando fontes conhecidas no sistema de arquivos local
- Criar um conjunto de fontes personalizado usando fontes remotas conhecidas no Web
- Criar um conjunto de fontes personalizado usando dados de fonte carregados em de memória
- cenários avançados
Introdução
Na maioria das vezes, os aplicativos usam as fontes instaladas localmente no sistema. O DirectWrite fornece acesso a essas fontes usando os métodos IDWriteFactory3::GetSystemFontSet ou IDWriteFactory::GetSystemFontCollection. Em alguns casos, os aplicativos também podem querer usar fontes incluídas como parte do Windows 10, mas que não estão instaladas atualmente no sistema atual. Essas fontes podem ser acessadas do serviço de fonte do Windows usando o método GetSystemFontSet ou chamando IDWriteFactory3::GetSystemFontCollection com includeDownloadableFonts definido como TRUE.
Em alguns cenários de aplicativo, no entanto, os aplicativos precisam usar fontes que não estão instaladas no sistema e não são fornecidas pelo Serviço de Fonte do Windows. Veja a seguir exemplos desses cenários:
- As fontes são inseridas como recursos em um binário de aplicativo.
- Os arquivos de fonte são agrupados em um pacote de aplicativo e armazenados em disco na pasta de instalação do aplicativo.
- O aplicativo é uma ferramenta de desenvolvimento de fontes que precisa carregar arquivos de fonte especificados pelo usuário.
- As fontes são inseridas em arquivos de documento que podem ser exibidos ou editados no aplicativo.
- O aplicativo usa fontes obtidas de um serviço de fonte da Web público.
- O aplicativo usa dados de fonte transmitidos por meio de um protocolo de rede privada.
O DirectWrite fornece APIs para trabalhar com fontes personalizadas nesses e em outros cenários semelhantes. Os dados de fonte personalizados podem vir de arquivos no sistema de arquivos local; de fontes remotas baseadas em nuvem acessadas usando HTTP; ou de fontes arbitrárias depois de terem sido carregadas em um buffer de memória.
Nota
Embora o DirectWrite tenha fornecido APIs para trabalhar com fontes personalizadas desde o Windows 7, APIs mais recentes foram adicionadas no Windows 10 e novamente na Atualização de Criadores do Windows 10 (versão prévia do build 15021 ou posterior) que facilitam a implementação de vários dos cenários mencionados. Este tópico se concentra nas APIs disponíveis na Janela 10. Para aplicativos que precisam funcionar em versões anteriores do Windows, consulte coleções de fontes personalizadas (Windows 7/8).
Resumo das APIs
Este tópico se concentra na funcionalidade fornecida pelas seguintes APIs:
- interface deIDWriteFontSet
- interfaceIDWriteFontSetBuilder
- interface deIDWriteFontSetBuilder1
- interfaceIDWriteFontFaceReference
- interfaceIDWriteFontFile
- métodoIDWriteFactory::CreateFontFileReference
- método IDWriteFactory::CreateCustomFontFileReference
- métodosIDWriteFactory3::CreateFontFaceReference
- estrutura DWRITE_FONT_PROPERTY
- enumeração DWRITE_FONT_PROPERTY_ID
- interface deIDWriteFontFileLoader
- método IDWriteFactory::RegisterFontFileLoader
- método IDWriteFactory::UnregisterFontFileLoader
- método IDWriteFactory5::CreateInMemoryFontFileLoader
- interface de IDWriteInMemoryFontFileLoader
- método IDWriteFactory5::CreateHttpFontFileLoader
- interface de IDWriteRemoteFontFileLoader
- interface deIDWriteFontDownloadQueue
- interface deIDWriteFontDownloadListener
- interface deIDWriteFontFileStream
- interface deIDWriteRemoteFontFileStream
- interface deIDWriteAsyncResult
- método IDWriteFactory5::AnalyzeContainerType
- método IDWriteFactory5::UnpackFontFile
Principais conceitos
Para entender as APIs do DirectWrite para trabalhar com fontes personalizadas, pode ser útil entender o modelo conceitual que está por trás dessas APIs. Os principais conceitos serão descritos aqui.
Quando o DirectWrite faz layout de texto ou renderização real, ele precisa acessar dados de fonte reais. Um objeto de face de fonte contém dados de fonte reais, que devem existir no sistema local. Mas para outras operações, como verificar a disponibilidade de uma fonte específica ou apresentar opções de fonte a um usuário, tudo o que é necessário é uma referência a uma fonte específica, não aos próprios dados de fonte. No DirectWrite, um objeto de referência de rosto de fonte contém apenas as informações necessárias para localizar e instanciar uma fonte. Como a referência de face da fonte não contém dados reais, o DirectWrite pode lidar com referências de rosto de fonte para as quais os dados reais estão em um local de rede remota, bem como quando os dados reais são locais.
Um conjunto de fontes é um conjunto de referências de rosto de fonte, juntamente com determinadas propriedades básicas e informativas que podem ser usadas na referência à fonte ou na comparação com outras fontes, como o nome da família ou um valor de peso de fonte. Os dados reais das várias fontes podem ser locais ou podem ser todos remotos ou alguma mistura.
Um conjunto de fontes pode ser usado para obter um objeto de coleção de fontes correspondente. Consulte conjuntos de fontes e coleções de fontes abaixo para obter mais detalhes.
A interface IDWriteFontSet fornece métodos que permitem a consulta de valores de propriedade, como nome de família ou peso da fonte, ou para referências de rosto de fonte que correspondem a valores de propriedade específicos. Depois de filtrar para uma seleção específica, uma instância da interface IDWriteFontFaceReference pode ser obtida, com métodos para download (se os dados de fonte reais forem atualmente remotos), para obter o objetoIDWriteFontFace3correspondente que pode ser usado para layout e renderização.
A interface IDWriteFontFile está subjacente a cada referência de rosto de fonte ou de rosto de fonte. Isso representa o local de um arquivo de fonte e tem dois componentes: um carregador de arquivo de fonte e uma chave de arquivo de fonte. O carregador de arquivo de fonte (IDWriteFontFileLoader) é usado para abrir um arquivo, se necessário, e retorna um fluxo com os dados (IDWriteFontFileStream). Dependendo do carregador, os dados podem estar localizados em um caminho de arquivo local, em uma URL remota ou em um buffer de memória. A chave é um valor definido pelo carregador que identifica exclusivamente o arquivo dentro do contexto do carregador, permitindo que o carregador localize os dados e crie um fluxo para ele.
Fontes personalizadas podem ser facilmente adicionadas a um conjunto de fontes personalizado, que por sua vez pode ser usado para filtrar ou organizar informações de fonte para fins como criar uma interface do usuário do seletor de fontes. O conjunto de fontes também pode ser usado para criar uma coleção de fontes para uso em APIs de nível superior, como IDWriteTextFormat e IDWriteTextLayout. A interfaceIDWriteFontSetBuilderpode ser usada para criar um conjunto de fontes personalizado que inclua várias fontes personalizadas. Ele também pode ser usado para criar um conjunto de fontes personalizado que mistura fontes personalizadas e fontes fornecidas pelo sistema; ou que mistura fontes com fontes diferentes para os dados reais – armazenamento local, URLs remotas e memória.
Conforme mencionado, uma referência de face de fonte pode se referir a dados de fonte em uma fonte remota, mas os dados devem ser locais para obter um objeto de face de fonte que possa ser usado para layout e renderização. O download de dados remotos é tratado por uma fila de download de fontes. Os aplicativos podem usar a interfaceIDWriteFontDownloadQueuepara enfileirar solicitações para baixar fontes remotas para iniciar o processo de download e registrar um objeto IDWriteFontDownloadListener para executar uma ação quando o processo de download for concluído.
Para a maioria das interfaces descritas aqui, o DirectWrite fornece implementações do sistema. A única exceção é a interface IDWriteFontDownloadListener, que um aplicativo implementa para executar ações específicas do aplicativo quando fontes remotas forem baixadas localmente. Os aplicativos podem ter motivos para fornecer suas próprias implementações personalizadas para determinadas outras interfaces, embora isso só seja necessário em cenários específicos e mais avançados. Por exemplo, um aplicativo precisaria fornecer uma implementação personalizada da interfaceIDWriteFontFileLoaderpara manipular arquivos de fonte no armazenamento local que usam o formato de contêiner WOFF2. Detalhes adicionais serão fornecidos abaixo.
Fontes e formatos de arquivo de fonte
Outro conceito importante que é útil para entender é a relação entre rostos de fonte individuais e arquivos de fonte que os contêm. A ideia de um arquivo de fonte OpenType (.ttf ou .otf) que contém uma única fonte é familiar. Mas o formato de fonte OpenType também permite uma Coleção de Fontes OpenType (.ttc ou .otc), que é um único arquivo que contém várias fontes. Os arquivos da Coleção OpenType geralmente são usados para fontes grandes que estão intimamente relacionadas e têm valores idênticos para determinados dados de fonte: combinando as fontes em um único arquivo, os dados comuns podem ser duplicados. Por esse motivo, uma referência de rosto de fonte ou de rosto de fonte precisa se referir não apenas a um arquivo de fonte (ou fonte de dados equivalente), mas também deve especificar um índice de fonte dentro desse arquivo, para o caso geral em que o arquivo pode ser um arquivo de coleção.
Para fontes usadas na Web, os dados de fonte geralmente são empacotados em determinados formatos de contêiner, WOFF ou WOFF2, que fornecem alguma compactação dos dados da fonte e algum nível de proteção contra pirataria e violação de licenças de fonte. Funcionalmente, um arquivo WOFF ou WOFF2 é equivalente a uma fonte OpenType ou um arquivo de Coleta de Fontes, mas os dados são codificados em um formato diferente que requer desempacotar antes que possam ser usados.
Determinadas APIs do DirectWrite podem lidar com rostos de fonte individuais, enquanto outras APIs podem lidar com arquivos que podem incluir arquivos da Coleção OpenType que contêm vários rostos. Da mesma forma, determinadas APIs lidam apenas com dados brutos em formato OpenType, enquanto outras APIs podem lidar com os formatos de contêiner empacotados, WOFF e WOFF2. Esses detalhes são fornecidos na discussão abaixo.
Conjuntos de fontes e coleções de fontes
Alguns aplicativos podem ser implementados para trabalhar com fontes usando a interfaceIDWriteFontCollection. Há uma correspondência direta entre uma coleção de fontes e um conjunto de fontes. Cada uma pode conter as mesmas fontes, mas elas as apresentam com uma organização diferente. Em qualquer coleção de fontes, um conjunto de fontes correspondente pode ser obtido e vice-versa.
Ao trabalhar com várias fontes personalizadas, é mais fácil usar uma interface do construtor de conjuntos de fontes para criar um conjunto de fontes personalizado e obter uma coleção de fontes depois que o conjunto de fontes é criado. O processo para criar um conjunto de fontes personalizado será descrito em detalhes abaixo. Para obter uma interfaceIDWriteFontCollection1 de um conjunto de fontes, o método IDWriteFactory3::CreateFontCollectionFromFontSet é usado.
Se o aplicativo tiver um objeto de coleção e precisar obter um conjunto de fontes correspondente, isso poderá ser feito usando o método IDWriteFontCollection1::GetFontSet.
Cenários comuns
Esta seção descreve alguns dos cenários mais comuns envolvendo conjuntos de fontes personalizados:
- Criando um conjunto de fontes personalizado usando fontes arbitrárias em caminhos no sistema de arquivos local.
- Criando um conjunto de fontes personalizado usando fontes conhecidas (talvez agrupadas com o aplicativo) que são armazenadas no sistema de arquivos local.
- Criando um conjunto de fontes personalizado usando fontes remotas conhecidas na Web.
- Criando um conjunto de fontes personalizado usando dados de fonte carregados na memória.
Implementações completas para esses cenários são fornecidas no de exemplo de Conjuntos de Fontes Personalizados do DirectWrite. Esse exemplo também ilustra mais um cenário avançado para lidar com dados de fontes empacotados em formatos de contêiner WOFF ou WOFF2, que serão discutidos abaixo.
Criando um conjunto de fontes usando fontes arbitrárias no sistema de arquivos local
Ao lidar com um conjunto arbitrário de arquivos de fonte no armazenamento local, o método IDWriteFontSetBuilder1::AddFontFile é conveniente, pois, em uma única chamada, ele pode lidar com todos os rostos de fonte em um arquivo de Coleção de Fontes OpenType, bem como todas as instâncias de uma fonte de variável OpenType. Isso está disponível no Windows 10 Creators Update (versão prévia do build 15021 ou posterior) e é recomendado sempre que disponível.
Para usar esse método, use o processo a seguir.
- 1. Comece criando a interface IDWriteFactory5:
- Para cada arquivo de fonte no sistema de arquivos local, crie umIDWriteFontFileque se refere a ele:
- Depois que todos os arquivos tiverem sido adicionados ao construtor de conjuntos de fontes, o conjunto de fontes personalizado poderá ser criado:
IDWriteFactory5* pDWriteFactory;
HRESULT hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory5),
reinterpret_cast<IUnknown**>(&pDWriteFactory)
);
2. Use a fábrica para obter a interface deIDWriteFontSetBuilder1:
IDWriteFontSetBuilder1* pFontSetBuilder;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontSetBuilder(&pFontSetBuilder);
}
IDWriteFontFile* pFontFile;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontFileReference(pFilePath, /* lastWriteTime*/ nullptr, &pFontFile);
}
4. Adicione o objetoIDWriteFontFile ao construtor de conjuntos de fontes usando o métodoAddFontFile:
hr = pFontSetBuilder->AddFontFile(pFontFile);
Se o caminho do arquivo especificado na chamada para CreateFontFileReference referenciado a algo diferente de um arquivo OpenType com suporte, a chamada para AddFontFile retornará um erro, DWRITE_E_FILEFORMAT.
IDWriteFontSet* pFontSet;
hr = pFontSetBuilder->CreateFontSet(&pFontSet);
Se o aplicativo precisar ser executado em versões do Windows 10 anteriores à Atualização de Criadores do Windows 10, o método AddFontFile não estará disponível. A disponibilidade pode ser detectada criando uma interface IDWriteFactory3 e, em seguida, usando QueryInterface para tentar obter uma interface deIDWriteFactory5: se isso for bem-sucedido, o IDWriteFontSetBuilder1 interface e método AddFontFile também estarão disponíveis.
Se o método AddFontFile não estiver disponível, o método IDWriteFontSetBuilder::AddFontFaceReference método deverá ser usado para adicionar faces de fonte individuais. Para permitir arquivos da Coleção de Fontes OpenType que contêm vários rostos, o método IDWriteFontFile::Analyze pode ser usado para determinar o número de faces contidas no arquivo. O processo é o seguinte.
- 1. Comece criando a interface de IDWriteFactory3:
- Depois que todos os rostos tiverem sido adicionados ao construtor de conjuntos de fontes, crie o conjunto de fontes personalizado, conforme mostrado acima.
IDWriteFactory3* pDWriteFactory;
HRESULT hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory5),
reinterpret_cast<IUnknown**>(&pDWriteFactory)
);
IDWriteFontSetBuilder* pFontSetBuilder;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontSetBuilder(&pFontSetBuilder);
}
IDWriteFontFile* pFontFile;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontFileReference(pFilePath, /* lastWriteTime*/ nullptr, &pFontFile);
}
Em vez de adicionar o arquivo diretamente ao construtor de conjuntos de fontes, precisamos determinar o número de faces e criar objetos IDWriteFontFaceReference individuais.
4. Use o método Analisar para obter o número de faces no arquivo.
BOOL isSupported;
DWRITE_FONT_FILE_TYPE fileType;
UINT32 numberOfFonts;
hr = pFontFile->Analyze(&isSupported, &fileType, /* face type */ nullptr, &numberOfFonts);
O método Analyze também definirá valores para os parâmetros isSupported e fileType. Se o arquivo não for um formato com suporte, o isSupported será FALSE e uma ação apropriada, como ignorar o arquivo, poderá ser tomada.
5. Faça loop sobre o número de fontes definidas no parâmetro numberOfFonts. Dentro do loop, crie um IDWriteFontFaceReference para cada par de arquivo/índice e adicione-o ao construtor de conjuntos de fontes.
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);
}
}
Um aplicativo pode ser projetado para que ele use o método deAddFontFile preferencial ao ser executado no Windows 10 Creators Update, mas volte a usar o método AddFontFaceReference ao executar em versões anteriores do Windows 10. Teste a disponibilidade da interface deIDWriteFactory5, conforme descrito acima e, em seguida, branch adequadamente. Essa abordagem é ilustrada no de exemplo de Conjuntos de Fontes Personalizados doDirectWrite.
Criando um conjunto de fontes usando fontes conhecidas no sistema de arquivos local
Conforme mencionado acima, cada referência de rosto de fonte em um conjunto de fontes está associada a determinadas propriedades informativas, como o nome da família e o peso da fonte. Quando fontes personalizadas são adicionadas a um construtor de conjuntos de fontes usando as chamadas de API listadas acima, essas propriedades informativas são obtidas diretamente dos dados reais da fonte, que são lidos à medida que a fonte é adicionada. Em algumas situações, no entanto, se um aplicativo tiver outra fonte de informações sobre uma fonte, ele poderá fornecer seus próprios valores personalizados para essas propriedades.
Como exemplo de como isso pode ser útil, suponha que um aplicativo agrupe algumas fontes que são usadas para apresentar elementos específicos da interface do usuário dentro do aplicativo. Às vezes, como com uma nova versão do aplicativo, as fontes específicas que o aplicativo usa para esses elementos podem precisar ser alteradas. Se o aplicativo tiver referências codificadas às fontes específicas, a substituição de uma fonte por outra exigirá a alteração de cada uma dessas referências. Em vez disso, se o aplicativo usa propriedades personalizadas para atribuir aliases funcionais com base no tipo de elemento ou texto que está sendo renderizado, mapeia cada alias para uma fonte específica em um só lugar e, em seguida, usa os aliases em todos os contextos em que as fontes são criadas e manipuladas e, em seguida, substituir uma fonte por outra requer apenas a alteração de um local onde o alias é mapeado para uma fonte específica.
Valores personalizados para propriedades informativas podem ser atribuídos quando o método IDWriteFontSetBuilder::AddFontFaceReference é chamado. O método para fazer isso é o seguinte; isso pode ser usado em qualquer versão do Windows 10.
Conforme mostrado acima, comece obtendo as interfaces IDWriteFactory3 e IDWriteFontSet. Para que cada rosto de fonte personalizado seja adicionado, crie umIDWriteFontFaceReference, conforme mostrado acima. Antes que isso seja adicionado ao construtor de conjuntos de fontes (dentro do loop na etapa 5, mostrado acima), no entanto, o aplicativo define os valores de propriedade personalizados a serem usados.
Um conjunto de valores de propriedade personalizados é definido usando uma matriz de estruturas de DWRITE_FONT_PROPERTY. Cada uma delas identifica uma propriedade específica da enumeração DWRITE_FONT_PROPERTY_ID e o valor da propriedade correspondente que deve ser usado.
Observe que todos os valores de propriedade são atribuídos como cadeias de caracteres. Se eles puderem ser exibidos posteriormente para os usuários, os valores alternativos de uma determinada propriedade para idiomas diferentes poderão ser definidos, mas isso não é necessário. Observe também que, se quaisquer valores de propriedade personalizados forem definidos pelo aplicativo, somente os valores especificados serão usados no conjunto de fontes; O DirectWrite não derivará valores diretamente da fonte para propriedades informativas usadas em um conjunto de fontes.
O exemplo a seguir define valores personalizados para três propriedades informativas: nome da família, nome completo e peso da fonte.
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 }
};
Depois de definir a matriz desejada de valores de propriedade para uma fonte, faça uma chamada para AddFontFaceRefence, passando a matriz de propriedades, bem como a referência de face da fonte.
hr = pFontSetBuilder->AddFontFaceReference(pFontFaceReference, props, ARRAYSIZE(props));
Depois que todos os rostos de fonte personalizados tiverem sido adicionados ao construtor de conjuntos de fontes, juntamente com suas propriedades personalizadas, crie o conjunto de fontes personalizado, conforme mostrado acima.
Criando um conjunto de fontes personalizado usando fontes remotas conhecidas na Web
As propriedades personalizadas são importantes para trabalhar com fontes remotas. Cada referência de rosto de fonte deve ter algumas propriedades informativas para caracterizar a fonte e distingui-la de outras fontes. Como os dados de fonte para fontes remotas não são locais, o DirectWrite não pode derivar propriedades diretamente dos dados da fonte. Portanto, as propriedades devem ser fornecidas explicitamente ao adicionar uma fonte remota ao construtor do conjunto de fontes.
A sequência de chamadas de API para adicionar fontes remotas a um conjunto de fontes é semelhante à sequência descrita para o cenário anterior. Como os dados da fonte são remotos, no entanto, as operações envolvidas para ler os dados reais da fonte serão diferentes do que ao trabalhar com arquivos no armazenamento local. Para essa situação, uma nova interface de nível inferior, IDWriteRemoteFontFileLoader, foi adicionada à Atualização de Criadores do Windows 10.
Para usar o carregador de arquivo de fonte remoto, ele deve primeiro ser registrado em uma fábrica do DirectWrite. O carregador precisará ser mantido pelo aplicativo enquanto as fontes associadas a ele estiverem sendo usadas. Depois que as fontes não estiverem mais em uso e, em algum momento, antes de a fábrica ser destruída, o carregador deverá não ser registrado. Isso pode ser feito no destruidor da classe que possui o objeto carregador. Estas etapas serão mostradas abaixo.
O método para criar um conjunto de fontes personalizado usando fontes remotas é o seguinte; isso requer a Atualização de Criadores do Windows 10.
- 1. Crie uma interface IDWriteFactory5, conforme mostrado acima.
2. Crie uma interface IDWriteFontSetBuilder, conforme mostrado acima.
3. Use a fábrica para obter um IDWriteRemoteFontFileLoader.
- Defina propriedades personalizadas para o rosto da fonte, conforme mostrado acima.
- Adicione a referência de face da fonte junto com as propriedades personalizadas ao construtor de conjuntos de fontes, conforme mostrado acima.
- Depois que todas as fontes tiverem sido adicionadas ao construtor de conjuntos de fontes, crie o conjunto de fontes, conforme mostrado acima.
- Em algum momento em que as fontes remotas não serão mais usadas, cancele o registro do carregador de arquivo de fonte remoto.
IDWriteRemoteFontFileLoader* pRemoteFontFileLoader;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateHttpFontFileLoader(
/* referrerURL */ nullptr,
/* extraHeaders */ nullptr,
&pRemoteFontFileLoader
);
}
Isso retorna uma implementação fornecida pelo sistema da interface do carregador de arquivo de fonte remota que é capaz de lidar com interações HTTP para baixar dados de fonte em nome do aplicativo. Uma URL de referenciador ou cabeçalhos extras pode ser especificada se necessário pelo serviço de fonte ou serviços que são a origem das fontes.
Importante
Observação de segurança: quando uma tentativa é feita para buscar uma fonte remota, o potencial existe para um invasor falsificar o servidor pretendido que será chamado. Nesse caso, as URLs de destino e de referenciador e os detalhes do cabeçalho seriam divulgados para o invasor. Os desenvolvedores de aplicativos são responsáveis por atenuar esse risco. O uso do protocolo HTTPS, em vez de HTTP, é recomendado.
Um único carregador de arquivo de fonte remota pode ser usado para várias fontes, embora carregadores diferentes possam ser usados se fontes forem obtidas de vários serviços que têm requisitos diferentes para URL do referenciador ou cabeçalhos extras.
4. Registre o carregador de arquivo de fonte remoto com a fábrica.
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->RegisterFontFileLoader(pRemoteFontFileLoader);
}
A partir deste ponto, as etapas para criar o conjunto de fontes personalizadas são semelhantes às descritas para arquivos de fonte locais conhecidos, com duas exceções importantes. Primeiro, o objetoIDWriteFontFileé criado usando a interface do carregador de arquivo de fonte remoto em vez de usar a fábrica. Em segundo lugar, o método Analyze não pode ser usado, pois os dados da fonte não são locais. Em vez disso, o aplicativo deve saber se o arquivo de fonte remota é um arquivo opentype font collection e, se sim, ele deve saber quais das fontes dentro da coleção ele usará e o índice para cada um. Portanto, as etapas restantes são as seguintes.
5. Para cada arquivo de fonte remoto, use a interface do carregador de arquivo de fonte remota para criar umIDWriteFontFile, especificando a URL necessária para acessar o arquivo de fonte.
IDWriteFontFile* pFontFile;
hr = pRemoteFontFileLoader->CreateFontFileReferenceFromUrl(
pDWriteFactory,
/* baseUrl */ L"https://github.com/",
/* fontFileUrl */ L"winjs/winjs/blob/master/src/fonts/Symbols.ttf?raw=true",
&pFontFile
);
Observe que a URL completa pode ser especificada no parâmetro fontFileUrl ou pode ser dividida em partes base e relativas. Se uma URL base for especificada, a concatenação dos valores baseUrl e fontFileUrl deverá fornecer a URL completa . O DirectWrite não fornecerá nenhum delimitador adicional.
Importante
Observação de segurança/desempenho: quando é feita uma tentativa de buscar uma fonte remota, não há garantia de que o Windows receberá uma resposta do servidor. Em alguns casos, um servidor pode responder com um erro não encontrado por um arquivo para uma URL relativa inválida, mas parar de responder se receber várias solicitações inválidas. Se o servidor não responder, o Windows acabará atingindo o tempo limite, embora isso possa levar vários minutos se várias buscas forem iniciadas. Você deve fazer o que puder para garantir que as URLs serão válidas quando as chamadas forem feitas.
Observe também que a URL pode apontar para um arquivo de fonte OpenType bruto (.ttf, .otf, .ttc, .otc), mas também pode apontar para fontes em um arquivo de contêiner WOFF ou WOFF2. Se um arquivo WOFF ou WOFF2 for referenciado, a implementação do DirectWrite do carregador de arquivo de fonte remota desempacotará automaticamente os dados da fonte do arquivo de contêiner.
6. Para cada índice de face de fonte dentro do arquivo de fonte remoto que deve ser usado, crie um IDWriteFontFaceReference.
IDWriteFontFaceReference* pFontFaceReference;
hr = pDWriteFactory->CreateFontFaceReference(pFontFile, /* faceIndex */ 0, DWRITE_FONT_SIMULATIONS_NONE, &pFontFaceReference);
hr = pDWriteFactory->UnregisterFontFileLoader(pRemoteFontFileLoader);
Depois que um conjunto de fontes personalizado com fontes remotas personalizadas é criado, o conjunto de fontes contém referências e propriedades informativas para as fontes remotas, mas os dados reais ainda são remotos. O suporte do DirectWrite para fontes remotas permite que uma referência de rosto de fonte seja mantida no conjunto de fontes e que uma fonte seja selecionada para uso em layout e renderização, mas que os dados reais não sejam baixados até que haja uma necessidade real de usá-la, como quando o layout de texto será executado.
Um aplicativo pode adotar uma abordagem inicial solicitando que o DirectWrite baixe os dados da fonte e, em seguida, aguardando a confirmação de um download bem-sucedido antes que qualquer processamento com a fonte seja iniciado. Mas um download de rede implica alguma latência de duração imprevisível, e o sucesso também é incerto. Por esse motivo, geralmente será melhor adotar uma abordagem diferente, permitindo que o layout e a renderização sejam feitos inicialmente usando fontes alternativas ou de fallback que já sejam locais, ao solicitar o download da fonte remota desejada em paralelo e atualizar os resultados depois que a fonte desejada tiver sido baixada.
Para solicitar que toda a fonte seja baixada antes de ser usada, o método IDWriteFontFaceReference::EnqueueFontDownloadRequest pode ser usado. Se a fonte for muito grande, apenas uma parte dos dados poderá ser necessária para processar cadeias de caracteres específicas. O DirectWrite fornece métodos adicionais que podem ser usados para solicitar partes dos dados de fonte necessários para determinado conteúdo, EnqueueCharacterDownloadRequest e EnqueueGlyphDownloadRequest.
Suponha que a abordagem a ser adotada no aplicativo seja permitir que o processamento seja feito inicialmente usando fontes locais, alternativas ou de fallback. O método IDWriteFontFallback::MapCharacters pode ser usado para identificar fontes de fallback locais e também enfileirará automaticamente uma solicitação para baixar a fonte preferencial. Além disso, se IDWriteTextLayout for usado e parte ou todo o texto no layout for formatado usando uma referência de fonte remota, o DirectWrite usará automaticamente o método MapCharacters para obter fontes de fallback locais e enfileirar uma solicitação para baixar os dados de fonte remota.
O DirectWrite mantém uma fila de download de fonte para cada fábrica e as solicitações feitas usando os métodos mencionados acima são adicionadas a essa fila. A fila de download de fonte pode ser obtida usando o métodoIDWriteFactory3::GetFontDownloadQueue.
Se uma solicitação de download for feita, mas os dados da fonte já estiverem locais, isso resultará em um no-op: nada será adicionado à fila de download. Um aplicativo pode verificar se a fila está vazia ou se há solicitações de download pendentes chamando o método IDWriteFontDownloadQueue::IsEmpty.
Depois que as solicitações de fonte remota tiverem sido adicionadas à fila, o processo de download deve ser iniciado. Quando fontes remotas são usadas em IDWriteTextLayout, o download será iniciado automaticamente quando o aplicativo chamar métodos IDWriteTextLayout que forçam operações de layout ou renderização, como os métodos GetLineMetrics ou Draw. Em outros cenários, o aplicativo deve iniciar o download diretamente chamando IDWriteFontDownloadQueue::BeginDownload.
Quando um download for concluído, caberá ao aplicativo executar as ações apropriadas , continuar com operações pendentes ou repetir operações que foram feitas inicialmente com fontes de fallback. (Se o layout de texto do DirectWrite estiver sendo usado, IDWriteTextLayout3::InvalidateLayout poderá ser usado para limpar os resultados temporários computados usando fontes de fallback.) Para que o aplicativo seja notificado quando o processo de download for concluído e tomar as ações apropriadas, o aplicativo deve fornecer uma implementação da interfaceIDWriteFontDownloadListenere passá-la para a chamada BeginDownload.
Importante
Observação de segurança/desempenho: quando é feita uma tentativa de buscar uma fonte remota, não há garantia de que o Windows receberá uma resposta do servidor. Se o servidor não responder, o Windows acabará atingindo o tempo limite, embora isso possa levar vários minutos se várias fontes remotas estiverem sendo buscadas, mas falhando. A chamada BeginDownload retornará imediatamente. Os aplicativos não devem bloquear a interface do usuário enquanto aguardam IDWriteFontDownloadListener::D ownloadCompleted ser chamado.
Implementações de exemplo dessas interações com a fila de download de fontes do DirectWrite e da interface deIDWriteFontDownloadListenerpodem ser vistas na de exemplo conjuntos de fontes personalizados doDirectWrite e também no de exemplo de Fontes Para Download do DirectWrite.
Criando um conjunto de fontes personalizado usando dados de fonte carregados na memória
Assim como as operações de baixo nível para ler dados de um arquivo de fonte são diferentes para arquivos em um disco local versus arquivos remotos na Web, o mesmo também é verdadeiro para dados de fonte carregados em um buffer de memória. Uma nova interface de baixo nível para lidar com dados de fonte na memória foi adicionada à Atualização de Criadores do Windows 10, IDWriteInMemoryFontFileLoader.
Assim como acontece com um carregador de arquivo de fonte remoto, um carregador de arquivo de fonte na memória deve primeiro ser registrado com uma fábrica do DirectWrite. O carregador precisará ser mantido pelo aplicativo enquanto as fontes associadas a ele estiverem sendo usadas. Depois que as fontes não estiverem mais em uso e, em algum momento, antes de a fábrica ser destruída, o carregador deverá não ser registrado. Isso pode ser feito no destruidor da classe que possui o objeto carregador. Estas etapas serão mostradas abaixo.
Se o aplicativo tiver informações separadas sobre os rostos de fonte representados pelos dados, ele poderá adicionar referências individuais de rosto de fonte a um construtor de conjuntos de fontes com propriedades personalizadas especificadas. Como os dados da fonte estão na memória local, no entanto, isso não é necessário; O DirectWrite poderá ler os dados diretamente para derivar os valores da propriedade.
O DirectWrite pressupõe que os dados da fonte estão no formato OpenType bruto, equivalente a um arquivo OpenType (.ttf, .otf, .ttc, .otc), mas na memória e não no disco. Os dados não podem estar em um formato de contêiner WOFF ou WOFF2. Os dados podem representar uma Coleção de Fontes OpenType. Se as propriedades personalizadas não estiverem sendo usadas, o método IDWriteFontSetBuilder1::AddFontFile pode ser usado para adicionar todos os rostos de fonte nos dados em uma única chamada.
Uma consideração importante para o cenário na memória é o tempo de vida dos dados. Se um ponteiro para o buffer for fornecido ao DirectWrite sem uma indicação clara de que há um proprietário, o DirectWrite fará uma cópia dos dados em um novo buffer de memória que ele possuirá. Para evitar a cópia de dados e alocação de memória adicional, o aplicativo pode passar um objeto proprietário de dados que implementa o IUnknown e que possui o buffer de memória que contém os dados da fonte. Ao implementar essa interface, o DirectWrite pode adicionar à contagem de refs do objeto, garantindo assim o tempo de vida dos dados de propriedade.
O método para criar um conjunto de fontes personalizado usando dados de fonte na memória é o seguinte; isso requer a Atualização de Criadores do Windows 10. Isso assumirá um objeto proprietário de dados implementado pelo aplicativo, que implementa o IUnknown e também tem métodos que retornam um ponteiro para o buffer de memória e o tamanho do buffer.
- 1. Crie uma interface IDWriteFactory5, conforme mostrado acima.
2. Crie uma interface [**IDWriteFontSetBuilder1**](/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontsetbuilder1), conforme mostrado acima.
3. Use a fábrica para obter um IDWriteInMemoryFontFileLoader.
IDWriteInMemoryFontFileLoader* pInMemoryFontFileLoader;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateInMemoryFontFileLoader(&pInMemoryFontFileLoader);
}
Isso retorna uma implementação fornecida pelo sistema da interface do carregador de arquivo de fonte na memória.
4. Registre o carregador de arquivo de fonte na memória com a fábrica.
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->RegisterFontFileLoader(pInMemoryFontFileLoader);
}
5. Para cada arquivo de fonte na memória, use o carregador de arquivo de fonte na memória para criar um 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. Adicione o objetoIDWriteFontFile ao construtor de conjuntos de fontes usando o método AddFontFile, conforme mostrado acima. Se houver uma necessidade, o aplicativo poderá criar objetos individuais IDWriteFontFaceReference com base no IDWriteFontFile, opcionalmente definir propriedades personalizadas para cada referência facial de fonte e, em seguida, adicionar a referência de rosto de fonte com propriedades personalizadas ao conjunto de fontes usando o método AddFontFaceReference, conforme mostrado acima.
7. Depois que todas as fontes tiverem sido adicionadas ao construtor de conjuntos de fontes, crie o conjunto de fontes personalizado, conforme mostrado acima.
8. Em algum momento em que as fontes na memória não serão mais usadas, cancele o registro do carregador de arquivo de fonte na memória.
hr = pDWriteFactory->UnregisterFontFileLoader(pInMemoryFontFileLoader);
Cenários avançados
Alguns aplicativos podem ter requisitos especiais que exigem processamento mais avançado do que o descrito acima.
Combinando conjuntos de fontes
Alguns aplicativos podem precisar criar um conjunto de fontes que inclua alguma combinação de itens de outros conjuntos de fontes. Por exemplo, um aplicativo pode querer criar um conjunto de fontes que combine todas as fontes instaladas no sistema com uma seleção de fontes personalizadas ou que combine fontes instaladas que correspondam a determinados critérios com outras fontes. O DirectWrite tem APIs para dar suporte à manipulação e à combinação de conjuntos de fontes.
Para combinar dois ou mais conjuntos de fontes, o método IDWriteFontSetBuilder::AddFontSet adiciona todas as fontes em determinado conjunto de fontes a serem adicionadas a um construtor de conjuntos de fontes em uma única chamada. Se apenas determinadas fontes de um conjunto de fontes existentes forem desejadas no novo conjunto de fontes, o método IDWriteFontSet::GetMatchingFonts pode ser usado para derivar um novo objeto de conjunto de fontes que foi filtrado para incluir apenas fontes correspondentes às propriedades especificadas. Esses métodos fornecem uma maneira fácil de criar um conjunto de fontes personalizado combinando fontes de dois ou mais conjuntos de fontes existentes
Usando dados da fonte WOFF ou WOFF2 local
Se um aplicativo tiver arquivos de fonte no sistema de arquivos local ou em um buffer de memória, mas usar os formatos de contêiner WOFF ou WOFF2, o DirectWrite (Windows 10 Creator Update ou posterior) fornecerá um método para desempacotar o formato de contêiner, IDWriteFactory5::UnpackFontFile, que retorna um IDWriteFontFileStream.
No entanto, o aplicativo precisará de uma maneira de obter o IDWriteFontFileStream em um objeto carregador de arquivo de fonte. Uma maneira de fazer isso é criar um IDWriteFontFileLoader personalizado implementação que encapsula o fluxo. Assim como acontece com outros carregadores de arquivo de fonte, isso deve ser registrado antes do uso e não registrado antes que a fábrica saia do escopo.
Se o carregador personalizado também for usado com arquivos de fonte brutos (não empacotados), o aplicativo também precisará fornecer uma implementação personalizada da interface deIDWriteFontFileStream dopara lidar com esses arquivos. No entanto, há maneiras mais fáceis de usar APIs discutidas acima para lidar com arquivos de fonte brutos. A necessidade de uma implementação de fluxo personalizado pode ser evitada usando caminhos de código separados para arquivos de fontes empacotados versus arquivos de fonte brutos.
Depois que um objeto carregador de arquivo de fonte personalizado é criado, os dados do arquivo de fonte empacotado são adicionados ao carregador por meios específicos do aplicativo. O carregador pode manipular vários arquivos de fonte, cada um deles identificado usando uma chave definida pelo aplicativo opaca para DirectWrite. Depois que um arquivo de fonte empacotado tiver sido adicionado ao carregador, o método IDWriteFactory::CreateCustomFontFileReference é usado para obter um IDWriteFontFile com base nesse carregador para os dados da fonte identificados por uma determinada chave.
O desempacotamento real dos dados da fonte pode ser feito à medida que fontes são adicionadas ao carregador, mas também pode ser tratado no método IDWriteFontFileLoader::CreateStreamFromKey, que o DirectWrite chamará quando precisar ler os dados da fonte pela primeira vez.
Depois que um objeto IDWriteFontFile tiver sido criado, as etapas restantes para adicionar as fontes a um conjunto de fontes personalizado serão conforme descrito acima.
Uma implementação usando essa abordagem é ilustrada no de exemplo de Conjuntos de Fontes Personalizados do DirectWrite.
Usando mecanismos de fonte remota do DirectWrite com implementação de rede personalizada de baixo nível
Os mecanismos do DirectWrite para lidar com fontes remotas podem ser divididos em mecanismos de nível superior , tendo conjuntos de fontes que incluem referências de rosto de fonte para fontes remotas, verificando a localidade dos dados da fonte e gerenciando a fila para solicitações de download de fontes e os mecanismos de nível inferior que lidam com o download real. Alguns aplicativos podem querer utilizar os mecanismos de fonte remota de nível superior, mas também exigem interações de rede personalizadas, como se comunicar com servidores usando protocolos diferentes de HTTP.
Para essa situação, um aplicativo precisará criar uma implementação personalizada da interfaceIDWriteRemoteFontFileLoaderque interaja com outras interfaces de nível inferior das maneiras necessárias. O aplicativo também precisará fornecer implementações personalizadas dessas interfaces de nível inferior: IDWriteRemoteFontFileStream e IDWriteAsyncResult. Essas três interfaces têm métodos de retorno de chamada que o DirectWrite chamará durante as operações de download.
Quando IDWriteFontDownloadQueue::BeginDownload for chamado, o DirectWrite fará consultas ao carregador de arquivo de fonte remoto sobre a localidade dos dados e solicitará o fluxo remoto. Se os dados não forem locais, ele chamará o método BeginDownload do fluxo. A implementação do fluxo não deve bloquear essa chamada, mas deve retornar imediatamente, retornando um objeto IDWriteAsyncResult que fornece o identificador de espera que o DirectWrite usará para aguardar a operação de download assíncrona. A implementação do fluxo personalizado é responsável por lidar com a comunicação remota. Quando o evento de conclusão tiver ocorrido, o DirectWrite chamará IDWriteAsyncResult::GetResult para determinar o resultado da operação. Se o resultado for bem-sucedido, espera-se que as chamadas readfragment subsequentes para o fluxo dos intervalos baixados sejam bem-sucedidas.
Importante
Observação de segurança/desempenho: quando é feita uma tentativa de buscar uma fonte remota, o potencial existe em geral para um invasor falsificar o servidor pretendido que está sendo chamado ou que o servidor pode não responder. Se você estiver implementando interações de rede personalizadas, poderá ter maior controle sobre mitigações do que ao lidar com servidores de terceiros. No entanto, cabe a você considerar mitigações apropriadas para evitar a divulgação de informações ou negação de serviço. Protocolos seguros, como HTTPS, são recomendados. Além disso, você deve criar em algum tempo limite para que o identificador de evento retornado ao DirectWrite eventualmente seja definido.
Cenários de suporte em versões anteriores do Windows
Os cenários descritos podem ter suporte no DirectWrite em versões anteriores do Windows, mas exigiriam muito mais implementação personalizada por parte do aplicativo usando as APIs mais limitadas que estavam disponíveis antes do Windows 10. Para obter mais informações, consulte coleções de fontes personalizadas (Windows 7/8).