Aracılığıyla paylaş


Yazı tipi seçimi

IDWriteFontSet4 arabirimi, yazı tipi kümesinden yazı tipi seçme yöntemlerini kullanıma sunar. Bu yöntemler, mevcut uygulamalar, belgeler ve yazı tipleri ile uyumluluğu korurken tipografik yazı tipi ailesi modeline geçiş yapmayı mümkün hale getirir.

Yazı tipi seçimi (bazen yazı tipi eşleştirme veya yazı tipi eşleme olarak adlandırılır), uygulamanız tarafından geçirilen giriş parametreleriyle en iyi eşleşen kullanılabilir yazı tiplerini seçme işlemidir. Giriş parametreleri bazen topluca mantıksal yazı tipi olarak adlandırılır. Mantıksal yazı tipi, bir yazı tipi aile adını ve aile içindeki belirli bir yazı tipini gösteren diğer öznitelikleri içerir. Yazı tipi seçim algoritması, mantıksal yazı tipini ("istediğiniz yazı tipi") kullanılabilir bir fiziksel yazı tipiyle ("sahip olduğunuz bir yazı tipi") eşleştirir.

Yazı tipi ailesi, ortak bir tasarımı paylaşan ancak ağırlık gibi özniteliklerde farklılık gösterebilen adlandırılmış bir yazı tipi grubudur. Yazı tipi ailesi modeli, bir aile içindeki yazı tiplerini ayırt etmek için hangi özniteliklerin kullanılabileceğini tanımlar. Yeni tipografik yazı tipi ailesi modeli , Windows'ta kullanılan önceki iki yazı tipi ailesi modeline göre birçok avantaja sahiptir. Ancak yazı tipi ailesi modellerini değiştirmek karışıklık ve uyumluluk sorunları için fırsatlar yaratır. IDWriteFontSet4 arabirimi tarafından kullanıma sunulan yöntemler, uyumluluk sorunlarını giderirken tipografik yazı tipi ailesi modelinin avantajlarını sunan karma bir yaklaşım uygular.

Bu konu başlığında, eski yazı tipi ailesi modelleri tipografik yazı tipi ailesi modeliyle karşılaştırılmaktadır; yazı tipi ailesi modellerini değiştirerek ortaya konan uyumluluk güçlüklerini açıklar; ve son olarak [IDWriteFontSet4](/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontset4) yöntemlerini kullanarak bu zorlukların nasıl üstesinden gelinebileceğini açıklar.

RBIZ yazı tipi ailesi modeli

GDI uygulama ekosisteminde kullanılan de facto yazı tipi ailesi modeli bazen "dört yazı tipi modeli" veya "RBIZ" modeli olarak adlandırılır. Bu modeldeki her yazı tipi ailesinin genellikle en çok dört yazı tipi vardır. "RBIZ" etiketi, bazı yazı tipi dosyaları için kullanılan adlandırma kuralından gelir, örneğin:

Dosya Adı Yazı Tipi Stili
verdana.ttf Düzenli
verdanab.ttf Kalın
verdanai.ttf İtalik
verdanaz.ttf Kalın İtalik

GDI ile, yazı tipini seçmek için kullanılan giriş parametreleri, aile adı (), ağırlık () ve italik (lfFaceNamelfWeightlfItalic) alanları içeren LOGFONT yapısı tarafından tanımlanır. Alan lfItalic DOĞRU veya YANLIŞ'tır. GDI, alanın FW_THIN (100) ile FW_BLACK (900) aralığında herhangi bir değer olmasını sağlarlfWeight, ancak geçmiş nedenlerle yazı tipleri aynı GDI yazı tipi ailesinde ikiden fazla kalınlık olmayacak şekilde uzun süredir tasarlanmıştır.

Erken zamanlardan itibaren popüler uygulama kullanıcı arabirimleri italik bir düğme (italik açmak ve kapatmak için) ve kalın bir düğme (normal ve kalın ağırlıklar arasında geçiş yapmak için) içeriyordu. Bir ailedeki yazı tiplerini seçmek için bu iki düğmenin kullanılması RBIZ modelini kabul eder. Bu nedenle, GDI ikiden fazla ağırlığı desteklese de, uygulama uyumluluğu yazı tipi geliştiricilerin GDI aile adını (OpenType ad kimliği 1) RBIZ modeliyle tutarlı bir şekilde ayarlamasına neden oldu.

