Selezione dei tipi di carattere
L'interfaccia IDWriteFontSet4 espone metodi per la selezione dei tipi di carattere da un set di caratteri. Questi metodi consentono di passare al modello di famiglia di caratteri tipografico mantenendo la compatibilità con applicazioni, documenti e tipi di carattere esistenti.
La selezione dei tipi di carattere (talvolta denominata mapping dei tipi di carattere o mapping dei tipi di carattere) è il processo di selezione dei tipi di carattere disponibili che corrispondono meglio ai parametri di input passati dall'applicazione. I parametri di input vengono talvolta definiti collettivamente come tipo di carattere logico. Un tipo di carattere logico include un nome di famiglia di caratteri e altri attributi che indicano un tipo di carattere specifico all'interno della famiglia. Un algoritmo di selezione dei caratteri corrisponde al tipo di carattere logico ("il tipo di carattere desiderato") a un tipo di carattere fisico disponibile ("un tipo di carattere che hai").
Una famiglia di caratteri è un gruppo denominato di tipi di carattere che condividono una progettazione comune, ma può variare in attributi come il peso. Un modello di famiglia di caratteri definisce gli attributi che possono essere usati per differenziare i tipi di carattere all'interno di una famiglia. Il nuovo modello di famiglia di tipi di carattere tipografico offre molti vantaggi rispetto ai due modelli di famiglia di caratteri precedenti usati in Windows. Tuttavia, la modifica dei modelli di famiglia di tipi di carattere crea opportunità di confusione e problemi di compatibilità. I metodi esposti dall'interfaccia IDWriteFontSet4 implementano un approccio ibrido che offre i vantaggi del modello di famiglia di caratteri tipografico mentre attenua i problemi di compatibilità.
In questo argomento vengono confrontati i modelli di famiglia di tipi di carattere meno recenti con il modello di famiglia di caratteri tipografico; spiega le sfide di compatibilità poste dalla modifica dei modelli di famiglia di tipi di carattere; e infine spiega come questi problemi possono essere superati usando i metodi [IDWriteFontSet4](/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontset4).
Modello di famiglia di tipi di carattere RBIZ
Il modello di famiglia di tipi di carattere de facto usato nell'ecosistema di applicazioni GDI è talvolta chiamato il modello "modello a quattro tipi di carattere" o "RBIZ". Ogni famiglia di caratteri in questo modello ha in genere quattro tipi di carattere. L'etichetta "RBIZ" deriva dalla convenzione di denominazione usata per alcuni file di carattere, ad esempio:
File Name | Stile |
---|---|
verdana.ttf | Normale |
verdanab.ttf | Bold |
verdanai.ttf | Corsivo |
verdanaz.ttf | Corsivo grassetto |
Con GDI, i parametri di input usati per selezionare un tipo di carattere sono definiti dalla struttura LOGFONT, che include i campi nome famiglia (), peso (lfWeight
) e corsivo (lfFaceName
lfItalic
). Il lfItalic
campo è TRUE o FALSE. GDI consente al lfWeight
campo di essere qualsiasi valore nell'intervallo FW_THIN (100) di FW_BLACK (900), ma per motivi cronologici i tipi di carattere sono stati progettati a lungo in modo che non siano presenti più di due pesi nella stessa famiglia di caratteri GDI.
Le interfacce utente dell'applicazione più diffuse dall'inizio includevano un pulsante corsivo (per attivare e disattivare il corsivo) e un pulsante grassetto (per attivare e disattivare un pulsante grassetto tra pesi normali e grassetti). L'uso di questi due pulsanti per selezionare i tipi di carattere all'interno di una famiglia presuppone il modello RBIZ. Pertanto, anche se GDI supporta più di due pesi, gli sviluppatori di tipi di carattere guidati dall'applicazione per impostare il nome della famiglia GDI (ID nome OpenType 1) in modo coerente con il modello RBIZ.
Si supponga, ad esempio, di voler aggiungere un peso "Nero" più pesante alla famiglia di caratteri Arial. Logicamente, questo tipo di carattere fa parte della famiglia Arial, quindi potrebbe essere previsto di selezionarlo impostandolo lfFaceName
su "Arial" e lfWeight
su FW_BLACK. Tuttavia, non è possibile che un utente dell'applicazione scelga tra tre pesi usando un pulsante in grassetto a due stati. La soluzione consiste nel assegnare al nuovo tipo di carattere un nome di famiglia diverso, quindi l'utente può selezionarlo scegliendo "Arial Black" nell'elenco delle famiglie di caratteri. Analogamente, non è possibile scegliere tra diverse larghezze nella stessa famiglia di caratteri usando solo pulsanti grassetto e corsivo, quindi le versioni strette di Arial hanno nomi di famiglia diversi nel modello RBIZ. Quindi abbiamo "Arial", "Arial Black" e "Arial Narrow" tipi di carattere nel modello RBIZ, anche se digitati tutti appartengono a una famiglia.
Da questi esempi è possibile vedere come le limitazioni di un modello di famiglia di caratteri possono influire sul modo in cui i tipi di carattere vengono raggruppati in famiglie. Poiché le famiglie di caratteri sono identificate da nome, questo significa che lo stesso tipo di carattere può avere nomi di famiglia diversi a seconda del modello di famiglia di caratteri in uso.
DirectWrite non supporta direttamente il modello di famiglia di tipi di carattere RBIZ, ma fornisce metodi di conversione in e dal modello RBIZ, ad esempio IDWriteGdiInterop::CreateFontFromLOGFONT e IDWriteGdiInterop::ConvertFontToLOGFONT. È anche possibile ottenere il nome della famiglia RBIZ di un tipo di carattere chiamando il relativo metodo IDWriteFont::GetInformationalStrings e specificando DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES.
Modello di famiglia di tipi di carattere di tipo stretch-stretch
Il modello di famiglia di tipi di carattere di tipo stretch-stretch è il modello di famiglia di caratteri originale usato da DirectWrite prima dell'introduzione del modello di famiglia di tipi di carattere tipografico. È noto anche come pendenza a larghezza di peso (WWS). Nel modello WWS i tipi di carattere all'interno della stessa famiglia possono essere diversi in base a tre proprietà: peso (DWRITE_FONT_WEIGHT), stretch (DWRITE_FONT_STRETCH) e stile (DWRITE_FONT_STYLE).
Il modello WWS è più flessibile del modello RBIZ in due modi. Prima di tutto, i tipi di carattere nella stessa famiglia possono essere differenziati in base all'estensione (o larghezza) e allo stile (regolare, corsivo o oblique). In secondo luogo, ci possono essere più di due pesi nella stessa famiglia. Questa flessibilità è sufficiente per consentire l'inserimento di tutte le varianti di Arial nella stessa famiglia WWS. La tabella seguente confronta le proprietà dei tipi di carattere RBIZ e WWS per una selezione di tipi di carattere Arial:
Nome completo | Nome famiglia RBIZ | lfWeight | lfItalic | WWS FamilyName | Peso | Estendi | Stile |
---|---|---|---|---|---|---|---|
Arial | Arial | 400 | 0 | Arial | 400 | 5 | 0 |
Arial Bold | Arial | 700 | 0 | Arial | 700 | 5 | 0 |
Arial Black | Arial Black | 900 | 0 | Arial | 900 | 5 | 0 |
Arial Narrow | Arial Narrow | 400 | 0 | Arial | 400 | 3 | 0 |
Arial Narrow Bold | Arial Narrow | 700 | 0 | Arial | 700 | 3 | 0 |
Come si può notare, "Arial Narrow" ha gli stessi lfWeight
valori e lfItalic
"Arial", quindi ha un nome di famiglia RBIZ diverso per evitare ambiguità. "Arial Black" ha un nome di famiglia RBIZ diverso per evitare di avere più di due pesi nella famiglia "Arial". Al contrario, tutti questi tipi di carattere si trovano nella stessa famiglia di stili di spessore.
Tuttavia, il modello di tipo stretch-stretch-style non è aperto. Se due tipi di carattere hanno lo stesso peso, l'estensione e lo stile, ma differiscono in un altro modo (ad esempio, dimensioni ottiche), non possono essere inclusi nella stessa famiglia di caratteri WWS. Questo ci porta al modello di famiglia di caratteri tipografico.
Modello di famiglia di caratteri tipografico
A differenza dei suoi predecessori, il modello di famiglia di caratteri tipografico è aperto. Supporta qualsiasi numero di assi di variazione all'interno di una famiglia di caratteri.
Se si pensa ai parametri di selezione dei tipi di carattere come coordinate in uno spazio di progettazione, il modello di stile spessore definisce un sistema di coordinate tridimensionale con peso, estensione e stile come assi. Ogni tipo di carattere in una famiglia WWS deve avere una posizione univoca definita dalle coordinate lungo questi tre assi. Per selezionare un tipo di carattere, specificare il nome e il peso della famiglia WWS, l'estensione e i parametri di stile.
Al contrario, il modello di famiglia di caratteri tipografico ha uno spazio di progettazione Ndimensionale. Una finestra di progettazione dei tipi di carattere può definire qualsiasi numero di assi di progettazione, ognuno identificato da un tag asse a quattro caratteri. La posizione del tipo di carattere specificato nello spazio di progettazione N-dimensionale è definita da una matrice di valori dell'asse, in cui ogni valore dell'asse comprende un tag asse e un valore a virgola mobile. Per selezionare un tipo di carattere, specificare un nome di famiglia tipografico e una matrice di valori dell'asse (DWRITE_FONT_AXIS_VALUE strutture).
Anche se il numero di assi di carattere è aperto, esistono alcuni assi registrati con significati standard e i valori di peso, estensione e stile possono essere mappati ai valori dell'asse registrati. DWRITE_FONT_WEIGHT può essere mappato a un valore dell'asse "wght" (DWRITE_FONT_AXIS_TAG_WEIGHT). DWRITE_FONT_STRETCH può essere mappato a un valore dell'asse "wdth" (DWRITE_FONT_AXIS_TAG_WIDTH). DWRITE_FONT_STYLE può essere mappato a una combinazione di valori dell'asse "ital" e "slnt" (DWRITE_FONT_AXIS_TAG_ITALIC e DWRITE_FONT_AXIS_TAG_SLANT).
Un altro asse registrato è "opsz" (DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE). Una famiglia di caratteri ottici come Sitka include tipi di carattere che differiscono lungo l'asse "opsz", il che significa che sono progettati per essere usati in dimensioni diverse. Il modello di famiglia di caratteri WWS non ha un asse di dimensioni ottici, quindi la famiglia di caratteri Sitka deve essere suddivisa in più famiglie di caratteri WWS: "Sitka Small", "Sitka Text", "Sitka Subheading" e così via. Ogni famiglia di caratteri WWS corrisponde a una dimensione ottica diversa e viene lasciata all'utente per specificare il nome della famiglia WWS destro per una determinata dimensione del carattere. Con il modello di famiglia di caratteri tipografici, l'utente può semplicemente scegliere "Sitka" e l'applicazione può impostare automaticamente il valore dell'asse "opsz" in base alle dimensioni del carattere.
Selezione dei tipi di carattere tipografico e tipi di carattere variabile
Il concetto di assi di variazione è spesso associato ai tipi di carattere variabile, ma si applica anche ai tipi di carattere statici. La tabella OpenType STAT (attributi di stile) dichiara quali assi di progettazione hanno un tipo di carattere e i valori di tali assi. Questa tabella è necessaria per i tipi di carattere variabile, ma è anche rilevante per i tipi di carattere statici.
L'API DirectWrite espone i valori dell'asse "wght", "wdth", "ital" e "slnt" per ogni tipo di carattere, anche se non sono presenti nella tabella STAT o se non esiste alcuna tabella STAT. Questi valori sono derivati dalla tabella STAT, se possibile. In caso contrario, sono derivati dal peso del carattere, dall'estensione del carattere e dallo stile del carattere.
Gli assi del carattere possono essere variabili o non variabili. Un carattere statico ha solo assi non variabili, mentre un tipo di carattere variabile può avere entrambi. Per usare un tipo di carattere variabile, è necessario creare un'istanza del tipo di carattere variabile in cui tutti gli assi delle variabili sono stati associati a valori specifici. L'interfaccia IDWriteFontFace rappresenta un carattere statico o un'istanza specifica di un tipo di carattere variabile. È possibile creare un'istanza arbitraria di un tipo di carattere variabile con valori dell'asse specificati. Inoltre, un tipo di carattere variabile può dichiarare istanze denominate nella tabella STAT con combinazioni predefinite di valori dell'asse. Le istanze denominate consentono a un tipo di carattere variabile di comportarsi in modo molto simile a una raccolta di tipi di carattere statici. Quando si enumera gli elementi di un IDWriteFontFamily o IDWriteFontSet, è presente un elemento per ogni tipo di carattere statico e per ogni istanza di carattere variabile denominata.
L'algoritmo di corrispondenza dei caratteri tipografico seleziona innanzitutto i potenziali candidati corrispondenti in base al nome della famiglia. Se i candidati di corrispondenza includono tipi di carattere variabile, tutti i candidati corrispondenti per lo stesso tipo di carattere variabile vengono compressi in un candidato di corrispondenza in cui ogni asse variabile viene assegnato un valore specifico il più vicino possibile al valore richiesto per tale asse. Se non è richiesto alcun valore per un asse di variabili, viene assegnato il valore predefinito per tale asse. L'ordine dei candidati di corrispondenza viene quindi determinato confrontando i valori dell'asse con i valori dell'asse richiesti.
Si consideri ad esempio la famiglia tipografica Sitka in Windows. Sitka è una famiglia di caratteri ottici, ovvero ha un asse "opsz". In Windows 11 Sitka viene implementato come due tipi di carattere variabile con i valori dell'asse seguenti. Si noti che gli opsz
assi e wght
sono variabili, mentre gli altri assi non sono variabili.
File Name | "opsz" | "wght" | "wdth" | "ital" | "slnt" |
---|---|---|---|---|---|
SitkaVF.ttf | 6-27.5 | 400-700 | 100 | 0 | 0 |
SitkaVF-Italic.ttf | 6-27.5 | 400-700 | 100 | 1 | -12 |
Si supponga che i valori dell'asse richiesti siano opsz:12 wght:475 wdth:100 ital:0 slnt:0
. Per ogni tipo di carattere variabile, viene creato un riferimento a un'istanza del tipo di carattere variabile in cui ogni asse di variabili viene assegnato un valore specifico. In nome, gli opsz
assi e wght
sono impostati rispettivamente su 12
e , 475
. Questo restituisce i tipi di carattere corrispondenti seguenti, con il carattere non corsivo classificato prima perché è una corrispondenza migliore per gli ital
assi e slnt
:
SitkaVF.ttf opsz:12 wght:475 wdth:100 ital:0 slnt0
SitkaVF-Italic.ttf opsz:12 wght:475 wdth:100 ital:1 slnt:-12
Nell'esempio precedente, i tipi di carattere corrispondenti sono istanze arbitrarie del tipo di carattere variabile. Non esiste alcuna istanza denominata di Sitka con peso 475. Al contrario, l'algoritmo di corrispondenza di tipo stretch-stretch restituisce solo istanze denominate.
Ordine di corrispondenza dei tipi di carattere
Esistono diversi metodi getMatchingFonts per il modello di famiglia di tipi di carattere di tipo stretch-style (IDWriteFontFamily::GetMatchingFonts) e il modello di famiglia di caratteri tipografico (IDWriteFontCollection2::GetMatchingFonts). In entrambi i casi, l'output è un elenco di tipi di carattere corrispondenti in ordine decrescente di priorità in base al modo in cui ogni carattere candidato corrisponde alle proprietà di input. In questa sezione viene descritto come viene determinata la priorità.
Nel modello di stile spessore, i parametri di input sono peso del carattere (DWRITE_FONT_WEIGHT), estensione del carattere (DWRITE_FONT_STRETCH) e stile di carattere (DWRITE_FONT_STYLE). L'algoritmo per trovare la corrispondenza migliore è stato documentato in un white paper del 2006 intitolato "Modello di selezione dei caratteri WPF" di Mikhail Leonov e David Brown. Vedere la sezione "Corrispondenza di un viso dall'elenco dei volti candidati". Questo documento riguardava Windows Presentation Foundation (WPF), ma DirectWrite successivamente usava lo stesso approccio.
L'algoritmo usa la nozione di vettore dell'attributo di carattere, che per una determinata combinazione di peso, estensione e stile viene calcolato come indicato di seguito:
FontAttributeVector.X = (stretch - 5) * 1100;
FontAttributeVector.Y = style * 700;
FontAttributeVector.Z = (weight - 400) * 5;
Si noti che ogni coordinata vettore viene normalizzata sottraendo il valore "normale" per l'attributo corrispondente e moltiplicando in base a una costante. I moltiplicatori compensano il fatto che gli intervalli di valori di input per peso, estensione e stile sono molto diversi. In caso contrario, il peso (100..999) domina lo stile (0,.2).
Per ogni candidato di corrispondenza, una distanza vettoriale e un prodotto dot vengono calcolati tra il vettore dell'attributo di carattere del candidato corrispondente e il vettore dell'attributo del tipo di carattere di input. Quando si confrontano due candidati, il candidato con la distanza vettore più piccola è la corrispondenza migliore. Se le distanze sono uguali, il candidato con il prodotto punto più piccolo è una corrispondenza migliore. Se il prodotto punto è uguale, le distanze lungo gli assi X, Y e Z vengono confrontati in tale ordine.
Il confronto delle distanze è abbastanza intuitivo, ma l'uso del prodotto punto come misura secondaria potrebbe richiedere una spiegazione. Si supponga che il peso di input sia semibold (600) e due pesi candidati siano neri (900) e semilight (300). La distanza di ogni peso candidato dal peso di input è la stessa, ma il peso nero si trova nella stessa direzione dall'origine (ovvero 400 o normale), quindi avrà un prodotto punto più piccolo.
L'algoritmo di corrispondenza tipografica è una generalizzazione di quella per lo stile di spessore. Ogni valore dell'asse viene considerato come coordinata in un vettore di attributi di tipo carattere N-dimensionale. Per ogni candidato di corrispondenza, una distanza vettoriale e un prodotto dot vengono calcolati tra il vettore dell'attributo di carattere del candidato corrispondente e il vettore dell'attributo del tipo di carattere di input. Il candidato con la distanza vettore più piccola è la corrispondenza migliore. Se le distanze sono uguali, il candidato con il prodotto punto più piccolo è una corrispondenza migliore. Se il prodotto dot è lo stesso, la presenza in una famiglia di stili di spessore specificata può essere usata come tie-breaker.
Per calcolare la distanza del vettore e il prodotto punto, il vettore dell'attributo carattere del candidato corrispondente e il vettore dell'attributo del tipo di carattere di input devono avere gli stessi assi. Pertanto, qualsiasi valore dell'asse mancante in entrambi i vettori viene compilato sostituendo il valore standard per tale asse. Le coordinate vettoriali vengono normalizzate sottraendo il valore standard (o "normale") per l'asse corrispondente e moltiplicando il risultato da un moltiplicatore specifico dell'asse. Di seguito sono riportati i moltiplicatori e i valori standard per ogni asse:
Asse | Moltiplicatore | Valore standard |
---|---|---|
"wght" | 5 | 400 |
"wdth" | 55 | 100 |
"ital" | 1400 | 0 |
"slnt" | 35 | 0 |
"opsz" | 1 | 12 |
altro | 1 | 0 |
I moltiplicatori sono coerenti con quelli usati dall'algoritmo di tipo spessore, ma ridimensionati in base alle esigenze. Ad esempio, la larghezza normale è 100, equivalente a 5. Questo restituisce un moltiplicatore pari a 55 vs. 1100. Attributo di stile legacy (0..2) entangles corsivo e obliqueness, che nel modello tipografico viene decomposto in un asse "ital" (0..1) e un asse "slnt" (-90..90). I moltiplicatori scelti per questi due assi danno risultati equivalenti all'algoritmo legacy se si presuppone un predefinito a 20 gradi per i tipi di carattere oblique.
Selezione di caratteri tipografici e dimensioni ottiche
Un'applicazione che usa il modello di famiglia di caratteri tipografici può implementare il ridimensionamento ottico specificando un opsz
valore dell'asse come parametro di selezione dei caratteri. Ad esempio, un'applicazione di elaborazione delle parole potrebbe specificare un opsz
valore dell'asse uguale alla dimensione del carattere in punti. In questo caso, un utente potrebbe selezionare "Sitka" come famiglia di caratteri e l'applicazione selezionare automaticamente un'istanza di Sitka con il valore dell'asse corretto opsz
. Nel modello WWS ogni dimensione ottica viene esposta come nome di famiglia diverso e l'utente deve selezionare quello destro.
In teoria, si potrebbe implementare il ridimensionamento ottico automatico nel modello di stile di spessore eseguendo l'override del valore dell'asse come passaggio separato dopo la opsz
selezione dei tipi di carattere. Tuttavia, questo funziona solo se il primo tipo di carattere corrispondente è un tipo di carattere variabile con un asse variabile opsz
.
opsz
Se si specifica come parametro di selezione dei tipi di carattere, la selezione dei tipi di carattere funziona altrettanto bene per i tipi di carattere statici. Ad esempio, la famiglia di caratteri Sitka viene implementata come tipi di carattere variabile in Windows 11, ma come raccolta di tipi di carattere statici in Windows 10. I tipi di carattere statici hanno intervalli di assi diversi e non sovrapposti opsz
(questi sono dichiarati come intervalli per scopi di selezione dei tipi di carattere, ma non sono assi variabili).
opsz
Se si specifica come parametro di selezione dei tipi di carattere, il tipo di carattere statico corretto per la dimensione ottica da selezionare.
Vantaggi della selezione dei caratteri tipografici e problemi di compatibilità
Il modello di selezione dei tipi di carattere tipografico presenta diversi vantaggi rispetto ai modelli precedenti, ma nel suo formato puro presenta alcuni potenziali problemi di compatibilità. Questa sezione descrive i vantaggi e i problemi di compatibilità. La sezione successiva descrive un modello di selezione dei tipi di carattere ibrido che mantiene i vantaggi durante la mitigazione dei problemi di compatibilità.
I vantaggi del modello di famiglia di caratteri tipografici sono:
I tipi di carattere possono essere raggruppati in famiglie, come previsto dalla finestra di progettazione, anziché essere suddivisi in sottofamilità a causa di limitazioni del modello di famiglia di caratteri.
Un'applicazione può selezionare automaticamente il valore dell'asse corretto
opsz
in base alle dimensioni del tipo di carattere, anziché esporre diverse dimensioni ottiche all'utente come famiglie di caratteri diverse.È possibile selezionare istanze arbitrarie di tipi di carattere variabile. Ad esempio, se un carattere variabile supporta pesi nell'intervallo continuo 100-900, il modello tipografico può selezionare qualsiasi peso in questo intervallo. I modelli della famiglia di caratteri meno recenti possono scegliere solo il peso più vicino tra le istanze denominate definite dal tipo di carattere.
I problemi di compatibilità con il modello di selezione dei tipi di carattere tipografico sono:
Alcuni tipi di carattere meno recenti non possono essere selezionati senza ambiguità usando solo i valori di famiglia e asse tipografici.
I documenti esistenti possono fare riferimento ai tipi di carattere in base al nome della famiglia WWS o al nome della famiglia RBIZ. Gli utenti potrebbero anche aspettarsi di usare i nomi di famiglia WWS e RBIZ. Ad esempio, un documento potrebbe specificare "Sitka Subheading" (nome della famiglia WWS) anziché "Sitka" (nome della famiglia tipografica).
Una libreria o un framework possono adottare il modello di famiglia di caratteri tipografico per sfruttare il ridimensionamento ottico automatico, ma non fornire un'API per specificare i valori dell'asse arbitrario. Anche se viene fornita una nuova API, il framework potrebbe dover usare applicazioni esistenti che specificano solo parametri di peso, estensione e stile.
Il problema di compatibilità con i tipi di carattere meno recenti si verifica perché il concetto di nome famiglia tipografico precede il concetto di valori dell'asse del carattere, che sono stati introdotti insieme ai tipi di carattere variabile in OpenType 1.8. Prima di OpenType 1.8, il nome della famiglia tipografica ha semplicemente espresso la finalità della finestra di progettazione che un set di tipi di carattere era correlato, ma senza alcuna garanzia che tali tipi di carattere possano essere differenziati a livello di codice in base alle relative proprietà. Come esempio ipotetico, si supponga che tutti i tipi di carattere seguenti abbiano il nome della famiglia tipografica "Legacy":
Nome completo | Famiglia WWS | Peso | Estendi | Stile | Famiglia di tipi | wght | wdth | Ital | slnt |
---|---|---|---|---|---|---|---|---|---|
Legacy | Legacy | 400 | 5 | 0 | Legacy | 400 | 100 | 0 | 0 |
Legacy Bold | Legacy | 700 | 5 | 0 | Legacy | 700 | 100 | 0 | 0 |
Nero legacy | Legacy | 900 | 5 | 0 | Legacy | 900 | 100 | 0 | 0 |
Legacy Soft | Legacy Soft | 400 | 5 | 0 | Legacy | 400 | 100 | 0 | 0 |
Legacy Soft Bold | Legacy Soft | 700 | 5 | 0 | Legacy | 700 | 100 | 0 | 0 |
Nero leggero legacy | Legacy Soft | 900 | 5 | 0 | Legacy | 900 | 100 | 0 | 0 |
La famiglia tipografica "Legacy" ha tre pesi e ogni peso ha varianti regolari e "Soft". Se questi tipi di carattere erano nuovi, potrebbero essere implementati come dichiarando un asse di progettazione SOFT. Tuttavia, questi tipi di carattere precedono OpenType 1.8, quindi gli unici assi di progettazione sono quelli derivati da peso, estensione e stile. Per ogni peso, questa famiglia di tipi di carattere ha due tipi di carattere con valori dell'asse identici, quindi non è possibile selezionare in modo ambiguo un tipo di carattere in questa famiglia usando solo i valori dell'asse.
Algoritmo di selezione dei tipi di carattere ibrido
Le API di selezione dei tipi di carattere descritte nella sezione successiva usano un algoritmo di selezione dei tipi di carattere ibrido che mantiene i vantaggi della selezione dei tipi di carattere durante la mitigazione dei problemi di compatibilità.
La selezione dei tipi di carattere ibrida fornisce un bridge da modelli di famiglia di tipi di carattere precedenti consentendo il mapping dei valori di tipo carattere, estensione del carattere e stile di carattere corrispondenti. Ciò consente di risolvere i problemi di compatibilità dei documenti e delle applicazioni.
Inoltre, l'algoritmo di selezione dei tipi di carattere ibrido consente al nome della famiglia specificato di essere un nome di famiglia tipografico, nome della famiglia di caratteri di tipo spessore, nome della famiglia GDI/RBIZ o un nome completo del tipo di carattere. La corrispondenza si verifica in uno dei modi seguenti, in ordine decrescente di priorità:
Il nome corrisponde a una famiglia tipografica ,ad esempio Sitka. La corrispondenza si verifica all'interno della famiglia tipografica e vengono usati tutti i valori dell'asse richiesti. Se il nome corrisponde anche a una sottofamiglia WWS ,ovvero una più piccola della famiglia tipografica, l'appartenenza alla sottofamiglia WWS viene usata come tie-breaker.
Il nome corrisponde a una famiglia WWS ,ad esempio Sitka Text. La corrispondenza si verifica all'interno della famiglia WWS e i valori dell'asse richiesti diversi da "wght", "wdth", "ital" e "slnt" vengono ignorati.
Il nome corrisponde a una famiglia GDI ,ad esempio Bahnschrift Condensed. La corrispondenza si verifica all'interno della famiglia RBIZ e i valori dell'asse richiesti diversi da "wght", "ital" e "slnt" vengono ignorati.
Il nome corrisponde a un nome completo , ad esempio Bahnschrift Bold Condensed. Il tipo di carattere corrispondente al nome completo viene restituito. I valori dell'asse richiesti vengono ignorati. La corrispondenza in base al nome completo del tipo di carattere è consentita perché GDI la supporta.
La sezione precedente ha descritto una famiglia tipografica ambigua denominata "Legacy". L'algoritmo ibrido consente di evitare l'ambiguità specificando "Legacy" o "Legacy Soft" come nome della famiglia. Se viene specificato "Legacy Soft", non c'è ambiguità perché la corrispondenza si verifica solo all'interno della famiglia WWS. Se "Legacy" è specfiied, tutti i tipi di carattere della famiglia tipografica vengono considerati candidati corrispondenti, ma l'ambiguità viene evitata usando l'appartenenza alla famiglia "Legacy" WWS come tie-breaker.
Si supponga che un documento specifichi un nome di famiglia, un'estensione e parametri di stile, ma non valori dell'asse. L'applicazione può prima convertire il peso, l'estensione, lo stile e le dimensioni del carattere in valori dell'asse chiamando IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues. L'applicazione può quindi passare sia il nome della famiglia che i valori dell'asse a IDWriteFontSet4::GetMatchingFonts. GetMatchingFonts restituisce un elenco di tipi di carattere corrispondenti nell'ordine di priorità e il risultato è appropriato se il nome della famiglia specificato è un nome di famiglia digitato, nome della famiglia di tipo spessore, nome della famiglia RBIZ o nome completo. Se la famiglia specificata ha un asse "opsz", la dimensione ottica appropriata viene selezionata automaticamente in base alle dimensioni del carattere.
Si supponga che un documento specifichi il peso, l'estensione e lo stile e specifica anche i valori dell'asse. In tal caso, i valori dell'asse esplicito possono essere passati anche a IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues e i valori dell'asse derivati restituiti dal metodo includono solo assi di carattere non specificati in modo esplicito. Pertanto, i valori dell'asse specificati in modo esplicito dal documento (o dall'applicazione) hanno la precedenza sui valori dell'asse derivati da peso, estensione, stile e dimensioni del carattere.
API di selezione dei tipi di carattere ibrido
Il modello di selezione dei tipi di carattere ibrido viene implementato dai metodi IDWriteFontSet4 seguenti:
Il metodo IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues converte le dimensioni del tipo di carattere, il peso, l'estensione e i parametri di stile ai valori dell'asse corrispondenti. Tutti i valori espliciti dell'asse passati dal client vengono esclusi dai valori dell'asse derivati.
Il metodo IDWriteFontSet4::GetMatchingFonts restituisce un elenco con priorità dei tipi di carattere corrispondenti in base al nome della famiglia e alla matrice di valori dell'asse. Come descritto in precedenza, il parametro nome famiglia può essere un nome di famiglia tipografico, nome della famiglia WWS, nome della famiglia RBIZ o nome completo. Il nome completo identifica uno stile di carattere specifico, ad esempio "Arial Bold Italic". GetMatchingFonts supporta la corrispondenza in base al nome completo per una maggiore comaptibiltiy con GDI, che lo consente anche.
Le altre API di DirectWrite seguenti usano anche l'algoritmo di selezione dei tipi di carattere ibrido:
IDWriteTextLayout se il modello di famiglia di caratteri tipografico è abilitato chiamando IDWriteTextLayout4::SetFontAxisValues o IDWriteTextLayout4::SetAutomaticFontAxes
Esempi di codice delle API di selezione dei tipi di carattere in uso
Questa sezione mostra un'applicazione console completa che illustra i metodi IDWriteFontSet4::GetMatchingFonts e IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues . In primo luogo sono incluse alcune intestazioni:
#include <dwrite_core.h>
#include <wil/com.h>
#include <iostream>
#include <string>
#include <vector>
Il metodo IDWriteFontSet4::GetMatchingFonts restituisce un elenco di tipi di carattere in ordine di priorità che corrispondono ai valori del nome della famiglia e dell'asse specificati. La funzione MatchAxisValues seguente restituisce i parametri in IDWriteFontSet4::GetMatchingFonts e l'elenco dei tipi di carattere corrispondenti nel set di caratteri restituiti.
// Forward declarations of overloaded output operators used by MatchAxisValues.
std::wostream& operator<<(std::wostream& out, DWRITE_FONT_AXIS_VALUE const& axisValue);
std::wostream& operator<<(std::wostream& out, IDWriteFontFile* fileReference);
std::wostream& operator<<(std::wostream& out, IDWriteFontFaceReference1* faceReference);
//
// MatchAxisValues calls IDWriteFontSet4::GetMatchingFonts with the
// specified parameters and outputs the matching fonts.
//
void MatchAxisValues(
IDWriteFontSet4* fontSet,
_In_z_ WCHAR const* familyName,
std::vector<DWRITE_FONT_AXIS_VALUE> const& axisValues,
DWRITE_FONT_SIMULATIONS allowedSimulations
)
{
// Write the input parameters.
std::wcout << L"GetMatchingFonts(\"" << familyName << L"\", {";
for (DWRITE_FONT_AXIS_VALUE const& axisValue : axisValues)
{
std::wcout << L' ' << axisValue;
}
std::wcout << L" }, " << allowedSimulations << L"):\n";
// Get the matching fonts for the specified family name and axis values.
wil::com_ptr<IDWriteFontSet4> matchingFonts;
THROW_IF_FAILED(fontSet->GetMatchingFonts(
familyName,
axisValues.data(),
static_cast<UINT32>(axisValues.size()),
allowedSimulations,
&matchingFonts
));
// Output the matching font face references.
UINT32 const fontCount = matchingFonts->GetFontCount();
for (UINT32 fontIndex = 0; fontIndex < fontCount; fontIndex++)
{
wil::com_ptr<IDWriteFontFaceReference1> faceReference;
THROW_IF_FAILED(matchingFonts->GetFontFaceReference(fontIndex, &faceReference));
std::wcout << L" " << faceReference.get() << L'\n';
}
std::wcout << std::endl;
}
Un'applicazione potrebbe avere parametri di peso, estensione e stile anziché (o oltre a) valori dell'asse. Ad esempio, l'applicazione potrebbe dover usare documenti che fanno riferimento ai tipi di carattere usando parametri di tipo RBIZ o di stile di spessore. Anche se l'applicazione aggiunge supporto per specificare valori dell'asse arbitrario, potrebbe essere necessario supportare anche i parametri meno recenti. A tale scopo, l'applicazione può chiamare IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues prima di chiamare IDWriteFontSet4::GetMatchingFonts.
La funzione MatchFont seguente accetta parametri di peso, estensione, stile e dimensioni del carattere oltre ai valori dell'asse. Inoltra questi parametri al metodo IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues per calcolare i valori dell'asse derivati, che vengono aggiunti ai valori dell'asse di input. Passa i valori dell'asse combinati alla funzione MatchAxisValues precedente.
struct FontStyleParams
{
FontStyleParams() {}
FontStyleParams(DWRITE_FONT_WEIGHT fontWeight) : fontWeight{ fontWeight } {}
FontStyleParams(float fontSize) : fontSize{ fontSize } {}
DWRITE_FONT_WEIGHT fontWeight = DWRITE_FONT_WEIGHT_NORMAL;
DWRITE_FONT_STRETCH fontStretch = DWRITE_FONT_STRETCH_NORMAL;
DWRITE_FONT_STYLE fontStyle = DWRITE_FONT_STYLE_NORMAL;
float fontSize = 0.0f;
};
//
// MatchFont calls IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues to convert
// the input parameters to axis values and then calls MatchAxisValues.
//
void MatchFont(
IDWriteFactory7* factory,
_In_z_ WCHAR const* familyName,
FontStyleParams styleParams = {},
std::vector<DWRITE_FONT_AXIS_VALUE> axisValues = {},
DWRITE_FONT_SIMULATIONS allowedSimulations = DWRITE_FONT_SIMULATIONS_BOLD | DWRITE_FONT_SIMULATIONS_OBLIQUE
)
{
wil::com_ptr<IDWriteFontSet2> fontSet2;
THROW_IF_FAILED(factory->GetSystemFontSet(/*includeDownloadableFonts*/ false, &fontSet2));
wil::com_ptr<IDWriteFontSet4> fontSet;
THROW_IF_FAILED(fontSet2->QueryInterface(&fontSet));
// Ensure the total number of axis values can't overflow a UINT32.
size_t const inputAxisCount = axisValues.size();
if (inputAxisCount > UINT32_MAX - DWRITE_STANDARD_FONT_AXIS_COUNT)
{
THROW_HR(E_INVALIDARG);
}
// Reserve space at the end of axisValues vector for the derived axis values.
// The maximum number of derived axis values is DWRITE_STANDARD_FONT_AXIS_COUNT.
axisValues.resize(inputAxisCount + DWRITE_STANDARD_FONT_AXIS_COUNT);
// Convert the weight, stretch, style, and font size to derived axis values.
UINT32 derivedAxisCount = fontSet->ConvertWeightStretchStyleToFontAxisValues(
/*inputAxisValues*/ axisValues.data(),
static_cast<UINT32>(inputAxisCount),
styleParams.fontWeight,
styleParams.fontStretch,
styleParams.fontStyle,
styleParams.fontSize,
/*out*/ axisValues.data() + inputAxisCount
);
// Resize the vector based on the actual number of derived axis values returned.
axisValues.resize(inputAxisCount + derivedAxisCount);
// Pass the combined axis values (explicit and derived) to MatchAxisValues.
MatchAxisValues(fontSet.get(), familyName, axisValues, allowedSimulations);
}
La funzione seguente illustra i risultati della chiamata alla funzione MatchFont precedente con alcuni input di esempio:
void TestFontSelection(IDWriteFactory7* factory)
{
// Request "Cambria" with bold weight, with and without allowing simulations.
// - Matches a typographic/WWS family.
// - Result includes all fonts in the family ranked by priority.
// - Useful because Cambria Bold might have fewer glyphs than Cambria Regular.
// - Simulations may be applied if allowed.
MatchFont(factory, L"Cambria", DWRITE_FONT_WEIGHT_BOLD);
MatchFont(factory, L"Cambria", DWRITE_FONT_WEIGHT_BOLD, {}, DWRITE_FONT_SIMULATIONS_NONE);
// Request "Cambria Bold".
// - Matches the full name of one static font.
MatchFont(factory, L"Cambria Bold");
// Request "Bahnschrift" with bold weight.
// - Matches a typographic/WWS family.
// - Returns an arbitrary instance of the variable font.
MatchFont(factory, L"Bahnschrift", DWRITE_FONT_WEIGHT_BOLD);
// Request "Segoe UI Variable" with two different font sizes.
// - Matches a typographic family name only (not a WWS family name).
// - Font size maps to "opsz" axis value (Note conversion from DIPs to points.)
// - Returns an arbitrary instance of the variable font.
MatchFont(factory, L"Segoe UI Variable", 16.0f);
MatchFont(factory, L"Segoe UI Variable", 32.0f);
// Same as above with an explicit opsz axis value.
// The explicit value is used instead of an "opsz" value derived from the font size.
MatchFont(factory, L"Segoe UI Variable", 16.0f, { { DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE, 32.0f } });
// Request "Segoe UI Variable Display".
// - Matches a WWS family (NOT a typographic family).
// - The input "opsz" value is ignored; the optical size of the family is used.
MatchFont(factory, L"Segoe UI Variable Display", 16.0f);
// Request "Segoe UI Variable Display Bold".
// - Matches the full name of a variable font instance.
// - All input axes are ignored; the axis values of the matching font are used.
MatchFont(factory, L"Segoe UI Variable Display Bold", 16.0f);
}
Di seguito è riportato l'output della funzione TestFontSelection precedente:
GetMatchingFonts("Cambria", { wght:700 wdth:100 ital:0 slnt:0 }, 3):
cambriab.ttf wght:700 wdth:100 ital:0 slnt:0
cambria.ttc wght:400 wdth:100 ital:0 slnt:0 BOLDSIM
cambriaz.ttf wght:700 wdth:100 ital:1 slnt:-12.4
cambriai.ttf wght:400 wdth:100 ital:1 slnt:-12.4 BOLDSIM
GetMatchingFonts("Cambria", { wght:700 wdth:100 ital:0 slnt:0 }, 0):
cambriab.ttf wght:700 wdth:100 ital:0 slnt:0
cambriaz.ttf wght:700 wdth:100 ital:1 slnt:-12.4
cambria.ttc wght:400 wdth:100 ital:0 slnt:0
cambriai.ttf wght:400 wdth:100 ital:1 slnt:-12.4
GetMatchingFonts("Cambria Bold", { wght:400 wdth:100 ital:0 slnt:0 }, 3):
cambriab.ttf wght:700 wdth:100 ital:0 slnt:0
GetMatchingFonts("Bahnschrift", { wght:700 wdth:100 ital:0 slnt:0 }, 3):
bahnschrift.ttf wght:700 wdth:100 ital:0 slnt:0
GetMatchingFonts("Segoe UI Variable", { wght:400 wdth:100 ital:0 slnt:0 opsz:12 }, 3):
SegUIVar.ttf opsz:12 wght:400 wdth:100 ital:0 slnt:0
GetMatchingFonts("Segoe UI Variable", { wght:400 wdth:100 ital:0 slnt:0 opsz:24 }, 3):
SegUIVar.ttf opsz:24 wght:400 wdth:100 ital:0 slnt:0
GetMatchingFonts("Segoe UI Variable", { opsz:32 wght:400 wdth:100 ital:0 slnt:0 }, 3):
SegUIVar.ttf opsz:32 wght:400 wdth:100 ital:0 slnt:0
GetMatchingFonts("Segoe UI Variable Display", { wght:400 wdth:100 ital:0 slnt:0 opsz:12 }, 3):
SegUIVar.ttf opsz:36 wght:400 wdth:100 ital:0 slnt:0
GetMatchingFonts("Segoe UI Variable Display Bold", { wght:400 wdth:100 ital:0 slnt:0 opsz:12 }, 3):
SegUIVar.ttf opsz:36 wght:700 wdth:100 ital:0 slnt:0
Di seguito sono riportate le implementazioni degli operatori di overload dichiarati in precedenza. Questi vengono usati da MatchAxisValues per scrivere i valori dell'asse di input e i riferimenti del carattere risultanti:
// Output a font axis value.
std::wostream& operator<<(std::wostream& out, DWRITE_FONT_AXIS_VALUE const& axisValue)
{
UINT32 tagValue = axisValue.axisTag;
WCHAR tagChars[5] =
{
static_cast<WCHAR>(tagValue & 0xFF),
static_cast<WCHAR>((tagValue >> 8) & 0xFF),
static_cast<WCHAR>((tagValue >> 16) & 0xFF),
static_cast<WCHAR>((tagValue >> 24) & 0xFF),
'\0'
};
return out << tagChars << L':' << axisValue.value;
}
// Output a file name given a font file reference.
std::wostream& operator<<(std::wostream& out, IDWriteFontFile* fileReference)
{
wil::com_ptr<IDWriteFontFileLoader> loader;
THROW_IF_FAILED(fileReference->GetLoader(&loader));
wil::com_ptr<IDWriteLocalFontFileLoader> localLoader;
if (SUCCEEDED(loader->QueryInterface(&localLoader)))
{
const void* fileKey;
UINT32 fileKeySize;
THROW_IF_FAILED(fileReference->GetReferenceKey(&fileKey, &fileKeySize));
WCHAR filePath[MAX_PATH];
THROW_IF_FAILED(localLoader->GetFilePathFromKey(fileKey, fileKeySize, filePath, MAX_PATH));
WCHAR* fileName = wcsrchr(filePath, L'\\');
fileName = (fileName != nullptr) ? fileName + 1 : filePath;
out << fileName;
}
return out;
}
// Output a font face reference.
std::wostream& operator<<(std::wostream& out, IDWriteFontFaceReference1* faceReference)
{
// Output the font file name.
wil::com_ptr<IDWriteFontFile> fileReference;
THROW_IF_FAILED(faceReference->GetFontFile(&fileReference));
std::wcout << fileReference.get();
// Output the face index if nonzero.
UINT32 const faceIndex = faceReference->GetFontFaceIndex();
if (faceIndex != 0)
{
out << L'#' << faceIndex;
}
// Output the axis values.
UINT32 const axisCount = faceReference->GetFontAxisValueCount();
std::vector<DWRITE_FONT_AXIS_VALUE> axisValues(axisCount);
THROW_IF_FAILED(faceReference->GetFontAxisValues(axisValues.data(), axisCount));
for (DWRITE_FONT_AXIS_VALUE const& axisValue : axisValues)
{
out << L' ' << axisValue;
}
// Output the simulations.
DWRITE_FONT_SIMULATIONS simulations = faceReference->GetSimulations();
if (simulations & DWRITE_FONT_SIMULATIONS_BOLD)
{
out << L" BOLDSIM";
}
if (simulations & DWRITE_FONT_SIMULATIONS_OBLIQUE)
{
out << L" OBLIQUESIM";
}
return out;
}
Per completare l'esempio, sono riportate le funzioni di analisi della riga di comando e la funzione principale :
char const g_usage[] =
"ParseCmdLine <args>\n"
"\n"
" -test Test sample font selection parameters.\n"
" <familyname> Sets the font family name.\n"
" -size <value> Sets the font size in DIPs.\n"
" -bold Sets weight to bold (700).\n"
" -weight <value> Sets a weight in the range 100-900.\n"
" -italic Sets style to DWRITE_FONT_STYLE_ITALIC.\n"
" -oblique Sets style to DWRITE_FONT_STYLE_OBLIQUE.\n"
" -stretch <value> Sets a stretch in the range 1-9.\n"
" -<axis>:<value> Sets an axis value (for example, -opsz:24).\n"
" -nosim Disallow font simulations.\n"
"\n";
struct CmdArgs
{
std::wstring familyName;
FontStyleParams styleParams;
std::vector<DWRITE_FONT_AXIS_VALUE> axisValues;
DWRITE_FONT_SIMULATIONS allowedSimulations = DWRITE_FONT_SIMULATIONS_BOLD | DWRITE_FONT_SIMULATIONS_OBLIQUE;
bool test = false;
};
template<typename T>
_Success_(return)
bool ParseEnum(_In_z_ WCHAR const* arg, long minValue, long maxValue, _Out_ T* result)
{
WCHAR* endPtr;
long value = wcstol(arg, &endPtr, 10);
*result = static_cast<T>(value);
return value >= minValue && value <= maxValue && *endPtr == L'\0';
}
_Success_(return)
bool ParseFloat(_In_z_ WCHAR const* arg, _Out_ float* value)
{
WCHAR* endPtr;
*value = wcstof(arg, &endPtr);
return *arg != L'\0' && *endPtr == L'\0';
}
bool ParseCommandLine(int argc, WCHAR** argv, CmdArgs& cmd)
{
for (int argIndex = 1; argIndex < argc; argIndex++)
{
WCHAR const* arg = argv[argIndex];
if (*arg != L'-')
{
if (!cmd.familyName.empty())
return false;
cmd.familyName = argv[argIndex];
}
else if (!wcscmp(arg, L"-test"))
{
cmd.test = true;
}
else if (!wcscmp(arg, L"-size"))
{
if (++argIndex == argc)
return false;
if (!ParseFloat(argv[argIndex], &cmd.styleParams.fontSize))
return false;
}
else if (!wcscmp(arg, L"-bold"))
{
cmd.styleParams.fontWeight = DWRITE_FONT_WEIGHT_BOLD;
}
else if (!wcscmp(arg, L"-weight"))
{
if (++argIndex == argc)
return false;
if (!ParseEnum(argv[argIndex], 100, 900, &cmd.styleParams.fontWeight))
return false;
}
else if (!wcscmp(arg, L"-italic"))
{
cmd.styleParams.fontStyle = DWRITE_FONT_STYLE_ITALIC;
}
else if (!wcscmp(arg, L"-oblique"))
{
cmd.styleParams.fontStyle = DWRITE_FONT_STYLE_OBLIQUE;
}
else if (!wcscmp(arg, L"-stretch"))
{
if (++argIndex == argc)
return false;
if (!ParseEnum(argv[argIndex], 1, 9, &cmd.styleParams.fontStretch))
return false;
}
else if (wcslen(arg) > 5 && arg[5] == L':')
{
// Example: -opsz:12
DWRITE_FONT_AXIS_VALUE axisValue;
axisValue.axisTag = DWRITE_MAKE_FONT_AXIS_TAG(arg[1], arg[2], arg[3], arg[4]);
if (!ParseFloat(arg + 6, &axisValue.value))
return false;
cmd.axisValues.push_back(axisValue);
}
else if (!wcscmp(arg, L"-nosim"))
{
cmd.allowedSimulations = DWRITE_FONT_SIMULATIONS_NONE;
}
else
{
return false;
}
}
return true;
}
int __cdecl wmain(int argc, WCHAR** argv)
{
CmdArgs cmd;
if (!ParseCommandLine(argc, argv, cmd))
{
std::cerr << "Invalid command. Type TestFontSelection with no arguments for usage.\n";
return 1;
}
if (cmd.familyName.empty() && !cmd.test)
{
std::cout << g_usage;
return 0;
}
wil::com_ptr<IDWriteFactory7> factory;
THROW_IF_FAILED(DWriteCoreCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory7),
(IUnknown**)&factory
));
if (!cmd.familyName.empty())
{
MatchFont(
factory.get(),
cmd.familyName.c_str(),
cmd.styleParams,
std::move(cmd.axisValues),
cmd.allowedSimulations
);
}
if (cmd.test)
{
TestFontSelection(factory.get());
}
}