fix(platform): decode GDI ANSI text with the font charset's code page

TextOutA / GetTextExtentPoint32A in the Win32 GDI shim always decoded with CP_ACP (1252), so the
server notices drawn by uiTip.TipBoard / BigBoard (CTextBar, a GB2312_CHARSET font in the 936
locale) showed GBK bytes as Latin-1 mojibake. Windows GDI decodes ANSI calls with the code page of
the selected font's charset; the shim now keeps lfCharSet on the font and does the same.
port.text_bar checks TextOutA of GBK bytes against TextOutW of the Unicode text, and the extent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-30 16:42:44 +09:00
co-authored by Claude Opus 5.5
parent 58a3d01027
commit 78d14776b8
2 changed files with 87 additions and 4 deletions
+30 -4
View File
@@ -62,6 +62,7 @@ struct HFONT__ : GdiObject
int descent = 0;
bool italic = false;
bool antialiased = false;
BYTE charset = ANSI_CHARSET;
};
struct HDC__
@@ -293,6 +294,7 @@ HFONT CreateFontIndirect(const LOGFONT* lplf)
for (const char* name : kLinked)
if (FT_Face linked = open_named(name); linked && linked != face) font->fallbacks.push_back(linked);
font->italic = lplf->lfItalic != 0;
font->charset = lplf->lfCharSet;
font->antialiased = lplf->lfQuality == ANTIALIASED_QUALITY;
const TT_OS2* os2 = static_cast<const TT_OS2*>(FT_Get_Sfnt_Table(face, FT_SFNT_OS2));
@@ -447,24 +449,48 @@ BOOL TextOutW(HDC hdc, int x, int y, LPCWSTR lpString, int c)
return TRUE;
}
// The ANSI GDI calls decode the bytes with the code page of the selected font's charset (CTextBar selects
// GetCharsetFromCodePage of the locale, e.g. GB2312_CHARSET for a 936 locale); ANSI/DEFAULT fall back to CP_ACP.
static UINT dc_code_page(HDC hdc)
{
switch (dc_font(hdc)->charset) {
case SHIFTJIS_CHARSET: return 932;
case HANGUL_CHARSET: return 949;
case GB2312_CHARSET: return 936;
case CHINESEBIG5_CHARSET: return 950;
case GREEK_CHARSET: return 1253;
case TURKISH_CHARSET: return 1254;
case VIETNAMESE_CHARSET: return 1258;
case HEBREW_CHARSET: return 1255;
case ARABIC_CHARSET: return 1256;
case BALTIC_CHARSET: return 1257;
case RUSSIAN_CHARSET: return 1251;
case THAI_CHARSET: return 874;
case EASTEUROPE_CHARSET: return 1250;
default: return CP_ACP;
}
}
BOOL TextOutA(HDC hdc, int x, int y, LPCSTR lpString, int c)
{
const int n = MultiByteToWideChar(CP_ACP, 0, lpString, c, nullptr, 0);
const UINT cp = dc_code_page(hdc);
const int n = MultiByteToWideChar(cp, 0, lpString, c, nullptr, 0);
if (n <= 0) return c == 0;
std::vector<WCHAR> wide(static_cast<size_t>(n));
MultiByteToWideChar(CP_ACP, 0, lpString, c, wide.data(), n);
MultiByteToWideChar(cp, 0, lpString, c, wide.data(), n);
return TextOutW(hdc, x, y, wide.data(), n);
}
BOOL GetTextExtentPoint32A(HDC hdc, LPCSTR lpString, int c, LPSIZE psizl)
{
const int n = MultiByteToWideChar(CP_ACP, 0, lpString, c, nullptr, 0);
const UINT cp = dc_code_page(hdc);
const int n = MultiByteToWideChar(cp, 0, lpString, c, nullptr, 0);
if (n <= 0)
{
if (c != 0 || !psizl) return FALSE;
return GetTextExtentPoint32W(hdc, nullptr, 0, psizl);
}
std::vector<WCHAR> wide(static_cast<size_t>(n));
MultiByteToWideChar(CP_ACP, 0, lpString, c, wide.data(), n);
MultiByteToWideChar(cp, 0, lpString, c, wide.data(), n);
return GetTextExtentPoint32W(hdc, wide.data(), n, psizl);
}