Örneğin, Arial yazı tipi ailesine daha ağır bir "Siyah" ağırlık eklemek istediğinizi varsayalım. Mantıksal olarak, bu yazı tipi Arial ailesinin bir parçasıdır, bu nedenle "Arial" ve lfWeightFW_BLACK olarak ayarlayarak lfFaceName bu yazı tipini seçmeniz beklenebilir. Ancak, bir uygulama kullanıcısına iki durumlu kalın düğmeyi kullanarak üç ağırlık arasında seçim yapma imkanı yoktur. Çözüm, yeni yazı tipine farklı bir aile adı vermekti, böylece kullanıcı yazı tipi aileleri listesinden "Arial Black" öğesini seçerek bunu seçebilir. Benzer şekilde, yalnızca kalın ve italik düğmeleri kullanarak aynı yazı tipi ailesindeki farklı genişlikler arasından seçim yapmanıza imkan yoktur, bu nedenle Arial'in dar sürümleri RBIZ modelinde farklı aile adlarına sahiptir. Bu nedenle RBIZ modelinde "Arial", "Arial Black" ve "Arial Narrow" yazı tipi aileleri var, ancak bunların hepsi tek bir aileye ait olsa da.

Bu örneklerden, bir yazı tipi ailesi modelinin sınırlamalarının yazı tiplerinin ailelere göre gruplandırılmış şeklini nasıl etkileyebileceğini görebilirsiniz. Yazı tipi aileleri ada göre tanımlandığından bu, kullandığınız yazı tipi aile modeline bağlı olarak aynı yazı tipinin farklı aile adlarına sahip olabileceği anlamına gelir.

DirectWrite, RBIZ yazı tipi ailesi modelini doğrudan desteklemez, ancak IDWriteGdiInterop::CreateFontFromLOGFONT ve IDWriteGdiInterop::ConvertFontToLOGFONT gibi RBIZ modeline ve modelinden dönüştürme yöntemleri sağlar. Ayrıca , IDWriteFont::GetInformationalStrings yöntemini çağırıp DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES belirterek bir yazı tipinin RBIZ ailesi adını da alabilirsiniz.

Kalınlık-esnetme stili yazı tipi ailesi modeli

Kalınlık-esnetme stili yazı tipi ailesi modeli, directWrite tarafından tipografik yazı tipi ailesi modeli kullanılmadan önce kullanılan özgün yazı tipi ailesi modelidir. Ağırlık genişliği eğimi (WWS) olarak da bilinir. WWS modelinde, aynı ailedeki yazı tipleri üç özellik tarafından sağır edilebilir: ağırlık (DWRITE_FONT_WEIGHT), esnetme (DWRITE_FONT_STRETCH) ve stil (DWRITE_FONT_STYLE).

WWS modeli, RBIZ modeline göre iki şekilde daha esnektir. İlk olarak, aynı ailedeki yazı tipleri esnetme (veya genişlik) ve kalınlık ve stil (normal, italik veya eğik) ile ayırt edilebilir. İkincisi, aynı ailede ikiden fazla ağırlık olabilir. Bu esneklik, Arial'in tüm varyantlarının aynı WWS ailesine dahil edilmesine izin vermek için yeterlidir. Aşağıdaki tabloda, RBIZ ve WWS yazı tipi özellikleri, arial yazı tipleri seçimi için karşılaştırır:

Adı ve Soyadı RBIZ Aile Adı lfAğırlık İtalik WWS FamilyName Ağırlık Uzatmak Stil
Arial Arial 400 0 Arial 400 5 0
Arial Kalın Arial 700 0 Arial 700 5 0
Arial Black Arial Black 900 0 Arial 900 5 0
Arial Dar Arial Dar 400 0 Arial 400 3 0
Arial Dar Kalın Arial Dar 700 0 Arial 700 3 0

Gördüğünüz gibi , "Arial Narrow" "Arial" ile aynı lfWeight ve lfItalic değerlere sahiptir, bu nedenle belirsizliği önlemek için farklı bir RBIZ ailesi adına sahiptir. "Arial Black", "Arial" ailesinde ikiden fazla ağırlıktan kaçınmak için farklı bir RBIZ aile adına sahiptir. Buna karşılık, bu yazı tiplerinin tümü aynı kalınlık-esnetme stili ailesinde yer alır.

Bununla birlikte, ağırlık-esnetme stilindeki model açık uçlu değildir. İki yazı tipi aynı kalınlık, esnetme ve stile sahipse ancak başka bir şekilde (örneğin optik boyut) farklıysa, aynı WWS yazı tipi ailesine eklenemez. Bu bizi tipografik yazı tipi ailesi modeline getirir.

Tipografik yazı tipi ailesi modeli

