Wybór czcionki
Interfejs IDWriteFontSet4 uwidacznia metody wybierania czcionek z zestawu czcionek. Te metody umożliwiają przejście do modelu rodziny czcionek typograficznego przy zachowaniu zgodności z istniejącymi aplikacjami, dokumentami i czcionkami.
Wybór czcionki (czasami nazywany dopasowywaniem czcionek lub mapowaniem czcionek) to proces wybierania dostępnych czcionek, które najlepiej pasują do parametrów wejściowych przekazywanych przez aplikację. Parametry wejściowe są czasami określane zbiorczo jako czcionka logiczna . Czcionka logiczna zawiera nazwę rodziny czcionek oraz inne atrybuty wskazujące konkretną czcionkę w rodzinie. Algorytm wyboru czcionki pasuje do czcionki logicznej ("czcionki, którą chcesz") do dostępnej czcionki fizycznej ("czcionka, którą masz").
Rodzina czcionek to nazwana grupa czcionek, które mają wspólny projekt, ale mogą różnić się atrybutami, takimi jak waga. Model rodziny czcionek określa, jakie atrybuty mogą być używane do rozróżniania czcionek w rodzinie. Nowy model rodziny czcionek typograficznej ma wiele zalet w porównaniu z dwoma poprzednimi modelami rodziny czcionek używanymi w systemie Windows. Jednak zmiana modeli rodziny czcionek stwarza możliwości pomyłek i problemów ze zgodnością. Metody uwidocznione przez interfejs IDWriteFontSet4 implementują podejście hybrydowe, które oferuje zalety modelu rodziny czcionek typograficznej przy jednoczesnym łagodzeniu problemów ze zgodnością.
W tym temacie porównano starsze modele rodziny czcionek z modelem rodziny czcionek typograficznej; wyjaśnia problemy ze zgodnością stwarzane przez zmianę modeli rodziny czcionek; i wreszcie wyjaśniono, jak te wyzwania można przezwyciężyć przy użyciu metod [IDWriteFontSet4 ](/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontset4).
Model rodziny czcionek RBIZ
De facto model rodziny czcionek używany w ekosystemie aplikacji GDI jest czasami nazywany "modelem czterech czcionek" lub "modelem RBIZ". Każda rodzina czcionek w tym modelu zwykle ma co najwyżej cztery czcionki. Etykieta "RBIZ" pochodzi z konwencji nazewnictwa używanej dla niektórych plików czcionek, na przykład:
Nazwa pliku | Styl czcionki |
---|---|
verdana.ttf | Regularny |
verdanab.ttf | Śmiały |
verdanai.ttf | Kursywa |
verdanaz.ttf | Pogrubiona kursywa |
W przypadku interfejsu GDI parametry wejściowe używane do wybierania czcionki są definiowane przez strukturę LOGFONT, która zawiera nazwę rodziny (lfFaceName
), wagę (lfWeight
) i kursywę (lfItalic
). Pole lfItalic
ma wartość TRUE lub FALSE. GDI umożliwia lfWeight
polem być dowolną wartością w zakresie FW_THIN (100) do FW_BLACK (900), ale z powodów historycznych czcionki zostały zaprojektowane tak, że nie ma więcej niż dwóch wag w tej samej rodzinie czcionek GDI.
Popularne interfejsy użytkownika aplikacji od początku dołączonego przycisku kursywy (aby włączyć i wyłączyć kursywę) oraz przycisk pogrubiony (przełączać się między normalnymi i pogrubionym wagami). Użycie tych dwóch przycisków do wybierania czcionek w rodzinie zakłada model RBIZ. W związku z tym, mimo że sam interfejs GDI obsługuje więcej niż dwie wagi, deweloperzy czcionek prowadzili zgodność aplikacji, aby ustawić nazwę rodziny GDI (identyfikator nazwy OpenType 1) w sposób zgodny z modelem RBIZ.
Załóżmy na przykład, że chcesz dodać cięższą wagę "" do rodziny czcionek Arial. Logicznie ta czcionka jest częścią rodziny Arial, więc można oczekiwać jej wybrania, ustawiając lfFaceName
na "Arial" i lfWeight
FW_BLACK. Nie ma jednak możliwości wyboru przez użytkownika aplikacji między trzema wagami przy użyciu przycisku pogrubienia dwustanowego. Rozwiązaniem było nadanie nowej czcionki innej nazwy rodziny, aby użytkownik mógł go wybrać, wybierając pozycję "Arial Black" z listy rodzin czcionek. Podobnie nie ma możliwości wyboru spośród różnych szerokości w tej samej rodzinie czcionek przy użyciu tylko pogrubionych i kursywy przycisków, więc wąskie wersje Arial mają różne nazwy rodzin w modelu RBIZ. W ten sposób mamy "Arial", "Arial Black" i "Arial Narrow" familes czcionek w modelu RBIZ, mimo że typograficzne te wszystkie należą do jednej rodziny.
W tych przykładach można zobaczyć, jak ograniczenia modelu rodziny czcionek mogą wpływać na sposób grupowania czcionek w rodziny. Ponieważ rodziny czcionek są identyfikowane według nazwy, oznacza to, że ta sama czcionka może mieć różne nazwy rodziny w zależności od używanego modelu rodziny czcionek.
Funkcja DirectWrite nie obsługuje bezpośrednio modelu rodziny czcionek RBIZ, ale udostępnia metody konwersji na i z modelu RBIZ, takie jak IDWriteGdiInterop::CreateFontFromLOGFONT i IDWriteGdiInterop::ConvertFontToLOGFONT. Możesz również uzyskać nazwę rodziny RBIZ czcionki, wywołując jej IDWriteFont::GetInformationalStrings metodę i określając DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES.
Model rodziny czcionek w stylu rozciągnięcia
Model rodziny czcionek w stylu odciągnięcia jest oryginalnym modelem rodziny czcionek używanym przez funkcję DirectWrite przed wprowadzeniem modelu rodziny czcionek typograficznej. Jest również znany jako nachylenie wagi (WWS). W modelu WWS czcionki w tej samej rodzinie mogą być różne przez trzy właściwości: waga (DWRITE_FONT_WEIGHT), stretch (DWRITE_FONT_STRETCH) i styl (DWRITE_FONT_STYLE).
Model WWS jest bardziej elastyczny niż model RBIZ na dwa sposoby. Po pierwsze czcionki w tej samej rodzinie mogą być rozróżniane przez rozciągnięcie (lub szerokość), a także wagę i styl (zwykły, kursywa lub ukośny). Po drugie, może istnieć więcej niż dwie wagi w tej samej rodzinie. Ta elastyczność jest wystarczająca do umożliwienia włączenia wszystkich wariantów Arial do tej samej rodziny WWS. W poniższej tabeli porównaliśmy właściwości czcionek RBIZ i WWS dla wybranych czcionek Arial:
Imię i nazwisko | Nazwa rodziny RBIZ | lfWeight | lfItalic | WWS FamilyName | Ciężar | Rozciągnąć | Styl |
---|---|---|---|---|---|---|---|
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 Wąskie pogrubienie | Arial Narrow | 700 | 0 | Arial | 700 | 3 | 0 |
Jak widać, "Arial Narrow" ma te same wartości lfWeight
i lfItalic
co "Arial", więc ma inną nazwę rodziny RBIZ, aby uniknąć niejednoznaczności. "Arial Black" ma inną nazwę rodziny RBIZ, aby uniknąć posiadania więcej niż dwóch wag w rodzinie "Arial". Z kolei wszystkie te czcionki znajdują się w tej samej rodzinie o różnych wagach.
Niemniej jednak model w stylu wagi nie jest otwarty. Jeśli dwie czcionki mają taką samą wagę, rozciągnięcie i styl, ale różnią się w inny sposób (na przykład rozmiar optyczny), nie mogą być uwzględnione w tej samej rodzinie czcionek WWS. Spowoduje to wyświetlenie modelu rodziny czcionek typograficznej.
Model rodziny czcionek typograficznych
W przeciwieństwie do poprzedników model rodziny czcionek typograficznej jest otwarty. Obsługuje dowolną liczbę osi odmiany w obrębie rodziny czcionek.
Jeśli myślisz o parametrach wyboru czcionki jako współrzędnych w przestrzeni projektowej, model w stylu wagi rozciągania definiuje trójwymiarowy układ współrzędnych z wagą, rozciągnięciem i stylem jako osiami. Każda czcionka w rodzinie usług WWS musi mieć unikatową lokalizację zdefiniowaną przez współrzędne wzdłuż tych trzech osi. Aby wybrać czcionkę, należy określić nazwę i wagę rodziny WWS, stretch i parametry stylu.
Natomiast model rodziny czcionek typograficznej ma przestrzeń projektową N-wymiarową. Projektant czcionek może zdefiniować dowolną liczbę osi projektowych, z których każda jest identyfikowana przez czteroznaczny tag osi . Lokalizacja danej czcionki w przestrzeni projektowej N-wymiarowej jest definiowana przez tablicę wartości osi , gdzie każda wartość osi składa się z tagu osi i wartości zmiennoprzecinkowej. Aby wybrać czcionkę, należy określić nazwę rodziny typograficznej i tablicę wartości osi (DWRITE_FONT_AXIS_VALUE struktury).
Mimo że liczba osi czcionek jest otwarta, istnieje kilka zarejestrowanych osi ze standardowymi znaczeniami, a wartości wagi, rozciągnięcia i stylu można mapować na zarejestrowane wartości osi. DWRITE_FONT_WEIGHT można zamapować na wartość osi "wght" (DWRITE_FONT_AXIS_TAG_WEIGHT). DWRITE_FONT_STRETCH można zamapować na wartość osi "wdth" (DWRITE_FONT_AXIS_TAG_WIDTH). DWRITE_FONT_STYLE można mapować na kombinację wartości osi "ital" i "slnt" (DWRITE_FONT_AXIS_TAG_ITALIC i DWRITE_FONT_AXIS_TAG_SLANT).
Inną zarejestrowaną osią jest "opsz" (DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE). Rodzina czcionek optycznych, takich jak Sitka, zawiera czcionki, które różnią się wzdłuż osi "opsz", co oznacza, że są one przeznaczone do użycia w różnych rozmiarach punktów. Model rodziny czcionek WWS nie ma osi rozmiaru optycznego, więc rodzina czcionek Sitka musi być podzielona na wiele rodzin czcionek WWS: "Sitka Small", "Sitka Text", "Sitka Podpozycja" itd. Każda rodzina czcionek WWS odpowiada innego rozmiaru optycznego i jest pozostawiona użytkownikowi w celu określenia prawej nazwy rodziny WWS dla danego rozmiaru czcionki. W modelu rodziny czcionek typograficznej użytkownik może po prostu wybrać pozycję "Sitka", a aplikacja może automatycznie ustawić wartość osi "opsz" na podstawie rozmiaru czcionki.
Wybór czcionki typograficznej i czcionki zmiennych
Koncepcja osi odmiany jest często skojarzona z czcionkami zmiennych, ale ma również zastosowanie do czcionek statycznych. Tabela STAT (atrybuty stylu) typu OpenType deklaruje, jakie osie projektowe ma czcionka i wartości tych osi. Ta tabela jest wymagana dla czcionek zmiennych, ale jest również istotna dla czcionek statycznych.
Interfejs API DirectWrite uwidacznia wartości osi "wght", "wdth", "ital" i "slnt" dla każdej czcionki, nawet jeśli nie są obecne w tabeli STAT lub jeśli nie ma tabeli STAT. Te wartości pochodzą z tabeli STAT, jeśli jest to możliwe. W przeciwnym razie pochodzą one z wagi czcionki, rozciągnięcia czcionki i stylu czcionki.
Osie czcionek mogą być zmienne lub inne niż zmienne. Czcionka statyczna ma tylko osie inne niż zmienne, podczas gdy czcionka zmiennej może mieć obie te wartości. Aby użyć czcionki zmiennej, należy utworzyć czcionkę zmiennej wystąpienie, w którym wszystkie osie zmiennych zostały powiązane z określonymi wartościami. InterfejsIDWriteFontFace reprezentuje czcionkę statyczną lub określone wystąpienie czcionki zmiennej. Istnieje możliwość utworzenia dowolnego wystąpienia czcionki zmiennej z określonymi wartościami osi. Ponadto czcionka zmiennej może deklarować nazwanych wystąpień w tabeli STAT ze wstępnie zdefiniowanymi kombinacjami wartości osi. Wystąpienia nazwane umożliwiają zachowanie czcionki zmiennej podobnie jak kolekcja czcionek statycznych. Podczas wyliczania elementów IDWriteFontFamily lub IDWriteFontSet, istnieje jeden element dla każdej czcionki statycznej i dla każdego nazwanego wystąpienia czcionki zmiennej.
Algorytm dopasowywania czcionek typograficznego najpierw wybiera potencjalne dopasowania kandydatów na podstawie nazwy rodziny. Jeśli kandydaci dopasowania zawierają czcionki zmiennych, wszystkie kandydatów dopasowania dla tej samej czcionki zmiennej są zwinięte do jednego kandydata dopasowania, w którym każda oś zmiennej ma przypisaną konkretną wartość jak najbliżej żądanej wartości dla tej osi. Jeśli nie ma żądanej wartości dla osi zmiennej, zostanie przypisana wartość domyślna dla tej osi. Kolejność kandydatów do dopasowania jest następnie określana przez porównanie ich wartości osi z żądanymi wartościami osi.
Rozważmy na przykład rodzinę typograficznej Sitka w systemie Windows. Sitka jest rodziną czcionek optycznych, co oznacza, że ma oś "opsz". W systemie Windows 11 Sitka jest implementowany jako dwie zmienne czcionki z następującymi wartościami osi. Należy pamiętać, że osie opsz
i wght
są zmienne, a pozostałe osie nie są zmienne.
Nazwa pliku | "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 |
Załóżmy, że żądane wartości osi są opsz:12 wght:475 wdth:100 ital:0 slnt:0
. Dla każdej czcionki zmiennej tworzymy odwołanie do czcionki zmiennej wystąpienia, w którym każda oś zmiennej ma przypisaną określoną wartość. Mianowicie osie opsz
i wght
są ustawione odpowiednio na 12
i 475
. Daje to następujące pasujące czcionki, z czcionką nie kursywą w pierwszej kolejności, ponieważ jest to lepsze dopasowanie dla osi ital
i 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
W powyższym przykładzie pasujące czcionki są dowolnymi wystąpieniami czcionek zmiennych. Nie ma nazwanego wystąpienia Sitka o wadze 475. Natomiast algorytm dopasowywania w stylu wagi zwraca tylko nazwane wystąpienia.
Kolejność dopasowywania czcionek
Istnieją różne przeciążone metody GetMatchingFonts dla modelu rodziny czcionek w stylu wagi (IDWriteFontFamily::GetMatchingFonts) i model rodziny czcionek typograficznej (IDWriteFontCollection2::GetMatchingFonts). W obu przypadkach dane wyjściowe są listą pasujących czcionek w kolejności malejącej priorytetu na podstawie tego, jak dobrze każda czcionka kandydata pasuje do właściwości wejściowych. W tej sekcji opisano sposób określania priorytetu.
W modelu w stylu wagi parametry wejściowe to waga czcionki (DWRITE_FONT_WEIGHT), rozciągnięcie czcionki (DWRITE_FONT_STRETCH) i styl czcionki (DWRITE_FONT_STYLE). Algorytm znajdowania najlepszego dopasowania został udokumentowany w białej księdze z 2006 r. zatytułowanej "WPF Font Selection Model" Mikhail Leonov i David Brown. Zobacz sekcję "Dopasowywanie twarzy z listy twarzy kandydata". Ten dokument dotyczył programu Windows Presentation Foundation (WPF), ale funkcja DirectWrite później użyła tego samego podejścia.
Algorytm używa pojęcia wektora atrybutu czcionki, który dla danej kombinacji wagi, rozciągnięcia i stylu jest obliczany w następujący sposób:
FontAttributeVector.X = (stretch - 5) * 1100;
FontAttributeVector.Y = style * 700;
FontAttributeVector.Z = (weight - 400) * 5;
Należy pamiętać, że każda współrzędna wektora jest normalna przez odjęcie wartości "normal" dla odpowiedniego atrybutu i pomnożenie przez stałą. Mnożniki rekompensują fakt, że zakresy wartości wejściowych dla wagi, rozciągnięcia i stylu są bardzo różne. W przeciwnym razie waga (100..999) zdominowałaby styl (0,2).
Dla każdego kandydata dopasowania odległość wektora i kropka produktu są obliczane między wektorem atrybutu czcionki kandydata dopasowania a wektorem atrybutu czcionki wejściowej. Porównując dwóch kandydatów do dopasowania, kandydat z mniejszą odległością wektora jest lepszym dopasowaniem. Jeśli odległości są takie same, kandydat z mniejszym produktem kropkowym jest lepszym dopasowaniem. Jeśli produkt kropkowy jest również taki sam, odległości wzdłuż osi X, Y i Z są porównywane w tej kolejności.
Porównanie odległości jest wystarczająco intuicyjne, ale użycie produktu kropkowego jako miary pomocniczej może wymagać pewnego wyjaśnienia. Załóżmy, że waga wejściowa jest średni (600), a dwie wagi kandydatów są czarne (900) i półlekkie (300). Odległość każdej wagi kandydata od wagi wejściowej jest taka sama, ale waga jest w tym samym kierunku od źródła (czyli 400 lub normalny), więc będzie miał mniejszy produkt kropkowy.
Algorytm dopasowywania typograficznego to uogólnianie tego algorytmu dla stylu odciągnięcia wagi. Każda wartość osi jest traktowana jako współrzędna w wektora atrybutu czcionki N-wymiarowej. Dla każdego kandydata dopasowania odległość wektora i kropka produktu są obliczane między wektorem atrybutu czcionki kandydata dopasowania a wektorem atrybutu czcionki wejściowej. Kandydat z mniejszą odległością wektora jest lepszym dopasowaniem. Jeśli odległości są takie same, kandydat z mniejszym produktem kropkowym jest lepszym dopasowaniem. Jeśli produkt kropkowy jest również taki sam, obecność w określonej rodzinie wagi stretch może być używana jako tie-breaker.
Aby obliczyć odległość wektora i kropkę, wektor atrybutu czcionki kandydata dopasowania i wektor atrybutu czcionki wejściowej musi mieć te same osie. W związku z tym każda brakująca wartość osi w obu wektorach jest wypełniana przez zastąpienie wartości standardowej dla tej osi. Współrzędne wektorowe są znormalizowane przez odjęcie standardowej wartości (lub "normalnej") dla odpowiedniej osi i pomnożenie wyniku przez mnożnik specyficzny dla osi. Poniżej przedstawiono mnożniki i standardowe wartości dla każdej osi:
Oś | Mnożnik | Wartość Standardowa |
---|---|---|
"wght" | 5 | 400 |
"wdth" | 55 | 100 |
"ital" | 1400 | 0 |
"slnt" | 35 | 0 |
"opsz" | 1 | 12 |
inny | 1 | 0 |
Mnożniki są zgodne z tymi, które są używane przez algorytm w stylu wagi, ale są skalowane w razie potrzeby. Na przykład normalna szerokość to 100, co odpowiada rozciągnięciu 5. Daje to mnożnik 55 vs. 1100. Starszy atrybut stylu (0..2) spląta kursywę i ukośność, która w modelu typograficznego jest rozłożona na oś "ital" (0..1) i oś "slnt" (-90..90). Wybrane mnożniki tych dwóch osi dają równoważne wyniki starszemu algorytmowi, jeśli zakładamy domyślny 20-stopniowy pochylenie dla czcionki ukośnej.
Wybór czcionki typograficznej i rozmiar optyczny
Aplikacja korzystająca z modelu rodziny czcionek typograficznej może implementować ustalanie rozmiaru optycznego, określając wartość osi opsz
jako parametr wyboru czcionki. Na przykład aplikacja do przetwarzania wyrazów może określić wartość osi opsz
równą rozmiarowi czcionki w punktach. W takim przypadku użytkownik może wybrać opcję "Sitka" jako rodzinę czcionek, a aplikacja automatycznie wybierze wystąpienie Sitka z poprawną wartością osi opsz
. W modelu WWS każdy rozmiar optyczny jest uwidoczniony jako inna nazwa rodziny i zależy od użytkownika, aby wybrać właściwy rozmiar.
Teoretycznie można zaimplementować automatyczne ustalanie rozmiaru optycznego w modelu w stylu wagi, przesłaniając wartość osi opsz
jako oddzielny krok po wybraniu czcionki. Jednak działa to tylko wtedy, gdy pierwsza zgodna czcionka jest czcionką zmiennej ze zmienną opsz
osią. Określanie opsz
jako parametr wyboru czcionki działa równie dobrze w przypadku czcionek statycznych. Na przykład rodzina czcionek Sitka jest implementowana jako czcionki zmiennych w systemie Windows 11, ale jako kolekcja czcionek statycznych w systemie Windows 10. Czcionki statyczne mają różne, nienakładające się zakresy osi opsz
(są one deklarowane jako zakresy dla celów wyboru czcionek, ale nie są to osie zmiennych). Określenie opsz
jako parametru wyboru czcionki umożliwia wybranie poprawnej czcionki statycznej dla rozmiaru optycznego.
Zalety wyboru czcionki typograficznej i problemy ze zgodnością
Model wyboru czcionki typograficznej ma kilka zalet w porównaniu z wcześniejszymi modelami, ale w czystej postaci ma pewne potencjalne problemy ze zgodnością. W tej sekcji opisano zalety i problemy ze zgodnością. W następnej sekcji opisano hybrydowy model wyboru czcionek, który zachowuje zalety podczas łagodzenia problemów ze zgodnością.
Zalety modelu rodziny czcionek typograficznej to:
Czcionki mogą być grupowane w rodziny zgodnie z oczekiwaniami projektanta, a nie podzielone na podskładniki z powodu ograniczeń modelu rodziny czcionek.
Aplikacja może automatycznie wybrać poprawną wartość osi
opsz
na podstawie rozmiaru czcionki, zamiast uwidaczniać użytkownikowi różne rozmiary optyczne jako różne rodziny czcionek.Można wybrać dowolne wystąpienia czcionek zmiennych. Jeśli na przykład czcionka zmiennej obsługuje wagi w zakresie ciągłym 100–900, model typograficzny może wybrać dowolną wagę w tym zakresie. Starsze modele rodziny czcionek mogą wybierać tylko najbliższą wagę spośród nazwanych wystąpień zdefiniowanych przez czcionkę.
Problemy ze zgodnością z modelem wyboru czcionki typograficznej to:
Niektórych starszych czcionek nie można wybrać jednoznacznie przy użyciu tylko wartości nazwy rodziny typograficznej i osi.
Istniejące dokumenty mogą odwoływać się do czcionek według nazwy rodziny WWS lub nazwy rodziny RBIZ. Użytkownicy mogą również oczekiwać używania nazw rodzin WWS i RBIZ. Na przykład dokument może określać "Podpozycję Sitki" (nazwę rodziny WWS) zamiast "Sitka" (nazwa rodziny typograficznej).
Biblioteka lub struktura może przyjąć model rodziny czcionek typograficznej, aby skorzystać z automatycznego określania rozmiaru optycznego, ale nie zapewnia interfejsu API do określania dowolnych wartości osi. Nawet jeśli zostanie udostępniony nowy interfejs API, platforma może wymagać pracy z istniejącymi aplikacjami, które określają tylko parametry wagi, rozciągnięcia i stylu.
Występuje problem ze zgodnością ze starszymi czcionkami, ponieważ pojęcie nazwy rodziny typograficznej poprzedza koncepcję wartości osi czcionek, które zostały wprowadzone wraz z czcionkami zmiennych w programie OpenType 1.8. Przed typem OpenType 1.8 nazwa rodziny typograficznej jedynie wyraziła intencję projektanta, że zestaw czcionek był powiązany, ale bez gwarancji, że te czcionki mogą być programowo zróżnicowane na podstawie ich właściwości. Jako hipotetyczny przykład załóżmy, że wszystkie następujące czcionki mają nazwę rodziny typograficznej "Legacy":
Imię i nazwisko | Rodzina WWS | Ciężar | Rozciągnąć | Styl | Rodzina literówek | wght | wdth | Ital | slnt |
---|---|---|---|---|---|---|---|---|---|
Spuścizna | Spuścizna | 400 | 5 | 0 | Spuścizna | 400 | 100 | 0 | 0 |
Starsza wersja pogrubiona | Spuścizna | 700 | 5 | 0 | Spuścizna | 700 | 100 | 0 | 0 |
Starsza wersja | Spuścizna | 900 | 5 | 0 | Spuścizna | 900 | 100 | 0 | 0 |
Starsza wersja soft | Starsza wersja soft | 400 | 5 | 0 | Spuścizna | 400 | 100 | 0 | 0 |
Starsze, miękkie pogrubienie | Starsza wersja soft | 700 | 5 | 0 | Spuścizna | 700 | 100 | 0 | 0 |
Starsza miękka | Starsza wersja soft | 900 | 5 | 0 | Spuścizna | 900 | 100 | 0 | 0 |
Rodzina typografii "Legacy" ma trzy wagi, a każda waga ma regularne i "Miękkie" warianty. Gdyby były to nowe czcionki, można je zaimplementować jako deklarowanie osi projektowej SOFT. Jednak te czcionki poprzedzają typ OpenType 1.8, więc ich jedynymi osiami projektowymi są te pochodzące z wagi, rozciągnięcia i stylu. Dla każdej wagi ta rodzina czcionek ma dwie czcionki z identycznymi wartościami osi, więc nie można jednoznacznie wybrać czcionki w tej rodzinie przy użyciu samych wartości osi.
Algorytm wyboru czcionki hybrydowej
Interfejsy API wyboru czcionek opisane w następnej sekcji używają hybrydowego algorytmu wyboru czcionek, który zachowuje zalety wyboru czcionki typograficznej przy jednoczesnym łagodzeniu problemów ze zgodnością.
Wybór czcionki hybrydowej zapewnia mostek ze starszych modeli rodziny czcionek, umożliwiając mapowanie wartości czcionki na odpowiednie wartości osi czcionki, rozciągnięcia czcionki i stylu czcionki. Pomaga to rozwiązać problemy ze zgodnością dokumentów i aplikacji.
Ponadto algorytm wyboru czcionki hybrydowej umożliwia określonej nazwy rodziny jako nazwę rodziny typograficznej, nazwę rodziny w stylu wagi, nazwę rodziny GDI/RBIZ lub pełną nazwę czcionki. Dopasowanie odbywa się na jeden z następujących sposobów, w kolejności malejącej priorytetu:
Nazwa pasuje do rodziny typograficznej (na przykład Sitka). Dopasowywanie występuje w rodzinie typograficznej i są używane wszystkie żądane wartości osi. Jeśli nazwa jest również zgodna z podfamily WWS (czyli jedną mniejszą niż rodzina typograficzne), wówczas członkostwo w podfamilinie WWS jest używane jako wyłącznik.
Nazwa jest zgodna z rodziną WWS (na przykład Sitka Text). Dopasowanie występuje w rodzinie WWS i żądane wartości osi inne niż "wght", "wdth", "ital" i "slnt" są ignorowane.
Nazwa jest zgodna z rodziną GDI (na przykład Bahnschrift Condensed). Dopasowanie występuje w rodzinie RBIZ i żądane wartości osi inne niż "wght", "ital" i "slnt" są ignorowane.
Nazwa jest zgodna z pełną nazwą (na przykład Bahnschrift Bold Condensed). Zwracana jest czcionka zgodna z pełną nazwą. Żądane wartości osi są ignorowane. Dopasowywanie według pełnej nazwy czcionki jest dozwolone, ponieważ interfejs GDI go obsługuje.
W poprzedniej sekcji opisano niejednoznaczną rodzinę typograficzną o nazwie "Legacy". Algorytm hybrydowy umożliwia uniknięcie niejednoznaczności przez określenie nazwy rodziny "Starsza wersja" lub "Starsza wersja soft". Jeśli określono wartość "Starsza wersja soft", nie ma wątpliwości, ponieważ dopasowanie występuje tylko w rodzinie usług WWS. Jeśli "Starsza wersja" jest spekulowana, wszystkie czcionki w rodzinie typograficznej są uważane za kandydatów do dopasowania, ale niejednoznaczność jest unikana przy użyciu członkostwa w rodzinie "Legacy" WWS jako tie-breaker.
Załóżmy, że dokument określa nazwę rodziny i wagę, rozciągnij i parametry stylu, ale nie ma wartości osi. Aplikacja może najpierw przekonwertować wagę, rozciągnięcie, styl i rozmiar czcionki na wartości osi, wywołując IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues. Następnie aplikacja może przekazać zarówno wartości nazwy rodziny, jak i osi do IDWriteFontSet4::GetMatchingFonts. GetMatchingFonts zwraca listę pasujących czcionek w kolejności priorytetu, a wynik jest odpowiedni, czy określona nazwa rodziny jest nazwą rodziny typograficznej, nazwą rodziny typu weight-stretch, nazwą rodziny RBIZ lub pełną nazwą. Jeśli określona rodzina ma oś "opsz", odpowiedni rozmiar optyczny jest automatycznie wybierany na podstawie rozmiaru czcionki.
Załóżmy, że dokument określa wagę, rozciąganie i styl, a również określa wartości osi. W takim przypadku jawne wartości osi można również przekazać do IDWriteFontSet4::ConvertWeightStretChStyleToFontAxisValues, a wartości osi pochodnej zwrócone przez metodę będą zawierać tylko osie czcionek, które nie zostały określone jawnie. W związku z tym wartości osi określone jawnie przez dokument (lub aplikację) mają pierwszeństwo przed wartościami osi pochodzącymi z wagi, rozciągnięcia, stylu i rozmiaru czcionki.
Interfejsy API wyboru czcionki hybrydowej
Model wyboru czcionki hybrydowej jest implementowany przez następujące metody IDWriteFontSet4:
Metoda IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues metoda konwertuje rozmiar czcionki, wagę, stretch i parametry stylu na odpowiednie wartości osi. Wszystkie jawne wartości osi przekazywane przez klienta są wykluczone z wartości osi pochodnej.
Metoda IDWriteFontSet4::GetMatchingFonts zwraca priorytetową listę pasujących czcionek przy użyciu nazwy rodziny i tablicy wartości osi. Jak opisano powyżej, parametr nazwy rodziny może być nazwą rodziny typograficznej, nazwą rodziny WWS, nazwą rodziny RBIZ lub pełną nazwą. (Pełna nazwa identyfikuje określony styl czcionki, taki jak "Arial Bold Italic". GetMatchingFonts obsługuje dopasowywanie według pełnej nazwy dla większej współmaptibiltii z użyciem interfejsu GDI, co pozwala również na to).
Następujące inne interfejsy API DirectWrite używają również algorytmu wyboru czcionek hybrydowych:
IDWriteTextLayout, jeśli model rodziny czcionek typograficznej jest włączony, wywołując IDWriteTextLayout4::SetFontAxisValues lub IDWriteTextLayout4::SetAutomaticFontAxes
Przykłady kodu interfejsów API wyboru czcionek w użyciu
W tej sekcji przedstawiono kompletną aplikację konsolową, która demonstruje IDWriteFontSet4::GetMatchingFonts i IDWriteFontSet4::ConvertWeightStretStyleToFontAxisValues metod. Najpierw uwzględnijmy niektóre nagłówki:
#include <dwrite_core.h>
#include <wil/com.h>
#include <iostream>
#include <string>
#include <vector>
Metoda IDWriteFontSet4::GetMatchingFonts zwraca listę czcionek zgodnie z określoną nazwą rodziny i wartościami osi. Następująca funkcja MatchAxisValues zwraca parametry IDWriteFontSet4::GetMatchingFonts oraz listę pasujących czcionek w zwróconym zestawie czcionek.
// 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;
}
Aplikacja może mieć parametry wagi, rozciągnięcia i stylu zamiast (lub oprócz) wartości osi. Na przykład aplikacja może wymagać pracy z dokumentami, które odwołują się do czcionek przy użyciu parametrów RBIZ lub weight-stretch-style. Nawet jeśli aplikacja dodaje obsługę określania dowolnych wartości osi, może być konieczne również obsługę starszych parametrów. W tym celu aplikacja może wywołać IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues przed wywołaniem IDWriteFontSet4::GetMatchingFonts.
Następująca funkcja MatchFont przyjmuje parametry wagi, rozciągnięcia, stylu i rozmiaru czcionki oprócz wartości osi. Przekazuje te parametry do IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues metody do wartości osi pochodnej obliczeniowej, które są dołączane do wartości osi wejściowej. Przekazuje ona połączone wartości osi do powyższej funkcji MatchAxisValues.
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);
}
Poniższa funkcja demonstruje wyniki wywoływania powyższej funkcji MatchFont z przykładowymi danymi wejściowymi:
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);
}
Poniżej przedstawiono dane wyjściowe powyższej funkcji TestFontSelection:
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
Poniżej przedstawiono implementacje przeciążonych operatorów zadeklarowanych powyżej. Są one używane przez MatchAxisValuesValues do zapisywania wartości osi wejściowej i wynikowych odwołań do twarzy czcionki:
// 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;
}
Aby zaokrąglić przykład, poniżej przedstawiono funkcje analizowania wiersza polecenia i głównej funkcji:
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());
}
}