Schriftartauswahl
Die IDWriteFontSet4 Schnittstelle macht Methoden zum Auswählen von Schriftarten aus einem Schriftartensatz verfügbar. Diese Methoden ermöglichen den Übergang zum typografischen Schriftfamilienmodells bei gleichzeitiger Beibehaltung der Kompatibilität mit vorhandenen Anwendungen, Dokumenten und Schriftarten.
Die Schriftartauswahl (manchmal auch als Schriftartenvergleich oder Schriftartzuordnung bezeichnet) ist der Prozess der Auswahl der verfügbaren Schriftarten, die am besten mit eingabeparametern übereinstimmen, die von Ihrer Anwendung übergeben werden. Die Eingabeparameter werden manchmal zusammen als logische Schriftartbezeichnet. Eine logische Schriftart enthält einen Schriftartfamiliennamen sowie andere Attribute, die eine bestimmte Schriftart innerhalb der Familie angeben. Ein Schriftauswahlalgorithmus entspricht der logischen Schriftart ("die gewünschte Schriftart") einer verfügbaren physischen Schriftart ("eine Schriftart, die Sie haben").
Eine Schriftartfamilie ist eine benannte Gruppe von Schriftarten, die ein gemeinsames Design gemeinsam nutzen, sich aber in Attributen wie der Gewichtung unterscheiden können. Ein Schriftfamilienmodell definiert, welche Attribute verwendet werden können, um Schriftarten innerhalb einer Familie zu unterscheiden. Das neue typografische Schriftartenfamilienmodell bietet gegenüber den beiden früheren Schriftartfamilienmodellen, die unter Windows verwendet werden, viele Vorteile. Durch das Ändern von Schriftfamilienmodellen entstehen jedoch Verwirrungs- und Kompatibilitätsprobleme. Die Methoden, die vom IDWriteFontSet4 Schnittstelle verfügbar gemacht werden, implementieren einen Hybridansatz, der die Vorteile des typografischen Schriftfamilienmodells bietet, während Kompatibilitätsprobleme mildern.
In diesem Thema werden die älteren Schriftfamilienmodelle mit dem typografischen Schriftfamilienmodell verglichen. es erklärt Kompatibilitätsprobleme, die sich durch das Ändern von Schriftfamilienmodellen ergeben; und schließlich wird erläutert, wie diese Herausforderungen mit den Methoden [IDWriteFontSet4](/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontset4) überwunden werden können.
RBIZ-Schriftartenfamilienmodell
Das de facto verwendete Schriftfamilienmodell im GDI-Anwendungsökosystem wird manchmal als "Vierschriftartmodell" oder "RBIZ" bezeichnet. Jede Schriftfamilie in diesem Modell verfügt in der Regel über höchstens vier Schriftarten. Die Bezeichnung "RBIZ" stammt aus der Benennungskonvention, die für einige Schriftartdateien verwendet wird, z. B.:
Dateiname | Schriftschnitt |
---|---|
verdana.ttf | Regelmäßig |
verdanab.ttf | Kühn |
verdanai.ttf | Kursiv |
verdanaz.ttf | Fett kursiv |
Bei GDI werden die Eingabeparameter, die zum Auswählen einer Schriftart verwendet werden, durch die LOGFONT Struktur definiert, die Familienname (lfFaceName
), Gewicht (lfWeight
) und kursiv (lfItalic
) Felder enthält. Das feld lfItalic
ist entweder WAHR oder FALSCH. GDI ermöglicht das lfWeight
-Feld, einen beliebigen Wert im Bereich FW_THIN (100) zu FW_BLACK (900), aber aus historischen Gründen wurden Schriftarten so entworfen, dass es nicht mehr als zwei Gewichtungen in derselben GDI-Schriftfamilie gibt.
Beliebte Anwendungsbenutzeroberflächen sind von anfang an kursiv formatiert (um kursiv zu aktivieren und zu deaktivieren) und eine fett formatierte Schaltfläche (um zwischen normaler und fetter Stärke umzuschalten). Die Verwendung dieser beiden Schaltflächen zum Auswählen von Schriftarten innerhalb einer Familie setzt voraus, dass das RBIZ-Modell verwendet wird. Obwohl GDI selbst mehr als zwei Gewichte unterstützt, führte die Anwendungskompatibilität dazu, dass Schriftartentwickler den GDI-Familiennamen (OpenType Name ID 1) auf eine Weise festlegen, die mit dem RBIZ-Modell konsistent war.
Angenommen, Sie wollten der Arial-Schriftfamilie eine schwerere "Schwarze" Gewichtung hinzufügen. Logischerweise ist diese Schriftart Teil der Arial-Familie, daher können Sie davon ausgehen, dass Sie sie auswählen, indem Sie lfFaceName
auf "Arial" und lfWeight
auf FW_BLACKfestlegen. Es gibt jedoch keine Möglichkeit, dass ein Anwendungsbenutzer zwischen drei Gewichtungen mit einer Fettschaltfläche mit zwei Status auswählen kann. Die Lösung bestand darin, der neuen Schriftart einen anderen Familiennamen zu geben, sodass der Benutzer ihn auswählen konnte, indem er "Arial Black" aus der Liste der Schriftartfamilien auswählt. Ebenso gibt es keine Möglichkeit, zwischen verschiedenen Breiten in derselben Schriftfamilie mit nur fett und kursiv formatierten Schaltflächen auszuwählen, sodass die schmalen Versionen von Arial unterschiedliche Familiennamen im RBIZ-Modell haben. Daher haben wir "Arial", "Arial Black" und "Arial Narrow" Schriftart Famile im RBIZ-Modell, obwohl diese alle typografielogisch in einer Familie gehören.
Anhand dieser Beispiele kann man sehen, wie sich die Einschränkungen eines Schriftfamilienmodells darauf auswirken können, wie Schriftarten in Familien gruppiert werden. Da Schriftfamilien anhand des Namens identifiziert werden, bedeutet dies, dass dieselbe Schriftart unterschiedliche Familiennamen aufweisen kann, je nachdem, welches Schriftartenfamilienmodell Sie verwenden.
DirectWrite unterstützt das RBIZ-Schriftartenfamilienmodell nicht direkt, bietet jedoch Methoden zum Konvertieren in und aus dem RBIZ-Modell, z. B. IDWriteGdiInterop::CreateFontFromLOGFONT und IDWriteGdiInterop::ConvertFontToLOGFONT. Sie können auch den RBIZ-Familiennamen einer Schriftart abrufen, indem Sie die IDWriteFont::GetInformationalStrings Methode aufrufen und DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMESangeben.
Familienmodell für Schriftarten im Breitendehnungsstil
Das Familienmodell für Schriftarten im Gewichts-Stretch-Stil ist das ursprüngliche Modell der Schriftfamilie, das von DirectWrite verwendet wird, bevor das typografische Schriftfamilienmodell eingeführt wurde. Es wird auch als Gewichtsbreitenneigung (WWS) bezeichnet. Im WWS-Modell können Schriftarten innerhalb derselben Familie durch drei Eigenschaften unterschiedlich sein: Gewicht (DWRITE_FONT_WEIGHT), Stretch (DWRITE_FONT_STRETCH) und Format (DWRITE_FONT_STYLE).
Das WWS-Modell ist flexibler als das RBIZ-Modell auf zwei Arten. Zuerst können Schriftarten in derselben Familie durch Dehnen (oder Breite) sowie Gewicht und Stil (normal, kursiv oder schräg) unterschieden werden. Zweitens kann es mehr als zwei Gewichte in derselben Familie geben. Diese Flexibilität reicht aus, damit alle Varianten von Arial in derselben WWS-Familie enthalten sein können. In der folgenden Tabelle werden die Schriftarteigenschaften RBIZ und WWS für eine Auswahl von Arial-Schriftarten verglichen:
Vollständiger Name | RBIZ Familienname | lfWeight | lfItalic | WWS FamilyName | Gewicht | Strecken | Stil |
---|---|---|---|---|---|---|---|
Arial | Arial | 400 | 0 | Arial | 400 | 5 | 0 |
Arial fett | Arial | 700 | 0 | Arial | 700 | 5 | 0 |
Arial Schwarz | Arial Schwarz | 900 | 0 | Arial | 900 | 5 | 0 |
Arial schmal | Arial schmal | 400 | 0 | Arial | 400 | 3 | 0 |
Arial Schmal fett | Arial schmal | 700 | 0 | Arial | 700 | 3 | 0 |
Wie Sie sehen können, hat "Arial Narrow" die gleichen lfWeight
und lfItalic
Werte wie "Arial", sodass es einen anderen RBIZ-Familiennamen hat, um Mehrdeutigkeit zu vermeiden. "Arial Black" hat einen anderen RBIZ-Familiennamen, um mehr als zwei Gewichte in der "Arial"-Familie zu vermeiden. Im Gegensatz dazu befinden sich alle diese Schriftarten in derselben Familie im Gewichts-Stretch-Stil.
Trotzdem ist das Modell im Gewichts-Stretch-Stil nicht offen. Wenn zwei Schriftarten dieselbe Stärke, Dehnen und Format aufweisen, sich aber auf andere Weise unterscheiden (z. B. optische Größe), können sie nicht in derselben WWS-Schriftfamilie enthalten sein. Dies bringt uns zum typografischen Schriftfamilienmodell.
Typografisches Schriftfamilienmodell
Im Gegensatz zu seinen Vorgängern ist das typografische Schriftfamilienmodell geöffnet. Sie unterstützt eine beliebige Anzahl von Variationsachsen innerhalb einer Schriftartfamilie.
Wenn Sie sich Schriftauswahlparameter als Koordinaten in einem Entwurfsbereich vorstellen, definiert das Modell im Gewichts-Stretch-Stil ein dreidimensionales Koordinatensystem mit Gewicht, Stretch und Stil als Achsen. Jede Schriftart in einer WWS-Familie muss eine eindeutige Position aufweisen, die durch ihre Koordinaten entlang dieser drei Achsen definiert ist. Wenn Sie eine Schriftart auswählen möchten, geben Sie einen WWS-Familiennamen und einen Gewichtungs-, Stretch- und Formatvorlagenparameter an.
Im Gegensatz dazu verfügt das typografische Schriftfamilienmodell über einen N-dimensionalen Entwurfsbereich. Ein Schriftart-Designer kann eine beliebige Anzahl von Entwurfsachsen definieren, die jeweils durch ein vierstelliges Achsentagidentifiziert werden. Die Position einer bestimmten Schriftart im N-dimensionalen Entwurfsbereich wird durch ein Array von Achsenwertendefiniert, wobei jeder Achsenwert ein Achsentag und einen Gleitkommawert umfasst. Zum Auswählen einer Schriftart geben Sie einen typografischen Familiennamen und ein Array von Achsenwerten (DWRITE_FONT_AXIS_VALUE Strukturen) an.
Obwohl die Anzahl der Schriftachsen geöffnet ist, gibt es einige registrierte Achsen mit Standardbedeutungen, und die Gewichtungs-, Stretch- und Formatvorlagenwerte können registrierten Achsenwerten zugeordnet werden. DWRITE_FONT_WEIGHT kann einem "Gezeichneten" (DWRITE_FONT_AXIS_TAG_WEIGHT) Achsenwert zugeordnet werden. DWRITE_FONT_STRETCH kann einem "wdth" (DWRITE_FONT_AXIS_TAG_WIDTH) Achsenwert zugeordnet werden. DWRITE_FONT_STYLE können einer Kombination aus "Kurs" und "slnt" (DWRITE_FONT_AXIS_TAG_ITALIC und DWRITE_FONT_AXIS_TAG_SLANT) Achsenwerten zugeordnet werden.
Eine weitere registrierte Achse ist "opsz" (DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE). Eine optische Schriftfamilie wie Sitka enthält Schriftarten, die sich entlang der Achse "opsz" unterscheiden, was bedeutet, dass sie für die Verwendung in unterschiedlichen Punktgrößen konzipiert sind. Das WwS-Schriftfamilienmodell verfügt nicht über eine optische Größenachse, daher muss die Sitka-Schriftfamilie in mehrere WWS-Schriftfamilien aufgeteilt werden: "Sitka Small", "Sitka Text", "Sitka Subheading" usw. Jede WWS-Schriftfamilie entspricht einem anderen optischen Schriftgrad, und sie bleibt dem Benutzer überlassen, um den richtigen WWS-Familiennamen für einen bestimmten Schriftgrad anzugeben. Mit dem typografischen Schriftfamilienmodell kann der Benutzer einfach "Sitka" auswählen, und die Anwendung kann automatisch den Achsenwert "opsz" basierend auf dem Schriftgrad festlegen.
Typografische Schriftartenauswahl und variable Schriftarten
Das Konzept der Variationsachsen ist häufig variablen Schriftarten zugeordnet, gilt aber auch für statische Schriftarten. Die OpenType STAT (Style Attributes) Tabelle deklariert, welche Entwurfsachsen eine Schriftart aufweist, und die Werte dieser Achsen. Diese Tabelle ist für variable Schriftarten erforderlich, ist aber auch für statische Schriftarten relevant.
Die DirectWrite-API macht "ght", "wdth", "ital" und "slnt" Achsenwerte für jede Schriftart verfügbar, auch wenn sie nicht in der STAT-Tabelle vorhanden sind oder keine STAT-Tabelle vorhanden ist. Diese Werte werden nach Möglichkeit aus der STAT-Tabelle abgeleitet. Andernfalls werden sie von der Schriftbreite, dem Schriftgrad und dem Schriftschnitt abgeleitet.
Schriftartachsen können variabel oder nicht variabel sein. Eine statische Schriftart verfügt nur über nicht variable Achsen, während eine variable Schriftart beides aufweisen kann. Um eine variable Schriftart zu verwenden, müssen Sie eine variable Schriftart Instanz erstellen, in der alle Variablenachsen an bestimmte Werte gebunden wurden. Die IDWriteFontFace Schnittstelle stellt entweder eine statische Schriftart oder eine bestimmte Instanz einer Variablenschriftart dar. Es ist möglich, eine beliebige Instanz einer Variablenschriftart mit angegebenen Achsenwerten zu erstellen. Darüber hinaus kann eine variable Schriftart benannten Instanzen deklarieren, die in der STAT- Tabelle mit vordefinierten Kombinationen von Achsenwerten. Benannte Instanzen ermöglichen es einer variablen Schriftart, sich ähnlich wie eine Sammlung statischer Schriftarten zu verhalten. Wenn Sie Elemente einer IDWriteFontFamily oder IDWriteFontSet-aufzählen, gibt es ein Element für jede statische Schriftart und für jede benannte Schriftartinstanz.
Der typografische Schriftabgleichsalgorithmus wählt zuerst potenzielle Übereinstimmungskandidaten basierend auf dem Familiennamen aus. Wenn die Übereinstimmungskandidaten variable Schriftarten enthalten, werden alle Übereinstimmungskandidaten für dieselbe Variable Schriftart in einen Übereinstimmungskandidaten reduziert, in dem jeder Variablenachse einem bestimmten Wert so nah wie möglich dem angeforderten Wert für diese Achse zugewiesen wird. Wenn für eine variable Achse kein angeforderter Wert vorhanden ist, wird ihm der Standardwert für diese Achse zugewiesen. Die Reihenfolge der Übereinstimmungskandidaten wird dann bestimmt, indem die Achsenwerte mit den angeforderten Achsenwerten verglichen werden.
Betrachten Sie beispielsweise die typografische Sitka-Familie in Windows. Sitka ist eine optische Schriftfamilie, was bedeutet, dass sie eine "opsz"-Achse hat. In Windows 11 wird Sitka als zwei variable Schriftarten mit den folgenden Achsenwerten implementiert. Beachten Sie, dass die opsz
und wght
Achsen variabel sind, während die anderen Achsen nicht variabel sind.
Dateiname | "opsz" | "wght" | "wdth" | "Kursiv" | "slnt" |
---|---|---|---|---|---|
SitkaVF.ttf | 6-27.5 | 400-700 | 100 | 0 | 0 |
SitkaVF-Italic.ttf | 6-27.5 | 400-700 | 100 | 1 | -12 |
Angenommen, die angeforderten Achsenwerte sind opsz:12 wght:475 wdth:100 ital:0 slnt:0
. Für jede variable Schriftart erstellen wir einen Verweis auf eine variable Schriftart Instanz, in der jeder Variablenachse ein bestimmter Wert zugewiesen wird. Die achsen opsz
und wght
werden auf 12
bzw. 475
festgelegt. Dies führt zu den folgenden übereinstimmenden Schriftarten, wobei die nicht kursiv formatierte Schriftart zuerst bewertet wird, da sie für die ital
und slnt
Achsen besser geeignet ist:
SitkaVF.ttf opsz:12 wght:475 wdth:100 ital:0 slnt0
SitkaVF-Italic.ttf opsz:12 wght:475 wdth:100 ital:1 slnt:-12
Im obigen Beispiel sind die übereinstimmenden Schriftarten willkürliche Schriftartinstanzen für variable Schriftarten. Es gibt keine benannte Instanz von Sitka mit Gewicht 475. Im Gegensatz dazu gibt der Abgleichsalgorithmus im Gewichtsdehnungsstil nur benannte Instanzen zurück.
Reihenfolge des Schriftabgleichs
Es gibt verschiedene überladene GetMatchingFonts Methoden für das Familienmodell der Schriftarten im Gewichts-Stretch-Stil (IDWriteFontFamily::GetMatchingFonts) und das typografische Schriftfamilienmodell (IDWriteFontCollection2::GetMatchingFonts). In beiden Fällen ist die Ausgabe eine Liste übereinstimmender Schriftarten in absteigender Reihenfolge der Priorität basierend darauf, wie gut jede Kandidatenschriftart den Eingabeeigenschaften entspricht. In diesem Abschnitt wird beschrieben, wie die Priorität bestimmt wird.
Im Modell mit gewichtetem Stretchstil sind die Eingabeparameter Schriftbreite (DWRITE_FONT_WEIGHT), Schriftdehnung (DWRITE_FONT_STRETCH) und Schriftschnitt (DWRITE_FONT_STYLE). Der Algorithmus zum Auffinden der besten Übereinstimmung wurde in einem 2006-Whitepaper mit dem Titel "WPF Font Selection Model" von Mikhail Leonov und David Brown dokumentiert. Weitere Informationen finden Sie im Abschnitt "Abgleichen eines Gesichts aus der Kandidaten-Gesichtsliste". In diesem Dokument ging es um Windows Presentation Foundation (WPF), aber DirectWrite hat später denselben Ansatz verwendet.
Der Algorithmus verwendet den Begriff des Schriftart-Attributvektors, der für eine bestimmte Kombination aus Gewicht, Stretch und Stil wie folgt berechnet wird:
FontAttributeVector.X = (stretch - 5) * 1100;
FontAttributeVector.Y = style * 700;
FontAttributeVector.Z = (weight - 400) * 5;
Beachten Sie, dass jede Vektorkoordinate normalisiert wird, indem der "normal"-Wert für das entsprechende Attribut subtrahiert und mit einer Konstante multipliziert wird. Die Multiplikatoren entschädigen die Tatsache, dass die Eingabewerte für Gewicht, Stretch und Stil sehr unterschiedlich sind. Andernfalls würde die Gewichtung (100,.999) den Stil (0,.2) beherrschen.
Für jeden Übereinstimmungskandidaten werden ein Vektorabstand und ein Punktprodukt zwischen dem Schriftart-Attributvektor des Kandidaten und dem Eingabeschrift-Attributvektor berechnet. Beim Vergleichen von zwei Übereinstimmungskandidaten ist der Kandidat mit dem kleineren Vektorabstand die bessere Übereinstimmung. Wenn die Abstände gleich sind, ist der Kandidat mit dem kleineren Punktprodukt eine bessere Übereinstimmung. Wenn das Punktprodukt auch gleich ist, werden die Abstände entlang der X-, Y- und Z-Achse in dieser Reihenfolge verglichen.
Der Vergleich von Abständen ist intuitiv genug, aber die Verwendung des Punktprodukts als sekundäres Maß erfordert möglicherweise eine Erklärung. Angenommen, die Eingabegewichtung ist semibold (600), und zwei Kandidatengewichte sind schwarz (900) und Semilight (300). Der Abstand jedes Kandidatengewichts von der Eingabegewichtung ist identisch, aber das schwarze Gewicht liegt in der gleichen Richtung vom Ursprung (d. h. 400 oder normal), sodass es ein kleineres Punktprodukt hat.
Der typografische Abgleichsalgorithmus ist eine Generalisierung des Algorithmus für die Gewichtsdehnung. Jeder Achsenwert wird als Koordinate in einem N-dimensionalen Schriftart-Attributvektor behandelt. Für jeden Übereinstimmungskandidaten werden ein Vektorabstand und ein Punktprodukt zwischen dem Schriftart-Attributvektor des Kandidaten und dem Eingabeschrift-Attributvektor berechnet. Der Kandidat mit dem kleineren Vektorabstand ist die bessere Übereinstimmung. Wenn die Abstände gleich sind, ist der Kandidat mit dem kleineren Punktprodukt eine bessere Übereinstimmung. Wenn das Dot-Produkt ebenfalls identisch ist, kann das Vorhandensein in einer angegebenen Familie im Gewichts-Stretch-Stil als Tie-Breaker verwendet werden.
Um den Vektorabstand und das Punktprodukt zu berechnen, muss der Schriftart-Attributvektor eines Kandidaten und der Eingabeschriftart-Attributvektor die gleichen Achsen aufweisen. Daher wird jeder fehlende Achsenwert in beiden Vektoren ausgefüllt, indem der Standardwert für diese Achse ersetzt wird. Vektorkoordinaten werden normalisiert, indem der Standardwert (oder "normal") für die entsprechende Achse subtrahiert und das Ergebnis mit einem achsenspezifischen Multiplikator multipliziert wird. Nachfolgend sind die Multiplizierer und Standardwerte für jede Achse aufgeführt:
Achse | Multiplikator | Richtwert |
---|---|---|
"wght" | 5 | 400 |
"wdth" | 55 | 100 |
"Kursiv" | 1400 | 0 |
"slnt" | 35 | 0 |
"opsz" | 1 | 12 |
andere | 1 | 0 |
Die Multiplikatoren entsprechen denen, die vom Algorithmus im Gewichts-Stretch-Stil verwendet werden, aber bei Bedarf skaliert werden. Die normale Breite beträgt beispielsweise 100, was 5 entspricht. Dies führt zu einem Multiplikator von 55 und 1100. Das Legacystil-Attribut (0..2) verangtet kursiv und schräg, die im typografischen Modell in eine "Kursiv"-Achse (0,.1) und eine "slnt"-Achse (-90..90) zerlegt werden. Die gewählten Multiplikatoren für diese beiden Achsen geben dem Legacyalgorithmus gleichwertige Ergebnisse, wenn wir für schräge Schriftarten eine Standardneigung von 20 Grad annehmen.
Typografische Schriftartauswahl und optische Größe
Eine Anwendung, die das typografische Schriftfamilienmodell verwendet, kann die optische Größenanpassung implementieren, indem sie einen opsz
Achsenwert als Schriftauswahlparameter angeben. Beispielsweise könnte eine Textverarbeitungsanwendung einen opsz
Achsenwert angeben, der dem Schriftgrad in Punkt entspricht. In diesem Fall könnte ein Benutzer "Sitka" als Schriftfamilie auswählen, und die Anwendung würde automatisch eine Instanz von Sitka mit dem richtigen opsz
Achsenwert auswählen. Unter dem WWS-Modell wird jede optische Größe als unterschiedlicher Familienname verfügbar gemacht, und es liegt an dem Benutzer, die richtige Größe auszuwählen.
Theoretisch könnte man die automatische optische Größenanpassung unter dem Modell der Gewichtsdehnung implementieren, indem der opsz
Achsenwert als separater Schritt nach Schriftartauswahl überschrieben wird. Dies funktioniert jedoch nur, wenn die erste übereinstimmende Schriftart eine variable Schriftart mit einer Variablen opsz
Achse ist. Die Angabe von opsz
als Parameter für die Schriftartauswahl eignet sich gleichermaßen gut für statische Schriftarten. Beispielsweise wird die Sitka-Schriftartfamilie als variable Schriftarten in Windows 11 implementiert, aber als Sammlung statischer Schriftarten in Windows 10. Die statischen Schriftarten weisen unterschiedliche, nicht überlappende opsz
Achsenbereiche auf (diese werden zu Schriftauswahlzwecken als Bereiche deklariert, sind jedoch keine variablen Achsen). Wenn Sie opsz
als Schriftauswahlparameter angeben, kann die richtige statische Schriftart für den optischen Schriftgrad ausgewählt werden.
Vorteile der typografischen Schriftartauswahl und Kompatibilitätsprobleme
Das typografische Schriftauswahlmodell hat gegenüber früheren Modellen mehrere Vorteile, hat aber in seiner reinen Form einige mögliche Kompatibilitätsprobleme. In diesem Abschnitt werden die Vorteile und Kompatibilitätsprobleme beschrieben. Im nächsten Abschnitt wird ein Hybridschriftartenauswahlmodell beschrieben, das die Vorteile bei gleichzeitiger Minderung der Kompatibilitätsprobleme bewahrt.
Vorteile des typografischen Schriftfamilienmodells sind:
Schriftarten können in Familien gruppiert werden, wie sie vom Designer vorgesehen sind, anstatt aufgrund von Einschränkungen des Schriftfamilienmodells in Unterfamilien aufgeteilt zu werden.
Eine Anwendung kann automatisch den richtigen
opsz
Achsenwert basierend auf dem Schriftgrad auswählen, anstatt dem Benutzer unterschiedliche optische Größen als verschiedene Schriftfamilien verfügbar zu machen.Beliebige Instanzen variabler Schriftarten können ausgewählt werden. Wenn beispielsweise eine variable Schriftart Gewichtungen im fortlaufenden Bereich 100-900 unterstützt, kann das typografische Modell beliebige Gewichtung in diesem Bereich auswählen. Die älteren Schriftfamilienmodelle können nur die nächste Gewichtung aus den benannten Instanzen auswählen, die von der Schriftart definiert werden.
Kompatibilitätsprobleme mit dem typografischen Schriftauswahlmodell sind:
Einige ältere Schriftarten können nicht eindeutig ausgewählt werden, indem nur typografische Familiennamen und Achsenwerte verwendet werden.
Vorhandene Dokumente können auf Schriftarten nach WWS-Familiennamen oder RBIZ-Familiennamen verweisen. Benutzer erwarten möglicherweise auch, WWS- und RBIZ-Familiennamen zu verwenden. Ein Dokument kann z. B. "Sitka-Unterüberschrift" (WWS-Familienname) anstelle von "Sitka" (typografischer Familienname) angeben.
Eine Bibliothek oder ein Framework verwendet möglicherweise das typografische Schriftfamilienmodell, um die automatische optische Größenanpassung zu nutzen, aber keine API zum Angeben beliebiger Achsenwerte bereitzustellen. Selbst wenn eine neue API bereitgestellt wird, muss das Framework möglicherweise mit vorhandenen Anwendungen arbeiten, die nur Gewichts-, Stretch- und Formatparameter angeben.
Das Kompatibilitätsproblem mit älteren Schriftarten tritt auf, da das Konzept des typografischen Familiennamens vor dem Konzept der Schriftachsenwerte liegt, die zusammen mit variablen Schriftarten in OpenType 1.8 eingeführt wurden. Vor OpenType 1.8 hat der typografische Familienname lediglich die Absicht des Designers zum Ausdruck gebracht, dass eine Reihe von Schriftarten miteinander verknüpft war, aber ohne Garantie, dass diese Schriftarten programmgesteuert auf grundlage ihrer Eigenschaften differenziert werden könnten. Nehmen wir als hypothetisches Beispiel an, dass alle folgenden Schriftarten den typografischen Familiennamen "Legacy" aufweisen:
Vollständiger Name | WWS Family | Gewicht | Strecken | Stil | Typofamilie | Tänze | wdth | Ital | slnt |
---|---|---|---|---|---|---|---|---|---|
Hinterlassenschaft | Hinterlassenschaft | 400 | 5 | 0 | Hinterlassenschaft | 400 | 100 | 0 | 0 |
Legacy Fett | Hinterlassenschaft | 700 | 5 | 0 | Hinterlassenschaft | 700 | 100 | 0 | 0 |
Legacy Schwarz | Hinterlassenschaft | 900 | 5 | 0 | Hinterlassenschaft | 900 | 100 | 0 | 0 |
Legacy Soft | Legacy Soft | 400 | 5 | 0 | Hinterlassenschaft | 400 | 100 | 0 | 0 |
Legacy Weich fett fett | Legacy Soft | 700 | 5 | 0 | Hinterlassenschaft | 700 | 100 | 0 | 0 |
Legacy Soft Black | Legacy Soft | 900 | 5 | 0 | Hinterlassenschaft | 900 | 100 | 0 | 0 |
Die typografische Familie "Legacy" hat drei Gewichte, und jedes Gewicht hat normale und "weiche" Varianten. Wenn es sich um neue Schriftarten handelte, könnten sie als Deklarierung einer SOFT-Entwurfsachse implementiert werden. Diese Schriftarten predate OpenType 1.8, sodass ihre einzigen Entwurfsachsen von Gewicht, Stretch und Stil abgeleitet sind. Für jede Gewichtung verfügt diese Schriftfamilie über zwei Schriftarten mit identischen Achsenwerten, sodass es nicht möglich ist, eine Schriftart in dieser Familie eindeutig mit Achsenwerten auszuwählen.
Hybrider Schriftauswahlalgorithmus
Die im nächsten Abschnitt beschriebenen ApIs für die Schriftartauswahl verwenden einen Algorithmus für die Hybridschriftauswahl, der die Vorteile der typografischen Schriftartauswahl bei gleichzeitiger Reduzierung der Kompatibilitätsprobleme bewahrt.
Die Hybridschriftartenauswahl bietet eine Brücke von älteren Schriftfamilienmodellen, indem die Werte für Schriftbreite, Schriftartendehnung und Schriftschnittwerte den entsprechenden Schriftachsenwerten zugeordnet werden können. Dies hilft bei der Behebung von Problemen mit Dokument- und Anwendungskompatibilität.
Darüber hinaus ermöglicht der Hybrid-Schriftartauswahlalgorithmus, dass der angegebene Familienname ein typografischer Familienname, ein Familienname im Gewichtsdehnungsstil, ein GDI/RBIZ-Familienname oder ein vollständiger Schriftartname sein kann. Der Abgleich erfolgt auf eine der folgenden Arten, in absteigender Reihenfolge der Priorität:
Der Name entspricht einer typografischen Familie (z. B. Sitka). Der Abgleich erfolgt innerhalb der typografischen Familie, und alle angeforderten Achsenwerte werden verwendet. Wenn der Name auch mit einer WWS-Unterfamilie übereinstimmt (d. h. eine kleiner als die typografische Familie), wird die Mitgliedschaft in der WWS-Unterfamilie als Tie-Breaker verwendet.
Der Name entspricht einer WWS-Familie (z. B. Sitka Text). Der Abgleich erfolgt innerhalb der WWS-Familie, und angeforderte Achsenwerte außer "wght", "wdth", "ital" und "slnt" werden ignoriert.
Der Name entspricht einer GDI-Familie (z. B. Bahnschrift Kondensiert). Der Abgleich erfolgt innerhalb der RBIZ-Familie und angeforderte Achsenwerte außer "Strich", "Kursiv" und "schräg" werden ignoriert.
Der Name entspricht einem vollständigen Namen (z. B. "Bahnschrift Bold Condensed"). Die Schriftart, die dem vollständigen Namen entspricht, wird zurückgegeben. Angeforderte Achsenwerte werden ignoriert. Der Abgleich nach vollständigem Schriftartnamen ist zulässig, da GDI ihn unterstützt.
Im vorherigen Abschnitt wurde eine mehrdeutige typografische Familie namens "Legacy" beschrieben. Der Hybridalgorithmus ermöglicht es, die Mehrdeutigkeit zu vermeiden, indem entweder "Legacy" oder "Legacy Soft" als Familienname angegeben wird. Wenn "Legacy Soft" angegeben wird, gibt es keine Mehrdeutigkeit, da der Abgleich nur innerhalb der WWS-Familie erfolgt. Wenn "Legacy" angegeben ist, werden alle Schriftarten in der typografischen Familie als Übereinstimmungskandidaten betrachtet, aber Mehrdeutigkeit wird vermieden, indem die Mitgliedschaft in der WWS-Familie "Legacy" als Tie-Breaker verwendet wird.
Angenommen, ein Dokument gibt einen Familiennamen und einen Familiennamen, einen Streckungs- und Formatvorlagenparameter an, jedoch keine Achsenwerte. Die Anwendung kann zuerst die Gewichtung, Dehnen, Formatvorlage und Schriftgrad in Achsenwerte konvertieren, indem IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValuesaufgerufen wird. Die Anwendung kann dann sowohl den Familiennamen als auch die Achsenwerte an IDWriteFontSet4::GetMatchingFontsübergeben. GetMatchingFonts eine Liste übereinstimmender Schriftarten in der Prioritätsreihenfolge zurück, und das Ergebnis ist angemessen, ob der angegebene Familienname ein typografischer Familienname, ein Familienname im Weight-Stretch-Stil, ein RBIZ-Familienname oder ein vollständiger Name ist. Wenn die angegebene Familie eine "opsz"-Achse aufweist, wird die entsprechende optische Größe basierend auf dem Schriftgrad automatisch ausgewählt.
Angenommen, ein Dokument gibt Gewichtung, Ziehung und Formatvorlage an, und auch Achsenwerte angibt. In diesem Fall können die expliziten Achsenwerte auch an IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValuesübergeben werden, und die von der Methode zurückgegebenen abgeleiteten Achsenwerte enthalten nur Schriftartachsen, die nicht explizit angegeben wurden. Daher haben Achsenwerte, die explizit vom Dokument (oder der Anwendung) angegeben wurden, Vorrang vor Achsenwerten, die von Gewichtung, Stretch, Formatvorlage und Schriftgrad abgeleitet wurden.
ApIs für die Hybridschriftauswahl
Das Hybridschriftartenauswahlmodell wird durch die folgenden IDWriteFontSet4 Methoden implementiert:
Die IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues Methode konvertiert Schriftgrad-, Gewichts-, Stretch- und Formatvorlagenparameter in die entsprechenden Achsenwerte. Alle vom Client übergebenen expliziten Achsenwerte werden von den abgeleiteten Achsenwerten ausgeschlossen.
Die IDWriteFontSet4::GetMatchingFonts Methode gibt eine priorisierte Liste der übereinstimmenden Schriftarten zurück, die einen Familiennamen und ein Array von Achsenwerten aufweisen. Wie oben beschrieben, kann der Familiennameparameter ein typografischer Familienname, WWS-Familienname, RBIZ-Familienname oder vollständiger Name sein. (Der vollständige Name identifiziert einen bestimmten Schriftschnitt, z. B. "Arial Bold Italic". GetMatchingFonts unterstützt den Abgleich nach vollständigem Namen für größere Komaptibiltiie mit GDI, was es ebenfalls zulässt.)
Die folgenden anderen DirectWrite-APIs verwenden auch den Hybridschriftauswahlalgorithmus:
IDWriteTextLayout, wenn das typografische Schriftfamilienmodell durch Aufrufen IDWriteTextLayout4::SetFontAxisValues oder IDWriteTextLayout4::SetAutomaticFontAxes
Codebeispiele für verwendete Schriftartauswahl-APIs
Dieser Abschnitt zeigt eine vollständige Konsolenanwendung, die die IDWriteFontSet4::GetMatchingFonts und IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues Methoden veranschaulicht. Zuerst fügen wir einige Kopfzeilen ein:
#include <dwrite_core.h>
#include <wil/com.h>
#include <iostream>
#include <string>
#include <vector>
Die IDWriteFontSet4::GetMatchingFonts Methode gibt eine Liste der Schriftarten in der Prioritätsreihenfolge zurück, die den angegebenen Familiennamen und Achsenwerten entsprechen. Im folgenden MatchAxisValues Funktion werden die Parameter in IDWriteFontSet4::GetMatchingFonts und die Liste der übereinstimmenden Schriftarten im zurückgegebenen Schriftartensatz ausgegeben.
// 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;
}
Eine Anwendung verfügt möglicherweise über Gewichts-, Stretch- und Formatvorlagenparameter anstelle von (oder zusätzlich zu) Achsenwerten. Beispielsweise muss die Anwendung möglicherweise mit Dokumenten arbeiten, die mit RBIZ- oder Weight-Stretch-Style-Parametern auf Schriftarten verweisen.For example, the application might need to work with documents that reference fonts using RBIZ or weight-stretch-style parameters. Auch wenn die Anwendung Unterstützung für die Angabe beliebiger Achsenwerte hinzufügt, muss sie möglicherweise auch die älteren Parameter unterstützen. Dazu kann die Anwendung IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues aufrufen, bevor IDWriteFontSet4::GetMatchingFontsaufgerufen wird.
Im folgenden MatchFont- Funktion werden zusätzlich zu Achsenwerten Gewichtungs-, Stretch-, Format- und Schriftgradparameter benötigt. Sie leitet diese Parameter an die IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues Methode weiter, um abgeleitete Achsenwerte zu berechnen, die an die Werte der Eingabeachse angefügt werden. Sie übergibt die kombinierten Achsenwerte an die oben MatchAxisValues Funktion.
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);
}
Die folgende Funktion veranschaulicht die Ergebnisse des Aufrufens der obigen MatchFont--Funktion mit einigen Beispieleingaben:
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);
}
Es folgt die Ausgabe der obigen TestFontSelection Funktion:
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
Nachfolgend sind die Implementierungen der oben deklarierten überladenen Operatoren aufgeführt. Diese werden von MatchAxisValues- verwendet, um die Werte der Eingabeachse und die resultierenden Schriftzeichenverweise zu schreiben:
// 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;
}
Zum Abrunden des Beispiels folgen Befehlszeilenanalysefunktionen und die Hauptfunktion:
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());
}
}