ð
Windows 10 Arial字体实际上不支持字符。Windows 10使用
"Segoe UI Emoji"
作为特定代码点的备用字体。
所以首先我们必须弄清楚是否使用了回退字体。然后检查字形索引,看看它是否是豆腐字符(通常显示为方形符号)
â¯
)
我们可以使用元文件来确定是否使用了字体替换。选择要显示的字体
HDC
使用
ScriptGetFontProperties
查找不支持的图示符的值。
使用
GetCharacterPlacement
查找字符串的图示符索引。如果glyph索引与不支持的glyph匹配,则代码点将被打印为tofu
.
编辑:
如果您尝试打印汉字等,则必须选择合适的字体(SimSun为中文)
这部分由
IMLangFontLink
. 这是一种不同类型的字体替换。
下面的示例将测试单个代码点(它可以扩展为处理字符串)。
Segoe UI
选择字体,然后选择汉字
请
,它将切换
微软雅黑效果
到
SimSun
对于表情符号,它将切换
到
Segoe UI Emoji
另请参见中的这篇文章
oldnewthing
TextOut
处理它(在Windows 10中处理正确,因此结果显示正常)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <windows.h>
#include <usp10.h>
#include <AtlBase.h>
#include <AtlCom.h>
#include <mlang.h>
#pragma comment(lib, "Usp10.lib")
int CALLBACK metafileproc(HDC, HANDLETABLE*, const ENHMETARECORD *record,
int, LPARAM logfont)
{
if(record->iType == EMR_EXTCREATEFONTINDIRECTW)
{
auto ptr = (const EMREXTCREATEFONTINDIRECTW*)record;
*(LOGFONT*)logfont = ptr->elfw.elfLogFont;
}
return 1;
}
HFONT GetFallbackFont(const wchar_t *str, HFONT hfont_test)
{
//use metafile to find the fallback font
auto metafile_hdc = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
auto metafile_oldfont = SelectObject(metafile_hdc, hfont_test);
SCRIPT_STRING_ANALYSIS ssa;
ScriptStringAnalyse(metafile_hdc, str, wcslen(str), 0, -1,
SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
0, NULL, NULL, NULL, NULL, NULL, &ssa);
ScriptStringOut(ssa, 0, 0, 0, NULL, 0, 0, FALSE);
ScriptStringFree(&ssa);
SelectObject(metafile_hdc, metafile_oldfont);
auto hmetafile = CloseEnhMetaFile(metafile_hdc);
LOGFONT logfont = { 0 };
EnumEnhMetaFile(0, hmetafile, metafileproc, &logfont, NULL);
wprintf(L"Selecting fallback font: %s\n", logfont.lfFaceName);
HFONT hfont = CreateFontIndirect(&logfont);
DeleteEnhMetaFile(hmetafile);
return hfont;
}
//IsTofu is for testing emojis
//It accepts a Unicode string
bool IsTofuError(HDC hdc, HFONT hfont_test, const wchar_t *str)
{
if(wcsstr(str, L" "))
{
wprintf(L"*** cannot test strings containing blank space\n");
}
auto hfont = GetFallbackFont(str, hfont_test);
auto oldfont = SelectObject(hdc, hfont);
//find the characters not supported in this font
//note, blank space is blank, unsupported fonts can be blank also
SCRIPT_CACHE sc = NULL;
SCRIPT_FONTPROPERTIES fp = { sizeof(fp) };
ScriptGetFontProperties(hdc, &sc, &fp);
ScriptFreeCache(&sc);
wprintf(L"SCRIPT_FONTPROPERTIES:\n");
wprintf(L" Blank: %d, Default: %d, Invalid: %d\n",
fp.wgBlank, fp.wgDefault, fp.wgInvalid);
// Get glyph indices for the string
GCP_RESULTS gcp_results = { sizeof(GCP_RESULTS) };
gcp_results.nGlyphs = wcslen(str);
auto wstr_memory = (wchar_t*)calloc(wcslen(str) + 1, sizeof(wchar_t));
gcp_results.lpGlyphs = wstr_memory;
GetCharacterPlacement(hdc, str, wcslen(str), 0, &gcp_results, GCP_GLYPHSHAPE);
//check the characters against wgBlank...
bool istofu = false;
wprintf(L"Glyphs:");
for(UINT i = 0; i < gcp_results.nGlyphs; i++)
{
wchar_t n = gcp_results.lpGlyphs[i];
wprintf(L"%d,", (int)n);
if(n == fp.wgBlank || n == fp.wgInvalid || n == fp.wgDefault)
istofu = true;
}
wprintf(L"\n");
free(wstr_memory);
SelectObject(hdc, oldfont);
DeleteObject(hfont);
if (istofu)
wprintf(L"Tofu error\n\n");
return istofu;
}
//get_font_link checks if there is font substitution,
//this usually applies to Asian fonts
//Note, this function doesn't accept a unicode string
//it only takes a single code point. You can imrpove it to accept strings
bool get_font_link(const wchar_t *single_codepoint,
HDC hdc,
HFONT &hfont_src,
HFONT &hfont_dst,
CComPtr<IMLangFontLink> &ifont,
CComPtr<IMLangCodePages> &icodepages)
{
DWORD codepages_dst[100] = { 0 };
LONG codepages_count = 100;
DWORD codepages = 0;
if(FAILED(icodepages->GetStrCodePages(single_codepoint, wcslen(single_codepoint),
0, codepages_dst, &codepages_count)))
return false;
codepages = codepages_dst[0];
if(FAILED(ifont->MapFont(hdc, codepages_dst[0], hfont_src, &hfont_dst)))
return false;
SelectObject(hdc, hfont_dst);
wchar_t buf[100];
GetTextFace(hdc, _countof(buf), buf);
wprintf(L"get_font_link:\nSelecting a different font: %s\n", buf);
return true;
}
int main()
{
CoInitialize(NULL);
{
CComPtr<IMultiLanguage> imultilang;
CComPtr<IMLangFontLink> ifont;
CComPtr<IMLangCodePages> icodepages;
if(FAILED(imultilang.CoCreateInstance(CLSID_CMultiLanguage))) return 0;
if(FAILED(imultilang->QueryInterface(&ifont))) return 0;
if(FAILED(imultilang->QueryInterface(&icodepages))) return 0;
//const wchar_t *single_codepoint = L"a";
//const wchar_t *single_codepoint = L"请";
const wchar_t *single_codepoint = L"ð";
auto hdc = GetDC(0);
auto memdc = CreateCompatibleDC(hdc);
auto hbitmap = CreateCompatibleBitmap(hdc, 1, 1);
auto oldbmp = SelectObject(memdc, hbitmap);
auto hfont_src = CreateFont(10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Segoe UI");
auto oldfont = SelectObject(hdc, hfont_src);
HFONT hfont_dst = NULL;
if(IsTofuError(hdc, hfont_src, single_codepoint))
{
if(!get_font_link(
single_codepoint, memdc, hfont_src, hfont_dst, ifont, icodepages))
wprintf(L"Can't find a substitution!\n");
}
SelectObject(memdc, oldbmp);
SelectObject(memdc, oldfont);
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(0, hdc);
DeleteObject(hfont_src);
if(ifont && hfont_dst)
ifont->ReleaseFont(hfont_dst);
}
CoUninitialize();
return 0;
}
输出:
IsTofu
是
false
会的
true
使用
GetUniscribeFallbackFont
从…起
this link
注意,Windows文档描述了
GetCharacterPlacement