Öncüllerinden farklı olarak, tipografik yazı tipi ailesi modeli açık uçludur . Yazı tipi ailesi içinde herhangi bir sayıda çeşitleme eksenini destekler.

Yazı tipi seçim parametrelerini bir tasarım alanında koordinat olarak düşünüyorsanız, ağırlık-esnetme stili model eksen olarak ağırlık, esnetme ve stil içeren üç boyutlu bir koordinat sistemi tanımlar. WWS ailesindeki her yazı tipinin, bu üç eksen boyunca koordinatlarıyla tanımlanan benzersiz bir konumu olmalıdır. Yazı tipi seçmek için bir WWS ailesi adı ve kalınlık, esnetme ve stil parametreleri belirtirsiniz.

Buna karşılık, tipografik yazı tipi ailesi modelinin N boyutlu bir tasarım alanı vardır. Yazı tipi tasarımcısı, her biri dört karakterli eksen etiketiyle tanımlanan herhangi bir sayıda tasarım ekseni tanımlayabilir. Belirli bir yazı tipinin N boyutlu tasarım alanında konumu, her eksen değerinin bir eksen etiketi ve kayan nokta değeri oluşturduğu eksen değerleri dizisi tarafından tanımlanır. Yazı tipi seçmek için, bir tipografik aile adı ve eksen değerleri dizisi (DWRITE_FONT_AXIS_VALUE yapıları) belirtirsiniz.

Yazı tipi eksenlerinin sayısı açık uçlu olsa da, standart anlamları olan birkaç kayıtlı eksen vardır ve kalınlık, esnetme ve stil değerleri kayıtlı eksen değerleriyle eşlenebilir. DWRITE_FONT_WEIGHT bir "wght" (DWRITE_FONT_AXIS_TAG_WEIGHT) eksen değerine eşlenebilir. DWRITE_FONT_STRETCH bir "wdth" (DWRITE_FONT_AXIS_TAG_WIDTH) eksen değerine eşlenebilir. DWRITE_FONT_STYLE "ital" ve "slnt" (DWRITE_FONT_AXIS_TAG_ITALIC ve DWRITE_FONT_AXIS_TAG_SLANT) eksen değerlerinin bir bileşimine eşlenebilir.

Bir diğer kayıtlı eksen de "opsz" (DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE). Sitka gibi optik bir yazı tipi ailesi, "opsz" ekseni boyunca farklılık gösteren yazı tipleri içerir, yani farklı nokta boyutlarında kullanılmak üzere tasarlanmıştır. WWS yazı tipi ailesi modelinin optik boyut ekseni yoktur, bu nedenle Sitka yazı tipi ailesinin birden çok WWS yazı tipi ailesine bölünmesi gerekir: "Sitka Small", "Sitka Text", "Sitka Subheading", vb. Her WWS yazı tipi ailesi farklı bir optik boyuta karşılık gelir ve belirli bir yazı tipi boyutu için doğru WWS ailesi adını belirtmek kullanıcıya bırakılır. Tipografik yazı tipi ailesi modeliyle, kullanıcı yalnızca "Sitka" seçebilir ve uygulama yazı tipi boyutuna göre otomatik olarak "opsz" ekseni değerini ayarlayabilir.

Tipografik yazı tipi seçimi ve değişken yazı tipleri

Çeşitleme eksenleri kavramı genellikle değişken yazı tipleriyle ilişkilendirilir, ancak statik yazı tipleri için de geçerlidir. OpenType STAT (stil öznitelikleri) tablosu, bir yazı tipinin hangi tasarım eksenlerine sahip olduğunu ve bu eksenlerin değerlerini bildirir. Bu tablo değişken yazı tipleri için gereklidir, ancak statik yazı tipleriyle de ilgilidir.

DirectWrite API'sinde STAT tablosunda olmasalar veya STAT tablosu olmasalar bile, her yazı tipi için "wght", "wdth", "ital" ve "slnt" eksen değerleri gösterilir. Bu değerler mümkünse STAT tablosundan türetilir. Aksi takdirde, bunlar yazı tipi kalınlığı, yazı tipi esnetme ve yazı tipi stilinden türetilir.

