wxWidgets / wxWidgets

Cross-Platform C++ GUI Library
https://www.wxwidgets.org/
6.01k stars 1.76k forks source link

wxFONTFAMILY mismatch default fonts in modern Windows #24178

Open 0tkl opened 8 months ago

0tkl commented 8 months ago

Description

Bug description:

In various system locales, text defined with wxFONTFAMILY_* may not displayed in expected styles.

Expected vs observed behaviour:

en-US

locale font family expected font observed font
English Script Segoe Script (compared with other locales) Arial

zh-CN zh-TW

locale font family expected font observed font
Chinese Simpified Default Microsoft YaHei UI (Since Windows 8)
微软雅黑 / Microsoft YaHei (Until Windows 7)
宋体 / SimSun
Chinese Simpified Script Segoe Script (compared with other locales) 宋体 / SimSun
Chinese Simpified Swiss Microsoft YaHei UI (Since Windows 8)
微软雅黑 / Microsoft YaHei (Until Windows 7)
宋体 / SimSun
Chinese Traditional Default Microsoft JhengHei UI (Since Windows 8)
微軟正黑體 / Microsoft JhengHei (Until Windows 7)
新細明體 / PMingLiU
Chinese Traditional Swiss Microsoft JhengHei UI (Since Windows 8)
微軟正黑體 / Microsoft JhengHei (Until Windows 7)
新細明體 / PMingLiU

ja-JP

locale font family expected font observed font
Japanese Default Yu Gothic UI (Since Windows 10)
Meiryo UI (Until Windows 8.1)
MS PGothic
Japanese Roman 游明朝 / Yu Mincho (Since Windows 8.1 or 7*)
MS PMincho (Until Windows 8 or Vista)
MS PMincho
Japanese Swiss Yu Gothic UI (Since Windows 10)
Meiryo UI (Until Windows 8.1)
MS PGothic

ko-KR

locale font family expected font observed font
Korean Default 맑은 고딕 / Malgun Gothic Gulim
Korean Swiss 맑은 고딕 / Malgun Gothic Gulim

To Reproduce:

Build the code and run in English, Chinese Simpified, Chinese Traditional, Japanese and Korean locales respectively.

#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#   include "wx/wx.h"
#endif

class MyApp : public wxApp {
public:
    virtual bool OnInit() override;
};

class MyFrame : public wxFrame {
public:
    MyFrame(const wxString& title);
};

wxIMPLEMENT_APP(MyApp);

bool MyApp::OnInit()
{
    if ( !wxApp::OnInit() ) return false;
    MyFrame *frame = new MyFrame(wxT("wxFONTFAMILY Test"));
    frame->Show(true);
    return true;
}

MyFrame::MyFrame(const wxString& title)
       : wxFrame(nullptr, wxID_ANY, title)
{
    wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
    auto text1 = new wxStaticText(this, wxID_ANY, "wxFONTFAMILY_DEFAULT");
    auto text2 = new wxStaticText(this, wxID_ANY, "wxFONTFAMILY_DECORATIVE");
    auto text3 = new wxStaticText(this, wxID_ANY, "wxFONTFAMILY_ROMAN");
    auto text4 = new wxStaticText(this, wxID_ANY, "wxFONTFAMILY_SCRIPT");
    auto text5 = new wxStaticText(this, wxID_ANY, "wxFONTFAMILY_SWISS");
    auto text6 = new wxStaticText(this, wxID_ANY, "wxFONTFAMILY_TELETYPE");

    text1->SetFont(wxFontInfo(16).Family(wxFONTFAMILY_DEFAULT));
    text2->SetFont(wxFontInfo(16).Family(wxFONTFAMILY_DECORATIVE));
    text3->SetFont(wxFontInfo(16).Family(wxFONTFAMILY_ROMAN));
    text4->SetFont(wxFontInfo(16).Family(wxFONTFAMILY_SCRIPT));
    text5->SetFont(wxFontInfo(16).Family(wxFONTFAMILY_SWISS));
    text6->SetFont(wxFontInfo(16).Family(wxFONTFAMILY_TELETYPE));

    sizer->Add(text1, wxSizerFlags().Center());
    sizer->Add(text2, wxSizerFlags().Center());
    sizer->Add(text3, wxSizerFlags().Center());
    sizer->Add(text4, wxSizerFlags().Center());
    sizer->Add(text5, wxSizerFlags().Center());
    sizer->Add(text6, wxSizerFlags().Center());
    SetSizer(sizer);
}

Platform and version information

vadz commented 8 months ago

I'm really not sure we can do anything about this, our wxFONTFAMILY_SWISS maps directly to Win32 FF_SWISS and if Windows selects an inappropriate font in this case, it probably indicates that the font describes itself incorrectly or something else outside of our control. I.e. I'm almost certain that if you write a simple program doing the same thing using Win32 API directly, the results would be the same. Of course, if you can actually test this and this turns out not to be the case, please let us know, but otherwise I think this is out of scope for wx.

And if you want to use a precise font, the best is really to let the user choose it and then save it using wxFont::GetNativeFontInfoDesc() for the subsequent runs.

0tkl commented 8 months ago

I assume wxFONTFAMILY_DEFAULT, by semantics, equals to “system UI font on user's device”.

Try this progam. In my device, it outputs the expected fonts across locales (Segoe UI, YaHei UI, JhengHei UI, Yu Gothic UI, Malgun Gothic…). The core thing is:

NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
Font default_font(hdc, &(ncm.lfMessageFont));

This solution had been used in Chromium. I just learned this solution from StackOverflow.

If assuming the UI font of all supporting operating systems are all sans-serifs, then SWISS can be mapped to DEFAULT.

vadz commented 8 months ago

I assume wxFONTFAMILY_DEFAULT, by semantics, equals to “system UI font on user's device”.

Actually this would be wxNORMAL_FONT (which uses the "solution from SO" for ~15 years, since 16b0c553987). wxFONTFAMILY_DEFAULT is not very useful, admittedly...

If assuming the UI font of all supporting operating systems are all sans-serifs, then SWISS can be mapped to DEFAULT.

I have no idea whether system default font is always sans-serif and I'm pretty sure that you can change it to a serif font if you want to, so this definitely seems wrong to me.