Conjuntos de fuentes personalizados
En este tema se describen varias maneras de usar fuentes personalizadas en la aplicación.
- introducción
- resumen de las API
- conceptos clave
- fuentes y formatos de archivo de fuente
- conjuntos de fuentes y colecciones de fuentes
-
escenarios comunes
- Creación de un conjunto de fuentes mediante fuentes arbitrarias en el sistema de archivos local
- Creación de un conjunto de fuentes mediante fuentes conocidas en el sistema de archivos local
- Creación de un conjunto de fuentes personalizado mediante fuentes remotas conocidas en el web
- Creación de un conjunto de fuentes personalizado con datos de fuente cargados en memoria
- escenarios avanzados
Introducción
La mayoría de las veces, las aplicaciones usan las fuentes que se instalan localmente en el sistema. DirectWrite proporciona acceso a estas fuentes mediante los métodos IDWriteFactory3::GetSystemFontSet o IDWriteFactory::GetSystemFontCollection. En algunos casos, es posible que las aplicaciones también quieran usar fuentes que se incluyen como parte de Windows 10, pero que no están instaladas actualmente en el sistema actual. Se puede acceder a estas fuentes desde el servicio de fuentes de Windows mediante el método GetSystemFontSet o llamando a IDWriteFactory3::GetSystemFontCollection con includeDownloadableFonts establecido en TRUE.
Sin embargo, en algunos escenarios de aplicación, las aplicaciones deben usar fuentes que no están instaladas en el sistema y no las proporciona el servicio de fuentes de Windows. A continuación se muestran ejemplos de estos escenarios:
- Las fuentes se incrustan como recursos dentro de un binario de la aplicación.
- Los archivos de fuente se agrupan dentro de un paquete de aplicación y se almacenan en el disco en la carpeta de instalación de la aplicación.
- La aplicación es una herramienta de desarrollo de fuentes que necesita cargar archivos de fuente especificados por el usuario.
- Las fuentes se incrustan en archivos de documento que se pueden ver o editar en la aplicación.
- La aplicación usa fuentes obtenidas de un servicio de fuentes web público.
- La aplicación usa datos de fuente transmitidos a través de un protocolo de red privada.
DirectWrite proporciona API para trabajar con fuentes personalizadas en estos y otros escenarios similares. Los datos de fuente personalizados pueden provenir de archivos en el sistema de archivos local; desde orígenes remotos basados en la nube a los que se accede mediante HTTP; o desde orígenes arbitrarios después de haberse cargado en un búfer de memoria.
Nota
Aunque DirectWrite ha proporcionado API para trabajar con fuentes personalizadas desde Windows 7, las API más recientes se agregaron en Windows 10 y de nuevo en Windows 10 Creators Update (compilación preliminar 15021 o posterior) que facilitan la implementación de varios de los escenarios mencionados. Este tema se centra en las API disponibles en La ventana 10. Para las aplicaciones que necesitan trabajar en versiones anteriores de Windows, consulte Custom Font Collections (Windows 7/8).
Resumen de las API
Este tema se centra en la funcionalidad proporcionada por las siguientes API:
- interfaz deIDWriteFontSet de
- interfazIDWriteFontSetBuilder de
- interfaz de IDWriteFontSetBuilder1
- interfaz IDWriteFontFaceReference
- interfazIDWriteFontFile de
- método IDWriteFactory::CreateFontFileReference
- método IDWriteFactory::CreateCustomFontFileReference
- métodos IDWriteFactory3::CreateFontFaceReference
- estructura de DWRITE_FONT_PROPERTY
- enumeración DWRITE_FONT_PROPERTY_ID
- interfaz de IDWriteFontFileLoader
- método IDWriteFactory::RegisterFontFileLoader
- método IDWriteFactory::UnregisterFontFileLoader
- método IDWriteFactory5::CreateInMemoryFontFileLoader
- interfaz de IDWriteInMemoryFontFileLoader
- método IDWriteFactory5::CreateHttpFontFileLoader
- interfaz de IDWriteRemoteFontFileLoader
- interfaz de IDWriteFontDownloadQueue
- interfaz de IDWriteFontDownloadListener
- interfaz IDWriteFontFileStream
- interfaz IDWriteRemoteFontFileStream
- interfaz de IDWriteAsyncResult
- método IDWriteFactory5::AnalyzeContainerType
- método IDWriteFactory5::UnpackFontFile
Conceptos clave
Para comprender las API de DirectWrite para trabajar con fuentes personalizadas, puede resultar útil comprender el modelo conceptual que subyace a estas API. Aquí se describen los conceptos clave.
Cuando DirectWrite realiza la representación o el diseño de texto real, debe tener acceso a los datos de fuente reales. Un objeto de cara de fuente contiene datos de fuente reales, que deben existir en el sistema local. Pero para otras operaciones, como comprobar la disponibilidad de una fuente determinada o presentar opciones de fuente a un usuario, todo lo que se necesita es una referencia a una fuente determinada, no a los datos de fuente reales. En DirectWrite, un objeto de referencia facial de fuente contiene solo la información necesaria para localizar y crear instancias de una fuente. Dado que la referencia de la cara de fuente no contiene datos reales, DirectWrite puede tratar con referencias de caras de fuente para las que los datos reales están en una ubicación de red remota, así como cuando los datos reales son locales.
Un conjunto de fuentes es un conjunto de referencias faciales de fuente, junto con ciertas propiedades básicas e informativas que se pueden usar en referencia a la fuente o compararlas con otras fuentes, como el nombre de familia o un valor de peso de fuente. Los datos reales de las distintas fuentes pueden ser locales, o bien pueden ser remotos o una mezcla.
Se puede usar un conjunto de fuentes para obtener un objeto de colección de fuentes correspondiente. Consulte Conjuntos de fuentes y colecciones de fuentes a continuación para obtener más detalles.
La interfaz IDWriteFontSet proporciona métodos que permiten consultar valores de propiedad como el nombre de familia o el peso de fuente, o para las referencias de caras de fuente que coinciden con valores de propiedad concretos. Después de filtrar por una selección determinada, se puede obtener una instancia del IDWriteFontFaceReference interfaz, con métodos para descargar (si los datos de fuente reales están actualmente remotos), para obtener el objeto IDWriteFontFace3 correspondienteque se puede usar para el diseño y la representación.
La interfaz IDWriteFontFile subyace a cada cara de fuente o referencia de cara de fuente. Esto representa la ubicación de un archivo de fuente y tiene dos componentes: un cargador de archivos de fuente y una clave de archivo de fuente. El cargador de archivos de fuente (IDWriteFontFileLoader) se usa para abrir un archivo si es necesario y devuelve una secuencia con los datos (IDWriteFontFileStream). Dependiendo del cargador, los datos se pueden ubicar en una ruta de acceso del archivo local, una dirección URL remota o en un búfer de memoria. La clave es un valor definido por el cargador que identifica de forma única el archivo dentro del contexto del cargador, lo que permite al cargador localizar los datos y crear una secuencia para él.
Las fuentes personalizadas se pueden agregar fácilmente a un conjunto de fuentes personalizado, que a su vez se puede usar para filtrar o organizar la información de fuentes con fines como la creación de una interfaz de usuario del selector de fuentes. El conjunto de fuentes también se puede usar para crear una colección de fuentes para usarla en API de nivel superior, como IDWriteTextFormat y IDWriteTextLayout. La interfazIDWriteFontSetBuilderse puede usar para crear un conjunto de fuentes personalizado que incluya varias fuentes personalizadas. También se puede usar para crear un conjunto de fuentes personalizado que mezcla fuentes personalizadas y fuentes proporcionadas por el sistema; o que combina fuentes con orígenes diferentes para los datos reales: almacenamiento local, direcciones URL remotas y memoria.
Como se mencionó, una referencia de cara de fuente puede hacer referencia a datos de fuente en un origen remoto, pero los datos deben ser locales para obtener un objeto de cara de fuente que se puede usar para el diseño y la representación. La descarga de datos remotos se controla mediante una cola de descarga de fuentes. Las aplicaciones pueden usar la interfaz de IDWriteFontDownloadQueue para poner en cola las solicitudes para descargar fuentes remotas para iniciar el proceso de descarga y registrar un IDWriteFontDownloadListener objeto para tomar medidas cuando se haya completado el proceso de descarga.
Para la mayoría de las interfaces descritas aquí, DirectWrite proporciona implementaciones del sistema. La única excepción es la interfaz IDWriteFontDownloadListener, que una aplicación implementa para realizar acciones específicas de la aplicación cuando se han descargado fuentes remotas localmente. Las aplicaciones pueden tener motivos para proporcionar sus propias implementaciones personalizadas para determinadas interfaces, aunque eso solo sería necesario en escenarios específicos y más avanzados. Por ejemplo, una aplicación tendría que proporcionar una implementación personalizada del IDWriteFontFileLoader interfaz para controlar los archivos de fuente en el almacenamiento local que usan el formato de contenedor WOFF2. A continuación se proporcionan detalles adicionales.
Fuentes y formatos de archivo de fuente
Otro concepto clave que resulta útil para comprender es la relación entre las caras de fuente individuales y los archivos de fuente que los contienen. La idea de un archivo de fuente OpenType (.ttf o .otf) que contiene una sola fuente es familiar. Pero el formato de fuente OpenType también permite una colección de fuentes OpenType (.ttc o .otc), que es un único archivo que contiene varias fuentes. Los archivos de colección OpenType se usan a menudo para fuentes grandes que están estrechamente relacionadas y tienen valores idénticos para determinados datos de fuente: al combinar las fuentes en un único archivo, los datos comunes se pueden desduplicar. Por este motivo, una referencia de cara de fuente o cara de fuente debe hacer referencia no solo a un archivo de fuente (o origen de datos equivalente), sino que también debe especificar un índice de fuente dentro de ese archivo, para el caso general en el que el archivo puede ser un archivo de colección.
Para las fuentes usadas en la Web, los datos de fuente a menudo se empaquetan en determinados formatos de contenedor, WOFF o WOFF2, que proporcionan cierta compresión de los datos de fuente y algún nivel de protección contra la piratería y la infracción de las licencias de fuentes. Funcionalmente, un archivo WOFF o WOFF2 es equivalente a una fuente OpenType o un archivo de colección de fuentes, pero los datos se codifican en un formato diferente que requiere desempaquetar antes de que se pueda usar.
Algunas API de DirectWrite pueden tratar caras de fuente individuales, mientras que otras API pueden controlar archivos que pueden incluir archivos de colección OpenType que contienen varias caras. Del mismo modo, algunas API solo tratan los datos sin procesar y con formato OpenType, mientras que otras API pueden controlar los formatos de contenedor empaquetados, WOFF y WOFF2. Estos detalles se proporcionan en la explicación siguiente.
Conjuntos de fuentes y colecciones de fuentes
Algunas aplicaciones se pueden implementar para trabajar con fuentes mediante la interfaz de IDWriteFontCollection. Hay una correspondencia directa entre una colección de fuentes y un conjunto de fuentes. Cada uno puede contener las mismas fuentes, pero las presentan con una organización diferente. Desde cualquier colección de fuentes, se puede obtener un conjunto de fuentes correspondiente y viceversa.
Al trabajar con una serie de fuentes personalizadas, es más fácil usar una interfaz de generador de conjuntos de fuentes para crear un conjunto de fuentes personalizado y, a continuación, obtener una colección de fuentes después de crear el conjunto de fuentes. El proceso para crear un conjunto de fuentes personalizado se describirá en detalle a continuación. Para obtener una interfaz deIDWriteFontCollection1de un conjunto de fuentes, se usa el método IDWriteFactory3::CreateFontCollectionFromFontSet.
Si la aplicación tiene un objeto de colección y necesita obtener un conjunto de fuentes correspondiente, esto se puede hacer mediante el método IDWriteFontCollection1::GetFontSet.
Escenarios comunes
En esta sección se describen algunos de los escenarios más comunes que implican conjuntos de fuentes personalizados:
- Crear un conjunto de fuentes personalizado mediante fuentes arbitrarias en rutas de acceso del sistema de archivos local.
- Crear un conjunto de fuentes personalizado mediante fuentes conocidas (quizás agrupadas con la aplicación) que se almacenan en el sistema de archivos local.
- Crear un conjunto de fuentes personalizado mediante fuentes conocidas y remotas en la Web.
- Crear un conjunto de fuentes personalizado mediante los datos de fuente cargados en la memoria.
Las implementaciones completas de estos escenarios se proporcionan en el ejemplo de conjuntos de fuentes personalizados directWrite. En este ejemplo también se muestra un escenario más avanzado para controlar los datos de fuente empaquetados en formatos de contenedor WOFF o WOFF2, que se tratarán a continuación.
Creación de un conjunto de fuentes mediante fuentes arbitrarias en el sistema de archivos local
Cuando se trabaja con un conjunto arbitrario de archivos de fuente en el almacenamiento local, el IDWriteFontSetBuilder1::AddFontFile método es cómodo, ya que, en una sola llamada, puede controlar todas las caras de fuente dentro de un archivo de colección de fuentes OpenType, así como todas las instancias de una fuente de variable OpenType. Esto está disponible en Windows 10 Creators Update (compilación preliminar 15021 o posterior) y se recomienda siempre que esté disponible.
Para usar este método, use el siguiente proceso.
- 1. Empiece por crear la interfaz IDWriteFactory5:
- Para cada archivo de fuente del sistema de archivos local, cree un IDWriteFontFile que haga referencia a él:
- Una vez agregados todos los archivos al generador de conjuntos de fuentes, se puede crear el conjunto de fuentes personalizado:
IDWriteFactory5* pDWriteFactory;
HRESULT hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory5),
reinterpret_cast<IUnknown**>(&pDWriteFactory)
);
2. Use la fábrica para obtener la interfazIDWriteFontSetBuilder1:
IDWriteFontSetBuilder1* pFontSetBuilder;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontSetBuilder(&pFontSetBuilder);
}
IDWriteFontFile* pFontFile;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontFileReference(pFilePath, /* lastWriteTime*/ nullptr, &pFontFile);
}
4. Agregue el objetoIDWriteFontFileal generador de conjuntos de fuentes mediante el métodoAddFontFile:
hr = pFontSetBuilder->AddFontFile(pFontFile);
Si la ruta de acceso del archivo especificada en la llamada a CreateFontFileReference hace referencia a algo distinto de un archivo OpenType compatible, la llamada a AddFontFile devolverá un error, DWRITE_E_FILEFORMAT.
IDWriteFontSet* pFontSet;
hr = pFontSetBuilder->CreateFontSet(&pFontSet);
Si la aplicación debe ejecutarse en versiones de Windows 10 anteriores a Windows 10 Creators Update, el método AddFontFile no estará disponible. La disponibilidad se puede detectar mediante la creación de una interfaz IDWriteFactory3 y, a continuación, usar QueryInterface para intentar obtener una interfaz de IDWriteFactory5: si esto se realiza correctamente, también estará disponible la interfazIDWriteFontSetBuilder1 y método AddFontFile.
Si el método AddFontFile no está disponible, se debe usar el método IDWriteFontSetBuilder::AddFontFaceReference para agregar caras de fuente individuales. Para permitir archivos openType Font Collection que contienen varias caras, se puede usar el método IDWriteFontFile::Analyze para determinar el número de caras contenidas en el archivo. El proceso es el siguiente.
- 1. Empiece por crear la interfaz IDWriteFactory3:
- Para cada archivo de fuente, cree un IDWriteFontFile, como se ha indicado anteriormente:
- Una vez agregadas todas las caras al generador del conjunto de fuentes, cree el conjunto de fuentes personalizado, como se muestra anteriormente.
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);
}
En lugar de agregar el archivo directamente al generador de conjuntos de fuentes, es necesario determinar el número de caras y crear objetos idWriteFontFaceReference individuales .
4. Use el método Analizar para obtener el número de caras del archivo.
BOOL isSupported;
DWRITE_FONT_FILE_TYPE fileType;
UINT32 numberOfFonts;
hr = pFontFile->Analyze(&isSupported, &fileType, /* face type */ nullptr, &numberOfFonts);
El método Analyze también establecerá valores para los parámetros isSupported y fileType. Si el archivo no es un formato compatible, isSupported será FALSE y se puede realizar la acción adecuada, como omitir el archivo.
5. Recorra el número de fuentes establecidas en el parámetro numberOfFonts. Dentro del bucle, cree un IDWriteFontFaceReference para cada par de archivo/índice y agréguelo al generador del conjunto de fuentes.
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);
}
}
Una aplicación se puede diseñar para que use el método AddFontFile preferido cuando se ejecute en Windows 10 Creators Update, pero recurra a usar el método AddFontFaceReference cuando se ejecuta en versiones anteriores de Windows 10. Pruebe la disponibilidad de la interfaz de IDWriteFactory5, tal como se ha descrito anteriormente y, a continuación, bifurque en consecuencia. Este enfoque se muestra en el ejemplo de conjuntos de fuentes personalizados de directWrite .
Creación de un conjunto de fuentes mediante fuentes conocidas en el sistema de archivos local
Como se mencionó anteriormente, cada referencia de cara de fuente en un conjunto de fuentes está asociada a determinadas propiedades informativas, como el nombre de familia y el peso de la fuente. Cuando se agregan fuentes personalizadas a un generador de conjuntos de fuentes mediante las llamadas API enumeradas anteriormente, estas propiedades informativas se obtienen directamente de los datos de fuente reales, que se leen cuando se agrega la fuente. Sin embargo, en algunas situaciones, si una aplicación tiene otra fuente de información sobre una fuente, es posible que desee proporcionar sus propios valores personalizados para estas propiedades.
Como ejemplo de cómo esto puede ser útil, supongamos que una aplicación agrupa algunas fuentes que se usan para presentar elementos de interfaz de usuario concretos dentro de la aplicación. En ocasiones, como con una nueva versión de la aplicación, es posible que sea necesario cambiar las fuentes específicas que usa la aplicación para estos elementos. Si la aplicación tiene referencias codificadas a las fuentes específicas, el reemplazo de una fuente con otra requerirá cambiar cada una de esas referencias. En su lugar, si la aplicación usa propiedades personalizadas para asignar alias funcionales en función del tipo de elemento o texto que se representa, asigna cada alias a una fuente específica en un solo lugar y, a continuación, usa los alias en todos los contextos en los que se crean y manipulan las fuentes, y, a continuación, reemplazar una fuente por otra requiere cambiar solo el lugar donde el alias se asigna a una fuente específica.
Se pueden asignar valores personalizados para propiedades informativas cuando se llama al método IDWriteFontSetBuilder::AddFontFaceReference. El método para hacerlo es el siguiente; esto se puede usar en cualquier versión de Windows 10.
Como se muestra anteriormente, empiece por obtener las interfaces de IDWriteFactory3 y IDWriteFontSet. Para cada cara de fuente personalizada que se va a agregar, cree una IDWriteFontFaceReference, como se muestra anteriormente. Antes de agregar esto al generador de conjuntos de fuentes (en el bucle del paso 5, que se muestra anteriormente), la aplicación define los valores de propiedad personalizados que se van a usar.
Un conjunto de valores de propiedad personalizados se define mediante una matriz de estructuras de DWRITE_FONT_PROPERTY. Cada una de estas identifica una propiedad determinada de la enumeración DWRITE_FONT_PROPERTY_ID y el valor de propiedad correspondiente que se va a usar.
Tenga en cuenta que todos los valores de propiedad se asignan como cadenas. Si posteriormente se pueden mostrar a los usuarios, se pueden establecer valores alternativos para una propiedad determinada para distintos idiomas, pero esto no es necesario. Tenga en cuenta también que, si la aplicación establece valores de propiedad personalizados, solo se usarán los valores especificados en el conjunto de fuentes; DirectWrite no derivará ningún valor directamente de la fuente para las propiedades informativas usadas en un conjunto de fuentes.
En el ejemplo siguiente se definen valores personalizados para tres propiedades informativas: nombre de familia, nombre completo y peso de fuente.
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 }
};
Después de definir la matriz deseada de valores de propiedad para una fuente, realice una llamada a AddFontFaceRefence, pasando la matriz de propiedades, así como la referencia de la cara de fuente.
hr = pFontSetBuilder->AddFontFaceReference(pFontFaceReference, props, ARRAYSIZE(props));
Una vez agregadas todas las caras de fuente personalizadas al generador de conjuntos de fuentes, junto con sus propiedades personalizadas, cree el conjunto de fuentes personalizado, como se muestra anteriormente.
Creación de un conjunto de fuentes personalizado con fuentes conocidas y remotas en la Web
Las propiedades personalizadas son importantes para trabajar con fuentes remotas. Cada referencia de cara de fuente debe tener algunas propiedades informativas para caracterizar la fuente y distinguirla de otras fuentes. Dado que los datos de fuente para fuentes remotas no son locales, DirectWrite no puede derivar propiedades directamente de los datos de fuente. Por lo tanto, las propiedades deben proporcionarse explícitamente al agregar una fuente remota al generador de conjuntos de fuentes.
La secuencia de llamadas API para agregar fuentes remotas a un conjunto de fuentes es similar a la secuencia descrita para el escenario anterior. Sin embargo, dado que los datos de fuente son remotos, las operaciones implicadas para leer los datos de fuente reales serán diferentes de cuando trabajen con archivos en el almacenamiento local. En esta situación, se ha agregado una nueva interfaz de nivel inferior, IDWriteRemoteFontFileLoader, en Windows 10 Creators Update.
Para usar el cargador de archivos de fuente remoto, primero debe registrarse con un generador de DirectWrite. La aplicación deberá mantener el cargador siempre que se usen las fuentes asociadas a ella. Una vez que las fuentes ya no están en uso y en algún momento antes de que se destruya la fábrica, el cargador debe anular el registro. Esto se puede hacer en el destructor de la clase que posee el objeto del cargador. Estos pasos se mostrarán a continuación.
El método para crear un conjunto de fuentes personalizado mediante fuentes remotas es el siguiente; esto requiere Windows 10 Creators Update.
- 1. Cree una interfaz IDWriteFactory5, como se muestra anteriormente.
2. Cree una interfaz IDWriteFontSetBuilder, como se muestra anteriormente.
3. Use la fábrica para obtener un IDWriteRemoteFontFileLoader.
- Defina propiedades personalizadas para la cara de fuente, como se muestra anteriormente.
- Agregue la referencia de la cara de fuente junto con las propiedades personalizadas al generador del conjunto de fuentes, como se muestra anteriormente.
- Una vez agregadas todas las fuentes al generador del conjunto de fuentes, cree el conjunto de fuentes, como se muestra anteriormente.
- En algún momento en que ya no se usarán las fuentes remotas, anule el registro del cargador de archivos de fuente remoto.
IDWriteRemoteFontFileLoader* pRemoteFontFileLoader;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateHttpFontFileLoader(
/* referrerURL */ nullptr,
/* extraHeaders */ nullptr,
&pRemoteFontFileLoader
);
}
Esto devuelve una implementación proporcionada por el sistema de la interfaz del cargador de archivos de fuente remota que puede controlar las interacciones HTTP para descargar datos de fuente en nombre de la aplicación. Se puede especificar una dirección URL de referencia o encabezados adicionales si es necesario por el servicio de fuentes o los servicios que son el origen de las fuentes.
Importante
Nota de seguridad: cuando se intenta capturar una fuente remota, existe la posibilidad de que un atacante se despoje del servidor deseado al que se llamará. En ese caso, las direcciones URL de destino y del remitente y los detalles del encabezado se revelarían al atacante. Los desarrolladores de aplicaciones son responsables de mitigar este riesgo. Se recomienda el uso del protocolo HTTPS, en lugar de HTTP.
Se puede usar un solo cargador de archivos de fuente remoto para varias fuentes, aunque se pueden usar cargadores diferentes si las fuentes se obtienen de varios servicios que tienen requisitos diferentes para la dirección URL de referencia o los encabezados adicionales.
4. Registre el cargador de archivos de fuente remoto con la fábrica.
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->RegisterFontFileLoader(pRemoteFontFileLoader);
}
Desde este punto, los pasos para crear el conjunto de fuentes personalizados son similares a los descritos para los archivos de fuente locales conocidos, con dos excepciones importantes. En primer lugar, el IDWriteFontFile objeto se crea mediante la interfaz del cargador de archivos de fuente remota en lugar de usar el generador. En segundo lugar, no se puede usar el método Analyze, ya que los datos de fuente no son locales. En su lugar, la aplicación debe saber si el archivo de fuente remoto es un archivo de colección de fuentes OpenType y, si es así, debe saber cuál de las fuentes de la colección usará y el índice de cada uno. Por lo tanto, los pasos restantes son los siguientes.
5. Para cada archivo de fuente remoto, use la interfaz del cargador de archivos de fuente remoto para crear una IDWriteFontFile, especificando la dirección URL necesaria para acceder al archivo de fuente.
IDWriteFontFile* pFontFile;
hr = pRemoteFontFileLoader->CreateFontFileReferenceFromUrl(
pDWriteFactory,
/* baseUrl */ L"https://github.com/",
/* fontFileUrl */ L"winjs/winjs/blob/master/src/fonts/Symbols.ttf?raw=true",
&pFontFile
);
Tenga en cuenta que la dirección URL completa se puede especificar en el parámetro fontFileUrl, o bien puede dividirse en partes base y relativas. Si se especifica una dirección URL base, la concatenación de los valores baseUrl y fontFileUrl debe proporcionar la dirección URL completa: DirectWrite no proporcionará ningún delimitador adicional.
Importante
Nota de seguridad y rendimiento: cuando se intenta capturar una fuente remota, no hay ninguna garantía de que Windows recibirá una respuesta del servidor. En algunos casos, un servidor puede responder con un error de archivo no encontrado para una dirección URL relativa no válida, pero dejar de responder si recibe varias solicitudes no válidas. Si el servidor no responde, Windows agotará el tiempo de espera, aunque esto puede tardar varios minutos si se inician varias capturas. Debe hacer lo que puede para asegurarse de que las direcciones URL serán válidas cuando se realicen llamadas.
Tenga en cuenta también que la dirección URL puede apuntar a un archivo de fuente OpenType sin procesar (.ttf, .otf, .ttc, .otc), pero también puede apuntar a fuentes en un archivo de contenedor WOFF o WOFF2. Si se hace referencia a un archivo WOFF o WOFF2, la implementación de DirectWrite del cargador de archivos de fuente remoto desempaquetará automáticamente los datos de fuente del archivo de contenedor.
6. Para cada índice de caras de fuente dentro del archivo de fuente remoto que se va a usar, cree un IDWriteFontFaceReference.
IDWriteFontFaceReference* pFontFaceReference;
hr = pDWriteFactory->CreateFontFaceReference(pFontFile, /* faceIndex */ 0, DWRITE_FONT_SIMULATIONS_NONE, &pFontFaceReference);
hr = pDWriteFactory->UnregisterFontFileLoader(pRemoteFontFileLoader);
Una vez creado un conjunto de fuentes personalizado con fuentes remotas personalizadas, el conjunto de fuentes contiene referencias y propiedades informativas para las fuentes remotas, pero los datos reales siguen siendo remotos. La compatibilidad con DirectWrite para fuentes remotas permite mantener una referencia facial de fuente en el conjunto de fuentes y para que se seleccione una fuente para su uso en el diseño y la representación, pero que los datos reales no se descarguen hasta que haya una necesidad real de usarlo, como cuando se realizará el diseño de texto.
Una aplicación puede adoptar un enfoque inicial solicitando que DirectWrite descargue los datos de fuente y, a continuación, espere a que se confirme una descarga correcta antes de que se inicie cualquier procesamiento con la fuente. Pero una descarga de red implica cierta latencia de una duración impredecible y el éxito también es incierta. Por este motivo, normalmente será mejor adoptar un enfoque diferente, lo que permite que el diseño y la representación se realicen inicialmente mediante fuentes alternativas o de reserva que ya sean locales, mientras se solicita la descarga de la fuente deseada, remota en paralelo y, a continuación, se actualizan los resultados una vez descargada la fuente deseada.
Para solicitar que se descargue toda la fuente antes de que se use, se puede usar el método IDWriteFontFaceReference::EnqueueFontDownloadRequest. Si la fuente es muy grande, solo se puede necesitar una parte de los datos para procesar cadenas concretas. DirectWrite proporciona métodos adicionales que se pueden usar para solicitar partes de los datos de fuente necesarios para contenido concreto, EnqueueCharacterDownloadRequest y EnqueueGlyphDownloadRequest.
Supongamos que el enfoque que se debe adoptar en la aplicación es permitir que el procesamiento se realice inicialmente mediante fuentes locales, alternativas o de reserva. El método IDWriteFontFallback::MapCharacters se puede usar para identificar fuentes de reserva locales y también pondrá en cola automáticamente una solicitud para descargar la fuente preferida. Además, si se usa IDWriteTextLayout y se usa parte o todo el texto del diseño mediante una referencia de fuente remota, DirectWrite usará automáticamente el método MapCharacters para obtener fuentes de reserva locales y poner en cola una solicitud para descargar los datos de fuente remotos.
DirectWrite mantiene una cola de descarga de fuentes para cada generador y las solicitudes realizadas mediante los métodos mencionados anteriormente se agregan a esa cola. La cola de descarga de fuentes se puede obtener mediante el método IDWriteFactory3::GetFontDownloadQueue.
Si se realiza una solicitud de descarga, pero los datos de fuente ya son locales, esto dará como resultado un no-op: Nothing se agregará a la cola de descarga. Una aplicación puede comprobar si la cola está vacía o hay solicitudes de descarga pendientes llamando al método IDWriteFontDownloadQueue::IsEmpty.
Una vez agregadas las solicitudes de fuentes remotas a la cola, se debe iniciar el proceso de descarga. Cuando las fuentes remotas se usan en IDWriteTextLayout, la descarga se iniciará automáticamente cuando la aplicación llame a IDWriteTextLayout métodos que fuerzan el diseño o las operaciones de representación, como los métodos GetLineMetrics o Draw. En otros escenarios, la aplicación debe iniciar la descarga directamente llamando a IDWriteFontDownloadQueue::BeginDownload.
Cuando se complete una descarga, será hasta la aplicación para realizar las acciones adecuadas, continuar con las operaciones pendientes o repetir las operaciones que se realizaron inicialmente con fuentes de reserva. (Si se usa el diseño de texto de DirectWrite, IDWriteTextLayout3::InvalidateLayout se pueden usar para borrar los resultados temporales calculados mediante fuentes de reserva). Para que se notifique a la aplicación cuando se haya completado el proceso de descarga y para realizar las acciones adecuadas, la aplicación debe proporcionar una implementación de la IDWriteFontDownloadListener interfaz y pasarla a la llamada BeginDownload.
Importante
Nota de seguridad y rendimiento: cuando se intenta capturar una fuente remota, no hay ninguna garantía de que Windows recibirá una respuesta del servidor. Si el servidor no responde, Windows agotará el tiempo de espera, aunque esto puede tardar varios minutos si se capturan varias fuentes remotas, pero se produce un error. La llamada BeginDownload devolverá inmediatamente. Las aplicaciones no deben bloquear la interfaz de usuario mientras esperan a que se llame a IDWriteFontDownloadListener::D ownloadCompleted.
Las implementaciones de ejemplo de estas interacciones con la cola de descarga de fuentes de DirectWrite y de la interfaz de IDWriteFontDownloadListener se pueden ver en la ejemplo conjuntos de fuentes personalizados de DirectWrite, y también en el ejemplo de fuentes descargables de DirectWrite.
Creación de un conjunto de fuentes personalizado mediante datos de fuente cargados en la memoria
Al igual que las operaciones de bajo nivel para leer datos de un archivo de fuente son diferentes para los archivos de un disco local frente a los archivos remotos en la Web, lo mismo también es cierto para los datos de fuente cargados en un búfer de memoria. Se ha agregado una nueva interfaz de bajo nivel para controlar los datos de fuentes en memoria en windows 10 Creators Update, IDWriteInMemoryFontFileLoader.
Al igual que con un cargador de archivos de fuente remoto, primero se debe registrar un cargador de archivos de fuente en memoria con un generador de DirectWrite. La aplicación deberá mantener el cargador siempre que se usen las fuentes asociadas a ella. Una vez que las fuentes ya no están en uso y en algún momento antes de que se destruya la fábrica, el cargador debe anular el registro. Esto se puede hacer en el destructor de la clase que posee el objeto del cargador. Estos pasos se mostrarán a continuación.
Si la aplicación tiene información independiente sobre las caras de fuente representadas por los datos, puede agregar referencias de caras de fuente individuales a un generador de conjuntos de fuentes con propiedades personalizadas especificadas. Puesto que los datos de fuente están en la memoria local, sin embargo, esto no es necesario; DirectWrite podrá leer los datos directamente para derivar los valores de propiedad.
DirectWrite supone que los datos de fuente están en formato Raw, OpenType, equivalente a un archivo OpenType (.ttf, .otf, .ttc, .otc), pero en memoria en lugar de en el disco. Los datos no pueden estar en formato de contenedor WOFF o WOFF2. Los datos pueden representar una colección de fuentes OpenType. Si no se usan propiedades personalizadas, se puede usar el método IDWriteFontSetBuilder1::AddFontFile para agregar todas las caras de fuente de los datos en una sola llamada.
Una consideración importante para el escenario en memoria es la duración de los datos. Si se proporciona un puntero al búfer a DirectWrite sin una indicación clara de que hay un propietario, DirectWrite realizará una copia de los datos en un nuevo búfer de memoria que poseerá. Para evitar la copia de datos y la asignación de memoria adicional, la aplicación puede pasar un objeto propietario de datos que implemente IUnknown y que posee el búfer de memoria que contiene los datos de fuente. Al implementar esta interfaz, DirectWrite puede agregar al recuento de referencias del objeto, lo que garantiza la duración de los datos de propiedad.
El método para crear un conjunto de fuentes personalizado mediante datos de fuente en memoria es el siguiente; esto requiere Windows 10 Creators Update. Esto presupone un objeto de propietario de datos implementado por la aplicación, que implementa IUnknown y también tiene métodos que devuelven un puntero al búfer de memoria y el tamaño del búfer.
- 1. Cree una interfaz IDWriteFactory5, como se muestra anteriormente.
2. Cree una interfaz [**IDWriteFontSetBuilder1**](/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontsetbuilder1), como se muestra anteriormente.
3. Use la fábrica para obtener un IDWriteInMemoryFontFileLoader.
IDWriteInMemoryFontFileLoader* pInMemoryFontFileLoader;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateInMemoryFontFileLoader(&pInMemoryFontFileLoader);
}
Esto devuelve una implementación proporcionada por el sistema de la interfaz del cargador de archivos de fuente en memoria.
4. Registre el cargador de archivos de fuente en memoria con la fábrica.
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->RegisterFontFileLoader(pInMemoryFontFileLoader);
}
5. Para cada archivo de fuente en memoria, use el cargador de archivos de fuente en memoria para crear una 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. Agregue el objeto IDWriteFontFile al generador de conjuntos de fuentes mediante el método AddFontFile, como se muestra anteriormente. Si es necesario, la aplicación puede crear idWriteFontFaceReference objetos individuales basados en el IDWriteFontFile, opcionalmente definir propiedades personalizadas para cada referencia de cara de fuente y, a continuación, agregar la referencia de cara de fuente con propiedades personalizadas al conjunto de fuentes mediante el método AddFontFaceReference, como se muestra anteriormente.
7. Una vez agregadas todas las fuentes al generador del conjunto de fuentes, cree el conjunto de fuentes personalizado, como se muestra anteriormente.
8. En algún momento en el que ya no se usarán las fuentes en memoria, anule el registro del cargador de archivos de fuente en memoria.
hr = pDWriteFactory->UnregisterFontFileLoader(pInMemoryFontFileLoader);
Escenarios avanzados
Algunas aplicaciones pueden tener requisitos especiales que requieren un procesamiento más avanzado que el descrito anteriormente.
Combinación de conjuntos de fuentes
Es posible que algunas aplicaciones necesiten crear un conjunto de fuentes que incluya alguna combinación de elementos de otros conjuntos de fuentes. Por ejemplo, una aplicación puede querer crear un conjunto de fuentes que combine todas las fuentes instaladas en el sistema con una selección de fuentes personalizadas o que combine fuentes instaladas que coincidan con determinados criterios con otras fuentes. DirectWrite tiene API para admitir la manipulación y la combinación de conjuntos de fuentes.
Para combinar dos o más conjuntos de fuentes, el método IDWriteFontSetBuilder::AddFontSet agrega todas las fuentes del conjunto de fuentes determinado que se agregarán a un generador de conjuntos de fuentes en una sola llamada. Si solo se desean determinadas fuentes de un conjunto de fuentes existente en el nuevo conjunto de fuentes, se puede usar el método IDWriteFontSet::GetMatchingFonts para derivar un nuevo objeto de conjunto de fuentes que se ha filtrado para incluir solo las fuentes que coinciden con las propiedades especificadas. Estos métodos proporcionan una manera sencilla de crear un conjunto de fuentes personalizado que combine fuentes a partir de dos o más conjuntos de fuentes existentes.
Uso de datos de fuente WOFF o WOFF2 locales
Si una aplicación tiene archivos de fuente en el sistema de archivos local o en un búfer de memoria, pero usan los formatos de contenedor WOFF o WOFF2, DirectWrite (Windows 10 Creator Update o posterior) proporciona un método para desempaquetar el formato de contenedor, IDWriteFactory5::UnpackFontFile, que devuelve un IDWriteFontFileStream.
Sin embargo, la aplicación necesitará una manera de obtener el IDWriteFontFileStream en un objeto de cargador de archivos de fuente. Una manera de hacerlo es crear una implementación de IDWriteFontFileLoaderpersonalizada que encapsula la secuencia. Al igual que con otros cargadores de archivos de fuente, debe registrarse antes de su uso y anular el registro antes de que la fábrica salga del ámbito.
Si el cargador personalizado también se usará con archivos de fuente sin procesar (sin empaquetar), la aplicación también tendría que proporcionar una implementación personalizada de la interfaz de IDWriteFontFileStream para controlar esos archivos. Sin embargo, hay maneras más sencillas de usar las API descritas anteriormente para controlar los archivos de fuente sin procesar. La necesidad de una implementación de flujo personalizada podría evitarse mediante rutas de acceso de código independientes para archivos de fuente empaquetados frente a archivos de fuente sin formato.
Después de crear un objeto de cargador de archivos de fuente personalizado, los datos del archivo de fuente empaquetado se agregan al cargador mediante medios específicos de la aplicación. El cargador puede controlar varios archivos de fuente, cada uno de los cuales se identifica mediante una clave definida por la aplicación que es opaca para DirectWrite. Después de agregar un archivo de fuente empaquetado al cargador, el IDWriteFactory::CreateCustomFontFileReference método se usa para obtener un IDWriteFontFile basado en ese cargador para los datos de fuente identificados por una clave determinada.
El desempaquetado real de los datos de fuente se puede realizar a medida que se agregan fuentes al cargador, pero también se puede controlar en el método IDWriteFontFileLoader::CreateStreamFromKey, al que DirectWrite llamará cuando primero necesite leer los datos de fuente.
Después de crear un objeto IDWriteFontFile, los pasos restantes para agregar las fuentes a un conjunto de fuentes personalizado serán como se describió anteriormente.
Una implementación con este enfoque se muestra en el ejemplo de conjuntos de fuentes personalizados de directWrite .
Uso de mecanismos de fuentes remotas de DirectWrite con implementación de red de bajo nivel personalizada
Los mecanismos directWrite para controlar las fuentes remotas se pueden dividir en mecanismos de nivel superior , que tienen conjuntos de fuentes que incluyen referencias de caras de fuente para fuentes remotas, comprobando la localidad de los datos de fuente y administrando la cola para las solicitudes de descarga de fuentes, y los mecanismos de nivel inferior que controlan la descarga real. Es posible que algunas aplicaciones quieran usar los mecanismos de fuentes remotas de nivel superior, pero también requieren interacciones de red personalizadas, como la comunicación con servidores que usan protocolos distintos de HTTP.
En esta situación, una aplicación tendrá que crear una implementación personalizada del IDWriteRemoteFontFileLoader interfaz que interactúe con otras interfaces de nivel inferior de las maneras necesarias. La aplicación también tendrá que proporcionar implementaciones personalizadas de estas interfaces de nivel inferior: IDWriteRemoteFontFileStreamy IDWriteAsyncResult. Estas tres interfaces tienen métodos de devolución de llamada a los que DirectWrite llamará durante las operaciones de descarga.
Cuando se llama a IDWriteFontDownloadQueue::BeginDownload, DirectWrite realizará consultas en el cargador de archivos de fuente remoto sobre la localidad de los datos y solicitará la secuencia remota. Si los datos no son locales, llamará al método BeginDownload de la secuencia. La implementación de la secuencia no debe bloquearse en esa llamada, pero debe devolver inmediatamente, pasando una IDWriteAsyncResult objeto que proporciona el identificador de espera DirectWrite usará para esperar en la operación de descarga asincrónica. La implementación de flujo personalizada es responsable de controlar la comunicación remota. Cuando se haya producido el evento de finalización, DirectWrite llamará a IDWriteAsyncResult::GetResult para determinar el resultado de la operación. Si el resultado es correcto, se espera que las llamadas ReadFragment posteriores a la secuencia para los intervalos descargados se realicen correctamente.
Importante
Nota de seguridad y rendimiento: cuando se intenta capturar una fuente remota, el potencial existe en general para que un atacante suplanta el servidor deseado al que se llama o que el servidor no responda. Si va a implementar interacciones de red personalizadas, puede tener un mayor control sobre las mitigaciones que cuando se trabaja con servidores de terceros. Sin embargo, es necesario tener en cuenta las mitigaciones adecuadas para evitar la divulgación de información o la denegación de servicio. Se recomiendan protocolos seguros como HTTPS. Además, debe compilar en un tiempo de espera para que el controlador de eventos devuelto a DirectWrite se establezca finalmente.
Escenarios auxiliares en versiones anteriores de Windows
Los escenarios que se han descrito se pueden admitir en DirectWrite en versiones anteriores de Windows, pero requerirían una implementación mucho más personalizada en la parte de la aplicación con las API más limitadas que estaban disponibles antes de Windows 10. Para obtener más información, vea Custom Font Collections (Windows 7/8).