zufuliu / notepad4

Notepad4 (Notepad2⨯2, Notepad2++) is a light-weight Scintilla based text editor for Windows with syntax highlighting, code folding, auto-completion and API list for many programming languages and documents, bundled with file browser plugin matepath.
Other
3.23k stars 209 forks source link

Fallback font #690

Open maboroshin opened 1 year ago

maboroshin commented 1 year ago

Asians want to change fonts to ASCII or CJK characters. English speakers want to display emoji and symbols. If a character is missing from one font, it is brought from another font. Current Scintilla doesn’t appear to have this feature.

VS Code

The font MS Gothic contains Kanji characters. Old "MS Gothic" is the default fallback font in VS Code. Specify editor.fontFamily in VS Code.

The following specified fonts don't include Kanji characters, so Kanji characters are rendered in MS Gothic font.

"editor.fontFamily": "Consolas,'Courier New'"

Meiryo is a newer font that includes Kanji characters. If you want to give priority to Meiryo, write as follows. Kanji characters are rendered in Meiryo font.

"editor.fontFamily": "Consolas, Meiryo"

English fonts suitable for programming are specified first. It is easy to distinguish between 0 and O, 1 and I and l. Because MS Gothic and Meiryo are difficult to distinguish.

Another way

Some people merge any two fonts for editors without font fallback. e.g. https://myrica.estable.jp/

CSS font-family

It seems to work the same as MS Code.

The font-family property specifies a list of fonts, from highest priority to lowest. Font selection does not stop at the first font in the list that is on the user's system. Rather, font selection is done one character at a time, so that if an available font does not have a glyph for a needed character, the latter fonts are tried. https://developer.mozilla.org/en-US/docs/Web/CSS/font-family

zufuliu commented 1 year ago

Current Scintilla uses system default fallback font that depends on rendering technology, GDI uses legacy font, DirectWrite uses new font.

Implement this requires lower level layout, which Scintilla is not planing to implement (at least in short term as far as I know).

maboroshin commented 1 year ago

Thank you. I'm not sure if I'm right. Is this what you mean?

  1. For example, specify an alphabet-only font on Scintilla.
  2. With that setting, try display Kanji characters on Scintilla.
  3. Then, the Windows fallback font is working.
zufuliu commented 1 year ago

Yes. it works for Chinese (SimSun used in GDI and Microsoft YaHei (UI) used in DirectWrite), I think it should also works for Japanese and Korean.

maboroshin commented 1 year ago

Thank you. There are several ways to changing font.

zufuliu commented 1 year ago

Custom fallback font is possible on Windows 8.1 (which add colored emoji) and later with DirectWrite, https://learn.microsoft.com/en-us/windows/win32/api/dwrite_2/nn-dwrite_2-idwritefontfallbackbuilder, will need to do some experiments.

https://sourceforge.net/p/scintilla/feature-requests/1456/

zufuliu commented 1 year ago

following code can be used to changed default font for CJK, however word wrap (maybe rendering too) is low after the change:

IDWriteFontFallback *customFontFallback = nullptr;

IDWriteFactory2 *pIDWriteFactory2 = static_cast<IDWriteFactory2 *>(pIDWriteFactory);
IDWriteFontFallbackBuilder *fontFallbackBuilder = nullptr;
hr = pIDWriteFactory2->CreateFontFallbackBuilder(&fontFallbackBuilder);
if (SUCCEEDED(hr)) {
    const DWRITE_UNICODE_RANGE ranges[] = {
        {0x3400, 0x4DBF}, // U+3400..U+4DBF CJK Unified Ideographs Extension A
        {0x4E00, 0x9FFF}, // U+4E00..U+9FFF CJK Unified Ideographs
        {0xF900, 0xFAFF}, // U+F900..U+FAFF CJK Compatibility Ideographs
    };
    const WCHAR *targetFamilyNames[] = {
        L"SimSun",
    };
    hr = fontFallbackBuilder->AddMapping(ranges, 3, targetFamilyNames, 1,
        nullptr, nullptr, nullptr, 1.0f);
    IDWriteFontFallback *fontFallback = nullptr;
    hr = pIDWriteFactory2->GetSystemFontFallback(&fontFallback);
    if (SUCCEEDED(hr)) {
        hr = fontFallbackBuilder->AddMappings(fontFallback);
        ReleaseUnknown(fontFallback);
    }
    hr = fontFallbackBuilder->CreateFontFallback(&fontFallback);
    if (SUCCEEDED(hr)) {
        customFontFallback = fontFallback;
    }
    ReleaseUnknown(fontFallbackBuilder);
}

static_cast<IDWriteTextLayout2 *>(pTextLayout)->SetFontFallback(customFontFallback);
maboroshin commented 1 year ago

I'm not sure how it should be implemented. If a CJK-only font existed, it would be in this range.

Blocks.txt