我需要为不同的语言获取最合适的字体。所以我可以在不使用GDI文本输出API的情况下绘制不同语言的文本,例如TextOut。
实际上,api TextOut就是这么做的。
HFONT hFont = NULL;
LOGFONT lg = {0};
lg.lfHeight = 14;
lg.lfWeight = FW_NORMAL;
wcscpy_s(lg.lfFaceName, LF_FACESIZE ,L"Arial");
hFont = CreateFontIndirect(&lg);
SelectObject(hdc,hFont);
TextOut(hdc, 0, 50, L"abc我爱", 5);
因为Arial字体不支持中文,所以TextOut不能绘制中文'我爱'。但它确实并选择了合适的字体,普通字体而不是某种艺术字体。
我如何模拟TextOut的功能,还是有其他方法可以为Windows下的一种语言找出最合适的字体?
答案 0 :(得分:2)
是的,可以这样做。以下是chrome项目的轻微修改代码片段,
// Callback to |EnumEnhMetaFile()| to intercept font creation.
int CALLBACK MetaFileEnumProc(HDC hdc,
HANDLETABLE* table,
CONST ENHMETARECORD* record,
int table_entries,
LPARAM log_font)
{
if (record->iType == EMR_EXTCREATEFONTINDIRECTW) {
const EMREXTCREATEFONTINDIRECTW* create_font_record =
reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
*reinterpret_cast<LOGFONT*>(log_font) = create_font_record->elfw.elfLogFont;
}
return 1;
}
// Finds a fallback font to use to render the specified |text| with respect to
// an initial |font|. Returns the resulting font via out param |result|. Returns
// |true| if a fallback font was found.
// Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
// TODO(asvitkine): This should be moved to font_fallback_win.cc.
bool ChooseFallbackFont(HDC hdc,
HFONT font,
const wchar_t* text,
int text_length,
LOGFONT* result)
{
// Use a meta file to intercept the fallback font chosen by Uniscribe.
HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
if (!meta_file_dc)
return false;
if (font)
SelectObject(meta_file_dc, font);
SCRIPT_STRING_ANALYSIS script_analysis;
HRESULT hresult =
ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1,
SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
0, NULL, NULL, NULL, NULL, NULL, &script_analysis);
if (SUCCEEDED(hresult)) {
hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE);
ScriptStringFree(&script_analysis);
}
bool found_fallback = false;
HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc);
if (SUCCEEDED(hresult)) {
LOGFONT log_font;
log_font.lfFaceName[0] = 0;
EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL);
if (log_font.lfFaceName[0]) {
*result = log_font;
found_fallback = true;
}
}
DeleteEnhMetaFile(meta_file);
return found_fallback;
}
示例代码:
std::wstring arabicStr = L"ششش";
hdc = BeginPaint(hWnd, &ps);
LOGFONT logFont = {0};
wcscpy_s(logFont.lfFaceName, L"微软雅黑");
HFONT hFont = CreateFontIndirect(&logFont);
ChooseFallbackFont(hdc, hFont, arabicStr.c_str(), arabicStr.length(), &logFont);
ATLTRACE(logFont.lfFaceName);
HFONT hFontNew = CreateFontIndirect(&logFont);
HFONT hFontOld = (HFONT)SelectObject(hdc, hFontNew);
wchar_t glyphs[10] = {0};
GCP_RESULTS gcpRet = {0};
gcpRet.lStructSize = sizeof(gcpRet);
gcpRet.lpGlyphs = glyphs;
gcpRet.nGlyphs = 10;
ATLASSERT(GetCharacterPlacement(hdc, arabicStr.c_str(), arabicStr.length(), GCP_MAXEXTENT, &gcpRet,
GCP_DISPLAYZWG | GCP_GLYPHSHAPE | GCP_REORDER ));
RECT rcClient;
GetClientRect(hWnd, &rcClient);
ExtTextOut(hdc, 200, 200, ETO_GLYPH_INDEX | ETO_RTLREADING, &rcClient, gcpRet.lpGlyphs, gcpRet.nGlyphs, NULL);
SelectObject(hdc, hFontOld);
DeleteObject(hFontNew);
DeleteObject(hFont);
EndPaint(hWnd, &ps);
微软雅黑
是一种不支持阿拉伯语的字体,因此我们需要阿拉伯语的后备字体。在我的框中,GDI会自动为阿拉伯语选择Microsoft Sans Serif
。
答案 1 :(得分:2)
我必须这样做。这是可能的,但它做了很多工作。
Windows实际上使用多种方案:Uniscribe,使用字体回退,它依赖于哪些字体涵盖哪些脚本的内部知识。更高级别的API,如GDI,使用字体链接,它依赖于注册表中隐藏的字体列表。
我扩展的代码依赖于Uniscribe将文本分解为相同脚本的运行。然后检查所选字体以查看它是否可以覆盖给定运行中的所有字形。
如果不能,则检查注册表中的字体链接字段。如果那些不存在或者没有首选字体的列表,它会枚举所有字体以查找涵盖所需脚本的候选者(缓存编码了遇到的每种字体覆盖哪些脚本的位掩码)。
然后根据跑步的实际覆盖范围对这些候选人进行评分(排名)(仍然缺少多少个字形)。从排名较高的候选人中,它选择了一个最相似的候选人#34;到首选字体。我认为那部分是基于字体的PANOSE编号,但它已经有一段时间了,所以我不记得清楚了。可能只是一些启发式方法来查看字体粗细和类似参数。
还有一个黑客尝试最小化字体开关的数量,理论上最好使用覆盖整个文本的一个备份字体而不是多次切换字体。我记得,这解决了一些问题,其中CJK字符串将从中文字体中选择一些字形,而从日语字体中选择其他字形。对于本地读者来说,最好尽可能将整个字符串放在一个字体中,即使首选字体是另一种字体。