I need to get the most suitable font for different language. So I could draw different language's text without using GDI text out API such as TextOut.
Actually, the api TextOut does it.
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);
Because the Arial font does not support Chinese, TextOut should not be able to draw the Chinese '我爱'. But it does and choose a suitable font, a normal font rather than some kind of art font, for it.
How could I simulate what TextOut does, or is there any other way to figure out the best suitable font for one language under Windows?
Yes, it is possible to do this. Here is the slight modified code snippet from chrome project,
// 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;
}
Sample code:
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);
微软雅黑
is a font which does not support arabic, so we need a fallback font for the arabic. In my box, GDI automatically choose Microsoft Sans Serif
for the arabic.