Yazı tipi eksenleri değişken veya değişken olmayan olabilir. Statik yazı tipi yalnızca değişken olmayan eksenlere sahipken, değişken yazı tipinde her ikisi de olabilir. Değişken yazı tipi kullanmak için, tüm değişken eksenlerinin belirli değerlere bağlı olduğu bir değişken yazı tipi örneği oluşturmanız gerekir. IDWriteFontFace arabirimi statik yazı tipini veya değişken yazı tipinin belirli bir örneğini temsil eder. Belirtilen eksen değerleriyle değişken yazı tipinin rastgele bir örneğini oluşturmak mümkündür. Buna ek olarak, değişken bir yazı tipi, stat tablosundaki adlandırılmış örnekleri, eksen değerlerinin önceden tanımlanmış birleşimleriyle bildirebilir. Adlandırılmış örnekler, değişken yazı tipinin statik yazı tipleri koleksiyonu gibi davranmasını sağlar. IDWriteFontFamily veya IDWriteFontSet öğelerini numaralandırdığınızda, her statik yazı tipi ve adlandırılmış değişken yazı tipi örneği için bir öğe vardır.

Tipografik yazı tipi eşleştirme algoritması önce aile adına göre olası eşleşme adaylarını seçer. Eşleşme adayları değişken yazı tipleri içerirse, aynı değişken yazı tipi için tüm eşleşme adayları, her değişken eksenine söz konusu eksen için istenen değere mümkün olduğunca yakın bir değer atandığı bir eşleşme adayına daraltılır. Değişken ekseni için istenen değer yoksa, bu eksen için varsayılan değer atanır. Eşleşme adaylarının sırası daha sonra eksen değerleri istenen eksen değerleriyle karşılaştırılarak belirlenir.

Örneğin, Windows'ta Sitka tipografik ailesini düşünün. Sitka optik bir yazı tipi ailesidir, yani "opsz" eksenine sahiptir. Windows 11'de Sitka, aşağıdaki eksen değerlerine sahip iki değişken yazı tipi olarak uygulanır. opsz ve wght eksenlerinin değişken, diğer eksenlerin ise değişken olmadığını unutmayın.

Dosya Adı "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

İstenen eksen değerlerinin olduğunu opsz:12 wght:475 wdth:100 ital:0 slnt:0varsayalım. Her değişken yazı tipi için, her değişken eksenine belirli bir değer atandığı değişken yazı tipi örneğine başvuru oluştururuz. Yani ve opszwght eksenleri sırasıyla ve 475olarak ayarlanır12. Bu, ve slnt eksenleri için ital daha iyi bir eşleşme olduğundan italik olmayan yazı tipi ilk sırada yer alan aşağıdaki eşleşen yazı tiplerini verir:

SitkaVF.ttf opsz:12 wght:475 wdth:100 ital:0 slnt0
SitkaVF-Italic.ttf opsz:12 wght:475 wdth:100 ital:1 slnt:-12

Yukarıdaki örnekte eşleşen yazı tipleri rastgele değişken yazı tipi örnekleridir. Sitka'nın 475 kilolu adlandırılmış bir örneği yoktur. Buna karşılık, ağırlık-esnetme stili eşleştirme algoritması yalnızca adlandırılmış örnekleri döndürür.

Yazı tipi eşleştirme sırası

Weight-stretch-style yazı tipi ailesi modeli (IDWriteFontFamily::GetMatchingFonts) ve tipografik yazı tipi ailesi modeli (IDWriteFontCollection2::GetMatchingFonts) için farklı aşırı yüklenmiş GetMatchingFonts yöntemleri vardır. Her iki durumda da çıkış, her aday yazı tipinin giriş özellikleriyle ne kadar iyi eşleştiklerine bağlı olarak azalan öncelik sırasına göre eşleşen yazı tiplerinin listesidir. Bu bölümde önceliğin nasıl belirlendiği açıklanmaktadır.

Kalınlık-esnetme stili modelinde giriş parametreleri yazı tipi kalınlığı (DWRITE_FONT_WEIGHT), yazı tipi esnetme (DWRITE_FONT_STRETCH) ve yazı tipi stilidir (DWRITE_FONT_STYLE). En iyi eşleşmeyi bulma algoritması, Mikhail Leonov ve David Brown tarafından "WPF Yazı Tipi Seçim Modeli" başlıklı 2006 teknik incelemesinde belgelenmiştir. "Aday yüz listesinden yüz eşleştirme" bölümüne bakın. Bu makale Windows Presentation Foundation (WPF) hakkındaydı, ancak DirectWrite daha sonra aynı yaklaşımı kullandı.

Algoritma, belirli bir ağırlık, esnetme ve stil bileşimi için aşağıdaki gibi hesaplanan yazı tipi öznitelik vektörünün gösterimini kullanır:

FontAttributeVector.X = (stretch - 5) * 1100;
FontAttributeVector.Y = style * 700;
FontAttributeVector.Z = (weight - 400) * 5;

