Use system fonts as fallback and improve system font handling.

Add support for font weight and stretch selection when using system fonts.
Add function to get system fallback font from a font name, style, text, and language code.
Implement system font support for Android.
Use system fonts as a last resort fallback.
This commit is contained in:
bruvzg
2022-11-21 15:04:01 +02:00
parent 015dc492de
commit ecec415988
51 changed files with 2756 additions and 150 deletions

View File

@ -43,12 +43,12 @@
#include "platform/windows/display_server_windows.h"
#include "servers/audio_server.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/text_server.h"
#include "windows_terminal_logger.h"
#include <avrt.h>
#include <bcrypt.h>
#include <direct.h>
#include <dwrite.h>
#include <knownfolders.h>
#include <process.h>
#include <regstr.h>
@ -189,6 +189,27 @@ void OS_Windows::initialize() {
IPUnix::make_default();
main_loop = nullptr;
CoInitialize(nullptr);
HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&dwrite_factory));
if (SUCCEEDED(hr)) {
hr = dwrite_factory->GetSystemFontCollection(&font_collection, false);
if (SUCCEEDED(hr)) {
dwrite_init = true;
hr = dwrite_factory->QueryInterface(&dwrite_factory2);
if (SUCCEEDED(hr)) {
hr = dwrite_factory2->GetSystemFontFallback(&system_font_fallback);
if (SUCCEEDED(hr)) {
dwrite2_init = true;
}
}
}
}
if (!dwrite_init) {
print_verbose("Unable to load IDWriteFactory, system font support is disabled.");
} else if (!dwrite2_init) {
print_verbose("Unable to load IDWriteFactory2, automatic system font fallback is disabled.");
}
}
void OS_Windows::delete_main_loop() {
@ -203,6 +224,22 @@ void OS_Windows::set_main_loop(MainLoop *p_main_loop) {
}
void OS_Windows::finalize() {
if (dwrite_factory2) {
dwrite_factory2->Release();
dwrite_factory2 = nullptr;
}
if (font_collection) {
font_collection->Release();
font_collection = nullptr;
}
if (system_font_fallback) {
system_font_fallback->Release();
system_font_fallback = nullptr;
}
if (dwrite_factory) {
dwrite_factory->Release();
dwrite_factory = nullptr;
}
#ifdef WINMIDI_ENABLED
driver_midi.close();
#endif
@ -726,21 +763,17 @@ Error OS_Windows::set_cwd(const String &p_cwd) {
}
Vector<String> OS_Windows::get_system_fonts() const {
if (!dwrite_init) {
return Vector<String>();
}
Vector<String> ret;
HashSet<String> font_names;
ComAutoreleaseRef<IDWriteFactory> dwrite_factory;
HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&dwrite_factory.reference));
ERR_FAIL_COND_V(FAILED(hr) || dwrite_factory.is_null(), ret);
ComAutoreleaseRef<IDWriteFontCollection> font_collection;
hr = dwrite_factory->GetSystemFontCollection(&font_collection.reference, false);
ERR_FAIL_COND_V(FAILED(hr) || font_collection.is_null(), ret);
UINT32 family_count = font_collection->GetFontFamilyCount();
for (UINT32 i = 0; i < family_count; i++) {
ComAutoreleaseRef<IDWriteFontFamily> family;
hr = font_collection->GetFontFamily(i, &family.reference);
HRESULT hr = font_collection->GetFontFamily(i, &family.reference);
ERR_CONTINUE(FAILED(hr) || family.is_null());
ComAutoreleaseRef<IDWriteLocalizedStrings> family_names;
@ -771,7 +804,98 @@ Vector<String> OS_Windows::get_system_fonts() const {
return ret;
}
String OS_Windows::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#endif
class FallbackTextAnalysisSource : public IDWriteTextAnalysisSource {
LONG _cRef = 1;
bool rtl = false;
Char16String string;
Char16String locale;
IDWriteNumberSubstitution *n_sub = nullptr;
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) {
if (IID_IUnknown == riid) {
AddRef();
*ppvInterface = (IUnknown *)this;
} else if (__uuidof(IMMNotificationClient) == riid) {
AddRef();
*ppvInterface = (IMMNotificationClient *)this;
} else {
*ppvInterface = nullptr;
return E_NOINTERFACE;
}
return S_OK;
}
ULONG STDMETHODCALLTYPE AddRef() {
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release() {
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef) {
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 p_text_position, WCHAR const **r_text_string, UINT32 *r_text_length) override {
if (p_text_position >= (UINT32)string.length()) {
*r_text_string = nullptr;
*r_text_length = 0;
return S_OK;
}
*r_text_string = reinterpret_cast<const wchar_t *>(string.get_data()) + p_text_position;
*r_text_length = string.length() - p_text_position;
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 p_text_position, WCHAR const **r_text_string, UINT32 *r_text_length) override {
if (p_text_position < 1 || p_text_position >= (UINT32)string.length()) {
*r_text_string = nullptr;
*r_text_length = 0;
return S_OK;
}
*r_text_string = reinterpret_cast<const wchar_t *>(string.get_data());
*r_text_length = p_text_position;
return S_OK;
}
DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() override {
return (rtl) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
}
HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 p_text_position, UINT32 *r_text_length, WCHAR const **r_locale_name) override {
*r_locale_name = reinterpret_cast<const wchar_t *>(locale.get_data());
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 p_text_position, UINT32 *r_text_length, IDWriteNumberSubstitution **r_number_substitution) override {
*r_number_substitution = n_sub;
return S_OK;
}
FallbackTextAnalysisSource(const Char16String &p_text, const Char16String &p_locale, bool p_rtl, IDWriteNumberSubstitution *p_nsub) {
_cRef = 1;
string = p_text;
locale = p_locale;
n_sub = p_nsub;
rtl = p_rtl;
};
virtual ~FallbackTextAnalysisSource() {}
};
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
String OS_Windows::_get_default_fontname(const String &p_font_name) const {
String font_name = p_font_name;
if (font_name.to_lower() == "sans-serif") {
font_name = "Arial";
@ -784,18 +908,157 @@ String OS_Windows::get_system_font_path(const String &p_font_name, bool p_bold,
} else if (font_name.to_lower() == "fantasy") {
font_name = "Gabriola";
}
return font_name;
}
ComAutoreleaseRef<IDWriteFactory> dwrite_factory;
HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&dwrite_factory.reference));
ERR_FAIL_COND_V(FAILED(hr) || dwrite_factory.is_null(), String());
DWRITE_FONT_WEIGHT OS_Windows::_weight_to_dw(int p_weight) const {
if (p_weight < 150) {
return DWRITE_FONT_WEIGHT_THIN;
} else if (p_weight < 250) {
return DWRITE_FONT_WEIGHT_EXTRA_LIGHT;
} else if (p_weight < 325) {
return DWRITE_FONT_WEIGHT_LIGHT;
} else if (p_weight < 375) {
return DWRITE_FONT_WEIGHT_SEMI_LIGHT;
} else if (p_weight < 450) {
return DWRITE_FONT_WEIGHT_NORMAL;
} else if (p_weight < 550) {
return DWRITE_FONT_WEIGHT_MEDIUM;
} else if (p_weight < 650) {
return DWRITE_FONT_WEIGHT_DEMI_BOLD;
} else if (p_weight < 750) {
return DWRITE_FONT_WEIGHT_BOLD;
} else if (p_weight < 850) {
return DWRITE_FONT_WEIGHT_EXTRA_BOLD;
} else if (p_weight < 925) {
return DWRITE_FONT_WEIGHT_BLACK;
} else {
return DWRITE_FONT_WEIGHT_EXTRA_BLACK;
}
}
ComAutoreleaseRef<IDWriteFontCollection> font_collection;
hr = dwrite_factory->GetSystemFontCollection(&font_collection.reference, false);
ERR_FAIL_COND_V(FAILED(hr) || font_collection.is_null(), String());
DWRITE_FONT_STRETCH OS_Windows::_stretch_to_dw(int p_stretch) const {
if (p_stretch < 56) {
return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
} else if (p_stretch < 69) {
return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
} else if (p_stretch < 81) {
return DWRITE_FONT_STRETCH_CONDENSED;
} else if (p_stretch < 93) {
return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
} else if (p_stretch < 106) {
return DWRITE_FONT_STRETCH_NORMAL;
} else if (p_stretch < 137) {
return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
} else if (p_stretch < 144) {
return DWRITE_FONT_STRETCH_EXPANDED;
} else if (p_stretch < 162) {
return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
} else {
return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
}
}
Vector<String> OS_Windows::get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale, const String &p_script, int p_weight, int p_stretch, bool p_italic) const {
if (!dwrite2_init) {
return Vector<String>();
}
String font_name = _get_default_fontname(p_font_name);
bool rtl = TS->is_locale_right_to_left(p_locale);
Char16String text = p_text.utf16();
Char16String locale = p_locale.utf16();
ComAutoreleaseRef<IDWriteNumberSubstitution> number_substitution;
HRESULT hr = dwrite_factory->CreateNumberSubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, reinterpret_cast<const wchar_t *>(locale.get_data()), true, &number_substitution.reference);
ERR_FAIL_COND_V(FAILED(hr) || number_substitution.is_null(), Vector<String>());
FallbackTextAnalysisSource fs = FallbackTextAnalysisSource(text, locale, rtl, number_substitution.reference);
UINT32 mapped_length = 0;
FLOAT scale = 0.0;
ComAutoreleaseRef<IDWriteFont> dwrite_font;
hr = system_font_fallback->MapCharacters(
&fs,
0,
(UINT32)text.length(),
font_collection,
reinterpret_cast<const wchar_t *>(font_name.utf16().get_data()),
_weight_to_dw(p_weight),
p_italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
_stretch_to_dw(p_stretch),
&mapped_length,
&dwrite_font.reference,
&scale);
if (FAILED(hr) || dwrite_font.is_null()) {
return Vector<String>();
}
ComAutoreleaseRef<IDWriteFontFace> dwrite_face;
hr = dwrite_font->CreateFontFace(&dwrite_face.reference);
if (FAILED(hr) || dwrite_face.is_null()) {
return Vector<String>();
}
UINT32 number_of_files = 0;
hr = dwrite_face->GetFiles(&number_of_files, nullptr);
if (FAILED(hr)) {
return Vector<String>();
}
Vector<ComAutoreleaseRef<IDWriteFontFile>> files;
files.resize(number_of_files);
hr = dwrite_face->GetFiles(&number_of_files, (IDWriteFontFile **)files.ptrw());
if (FAILED(hr)) {
return Vector<String>();
}
Vector<String> ret;
for (UINT32 i = 0; i < number_of_files; i++) {
void const *reference_key = nullptr;
UINT32 reference_key_size = 0;
ComAutoreleaseRef<IDWriteLocalFontFileLoader> loader;
hr = files.write[i]->GetLoader((IDWriteFontFileLoader **)&loader.reference);
if (FAILED(hr) || loader.is_null()) {
continue;
}
hr = files.write[i]->GetReferenceKey(&reference_key, &reference_key_size);
if (FAILED(hr)) {
continue;
}
WCHAR file_path[MAX_PATH];
hr = loader->GetFilePathFromKey(reference_key, reference_key_size, &file_path[0], MAX_PATH);
if (FAILED(hr)) {
continue;
}
String fpath = String::utf16((const char16_t *)&file_path[0]);
WIN32_FIND_DATAW d;
HANDLE fnd = FindFirstFileW((LPCWSTR)&file_path[0], &d);
if (fnd != INVALID_HANDLE_VALUE) {
String fname = String::utf16((const char16_t *)d.cFileName);
if (!fname.is_empty()) {
fpath = fpath.get_base_dir().path_join(fname);
}
FindClose(fnd);
}
ret.push_back(fpath);
}
return ret;
}
String OS_Windows::get_system_font_path(const String &p_font_name, int p_weight, int p_stretch, bool p_italic) const {
if (!dwrite_init) {
return String();
}
String font_name = _get_default_fontname(p_font_name);
UINT32 index = 0;
BOOL exists = false;
font_collection->FindFamilyName((const WCHAR *)font_name.utf16().get_data(), &index, &exists);
HRESULT hr = font_collection->FindFamilyName((const WCHAR *)font_name.utf16().get_data(), &index, &exists);
if (FAILED(hr)) {
return String();
}
@ -807,7 +1070,7 @@ String OS_Windows::get_system_font_path(const String &p_font_name, bool p_bold,
}
ComAutoreleaseRef<IDWriteFont> dwrite_font;
hr = family->GetFirstMatchingFont(p_bold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, p_italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL, &dwrite_font.reference);
hr = family->GetFirstMatchingFont(_weight_to_dw(p_weight), _stretch_to_dw(p_stretch), p_italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL, &dwrite_font.reference);
if (FAILED(hr) || dwrite_font.is_null()) {
return String();
}
@ -1192,7 +1455,7 @@ String OS_Windows::get_unique_id() const {
bool OS_Windows::_check_internal_feature_support(const String &p_feature) {
if (p_feature == "system_fonts") {
return true;
return dwrite_init;
}
if (p_feature == "pc") {
return true;