自訂字型集
本主題說明您可以在應用程式中使用自訂字型的各種方式。
簡介
在大部分情況下,應用程式會使用安裝在本機系統上的字型。 DirectWrite會使用IDWriteFactory3::GetSystemFontSet或IDWriteFactory::GetSystemFontCollection方法來存取這些字型。 在某些情況下,應用程式可能也會想要使用包含在Windows 10但目前未安裝在目前系統上的字型。 您可以使用 GetSystemFontSet 方法,或呼叫 IDWriteFactory3::GetSystemFontCollection 並將 includeDownloadableFonts 設為 TRUE,從 Windows 字型服務存取這類字型。
不過,在某些應用程式案例中,應用程式必須使用未安裝在系統中的字型,而且 Windows 字型服務不會提供。 以下是這類案例的範例:
- 字型會內嵌為應用程式二進位檔內的資源。
- 字型檔案會組合在應用程式套件內,並儲存在應用程式安裝資料夾下的磁片上。
- 應用程式是需要載入使用者指定字型檔案的字型開發工具。
- 字型會內嵌在可在應用程式中檢視或編輯的檔檔中。
- 應用程式會使用從公用 Web 字型服務取得的字型。
- 應用程式會使用透過私人網路通訊協定串流的字型資料。
DirectWrite提供 API 來處理這些和類似案例中的自訂字型。 自訂字型資料可能來自本機檔案系統中的檔案;從使用 HTTP 存取的遠端雲端式來源;或從任意來源載入記憶體緩衝區之後。
注意
雖然自 Windows 7 以來,DirectWrite已提供使用自訂字型的 API,但較新的 API 已新增至 Windows 10,並在 Windows 10 Creators Update (Preview 組建 15021 或更新版本中再次新增 API) ,讓您更輕鬆地實作上述幾個案例。 本主題著重于視窗 10 中可用的 API。 如需需要處理舊版 Windows 的應用程式,請參閱 自訂字型集合 (Windows 7/8) 。
API 的摘要
本主題著重于下列 API 所提供的功能:
- IDWriteFontSet 介面
- IDWriteFontSetBuilder 介面
- IDWriteFontSetBuilder1 介面
- IDWriteFontFaceReference 介面
- IDWriteFontFile 介面
- IDWriteFactory::CreateFontFileReference 方法
- IDWriteFactory::CreateCustomFontFileReference 方法
- IDWriteFactory3::CreateFontFaceReference 方法
- DWRITE_FONT_PROPERTY 結構
- DWRITE_FONT_PROPERTY_ID 列舉
- IDWriteFontFileLoader 介面
- IDWriteFactory::RegisterFontFileLoader 方法
- IDWriteFactory::UnregisterFontFileLoader 方法
- IDWriteFactory5::CreateInMemoryFontFileLoader 方法
- IDWriteInMemoryFontFileLoader 介面
- IDWriteFactory5::CreateHttpFontFileLoader 方法
- IDWriteRemoteFontFileLoader 介面
- IDWriteFontDownloadQueue 介面
- IDWriteFontDownloadListener 介面
- IDWriteFontFileStream 介面
- IDWriteRemoteFontFileStream 介面
- IDWriteAsyncResult 介面
- IDWriteFactory5::AnalyzeContainerType 方法
- IDWriteFactory5::UnpackFontFile 方法
重要概念
若要瞭解使用自訂字型的DirectWrite API,瞭解這些 API 的基礎概念模型會很有説明。 這裡將說明重要概念。
當DirectWrite實際文字配置或轉譯時,它必須存取實際的字型資料。 字型臉部物件會保存實際字型資料,該資料必須存在於本機系統中。 但對於其他作業,例如檢查特定字型的可用性,或向使用者呈現字型選擇,只需要特定字型的參考,而不是實際的字型資料本身。 在DirectWrite中,字型臉部參考物件只保留尋找並具現化字型所需的資訊。 因為字型臉部參考不會保存實際資料,所以DirectWrite可以處理實際資料位於遠端網路位置的字型臉部參考,以及實際資料位於本機時。
字型集是一組字型臉部參考,以及可用於參照字型的特定基本資訊屬性,或比較其他字型,例如系列名稱或字型粗細值。 各種字型的實際資料可能是本機的,也可能全部是遠端,或部分混合。
字型集可用來取得對應的字型集合物件。 如需詳細資訊,請參閱下方的字型集和字型集合。
IDWriteFontSet 介面提供方法,允許查詢屬性值,例如系列名稱或字型粗細,或是符合特定屬性值的字型臉部參考。 篩選到特定選取範圍之後,可以取得 IDWriteFontFaceReference 介面的實例,方法是下載 (如果實際字型資料目前是遠端) ,則取得可用於版面配置和轉譯的對應 IDWriteFontFace3 物件。
IDWriteFontFile 介面會底下每個字型臉部或字型臉部參考。 這代表字型檔案的位置,而且有兩個元件:字型檔案載入器和字型檔案索引鍵。 字型檔案載入器 (IDWriteFontFileLoader) 是用來視需要開啟檔案,並傳回具有 IDWriteFontFileStream (資料的資料流程) 。 視載入器而定,資料可能位於本機檔案路徑、遠端 URL 或記憶體緩衝區中。 索引鍵是載入器定義的值,可唯一識別載入器內容中的檔案,讓載入器找出資料並為其建立資料流程。
自訂字型可以輕鬆地新增至自訂字型集,進而用於篩選或組織字型資訊,例如建立字型選擇器使用者介面。 字型集也可以用來建立字型集合,以用於較高層級的 API,例如 IDWriteTextFormat 和 IDWriteTextLayout。 IDWriteFontSetBuilder介面可用來建立包含數個自訂字型的自訂字型集。 它也可以用來建立自訂字型集,以混合自訂字型和系統提供的字型;或混合字型與實際資料的不同來源 ,也就是本機儲存體、遠端 URL 和記憶體。
如前所述,字型臉部參考可能會參照遠端來源的字型資料,但資料必須是本機資料,才能取得可用於版面配置和轉譯的字型臉部物件。 下載遠端資料是由字型下載佇列處理。 應用程式可以使用 IDWriteFontDownloadQueue 介面來排入佇列要求,以下載遠端字型以起始下載程式,以及註冊 IDWriteFontDownloadListener 物件,以在下載程式完成時採取動作。
針對這裡所述的大部分介面,DirectWrite提供系統實作。 其中一個例外狀況是 IDWriteFontDownloadListener 介面,應用程式會在本機下載遠端字型時實作以採取應用程式特定的動作。 應用程式可能會有理由為某些其他介面提供自己的自訂實作,但只有在更進階的特定案例中才需要這個實作。 例如,應用程式必須提供 IDWriteFontFileLoader 介面的自訂實作,以處理使用 WOFF2 容器格式之本機儲存體中的字型檔案。 以下將提供其他詳細資料。
字型和字型檔案格式
另一個有助於瞭解的重要概念是個別字型臉部與包含它們之字型檔案之間的關聯性。 包含單一字型的 OpenType 字型檔案 (.ttf 或 .otf) 的概念很熟悉。 但 OpenType 字型格式也允許 OpenType Font Collection (.ttc 或 .otc) ,這是包含多個字型的單一檔案。 OpenType 集合檔案通常用於與特定字型資料緊密相關的大型字型,且具有相同值:藉由在單一檔案中結合字型,可取消複製一般資料。 基於這個理由,字型臉部或字型臉部參考不僅需要參考字型檔案 (或對等資料來源) ,也必須在該檔案中指定字型索引,以取得檔案可能是集合檔案的一般案例。
對於 Web 上所使用的字型,字型資料通常會封裝成某些容器格式 WOFF 或 WOFF2,以提供一些字型資料的壓縮,以及某些層級的保護,以防止字型授權的異常和違規。 在功能上,WOFF 或 WOFF2 檔案相當於 OpenType 字型或字型集合檔案,但資料會以需要解壓縮的不同格式編碼,才能使用。
某些DirectWrite API 可能會處理個別字型臉部,而其他 API 可以處理可能包含包含多個臉部之 OpenType 集合檔案的檔案。 同樣地,某些 API 只會處理未經處理、OpenType 格式的資料,而其他 API 可以處理已封裝的 WOFF 和 WOFF2 容器格式。 以下討論中會提供這些詳細資料。
字型集和字型集合
某些應用程式可以使用 IDWriteFontCollection 介面來使用字型。 字型集合與字型集之間有直接對應。 每個字型都可以保留相同的字型,但會以不同的組織呈現它們。 您可以從任何字型集合取得對應的字型集,反之亦然。
使用許多自訂字型時,最簡單的方法是使用字型集產生器介面來建立自訂字型集,然後在建立字型集之後取得字型集合。 建立自訂字型集的程式如下所述。 若要從字型集取得 IDWriteFontCollection1 介面,則會使用 IDWriteFactory3::CreateFontCollectionFromFontSet 方法。
如果應用程式具有集合物件,而且需要取得對應的字型集,可以使用 IDWriteFontCollection1::GetFontSet 方法來完成。
常見案例
本節說明涉及自訂字型集的一些最常見案例:
- 在本機檔案系統的路徑上使用任意字型建立自訂字型集。
- 使用已知字型建立自訂字型集 (可能與儲存在本機檔案系統中的應用程式) 搭配使用。
- 使用已知的遠端字型在網路上建立自訂字型集。
- 使用載入記憶體的字型資料建立自訂字型集。
這些案例的完整實作會在自訂字型集範例中提供DirectWrite。 該範例也會說明處理以 WOFF 或 WOFF2 容器格式封裝的字型資料的一個進階案例,如下所述。
在本機檔案系統中使用任意字型建立字型集
在本機儲存體中處理任意的字型檔案集時, IDWriteFontSetBuilder1::AddFontFile 方法很方便,因為在單一呼叫中,它可以處理 OpenType Font Collection 檔案內的所有字型臉部,以及 OpenType 變數字型的所有實例。 這可在Windows 10 Creators Update (預覽版 15021 或更新版本) 中取得,並建議隨時使用。
若要使用此方法,請使用下列程式。
- 1.從建立 IDWriteFactory5 介面開始:
- 針對本機檔案系統中的每個字型檔案,建立參考它的 IDWriteFontFile :
- 將所有檔案新增至字型集產生器之後,即可建立自訂字型集:
IDWriteFactory5* pDWriteFactory;
HRESULT hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory5),
reinterpret_cast<IUnknown**>(&pDWriteFactory)
);
2.使用 Factory 來取得 IDWriteFontSetBuilder1介面:
IDWriteFontSetBuilder1* pFontSetBuilder;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontSetBuilder(&pFontSetBuilder);
}
IDWriteFontFile* pFontFile;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontFileReference(pFilePath, /* lastWriteTime*/ nullptr, &pFontFile);
}
4.使用AddFontFile方法,將IDWriteFontFile物件新增至字型集產生器:
hr = pFontSetBuilder->AddFontFile(pFontFile);
如果在呼叫 CreateFontFileReference 中指定的檔案路徑參考支援的 OpenType 檔案以外的專案,則 AddFontFile 的呼叫會傳回錯誤,DWRITE_E_FILEFORMAT。
IDWriteFontSet* pFontSet;
hr = pFontSetBuilder->CreateFontSet(&pFontSet);
如果應用程式必須在Windows 10 Creators Update之前的Windows 10版本上執行,則無法使用 AddFontFile 方法。 您可以藉由建立 IDWriteFactory3 介面,然後使用 QueryInterface 嘗試取得 IDWriteFactory5 介面來偵測可用性:如果成功,則也可以使用 IDWriteFontSetBuilder1 介面和 AddFontFile 方法。
如果 AddFontFile 方法無法使用,則必須使用 IDWriteFontSetBuilder::AddFontFaceReference 方法來新增個別字型臉部。 若要允許包含多個臉部的 OpenType 字型集合檔案,可以使用 IDWriteFontFile::Analyze 方法來判斷檔案中包含的臉部數目。 此程式如下所示。
- 1.從建立 IDWriteFactory3 介面開始:
- 使用 Factory 來取得 IDWriteFontSetBuilder 介面:
- 針對每個字型檔案,建立 IDWriteFontFile,如下所示:
- 將所有臉部新增至字型集產生器之後,請建立自訂字型集,如上所示。
IDWriteFactory3* pDWriteFactory;
HRESULT hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory5),
reinterpret_cast<IUnknown**>(&pDWriteFactory)
);
IDWriteFontSetBuilder* pFontSetBuilder;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontSetBuilder(&pFontSetBuilder);
}
IDWriteFontFile* pFontFile;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateFontFileReference(pFilePath, /* lastWriteTime*/ nullptr, &pFontFile);
}
我們需要判斷臉部數目,並建立個別 的 IDWriteFontFaceReference 物件,而不是直接將檔案新增至字型集產生器。
4.使用 Analyze 方法來取得檔案中的臉部數目。
BOOL isSupported;
DWRITE_FONT_FILE_TYPE fileType;
UINT32 numberOfFonts;
hr = pFontFile->Analyze(&isSupported, &fileType, /* face type */ nullptr, &numberOfFonts);
Analyze方法也會設定 isSupported 和 fileType 參數的值。 如果檔案不是支援的格式,則 isSupported 會是 FALSE,而且可以採取適當的動作,例如忽略檔案。
5.迴圈查看 numberOfFonts 參數中設定的字型數目。 在迴圈中,為每個檔案/索引組建立 IDWriteFontFaceReference ,並將它新增至字型集產生器。
for (uint32_t fontIndex = 0; fontIndex < numberOfFonts; fontIndex++)
{
IDWriteFontFaceReference* pFontFaceReference;
hr = pDWriteFactory->CreateFontFaceReference(pFontFile, fontIndex, DWRITE_FONT_SIMULATIONS_NONE, &pFontFaceReference);
if (SUCCEEDED(hr))
{
hr = pFontSetBuilder->AddFontFaceReference(pFontFaceReference);
}
}
您可以設計應用程式,讓它在Windows 10 Creators Update上執行時使用慣用的 AddFontFile方法,但在舊版Windows 10上執行時,會回到使用AddFontFaceReference方法。 測試 IDWriteFactory5 介面的可用性,如上所述,然後據以分支。 此方法會在自訂字型集範例中說明DirectWrite。
使用本機檔案系統中的已知字型建立字型集
如上所述,字型集中的每個字型臉部參考都會與特定資訊屬性相關聯,例如系列名稱和字型粗細。 使用上面所列的 API 呼叫將自訂字型新增至字型集產生器時,這些參考屬性會直接從實際字型資料取得,當新增字型時會讀取。 不過,在某些情況下,如果應用程式有另一個有關字型的資訊來源,它可能會想要為這些屬性提供自己的自訂值。
例如,這可能很有用,假設應用程式會組合一些用來呈現應用程式內特定使用者介面元素的字型。 有時候,例如使用新的應用程式版本,應用程式針對這些專案使用的特定字型可能需要變更。 如果應用程式已編碼特定字型的參考,則以另一個字型取代一個字型將需要變更其中每一個參考。 相反地,如果應用程式使用自訂屬性來根據所呈現的專案或文字類型來指派功能別名,請將每個別名對應至一個位置的特定字型,然後在建立及操作字型的所有內容中使用別名,然後將一個字型取代為另一個字型,只需要變更別名對應至特定字型的位置。
呼叫 IDWriteFontSetBuilder::AddFontFaceReference 方法時,可以指派參考屬性的自訂值。 執行此動作的方法如下所示;這可用於任何Windows 10版本。
如上所示,首先取得 IDWriteFactory3 和 IDWriteFontSet 介面。 若要新增每個自訂字型臉部,請建立 IDWriteFontFaceReference,如上所示。 在將這個新增至步驟 5 迴圈內的字型集產生器 (之前,如上述) 所示,應用程式會定義要使用的自訂屬性值。
一組自訂屬性值是使用 DWRITE_FONT_PROPERTY 結構的陣列來定義。 其中每一個都會識別 DWRITE_FONT_PROPERTY_ID 列舉中的特定屬性,以及要使用的對應屬性值。
請注意,所有屬性值都會指派為字串。 如果稍後可能會向使用者顯示這些值,則可能會設定不同語言之指定屬性的替代值,但這並非必要專案。 另請注意,如果應用程式設定了任何自訂屬性值,則只有指定的值會用於 Font set 內;DirectWrite不會直接從字型衍生任何值,以供字型集中使用的資訊屬性使用。
下列範例會定義三個參考屬性的自訂值:系列名稱、完整名稱和字型粗細。
DWRITE_FONT_PROPERTY props[] =
{
{ DWRITE_FONT_PROPERTY_ID_FAMILY_NAME, L"My Icon Font", L"en-US" },
{ DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"My Icon Font", L"en-US" },
{ DWRITE_FONT_PROPERTY_ID_WEIGHT, L"400", nullptr }
};
定義字型屬性值的所需陣列之後,請呼叫 AddFontFaceRefence,傳遞屬性陣列以及字型臉部參考。
hr = pFontSetBuilder->AddFontFaceReference(pFontFaceReference, props, ARRAYSIZE(props));
將所有自訂字型臉部新增至字型集產生器之後,以及其自訂屬性,請建立自訂字型集,如上所示。
在網路上使用已知的遠端字型建立自訂字型集
自訂屬性對於使用遠端字型很重要。 每個字型臉部參考都必須有一些資訊屬性來描述字型,並區分它與其他字型。 由於遠端字型的字型資料不是本機字型,所以DirectWrite無法直接從字型資料衍生屬性。 因此,將遠端字型新增至字型集產生器時,必須明確提供屬性。
將遠端字型新增至字型集的 API 呼叫順序類似于先前案例所述的序列。 不過,由於字型資料是遠端的,因此讀取實際字型資料的作業會與使用本機儲存體中的檔案時不同。 在此情況下,已在 Windows 10 Creators Update 中新增新的較低層級介面IDWriteRemoteFontFileLoader。
若要使用遠端字型檔案載入器,必須先向DirectWrite處理站註冊。 只要正在使用與應用程式相關聯的字型,載入器就必須由應用程式保留。 一旦不再使用字型,而且在處理站終結之前的某些時間點,必須取消註冊載入器。 這可以在擁有 loader 物件的類別解構函式中完成。 這些步驟如下所示。
使用遠端字型建立自訂字型集的方法如下:這需要Windows 10 Creators Update。
- 1.建立 IDWriteFactory5 介面,如上所示。
2.建立 IDWriteFontSetBuilder 介面,如上所示。
3.使用 Factory 取得 IDWriteRemoteFontFileLoader。
- 定義字型臉部的自訂屬性,如上所示。
- 將字型臉部參考以及自訂屬性新增至字型集產生器,如上所示。
- 將所有字型新增至字型集產生器之後,請建立字型集,如上所示。
- 當遠端字型不再使用時,請取消註冊遠端字型檔案載入器。
IDWriteRemoteFontFileLoader* pRemoteFontFileLoader;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateHttpFontFileLoader(
/* referrerURL */ nullptr,
/* extraHeaders */ nullptr,
&pRemoteFontFileLoader
);
}
這會傳回遠端字型檔案載入器介面的系統提供實作,其能夠處理 HTTP 互動,以代表應用程式下載字型資料。 如果字型服務或服務是字型來源,可以指定參照者 URL 或額外標頭。
重要
安全性注意事項:嘗試擷取遠端字型時,攻擊者可能會詐騙即將呼叫的預定伺服器。 在此情況下,目標與參考者 URL 和標頭詳細資料會向攻擊者揭露。 應用程式開發人員負責減輕此風險。 建議使用 HTTPS 通訊協定,而不是使用 HTTP。
單一遠端字型檔案載入器可用於多個字型,不過,如果字型是從多個具有不同參考者 URL 或額外標頭需求的服務取得,則可以使用不同的載入器。
4.向處理站註冊遠端字型檔案載入器。
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->RegisterFontFileLoader(pRemoteFontFileLoader);
}
此時,建立自訂字型集的步驟與已知本機字型檔案中所述的步驟類似,但有兩個重要的例外狀況。 首先, IDWriteFontFile 物件是使用遠端字型檔案載入器介面來建立,而不是使用 Factory。 其次,無法使用 Analyze 方法,因為字型資料不是本機的。 相反地,應用程式必須知道遠端字型檔案是否為 OpenType 字型集合檔案,如果是的話,則必須知道其將使用的集合中哪些字型,以及每個字型的索引。 因此,其餘步驟如下所示。
5.針對每個遠端字型檔案,請使用遠端字型檔案載入器介面來建立 IDWriteFontFile,並指定存取字型檔案所需的 URL。
IDWriteFontFile* pFontFile;
hr = pRemoteFontFileLoader->CreateFontFileReferenceFromUrl(
pDWriteFactory,
/* baseUrl */ L"https://github.com/",
/* fontFileUrl */ L"winjs/winjs/blob/master/src/fonts/Symbols.ttf?raw=true",
&pFontFile
);
請注意,您可以在 fontFileUrl 參數中指定完整的 URL,也可以分割成基底和相對部分。 如果指定基底 URL,則 baseUrl 和 fontFileUrl 值的串連必須提供完整的 URL,DirectWrite不會提供任何其他分隔符號。
重要
安全性/效能注意事項:嘗試擷取遠端字型時,不保證 Windows 會收到來自伺服器的回應。 在某些情況下,伺服器可能會回應無效相對 URL 的檔案找不到錯誤,但如果收到多個不正確要求,請停止回應。 如果伺服器沒有回應,Windows 最終會逾時,但如果起始多個擷取,這可能需要幾分鐘的時間。 您應該執行哪些動作,以確保 URL 在進行呼叫時有效。
另請注意,URL 可以指向原始 OpenType 字型檔案 (.ttf、.otf、.ttc、.otc) ,但也可以指向 WOFF 或 WOFF2 容器檔案中的字型。 如果參考 WOFF 或 WOFF2 檔案,則遠端字型檔案載入器的DirectWrite實作會自動從容器檔案解除封裝字型資料。
6.針對要使用的遠端字型檔案中的每個字型臉部索引,建立 IDWriteFontFaceReference。
IDWriteFontFaceReference* pFontFaceReference;
hr = pDWriteFactory->CreateFontFaceReference(pFontFile, /* faceIndex */ 0, DWRITE_FONT_SIMULATIONS_NONE, &pFontFaceReference);
hr = pDWriteFactory->UnregisterFontFileLoader(pRemoteFontFileLoader);
建立具有自訂遠端字型的自訂字型集之後,字型集會包含遠端字型的參考和參考屬性,但實際資料仍然為遠端。 DirectWrite支援遠端字型時,允許在字型集中維護字型臉部參考,以及要選取字型以供配置和轉譯使用,但在實際需要使用資料之前,才會下載實際資料,例如何時執行文字版面配置。
應用程式可以透過要求DirectWrite下載字型資料,然後在開始任何使用字型處理之前等待確認成功下載,來採用預先方法。 但網路下載意指無法預測持續時間的一些延遲,而且成功也不確定。 基於這個理由,通常最好採用不同的方法,讓版面配置和轉譯一開始使用已經是本機的替代字型或後援字型來完成,同時要求下載所需的遠端字型,然後在下載所需的字型之後更新結果。
若要要求在使用整個字型之前先下載,可以使用 IDWriteFontFaceReference::EnqueueFontDownloadRequest 方法。 如果字型非常大,則處理特定字串時可能只需要一部分的資料。 DirectWrite提供其他方法,可用來要求特定內容所需的字型資料部分、EnqueueCharacterDownloadRequest和EnqueueGlyphDownloadRequest。
假設應用程式中要採取的方法是允許一開始使用本機、替代或後援字型來完成處理。 IDWriteFontFallback::MapCharacters方法可用來識別本機後援字型,而且也會自動加入要求以下載慣用字型。 此外,如果使用IDWriteTextLayout,且配置中的部分或所有文字是使用遠端字型參考來格式化,則DirectWrite會自動使用MapCharacters方法來取得本機後援字型,並加入要求以下載遠端字型資料。
DirectWrite會維護每個處理站的字型下載佇列,並使用上述方法提出的要求會新增至該佇列。 字型下載佇列可以使用 IDWriteFactory3::GetFontDownloadQueue 方法取得。
如果已提出下載要求,但字型資料已在本機,這會導致無作用:不會新增任何內容至下載佇列。 應用程式可以呼叫 IDWriteFontDownloadQueue::IsEmpty 方法來檢查佇列是空的,還是有擱置的下載要求。
將遠端字型要求新增至佇列之後,必須起始下載程式。 當 IDWriteTextLayout中使用遠端字型時,當應用程式呼叫強制配置或轉譯作業的 IDWriteTextLayout 方法,例如 GetLineMetrics 或 Draw 方法時,會自動起始下載。 在其他情況下,應用程式必須呼叫 IDWriteFontDownloadQueue::BeginDownload來直接起始下載。
下載完成時,應用程式會採取適當的動作—繼續進行擱置的作業,或一開始使用後援字型完成的重複作業。 (如果使用DirectWrite的文字配置,則IDWriteTextLayout3::InvalidateLayout可用來清除使用後援字型計算的暫存結果。) 為了讓應用程式在下載程式完成並採取適當動作時收到通知,應用程式必須提供IDWriteFontDownloadListener介面的實作,並將它傳遞至 BeginDownload 呼叫。
重要
安全性/效能注意事項:嘗試擷取遠端字型時,不保證 Windows 會收到來自伺服器的回應。 如果伺服器沒有回應,Windows 最終會逾時,但如果擷取多個遠端字型但失敗,這可能需要幾分鐘的時間。 BeginDownload 呼叫會立即傳回。 應用程式不應該在等候 IDWriteFontDownloadListener::D ownloadCompleted 呼叫 時封鎖 UI。
這些與DirectWrite字型下載佇列和IDWriteFontDownloadListener介面互動的範例實作,可以在DirectWrite自訂字型集範例中,以及在DirectWrite 可下載的字型範例中看到。
使用載入記憶體的字型資料建立自訂字型集
就如同從字型檔案讀取資料的低階作業對於本機磁片上的檔案與 Web 上的遠端檔案不同,同樣也適用于載入記憶體緩衝區的字型資料。 已在IDWriteInMemoryFontFileLoader Windows 10 Creators Update中新增用於處理記憶體內部字型資料的新低階介面。
如同遠端字型檔案載入器,記憶體內部字型檔案載入器必須先向DirectWrite處理站註冊。 只要正在使用與應用程式相關聯的字型,載入器就必須由應用程式保留。 一旦不再使用字型,而且在處理站終結之前的某些時間點,必須取消註冊載入器。 這可以在擁有載入器物件的 類別解構函式中完成。 這些步驟如下所示。
如果應用程式有資料所代表字型臉部的個別資訊,則可以使用指定的自訂屬性,將個別字型臉部參考新增至字型集產生器。 不過,由於字型資料位於本機記憶體中,因此不需要這樣做;DirectWrite將能夠直接讀取資料,以衍生屬性值。
DirectWrite假設字型資料是原始、OpenType 格式,相當於 OpenType 檔案 (.ttf、.otf、.ttc、.otc) ,但在記憶體中而不是磁片上。 資料不能是 WOFF 或 WOFF2 容器格式。 資料可以代表 OpenType 字型集合。 如果未使用自訂屬性,則可以使用 IDWriteFontSetBuilder1::AddFontFile 方法,在單一呼叫中新增資料中的所有字型臉部。
記憶體內部案例的重要考慮是資料的存留期。 如果提供緩衝區的指標給DirectWrite,但沒有清楚指出有擁有者,則DirectWrite會將資料複本複製到其擁有的新記憶體緩衝區。 為了避免複製資料和額外的記憶體配置,應用程式可以傳遞實作 IUnknown 的資料擁有者物件,並擁有包含字型資料的記憶體緩衝區。 藉由實作這個介面,DirectWrite可以加入物件的 ref 計數,藉此確保擁有的資料存留期。
使用記憶體內部字型資料建立自訂字型集的方法如下:這需要Windows 10 Creators Update。 這會假設應用程式實作的資料擁有者物件,該物件會實作 IUnknown,而且也有方法會傳回記憶體緩衝區的指標和緩衝區的大小。
- 1.建立 IDWriteFactory5 介面,如上所示。
2.建立 [**IDWriteFontSetBuilder1**] (/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontsetbuilder1) 介面,如下所示。
3.使用 Factory 取得 IDWriteInMemoryFontFileLoader。
IDWriteInMemoryFontFileLoader* pInMemoryFontFileLoader;
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->CreateInMemoryFontFileLoader(&pInMemoryFontFileLoader);
}
這會傳回記憶體內部字型檔案載入器介面的系統提供實作。
4.向 Factory 註冊記憶體內部字型檔案載入器。
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->RegisterFontFileLoader(pInMemoryFontFileLoader);
}
5.針對每個記憶體內部字型檔案,請使用記憶體內部字型檔案載入器來建立 IDWriteFontFile。
IDWriteFontFile* pFontFile;
hr = pInMemoryFontFileLoader->CreateInMemoryFontFileReference(
pDWriteFactory,
pFontDataOwner->fontData /* returns void* */,
pFontDataOwner->fontDataSize /* returns UINT32 */,
pFontDataOwner /* ownerObject, owns the memory with font data and implements IUnknown */,
&pFontFile
);
6.使用AddFontFile方法將IDWriteFontFile物件新增至字型集產生器,如上所示。 如果需要,應用程式可以改為根據IDWriteFontFile建立個別的 IDWriteFontFaceReference物件,並選擇性地定義每個字型臉部參考的自訂屬性,然後使用AddFontFaceReference方法將具有自訂屬性的字型參考新增至字型集,如上所示。
7.將所有字型新增至字型集產生器之後,請建立自訂字型集,如上所示。
8.當記憶體內部字型不再使用時,請取消註冊記憶體內部字型檔案載入器。
hr = pDWriteFactory->UnregisterFontFileLoader(pInMemoryFontFileLoader);
進階案例
某些應用程式可能會有需要比上述更進階處理的特殊需求。
結合字型集
某些應用程式可能需要建立字型集,其中包含來自其他字型集的專案組合。 例如,應用程式可能會想要建立一個字型集,以結合系統上安裝的所有字型與自訂字型的選取專案,或結合符合特定準則的字型與其他字型。 DirectWrite具有 API 來支援字型集的操作和組合。
若要結合兩個或多個字型集, IDWriteFontSetBuilder::AddFontSet 方法會新增指定字型集中的所有字型,以在單一呼叫中新增至字型集產生器。 如果新字型集中只想要來自現有字型集的特定字型,則 IDWriteFontSet::GetMatchingFonts 方法可用來衍生已篩選為只包含符合指定屬性之字型的新字型集物件。 這些方法可讓您輕鬆建立自訂字型集,結合來自兩個或多個現有字型集的字型
使用本機 WOFF 或 WOFF2 字型資料
如果應用程式在本機檔案系統或記憶體緩衝區中有字型檔案,但它們使用 WOFF 或 WOFF2 容器格式,DirectWrite (Windows 10 Creator Update 或更新版本) 提供將容器格式IDWriteFactory5::UnpackFontFile解壓縮的方法,這會傳回IDWriteFontFileStream。
不過,應用程式需要一種方式,才能將 IDWriteFontFileStream 放入字型檔案載入器物件。 其中一個做法是建立包裝資料流程的自訂 IDWriteFontFileLoader 實作。 如同其他字型檔案載入器,這必須在使用前註冊,並在處理站超出範圍之前取消註冊。
如果自訂載入器也會與未經處理的 (未封裝) 字型檔案搭配使用,則應用程式也需要提供 IDWriteFontFileStream 介面的自訂實作來處理這些檔案。 不過,有一些使用上述 API 來處理原始字型檔案的簡單方式。 您可以使用封裝字型檔案與原始字型檔案的個別程式碼路徑,來避免自訂資料流程實作的需求。
建立自訂字型檔案載入器物件之後,會以應用程式特定方式將封裝的字型檔案資料新增至載入器。 載入器可以處理多個字型檔案,每個檔案都是使用不透明應用程式定義的索引鍵來識別DirectWrite。 將封裝的字型檔案新增至載入器之後, 會使用 IDWriteFactory::CreateCustomFontFileReference 方法來根據指定索引鍵所識別的字型資料取得 IDWriteFontFile 。
當字型新增至載入器時,可以實際解除封裝字型資料,但也可以在IDWriteFontFileLoader::CreateStreamFromKey方法中處理,DirectWrite第一次需要讀取字型資料時呼叫。
建立 IDWriteFontFile 物件之後,將字型新增至自訂字型集的其餘步驟將會如上所述。
使用此方法的實作會在DirectWrite自訂字型集範例中說明。
搭配自訂低階網路實作使用DirectWrite遠端字型機制
處理遠端字型的DirectWrite機制可以分成較高層級的機制—具有字型集,包括遠端字型的字型參考、檢查字型資料位置,以及管理字型下載要求的佇列,以及處理實際下載的較低層級機制。 某些應用程式可能想要利用較高層級的遠端字型機制,但也需要自訂網路互動,例如使用 HTTP 以外的通訊協定與伺服器通訊。
在此情況下,應用程式必須建立 IDWriteRemoteFontFileLoader 介面的自訂實作,以必要的方式與其他較低層級介面互動。 應用程式也需要提供這些較低層級介面的自訂實作: IDWriteRemoteFontFileStream和 IDWriteAsyncResult。 這三個介面具有回呼方法,DirectWrite會在下載作業期間呼叫。
呼叫IDWriteFontDownloadQueue::BeginDownload時,DirectWrite會對遠端字型檔案載入器進行有關資料位置的查詢,並要求遠端資料流。 如果資料不是本機資料,則會呼叫資料流程的 BeginDownload 方法。 資料流程實作不應該在該呼叫上封鎖,但應該立即傳回IDWriteAsyncResult物件,以提供等候控制碼DirectWrite會用來等候非同步下載作業。 自訂資料流程實作負責處理遠端通訊。 當完成事件發生時,DirectWrite會呼叫IDWriteAsyncResult::GetResult來判斷作業的結果。 如果結果成功,則後續的 ReadFragment 呼叫已下載範圍的資料流程將會成功。
重要
安全性/效能注意事項:嘗試擷取遠端字型時,攻擊者通常會有詐騙所呼叫之預定伺服器的可能性,或伺服器可能沒有回應。 如果您要實作自訂網路互動,可能比處理協力廠商伺服器時更能控制風險降低措施。 不過,您可以考慮適當的風險降低措施,以避免資訊洩漏或拒絕服務。 建議使用 HTTPS 之類的安全通訊協定。 此外,您應該在一些逾時中建置,讓傳回給DirectWrite的事件控制碼最終會設定。
支援舊版 Windows 上的案例
在舊版 Windows 上DirectWrite可支援描述的案例,但在應用程式中,使用Windows 10之前可用的更有限 API,需要更多自訂實作。 如需詳細資訊,請參閱 Windows 7/8 (自訂字型集合) 。