Her vektör koordinatı, karşılık gelen öznitelik için "normal" değeri çıkarılarak ve bir sabitle çarpılarak normalleştirilir. Çarpanlar ağırlık, esnetme ve stil için giriş değerleri aralıklarının çok farklı olduğu gerçeğini telafi eder. Aksi takdirde, ağırlık (100..999) stil üzerinde baskın olur (0..2).

Her eşleşme adayı için, eşleşme adayının yazı tipi öznitelik vektör ve giriş yazı tipi öznitelik vektörleri arasında bir vektör uzaklığı ve noktalı bir ürün hesaplanır. İki eşleşme adayı karşılaştırılırken, vektör uzaklığı daha küçük olan aday daha iyi bir eşleşmedir. Mesafeler aynıysa, daha küçük nokta ürünü olan aday daha iyi bir eşleşmedir. Noktalı ürün de aynıysa X, Y ve Z eksenleri boyunca olan mesafeler bu sırada karşılaştırılır.

Mesafeleri karşılaştırmak yeterince sezgiseldir, ancak nokta ürünü ikincil ölçü olarak kullanmak biraz açıklama gerektirebilir. Giriş ağırlığının yarıbol (600) olduğunu ve iki aday ağırlığının siyah (900) ve yarı ışık (300) olduğunu varsayalım. Her aday ağırlığının giriş ağırlığına olan uzaklığı aynıdır, ancak siyah ağırlık çıkış noktasıyla aynı yöndedir (yani, 400 veya normal), bu nedenle daha küçük bir nokta ürününe sahip olacaktır.

Tipografik eşleştirme algoritması, ağırlık-esnetme stili için genelleştirmedir. Her eksen değeri, N boyutlu yazı tipi öznitelik vektörlerinde bir koordinat olarak değerlendirilir. Her eşleşme adayı için, eşleşme adayının yazı tipi öznitelik vektör ve giriş yazı tipi öznitelik vektörleri arasında bir vektör uzaklığı ve noktalı bir ürün hesaplanır. Vektör uzaklığı daha küçük olan aday daha iyi bir eşleşmedir. Mesafeler aynıysa, daha küçük nokta ürünü olan aday daha iyi bir eşleşmedir. Noktalı ürün de aynıysa, belirli bir ağırlık-esnetme stili ailedeki varlık, kravat kesici olarak kullanılabilir.

Vektör uzaklığı ve nokta ürününü hesaplamak için, adayın yazı tipi öznitelik vektöründe ve giriş yazı tipi öznitelik vektöründe aynı eksenler olmalıdır. Bu nedenle, her iki vektördeki eksik eksen değerleri, bu eksen için standart değer değiştirilerek doldurulur. Vektör koordinatları, karşılık gelen eksen için standart (veya "normal") değeri çıkarılarak ve sonucu eksene özgü bir çarpanla çarparak normalleştirilir. Her eksen için çarpanlar ve standart değerler şunlardır:

Eksen Çarpan Standart Değer
"wght" 5 400
"wdth" 55 100
"ital" 1400 0
"slnt" 35 0
"opsz" 1 12
diğer 1 0

Çarpanlar, ağırlık-esnetme stili algoritma tarafından kullanılanlarla tutarlıdır, ancak gerektiğinde ölçeklendirilir. Örneğin, normal genişlik 100'dür ve esnetme 5'e eşdeğerdir. Bu, 55 ile 1100 arasında bir çarpan verir. Eski stil özniteliği (0..2), tipografik modelde bir "ital" eksenine (0..1) ve "slnt" eksenine (-90...90) ayrıştırılan italik ve eğikliği dolandırır. Bu iki eksen için seçilen çarpanlar, eğik yazı tipleri için varsayılan 20 derece eğimi varsayarsak eski algoritmaya eşdeğer sonuçlar verir.

Tipografik yazı tipi seçimi ve optik boyut

Tipografik yazı tipi ailesi modelini kullanan bir opsz uygulama, bir eksen değerini yazı tipi seçim parametresi olarak belirterek optik boyutlandırma uygulayabilir. Örneğin, sözcük işleme uygulaması nokta cinsinden yazı tipi boyutuna eşit bir opsz eksen değeri belirtebilir. Bu durumda, bir kullanıcı yazı tipi ailesi olarak "Sitka" öğesini seçebilir ve uygulama otomatik olarak doğru opsz eksen değerine sahip bir Sitka örneği seçer. WWS modeli altında her optik boyut farklı bir aile adı olarak görünür ve doğru olanı seçmek kullanıcıya aittir.

