Conjuntos de fontes personalizados
Este tópico descreve várias maneiras pelas quais você pode usar fontes personalizadas em seu aplicativo.
- Introdução
- Resumo das APIs
- Conceitos-chave
- Fontes e formatos de arquivo de fonte
- Conjuntos de fontes e coleções de fontes
-
Cenários comuns
- Criando um conjunto de fontes usando fontes arbitrárias no sistema de arquivos local
- Criando um conjunto de fontes usando fontes conhecidas no sistema de arquivos local
- Criando um conjunto de fontes personalizado usando fontes conhecidas e remotas na Web
- Criando um conjunto de fontes personalizado usando dados de fonte carregados na memória
- Cenários avançados
Introdução
Na maioria das vezes, os aplicativos usam as fontes instaladas localmente no sistema. DirectWrite fornece acesso a essas fontes usando o IDWriteFactory3::GetSystemFontSet ou IDWriteFactory::GetSystemFontCollection métodos. Em alguns casos, os aplicativos também podem querer usar fontes incluídas como parte do Windows 10, mas que não estão atualmente instaladas no sistema atual. Essas fontes podem ser acessadas a partir do serviço de fontes do Windows usando o método GetSystemFontSet ou chamando IDWriteFactory3::GetSystemFontCollection com includeDownloadableFonts definido como TRUE.
Em alguns cenários de aplicativos, 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 Fontes do Windows. Seguem-se exemplos de tais cenários:
- As fontes são incorporadas como recursos dentro de 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 incorporadas em arquivos de documentos que podem ser visualizados ou editados no aplicativo.
- O aplicativo usa fontes obtidas de um serviço público de fontes da Web.
- O aplicativo usa dados de fonte transmitidos através 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 personalizada podem vir de arquivos no sistema de arquivos local; de fontes remotas, baseadas na nuvem, acessadas usando HTTP; ou de fontes arbitrárias depois de ter sido carregado em um buffer de memória.
Observação
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 para Criadores do Windows 10 (versão de visualização 15021 ou posterior) que facilitam a implementação de vários dos cenários mencionados. Este tópico se concentra nas APIs disponíveis no Windows 10. Para aplicativos que precisam funcionar em versões anteriores do Windows, consulte Custom Font Collections (Windows 7/8).
Resumo das APIs
Este tópico se concentra na funcionalidade fornecida pelas seguintes APIs:
- Interface de IDWriteFontSet
- interface IDWriteFontSetBuilder
- Interface IDWriteFontSetBuilder1
- Interface de IDWriteFontFaceReference
- Interface de IDWriteFontFile
- IDWriteFactory::CreateFontFileReference método
- IDWriteFactory::CreateCustomFontFileReference método
- IDWriteFactory3::CreateFontFaceReference métodos
- DWRITE_FONT_PROPERTY estrutura
- DWRITE_FONT_PROPERTY_ID enumeração
- Interface de IDWriteFontFileLoader
- IDWriteFactory::RegisterFontFileLoader método
- IDWriteFactory::UnregisterFontFileLoader método
- IDWriteFactory5::CreateInMemoryFontFileLoader
- interface de IDWriteInMemoryFontFileLoader
- IDWriteFactory5::CreateHttpFontFileLoader método
- Interface de IDWriteRemoteFontFileLoader
- interface de IDWriteFontDownloadQueue
- IDWriteFontDownloadListener interface
- Interface de IDWriteFontFileStream
- Interface de IDWriteRemoteFontFileStream
- interface IDWriteAsyncResult
- IDWriteFactory5::AnalyzeContainerType método
- IDWriteFactory5::UnpackFontFile método
Conceitos-chave
Para entender as APIs do DirectWrite para trabalhar com fontes personalizadas, pode ser útil entender o modelo conceitual subjacente a essas APIs. Os conceitos-chave serão descritos aqui.
Quando o DirectWrite faz layout ou renderização de texto real, ele precisa acessar dados de fonte reais. Um objeto font face 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 para um usuário, tudo o que é necessário é uma referência a uma fonte específica, não os dados de fonte em si. No DirectWrite, um objeto de referência de face de fonte contém apenas as informações necessárias para localizar e instanciar uma fonte. Como a referência de face de fonte não contém dados reais, o DirectWrite pode lidar com referências de face de fonte para as quais os dados reais estão em um local de rede remoto, bem como quando os dados reais são locais.
Um conjunto de fontes é um conjunto de referências faciais de fontes, juntamente com certas propriedades básicas e informativas que podem ser usadas para se referir à fonte ou compará-la com outras fontes, como o nome da família ou um valor de espessura da fonte. Os dados reais para as 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 consultar valores de propriedade, como nome de família ou espessura de fonte, ou referências de face de fonte que correspondem a valores de propriedade específicos. Depois de filtrar até uma seleção específica, uma instância da interfaceIDWriteFontFaceReference dopode ser obtida, com métodos para download (se os dados de fonte reais estiverem remotos no momento), para obter o objeto deIDWriteFontFace3 correspondenteque pode ser usado para layout e renderização.
A interface IDWriteFontFile está subjacente a cada face de fonte ou referência de face 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 arquivos 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, 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 eles.
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 a criação de uma interface de usuário de 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 inclui 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 diferentes fontes para os dados reais — armazenamento local, URLs remotos e memória.
Como 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 interface deIDWriteFontDownloadQueue dopara enfileirar solicitações para baixar fontes remotas para iniciar o processo de download e para 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 de sistema. A única exceção é a interfaceIDWriteFontDownloadListener, que um aplicativo implementa para executar ações específicas do aplicativo quando fontes remotas foram 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 interface deIDWriteFontFileLoader dopara 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-chave que é útil entender é a relação entre faces de fontes individuais e arquivos de fontes que as contêm. A ideia de um arquivo de fonte OpenType (.ttf ou .otf) contendo 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 OpenType Collection são frequentemente 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 desduplicados. Por esse motivo, uma referência de rosto de fonte ou face 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 são frequentemente compactados em certos formatos de contêiner, WOFF ou WOFF2, que fornecem alguma compactação dos dados de 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 arquivo de coleção de fontes, mas os dados são codificados em um formato diferente que requer descompactação antes de poder ser usado.
Certas APIs do DirectWrite podem lidar com faces de fontes individuais, enquanto outras APIs podem lidar com arquivos que podem incluir arquivos da Coleção OpenType que contêm várias faces. Da mesma forma, certas APIs lidam apenas com dados brutos no 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 o IDWriteFontCollection interface. Há uma correspondência direta entre uma coleção de fontes e um conjunto de fontes. Cada um pode conter as mesmas fontes, mas apresentam-nas com uma organização diferente. A partir de 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, em seguida, obter uma coleção de fontes depois que o conjunto de fontes é criado. O processo de criação de um conjunto de fontes personalizado será descrito em detalhes abaixo. Para obter um IDWriteFontCollection1 interface de um conjunto de fontes, o IDWriteFactory3::CreateFontCollectionFromFontSet método é 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étodoIDWriteFontCollection1::GetFontSet.
Cenários comuns
Esta seção descreve alguns dos cenários mais comuns envolvendo conjuntos de fontes personalizados:
- Criação de um conjunto de fontes personalizado usando fontes arbitrárias em caminhos no sistema de arquivos local.
- Criar um conjunto de fontes personalizado usando fontes conhecidas (talvez empacotadas com o aplicativo) que são armazenadas no sistema de arquivos local.
- Criação de um conjunto de fontes personalizado usando fontes conhecidas e remotas na Web.
- Criação de um conjunto de fontes personalizado usando dados de fonte carregados na memória.
As 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 um cenário mais avançado para lidar com dados de fonte embalados 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 IDWriteFontSetBuilder1::AddFontFile método é conveniente, uma vez que, em uma única chamada, ele pode lidar com todas as faces de fonte dentro de um arquivo OpenType Font Collection, bem como todas as instâncias para uma fonte variável OpenType. Isso está disponível no Windows 10 Creators Update (versão de visualização 15021 ou posterior) e é recomendado sempre que disponível.
Para usar esse método, use o seguinte processo.
- 1. Comece criando o interface IDWriteFactory5:
- Para cada arquivo de fonte no sistema de arquivos local, crie um IDWriteFontFile que se refira 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 o IDWriteFontSetBuilder1 interface:
IDWriteFontSetBuilder1* pFontSetBuilder;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontSetBuilder(&pFontSetBuilder);
}
IDWriteFontFile* pFontFile;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontFileReference(pFilePath, /* lastWriteTime*/ nullptr, &pFontFile);
}
4. Adicione o objeto IDWriteFontFile ao construtor de conjuntos de fontes usando o métodoAddFontFile:
hr = pFontSetBuilder->AddFontFile(pFontFile);
Se o caminho do arquivo especificado na chamada para CreateFontFileReference referido a algo diferente de um arquivo OpenType suportado, 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 detetada criando uma interfaceIDWriteFactory3e, em seguida, usando QueryInterface para tentar obter uma interfaceIDWriteFactory5: se isso for bem-sucedido, a interface IDWriteFontSetBuilder1 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 deverá ser usado para adicionar faces de fonte individuais. Para permitir arquivos OpenType Font Collection que contêm várias faces, 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 o interface IDWriteFactory3:
- Use a fábrica para obter o IDWriteFontSetBuilder interface:
- Para cada arquivo de fonte, crie um IDWriteFontFile, como acima:
- Depois que todas as faces tiverem sido adicionadas ao construtor de conjuntos de fontes, crie o conjunto de fontes personalizado, como 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 objetosIDWriteFontFaceReferenceindividuais.
4. Use o método Analyze 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 suportado, isSupported será FALSE e a ação apropriada, como ignorar o arquivo, poderá ser tomada.
5. 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 do conjunto 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 deAddFontFilepreferido ao ser executado na Atualização de Criadores do Windows 10, mas volte a usar o método AddFontFaceReference quando executado em versões anteriores do Windows 10. Teste a disponibilidade da interfaceIDWriteFactory5 do, conforme descrito acima, e ramificar de acordo. Essa abordagem é ilustrada no exemplo de DirectWrite Custom Font Sets.
Criando um conjunto de fontes usando fontes conhecidas no sistema de arquivos local
Como mencionado acima, cada referência de face de fonte em um conjunto de fontes está associada a certas propriedades informativas, como nome de família e 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 de fonte reais, 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 pode querer 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 no 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 para as 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 lugar e, em seguida, usa os aliases em todos os contextos em que as fontes são criadas e manipuladas, a substituição de 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.
Como mostrado acima, comece obtendo o IDWriteFactory3 e interfaces IDWriteFontSet. Para cada face de fonte personalizada a ser adicionada, crie um IDWriteFontFaceReference, 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 personalizada a serem usados.
Um conjunto de valores de propriedade personalizada é definido usando uma matriz de estruturas DWRITE_FONT_PROPERTY. Cada um deles identifica uma propriedade específica do DWRITE_FONT_PROPERTY_ID enum, 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, valores alternativos para 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 personalizada forem definidos pelo aplicativo, somente os valores especificados serão usados dentro do conjunto de fontes; O DirectWrite não derivará nenhum valor 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 espessura 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 propriedade, bem como a referência de face da fonte.
hr = pFontSetBuilder->AddFontFaceReference(pFontFaceReference, props, ARRAYSIZE(props));
Depois que todas as faces de fontes personalizadas tiverem sido adicionadas 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 conhecidas e remotas na Web
As propriedades personalizadas são importantes para trabalhar com fontes remotas. Cada referência de face 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 de 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 de fonte reais 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 na atualização de criadores do Windows 10.
Para usar o carregador de arquivos de fonte remoto, ele deve primeiro ser registrado em uma fábrica DirectWrite. O carregador precisará ser mantido pelo aplicativo enquanto as fontes associadas a ele estiverem sendo usadas. Uma vez que as fontes não estão mais em uso, e em algum momento antes da fábrica ser destruída, o carregador deve ser cancelado. Isso pode ser feito no destruidor para a 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 o Windows 10 Creators Update.
- 1. Crie uma interface IDWriteFactory5, como mostrado acima.
2. Crie um IDWriteFontSetBuilder interface, como mostrado acima.
3. Use a fábrica para obter um IDWriteRemoteFontFileLoader.
- Defina propriedades personalizadas para o rosto da fonte, como mostrado acima.
- Adicione a referência de face da fonte juntamente com propriedades personalizadas ao construtor de conjuntos de fontes, conforme mostrado acima.
- Depois que todas as fontes tiverem sido adicionadas ao construtor do conjunto de fontes, crie o conjunto de fontes, como mostrado acima.
- Em algum momento quando as fontes remotas não forem mais usadas, cancele o registro do carregador de arquivos 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 remota do carregador de arquivos de fonte que é capaz de lidar com interações HTTP para baixar dados de fonte em nome do aplicativo. Um URL de referência ou cabeçalhos extras podem ser especificados se exigido pelo serviço de fonte ou serviços que são a origem das fontes.
Importante
Observação de segurança: Quando é feita uma tentativa de buscar uma fonte remota, existe a possibilidade de um invasor falsificar o servidor pretendido que será chamado. Nesse caso, os URLs de destino e de referência e os detalhes do cabeçalho seriam divulgados ao atacante. Os desenvolvedores de aplicativos são responsáveis por mitigar esse risco. Recomenda-se o uso do protocolo HTTPS, em vez de HTTP.
Um único carregador de arquivos de fonte remoto pode ser usado para várias fontes, embora carregadores diferentes possam ser usados se as fontes forem obtidas de vários serviços que tenham requisitos diferentes para URL de referência ou cabeçalhos extras.
4. Registre o carregador de arquivos 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 personalizado são semelhantes às descritas para arquivos de fonte locais conhecidos, com duas exceções importantes. Primeiro, o objeto IDWriteFontFile é criado usando a interface remota do carregador de arquivos de fonte 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 remoto é um arquivo OpenType Font Collection e, em caso afirmativo, ele deve saber qual das fontes dentro da coleção ele usará e o índice de cada uma. Por conseguinte, os restantes passos são os seguintes.
5. Para cada arquivo de fonte remoto, use a interface do carregador de arquivos de fonte remota para criar um IDWriteFontFile, 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 relativa. 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
Nota de segurança/desempenho: Quando é feita uma tentativa de obter um tipo de letra remoto, não há garantia de que o Windows receberá uma resposta do servidor. Em alguns casos, um servidor pode responder com um erro de arquivo não encontrado 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á expirando, embora isso possa levar vários minutos se várias buscas forem iniciadas. Você deve fazer o que puder para garantir que os URLs sejam válidos 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 contêiner WOFF ou WOFF2. Se um arquivo WOFF ou WOFF2 for referenciado, a implementação DirectWrite do carregador de arquivos de fonte remoto descompactará automaticamente os dados da fonte do arquivo contêiner.
6. Para cada índice de face de fonte dentro do arquivo de fonte remoto que 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 DirectWrite para fontes remotas permite que uma referência de face 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á-los, 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á são locais, enquanto solicita o download da fonte remota desejada em paralelo e, em seguida, atualizando os resultados assim que a fonte desejada for baixada.
Para solicitar que a fonte inteira 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 pode ser necessária para processar cadeias de caracteres específicas. DirectWrite fornece métodos adicionais que podem ser usados para solicitar partes dos dados de fonte necessários para conteúdo específico, 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 preferida. 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 fontes 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 fontes pode ser obtida usando o método IDWriteFactory3::GetFontDownloadQueue.
Se uma solicitação de download for feita, mas os dados da fonte já forem locais, isso resultará em uma 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 IDWriteFontDownloadQueue::IsEmpty método.
Depois que as solicitações de fontes remotas forem 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 IDWriteTextLayout métodos 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 tomar as ações apropriadas — prosseguir 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 pode ser usado para limpar os resultados temporários calculados usando fontes de fallback.) Para que o aplicativo seja notificado quando o processo de download for concluído e tome as ações apropriadas, o aplicativo deve fornecer uma implementação da interfaceIDWriteFontDownloadListenere passar isso para a chamada BeginDownload.
Importante
Nota de segurança/desempenho: Quando é feita uma tentativa de obter um tipo de letra remoto, não há garantia de que o Windows receberá uma resposta do servidor. Se o servidor não responder, o Windows acabará expirando, 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 que IDWriteFontDownloadListener::D ownloadCompleted seja chamado.
Implementações de exemplo dessas interações com a fila de download de fontes do DirectWrite e da interfaceIDWriteFontDownloadListener dopodem ser vistas no de exemplo de conjuntos de fontes personalizados do DirectWritee também no exemplo de DirectWrite Downloadable Fonts.
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 é verdade 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 na Atualização de Criadores do Windows 10, IDWriteInMemoryFontFileLoader.
Tal como acontece com um carregador de ficheiros de tipo de letra remoto, um carregador de ficheiros de tipo de letra na memória tem de ser primeiro registado numa fábrica DirectWrite. O carregador precisará ser mantido pelo aplicativo enquanto as fontes associadas a ele estiverem sendo usadas. Uma vez que as fontes não estão mais em uso, e em algum momento antes da fábrica ser destruída, o carregador deve ser cancelado. Isso pode ser feito no destruidor para a classe que possui o objeto carregador. Estas etapas serão mostradas abaixo.
Se o aplicativo tiver informações separadas sobre as faces de fontes representadas pelos dados, ele poderá adicionar referências de faces de fontes individuais 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; DirectWrite será capaz de ler os dados diretamente para derivar os valores de propriedade.
O DirectWrite assume 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 poderá ser usado para adicionar todas as faces 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 de proprietário de dados que implementa 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 ref 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 o Windows 10 Creators Update. Isso assumirá um objeto de proprietário de dados implementado pelo aplicativo, que implementa 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, como mostrado acima.
2. Crie uma interface [**IDWriteFontSetBuilder1**](/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontsetbuilder1), como 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 arquivos de fonte na memória.
4. Registre o carregador de arquivos 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 arquivos 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 objeto IDWriteFontFile ao construtor de conjuntos de fontes usando o método AddFontFile, conforme mostrado acima. Se houver necessidade, o aplicativo pode, em vez disso, criar objetos individuais IDWriteFontFaceReference com base no IDWriteFontFile, opcionalmente definir propriedades personalizadas para cada referência de face de fonte e, em seguida, adicionar a referência de face de fonte com propriedades personalizadas ao conjunto de fontes usando o métodoAddFontFaceReference, conforme mostrado acima.
7. Depois que todas as fontes tiverem sido adicionadas ao construtor do conjunto de fontes, crie o conjunto de fontes personalizado, como mostrado acima.
8. Em algum momento quando as fontes na memória não forem mais usadas, cancele o registro do carregador de arquivos de fonte na memória.
hr = pDWriteFactory->UnregisterFontFileLoader(pInMemoryFontFileLoader);
Cenários avançados
Algumas aplicações podem ter requisitos especiais que requerem um processamento mais avançado do que o descrito acima.
Combinação de 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 suportar a manipulação e a 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 existente forem desejadas no novo conjunto de fontes, o IDWriteFontSet::GetMatchingFonts método 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 de fonte WOFF ou WOFF2 locais
Se um aplicativo tiver arquivos de fonte no sistema de arquivos local ou em um buffer de memória, mas eles usarem os formatos de contêiner WOFF ou WOFF2, o DirectWrite (Windows 10 Creator Update ou posterior) fornecerá um método para descompactar 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 arquivos de fonte. Uma maneira de fazer isso é criar um personalizado IDWriteFontFileLoader implementação que encapsula o fluxo. Tal como acontece com outros carregadores de ficheiros de fontes, este deve ser registado antes da utilização e não registado antes de a fábrica sair do âmbito.
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 interfaceIDWriteFontFileStreampara 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 poderia ser evitada usando caminhos de código separados para arquivos de fonte compactados versus arquivos de fonte brutos.
Depois que um objeto de carregador de arquivos de fonte personalizado é criado, os dados do arquivo de fonte compactado são adicionados ao carregador por meios específicos do aplicativo. O carregador pode lidar com vários arquivos de fonte, cada um dos quais é identificado usando uma chave definida pelo aplicativo que é opaca para DirectWrite. Depois que um arquivo de fonte compactado foi adicionado ao carregador, o método IDWriteFactory::CreateCustomFontFileReference é usado para obter umIDWriteFontFilebaseado nesse carregador para os dados de fonte identificados por uma determinada chave.
O descompactamento real dos dados da fonte pode ser feito à medida que as fontes são adicionadas ao carregador, mas também pode ser manipulado no IDWriteFontFileLoader::CreateStreamFromKey método, 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 as descritas acima.
Uma implementação usando essa abordagem é ilustrada no exemplo de DirectWrite Custom Font Sets.
Usando mecanismos de fonte remota DirectWrite com implementação de rede de baixo nível personalizada
Os mecanismos DirectWrite para lidar com fontes remotas podem ser divididos em mecanismos de nível superior — com conjuntos de fontes que incluem referências de faces de fontes 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 mais alto, mas também exigem interações de rede personalizadas, como a comunicação com servidores usando protocolos diferentes do HTTP.
Para essa situação, um aplicativo precisará criar uma implementação personalizada do IDWriteRemoteFontFileLoader interface que interage 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: IDWriteRemoteFontFileStreame 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 é chamado, o DirectWrite fará consultas ao carregador de arquivos de fonte remoto sobre a localidade dos dados e solicitará o fluxo remoto. Se os dados não forem locais, eles chamarão o método BeginDownload do fluxo. A implementação de fluxo não deve bloquear nessa chamada, mas deve retornar imediatamente, passando de volta um IDWriteAsyncResult objeto que fornece o identificador de espera que o DirectWrite usará para aguardar a operação de download assíncrono. A implementação de 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 para os intervalos baixados sejam bem-sucedidas.
Importante
Nota de segurança/desempenho: Quando é feita uma tentativa de obter um tipo de letra remoto, existe geralmente a possibilidade de um intruso falsificar o servidor pretendido que está a ser chamado ou de o servidor poder não responder. Se você estiver implementando interações de rede personalizadas, poderá ter maior controle sobre atenuaçõ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 algum tempo limite para que o identificador de evento retornado ao DirectWrite acabe sendo definido.
Cenários de suporte em versões anteriores do Windows
Os cenários descritos podem ser suportados 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).