Teoride, eksen değerini yazı tipi seçiminden sonra ayrı bir adım olarak geçersiz kılarak opsz ağırlık-esnetme stili modeli altında otomatik optik boyutlandırma gerçekleştirebilir. Ancak, bu yalnızca ilk eşleşen yazı tipi değişken ekseni olan bir değişken opsz yazı tipiyse çalışır. Yazı tipi seçim parametresi olarak belirtme opsz , statik yazı tipleri için de aynı şekilde çalışır. Örneğin, Sitka yazı tipi ailesi Windows 11'de değişken yazı tipleri olarak, ancak Windows 10'da statik yazı tipleri koleksiyonu olarak uygulanır. Statik yazı tiplerinin farklı, çakışmayan opsz eksen aralıkları vardır (bunlar yazı tipi seçimi için aralıklar olarak bildirilir, ancak değişken eksenler değildir). opsz Yazı tipi seçimi parametresi olarak belirtmek, optik boyutun seçilmesi için doğru statik yazı tipini etkinleştirir.

Tipografik yazı tipi seçimi avantajları ve uyumluluk sorunları

Tipografik yazı tipi seçim modelinin önceki modellere göre çeşitli avantajları vardır, ancak saf haliyle bazı olası uyumluluk sorunları vardır. Bu bölümde avantajları ve uyumluluk sorunları açıklanmaktadır. Sonraki bölümde, uyumluluk sorunlarını giderirken avantajları koruyan karma yazı tipi seçim modeli açıklanmaktadır.

Tipografik yazı tipi ailesi modelinin avantajları şunlardır:

  • Yazı tipleri, yazı tipi ailesi modelinin sınırlamaları nedeniyle alt ailelere bölünmek yerine tasarımcı tarafından amaçlandığı gibi aileler halinde gruplandırılabilir.

  • Bir uygulama, kullanıcıya farklı optik boyutları farklı yazı tipi aileleri olarak görüntülemek yerine yazı tipi boyutuna göre doğru opsz eksen değerini otomatik olarak seçebilir.

  • Değişken yazı tiplerinin rastgele örnekleri seçilebilir. Örneğin, değişken yazı tipi 100-900 aralığındaki ağırlıkları destekliyorsa, tipografik model bu aralıktaki herhangi bir ağırlığı seçebilir. Eski yazı tipi ailesi modelleri, yazı tipi tarafından tanımlanan adlandırılmış örnekler arasından yalnızca en yakın ağırlığı seçebilir.

Tipografik yazı tipi seçim modeliyle ilgili uyumluluk sorunları şunlardır:

  • Bazı eski yazı tipleri yalnızca tipografik aile adı ve eksen değerleri kullanılarak kesin olarak seçilemez.

  • Mevcut belgeler WWS aile adına veya RBIZ aile adına göre yazı tiplerine başvurabilir. Kullanıcılar WWS ve RBIZ aile adlarını da kullanmayı bekleyebilir. Örneğin, bir belge "Sitka" (tipografik aile adı) yerine "Sitka Alt Başlığı" (WWS ailesi adı) belirtebilir.

  • Kitaplık veya çerçeve, otomatik optik boyutlandırmadan yararlanmak için tipografik yazı tipi ailesi modelini benimseyebilir, ancak rastgele eksen değerlerini belirlemeye yönelik bir API sağlamayabilir. Yeni bir API sağlansa bile çerçevenin yalnızca ağırlık, esnetme ve stil parametrelerini belirten mevcut uygulamalarla çalışması gerekebilir.

Daha eski yazı tipleri ile uyumluluk sorunu, tipografik aile adı kavramının, OpenType 1.8'de değişken yazı tipleri ile birlikte sunulan yazı tipi ekseni değerleri kavramını önyazdığı için ortaya çıkar. OpenType 1.8'in öncesinde, tipografik aile adı yalnızca tasarımcının bir yazı tipi kümesinin ilişkili olduğu amacını ifade etti, ancak bu yazı tiplerinin özelliklerine göre program aracılığıyla ayırt edilebileceği garanti edilemedi. Varsayımsal bir örnek olarak, aşağıdaki yazı tiplerinin tümünün "Eski" tipografik aile adına sahip olduğunu varsayalım:

Adı ve Soyadı WWS Ailesi Ağırlık Uzatmak Stil Typo Ailesi wght wdth Ital slnt
Miras Miras 400 5 0 Miras 400 100 0 0
Eski Kalın Miras 700 5 0 Miras 700 100 0 0
Eski Siyah Miras 900 5 0 Miras 900 100 0 0
Eski Geçici Yazılım Eski Geçici Yazılım 400 5 0 Miras 400 100 0 0
Eski Yumuşak Kalın Eski Geçici Yazılım 700 5 0 Miras 700 100 0 0
Eski Yumuşak Siyah Eski Geçici Yazılım 900 5 0 Miras 900 100 0 0

"Eski" tipografik ailesinin üç ağırlığı vardır ve her ağırlığın normal ve "Yumuşak" çeşitleri vardır. Bunlar yeni yazı tipleri olsaydı, SOFT tasarım ekseni bildirilirken uygulanabilirdi. Ancak, bu yazı tipleri OpenType 1.8'den önce kaydedilir, bu nedenle tek tasarım eksenleri ağırlık, esnetme ve stilden türetilenlerdir. Her kalınlık için, bu yazı tipi ailesinin aynı eksen değerlerine sahip iki yazı tipi vardır, bu nedenle yalnızca eksen değerlerini kullanarak bu ailedeki bir yazı tipini kesin olarak seçmek mümkün değildir.

Karma yazı tipi seçim algoritması

Sonraki bölümde açıklanan yazı tipi seçimi API'leri, uyumluluk sorunlarını giderirken tipografik yazı tipi seçiminin avantajlarını koruyan bir karma yazı tipi seçim algoritması kullanır.

Karma yazı tipi seçimi, yazı tipi kalınlığı, yazı tipi esnetme ve yazı tipi stili değerlerinin ilgili yazı tipi ekseni değerlerine eşlenmesine olanak tanıyarak eski yazı tipi ailesi modellerinden bir köprü sağlar. Bu, belge ve uygulama uyumluluğu sorunlarını gidermeye yardımcı olur.

Ayrıca karma yazı tipi seçim algoritması, belirtilen aile adının tipografik bir aile adı, kalınlık-esnetme stilinde bir aile adı, GDI/RBIZ aile adı veya tam yazı tipi adı olmasını sağlar. Eşleştirme aşağıdaki yollardan birinde, azalan öncelik sırasına göre gerçekleşir:

  1. Ad bir tipografik aileyle (örneğin, Sitka) eşleşir. Eşleştirme, tipografik aile içinde gerçekleşir ve istenen tüm eksen değerleri kullanılır. Ad aynı zamanda bir WWS alt ailesi ile eşleşiyorsa (yani, tipografik aileden küçük bir tane) WWS alt ailesindeki üyelik, kravat kesici olarak kullanılır.

  2. Ad bir WWS ailesi ile eşleşir (örneğin, Sitka Metni). Eşleştirme WWS ailesi içinde gerçekleşir ve "wght", "wdth", "ital" ve "slnt" dışındaki istenen eksen değerleri yoksayılır.

  3. Ad bir GDI ailesi ile eşleşir (örneğin, Bahnschrift Condensed). Eşleştirme RBIZ ailesi içinde gerçekleşir ve "wght", "ital" ve "slnt" dışındaki istenen eksen değerleri yoksayılır.

  4. Ad bir tam adla eşleşir (örneğin, Bahnschrift Bold Condensed). Tam adla eşleşen yazı tipi döndürülür. İstenen eksen değerleri yoksayılır. GDI tarafından desteklendiğinden tam yazı tipi adıyla eşleştirmeye izin verilir.

Önceki bölümde "Eski" adlı belirsiz bir tipografi ailesi açıklanmıştır. Karma algoritma, aile adı olarak "Eski" veya "Eski Geçici" belirterek belirsizliğin önüne geçilmesine olanak tanır. "Eski Geçici Yazılım" belirtilirse, eşleştirme yalnızca WWS ailesi içinde gerçekleştiğinden belirsizlik olmaz. "Eski" belirtilirse, tipografi ailesindeki tüm yazı tipleri eşleşme adayı olarak kabul edilir, ancak "Eski" WWS ailesindeki üyelik kravat kesici olarak kullanıldığında belirsizlikten kaçınılır.

Belgenin bir aile adı ve kalınlık, esnetme ve stil parametrelerini belirttiğini ancak eksen değeri olmadığını varsayalım. Uygulama öncelikle IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues çağrısı yaparak ağırlık, esnetme, stil ve yazı tipi boyutunu eksen değerlerine dönüştürebilir. Uygulama daha sonra hem aile adını hem de eksen değerlerini IDWriteFontSet4::GetMatchingFonts'a geçirebilir. GetMatchingFonts , eşleşen yazı tiplerinin listesini öncelik sırasına göre döndürür ve belirtilen aile adının tipografik bir aile adı, kalınlık-esnetme stili aile adı, RBIZ ailesi adı veya tam ad olması farketmeksizin sonuç uygundur. Belirtilen ailede "opsz" ekseni varsa, yazı tipi boyutuna göre uygun optik boyut otomatik olarak seçilir.

Belgenin kalınlık, esnetme ve stil ile eksen değerlerini belirttiğini varsayalım. Bu durumda, açık eksen değerleri IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues'a da geçirilebilir ve yöntemi tarafından döndürülen türetilmiş eksen değerleri yalnızca açıkça belirtilmemiş yazı tipi eksenlerini içerir. Bu nedenle, belge (veya uygulama) tarafından açıkça belirtilen eksen değerleri ağırlık, esnetme, stil ve yazı tipi boyutundan türetilen eksen değerlerinden önceliklidir.

Karma yazı tipi seçimi API'leri

Karma yazı tipi seçim modeli aşağıdaki IDWriteFontSet4 yöntemleri tarafından uygulanır:

  • IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues yöntemi yazı tipi boyutu, kalınlığı, esnetme ve stil parametrelerini ilgili eksen değerlerine dönüştürür. İstemci tarafından geçirilen tüm açık eksen değerleri türetilmiş eksen değerlerinden hariç tutulur.

  • IDWriteFontSet4::GetMatchingFonts yöntemi, bir aile adı ve eksen değerleri dizisi verilen eşleşen yazı tiplerinin önceliklendirilmiş bir listesini döndürür. Yukarıda açıklandığı gibi, aile adı parametresi bir tipografik aile adı, WWS aile adı, RBIZ aile adı veya tam ad olabilir. (Tam ad, "Arial Kalın İtalik" gibi belirli bir yazı tipi stilini tanımlar. GetMatchingFonts , GDI ile daha fazla uyumluluk için tam ada göre eşleştirmeyi destekler ve bu da buna izin verir.)

Aşağıdaki diğer DirectWrite API'leri de karma yazı tipi seçim algoritmasını kullanır:

Kullanılan yazı tipi seçimi API'lerinin kod örnekleri

Bu bölümde IDWriteFontSet4::GetMatchingFonts ve IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues yöntemlerini gösteren eksiksiz bir konsol uygulaması gösterilmektedir. İlk olarak bazı üst bilgileri ekleyelim:

#include <dwrite_core.h>
#include <wil/com.h>
#include <iostream>
#include <string>
#include <vector>

IDWriteFontSet4::GetMatchingFonts yöntemi, belirtilen aile adı ve eksen değerleriyle eşleşen yazı tiplerinin listesini öncelik sırasına göre döndürür. Aşağıdaki MatchAxisValues işlevi, idwriteFontSet4::GetMatchingFonts parametrelerini ve döndürülen yazı tipi kümesindeki eşleşen yazı tiplerinin listesini çıkarır.

// 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;
}

Bir uygulamanın eksen değerleri yerine ağırlık, esnetme ve stil parametreleri olabilir. Örneğin, uygulamanın RBIZ veya kalınlık-esnetme stili parametreleri kullanarak yazı tiplerine başvuran belgelerle çalışması gerekebilir. Uygulama rastgele eksen değerleri belirtme desteği eklese bile, eski parametreleri de desteklemesi gerekebilir. Bunu yapmak için uygulama, IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues'uIDWriteFontSet4::GetMatchingFonts çağrısından önce çağırabilir.

Aşağıdaki MatchFont işlevi eksen değerlerine ek olarak ağırlık, esnetme, stil ve yazı tipi boyutu parametrelerini alır. Giriş ekseni değerlerine eklenen türetilmiş eksen değerlerini hesaplamak için bu parametreleri IDWriteFontSet4::ConvertWeightStretchStyleToFontAxisValues yöntemine iletir. Birleştirilmiş eksen değerlerini yukarıdaki MatchAxisValues işlevine geçirir.

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);
}

Aşağıdaki işlev, bazı örnek girişlerle yukarıdaki MatchFont işlevini çağırmanın sonuçlarını gösterir:

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);
}

Yukarıdaki TestFontSelection işlevinin çıkışı aşağıdadır:

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

Yukarıda bildirilen aşırı yüklenmiş işleçlerin uygulamaları aşağıdadır. Bunlar MatchAxisValues tarafından giriş ekseni değerlerini ve sonuçta elde edilen yazı tipi yüz başvurularını yazmak için kullanılır:

// 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;
}

Örneği yuvarlamak için komut satırı ayrıştırma işlevleri ve ana işlev aşağıda verilmiştir:

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());
    }
}