Browse Source

Merge pull request #62973 from bruvzg/sysfont_support

Rémi Verschelde 3 years ago
parent
commit
cc5135959b

+ 1 - 1
.github/workflows/linux_builds.yml

@@ -115,7 +115,7 @@ jobs:
           sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
           sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
               libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev \
               libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev \
               libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm xvfb wget unzip \
               libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm xvfb wget unzip \
-              llvm libspeechd-dev speech-dispatcher
+              llvm libspeechd-dev speech-dispatcher fontconfig libfontconfig-dev
 
 
       - name: Setup Godot build cache
       - name: Setup Godot build cache
         uses: ./.github/actions/godot-cache
         uses: ./.github/actions/godot-cache

+ 10 - 0
core/core_bind.cpp

@@ -231,6 +231,14 @@ void OS::crash(const String &p_message) {
 	CRASH_NOW_MSG(p_message);
 	CRASH_NOW_MSG(p_message);
 }
 }
 
 
+Vector<String> OS::get_system_fonts() const {
+	return ::OS::get_singleton()->get_system_fonts();
+}
+
+String OS::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
+	return ::OS::get_singleton()->get_system_font_path(p_font_name, p_bold, p_italic);
+}
+
 String OS::get_executable_path() const {
 String OS::get_executable_path() const {
 	return ::OS::get_singleton()->get_executable_path();
 	return ::OS::get_singleton()->get_executable_path();
 }
 }
@@ -589,6 +597,8 @@ void OS::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_processor_count"), &OS::get_processor_count);
 	ClassDB::bind_method(D_METHOD("get_processor_count"), &OS::get_processor_count);
 	ClassDB::bind_method(D_METHOD("get_processor_name"), &OS::get_processor_name);
 	ClassDB::bind_method(D_METHOD("get_processor_name"), &OS::get_processor_name);
 
 
+	ClassDB::bind_method(D_METHOD("get_system_fonts"), &OS::get_system_fonts);
+	ClassDB::bind_method(D_METHOD("get_system_font_path", "font_name", "bold", "italic"), &OS::get_system_font_path, DEFVAL(false), DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
 	ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
 	ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));

+ 2 - 0
core/core_bind.h

@@ -170,6 +170,8 @@ public:
 	void alert(const String &p_alert, const String &p_title = "ALERT!");
 	void alert(const String &p_alert, const String &p_title = "ALERT!");
 	void crash(const String &p_message);
 	void crash(const String &p_message);
 
 
+	Vector<String> get_system_fonts() const;
+	String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const;
 	String get_executable_path() const;
 	String get_executable_path() const;
 	int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
 	int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
 	int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
 	int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);

+ 2 - 0
core/os/os.h

@@ -142,6 +142,8 @@ public:
 	virtual void set_low_processor_usage_mode_sleep_usec(int p_usec);
 	virtual void set_low_processor_usage_mode_sleep_usec(int p_usec);
 	virtual int get_low_processor_usage_mode_sleep_usec() const;
 	virtual int get_low_processor_usage_mode_sleep_usec() const;
 
 
+	virtual Vector<String> get_system_fonts() const { return Vector<String>(); };
+	virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const { return String(); };
 	virtual String get_executable_path() const;
 	virtual String get_executable_path() const;
 	virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0;
 	virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0;
 	virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;
 	virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;

+ 1 - 1
doc/classes/FontFile.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <?xml version="1.0" encoding="UTF-8" ?>
 <class name="FontFile" inherits="Font" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 <class name="FontFile" inherits="Font" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 	<brief_description>
 	<brief_description>
-		FontFile source data and prerendered glyph cache, imported from dynamic or bitmap font.
+		Font source data and prerendered glyph cache, imported from dynamic or bitmap font.
 	</brief_description>
 	</brief_description>
 	<description>
 	<description>
 		[FontFile] contains a set of glyphs to represent Unicode characters imported from a font file, as well as a cache of rasterized glyphs, and a set of fallback [Font]s to use.
 		[FontFile] contains a set of glyphs to represent Unicode characters imported from a font file, as well as a cache of rasterized glyphs, and a set of fallback [Font]s to use.

+ 17 - 0
doc/classes/OS.xml

@@ -385,6 +385,23 @@
 				[b]Note:[/b] Shared storage is implemented on Android and allows to differentiate between app specific and shared directories. Shared directories have additional restrictions on Android.
 				[b]Note:[/b] Shared storage is implemented on Android and allows to differentiate between app specific and shared directories. Shared directories have additional restrictions on Android.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="get_system_font_path" qualifiers="const">
+			<return type="String" />
+			<argument index="0" name="font_name" type="String" />
+			<argument index="1" name="bold" type="bool" default="false" />
+			<argument index="2" name="italic" type="bool" default="false" />
+			<description>
+				Returns path to the system font file with [code]font_name[/code] and style. Return empty string if no matching fonts found.
+				[b]Note:[/b] This method is implemented on iOS, Linux, macOS and Windows.
+			</description>
+		</method>
+		<method name="get_system_fonts" qualifiers="const">
+			<return type="PackedStringArray" />
+			<description>
+				Returns list of font family names available.
+				[b]Note:[/b] This method is implemented on iOS, Linux, macOS and Windows.
+			</description>
+		</method>
 		<method name="get_thread_caller_id" qualifiers="const">
 		<method name="get_thread_caller_id" qualifiers="const">
 			<return type="int" />
 			<return type="int" />
 			<description>
 			<description>

+ 44 - 0
doc/classes/SystemFont.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SystemFont" inherits="Font" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Font loaded from a system font.
+		[b]Note:[/b] This class is implemented on iOS, Linux, macOS and Windows, on other platforms it will fallback to default theme font.
+	</brief_description>
+	<description>
+		[SystemFont] loads a font from a system font with the first matching name from [member font_names].
+		It will attempt to match font style, but it's not guaranteed.
+		The returned font might be part of a font collection or be a variable font with OpenType "weight" and/or "italic" features set.
+		You can create [FontVariation] of the system font for fine control over its features.
+	</description>
+	<tutorials>
+	</tutorials>
+	<members>
+		<member name="antialiased" type="bool" setter="set_antialiased" getter="is_antialiased" default="true">
+			If set to [code]true[/code], font 8-bit anitialiased glyph rendering is supported and enabled.
+		</member>
+		<member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]">
+			Array of fallback [Font]s.
+		</member>
+		<member name="font_names" type="PackedStringArray" setter="set_font_names" getter="get_font_names" default="PackedStringArray()">
+			Array of font family names to search, first matching font found is used.
+		</member>
+		<member name="font_style" type="int" setter="set_font_style" getter="get_font_style" enum="TextServer.FontStyle" default="0">
+			Font style flags, see [enum TextServer.FontStyle].
+		</member>
+		<member name="force_autohinter" type="bool" setter="set_force_autohinter" getter="is_force_autohinter" default="false">
+			If set to [code]true[/code], auto-hinting is supported and preferred over font built-in hinting.
+		</member>
+		<member name="generate_mipmaps" type="bool" setter="set_generate_mipmaps" getter="get_generate_mipmaps" default="false">
+			If set to [code]true[/code], generate mipmaps for the font textures.
+		</member>
+		<member name="hinting" type="int" setter="set_hinting" getter="get_hinting" enum="TextServer.Hinting" default="1">
+			Font hinting mode.
+		</member>
+		<member name="oversampling" type="float" setter="set_oversampling" getter="get_oversampling" default="0.0">
+			Font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead.
+		</member>
+		<member name="subpixel_positioning" type="int" setter="set_subpixel_positioning" getter="get_subpixel_positioning" enum="TextServer.SubpixelPositioning" default="1">
+			Font glyph sub-pixel positioning mode. Subpixel positioning provides shaper text and better kerning for smaller font sizes, at the cost of memory usage and font rasterization speed. Use [constant TextServer.SUBPIXEL_POSITIONING_AUTO] to automatically enable it based on the font size.
+		</member>
+	</members>
+</class>

+ 20 - 17
editor/editor_properties_array_dict.h

@@ -81,10 +81,7 @@ class EditorPropertyArray : public EditorProperty {
 	GDCLASS(EditorPropertyArray, EditorProperty);
 	GDCLASS(EditorPropertyArray, EditorProperty);
 
 
 	PopupMenu *change_type = nullptr;
 	PopupMenu *change_type = nullptr;
-	bool updating = false;
-	bool dropping = false;
 
 
-	Ref<EditorPropertyArrayObject> object;
 	int page_length = 20;
 	int page_length = 20;
 	int page_index = 0;
 	int page_index = 0;
 	int changing_type_index;
 	int changing_type_index;
@@ -106,29 +103,35 @@ class EditorPropertyArray : public EditorProperty {
 	Button *reorder_selected_button = nullptr;
 	Button *reorder_selected_button = nullptr;
 
 
 	void _page_changed(int p_page);
 	void _page_changed(int p_page);
-	void _length_changed(double p_page);
-	void _add_element();
-	void _edit_pressed();
-	void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false);
-	void _change_type(Object *p_button, int p_index);
-	void _change_type_menu(int p_index);
-
-	void _object_id_selected(const StringName &p_property, ObjectID p_id);
-	void _remove_pressed(int p_index);
-
-	void _button_draw();
-	bool _is_drop_valid(const Dictionary &p_drag_data) const;
-	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
-	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
 
 
 	void _reorder_button_gui_input(const Ref<InputEvent> &p_event);
 	void _reorder_button_gui_input(const Ref<InputEvent> &p_event);
 	void _reorder_button_down(int p_index);
 	void _reorder_button_down(int p_index);
 	void _reorder_button_up();
 	void _reorder_button_up();
 
 
 protected:
 protected:
+	Ref<EditorPropertyArrayObject> object;
+
+	bool updating = false;
+	bool dropping = false;
+
 	static void _bind_methods();
 	static void _bind_methods();
 	void _notification(int p_what);
 	void _notification(int p_what);
 
 
+	virtual void _add_element();
+	virtual void _length_changed(double p_page);
+	virtual void _edit_pressed();
+	virtual void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false);
+	virtual void _change_type(Object *p_button, int p_index);
+	virtual void _change_type_menu(int p_index);
+
+	virtual void _object_id_selected(const StringName &p_property, ObjectID p_id);
+	virtual void _remove_pressed(int p_index);
+
+	virtual void _button_draw();
+	virtual bool _is_drop_valid(const Dictionary &p_drag_data) const;
+	virtual bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+	virtual void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+
 public:
 public:
 	void setup(Variant::Type p_array_type, const String &p_hint_string = "");
 	void setup(Variant::Type p_array_type, const String &p_hint_string = "");
 	virtual void update_property() override;
 	virtual void update_property() override;

+ 1 - 1
editor/icons/FontFile.svg

@@ -1 +1 @@
-<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1.5 1v3h1a1 1 0 0 1 1-1h2v6a1 1 0 0 1-1 1v1h4v-1a1 1 0 0 1-1-1V3h2a1 1 0 0 1 1 1h1V1h-6Z" fill="#e0e0e0"/><path d="M4.5 5v3h1a1 1 0 0 1 1-1h2v6a1 1 0 0 1-1 1v1h4v-1a1 1 0 0 1-1-1V7h2a1 1 0 0 1 1 1h1V5h-6Z" fill="#ff5f5f"/></svg>
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7.5 7.5h-1a.519.519 0 0 0-.281.084A.491.491 0 0 0 6 8v.5h-.5V9a1 1 0 0 1-1 1v1H8V9.854A1 1 0 0 1 7.5 9V7.5zM1.5 1v3h1a1 1 0 0 1 1-1h2v1.5h2V3h2a1 1 0 0 1 1 1h1V1h-10z" style="fill:#e0e0e0;fill-opacity:1"/><path d="M4.5 5v3h1a1 1 0 0 1 1-1h2v6a1 1 0 0 1-1 1v1h4v-1a1 1 0 0 1-1-1V7h2a1 1 0 0 1 1 1h1V5h-6z" fill="#ff5f5f"/></svg>

+ 1 - 1
editor/icons/FontVariation.svg

@@ -1 +1 @@
-<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M2.437 1 1.379 4h1A.84 1.192 50 0 1 3.73 3h2L3.615 9a.84 1.192 50 0 1-1.352 1l-.353 1h4l.353-1a.84 1.192 50 0 1-.648-1l2.116-6h2a.84 1.192 50 0 1 .648 1h1l1.058-3h-6Z" fill="#e0e0e0"/><path d="m4.621 5-.705 2-.353 1h1a.84 1.192 49.998 0 1 1.353-1h2L5.8 13a.84 1.192 49.998 0 1-1.353 1l-.353 1h4l.353-1a.84 1.192 49.998 0 1-.647-1l2.116-6h2a.84 1.192 49.998 0 1 .647 1h1l.353-1 .705-2h-6Z" fill="#ff5f5f"/></svg>
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M5.975 11 7.21 7.5H5.916a.478.478 0 0 0-.113.016.837.837 0 0 0-.127.043c-.044.018-.089.04-.133.066l-.043.027V9a1 1 0 0 1-1 1v1h1.475zM1.5 1v3h1a1 1 0 0 1 1-1h2v1.5h2V3h2a1 1 0 0 1 1 1h1V1h-10z" style="fill:#e0e0e0;fill-opacity:1"/><path d="m4.621 5-.705 2-.353 1h1a.84 1.192 49.998 0 1 1.353-1h2L5.8 13a.84 1.192 49.998 0 1-1.353 1l-.353 1h4l.353-1a.84 1.192 49.998 0 1-.647-1l2.116-6h2a.84 1.192 49.998 0 1 .647 1h1l.353-1 .705-2h-6z" fill="#ff5f5f"/></svg>

+ 1 - 0
editor/icons/SystemFont.svg

@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path style="fill:#e0e0e0;fill-opacity:1;stroke-width:.714755" d="m5.787 1-.402 1.613c-.352.265-.71.122-1.012-.111l-.904-.541L2.46 2.973l.853 1.425c-.058.438-.412.586-.79.635-.343.065-.674.216-1.024.213V6.72c.367 0 .715.157 1.074.224.371.032.716.243.727.65l-.84 1.4 1.008 1.01c.443-.266.895-.53 1.33-.802.349-.044.675.139.674.506l.314 1.258c.459-.059 1.099.115 1.45-.082.117-.475.242-.954.35-1.428A.67.67 0 0 1 8 9.195V7.5H6.5a.519.519 0 0 0-.281.084A.491.491 0 0 0 6 8v.5H4v-4h5.75c-.005-.22.107-.434.254-.625l.543-.902L9.535 1.96l-1.426.853c-.437-.058-.588-.412-.636-.79L7.217 1h-1.43z"/><path d="M4.5 5v3h1a1 1 0 0 1 1-1h2v6a1 1 0 0 1-1 1v1h4v-1a1 1 0 0 1-1-1V7h2a1 1 0 0 1 1 1h1V5h-6z" fill="#ff5f5f"/></svg>

+ 107 - 29
editor/plugins/font_config_plugin.cpp

@@ -895,17 +895,45 @@ void FontPreview::_notification(int p_what) {
 			Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
 			Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
 			int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
 			int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
 			Color text_color = get_theme_color(SNAME("font_color"), SNAME("Label"));
 			Color text_color = get_theme_color(SNAME("font_color"), SNAME("Label"));
-			font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + 2 * EDSCALE), name, HORIZONTAL_ALIGNMENT_CENTER, get_size().x, font_size, text_color);
 
 
 			// Draw font preview.
 			// Draw font preview.
-			Vector2 pos = Vector2(0, font->get_height(font_size)) + (get_size() - Vector2(0, font->get_height(font_size)) - line->get_size()) / 2;
-			line->draw(get_canvas_item(), pos, text_color);
-
-			// Draw font baseline.
-			Color line_color = text_color;
-			line_color.a *= 0.6;
-			draw_line(Vector2(0, pos.y + line->get_line_ascent()), Vector2(pos.x - 5, pos.y + line->get_line_ascent()), line_color);
-			draw_line(Vector2(pos.x + line->get_size().x + 5, pos.y + line->get_line_ascent()), Vector2(get_size().x, pos.y + line->get_line_ascent()), line_color);
+			bool prev_ok = true;
+			if (prev_font.is_valid()) {
+				if (prev_font->get_font_name().is_empty()) {
+					prev_ok = false;
+				} else {
+					String name;
+					if (prev_font->get_font_style_name().is_empty()) {
+						name = prev_font->get_font_name();
+					} else {
+						name = vformat("%s (%s)", prev_font->get_font_name(), prev_font->get_font_style_name());
+					}
+					if (prev_font->is_class("FontVariation")) {
+						name += " " + TTR(" - Variation");
+					}
+					font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + 2 * EDSCALE), name, HORIZONTAL_ALIGNMENT_CENTER, get_size().x, font_size, text_color);
+
+					String sample;
+					static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
+					for (int i = 0; i < sample_base.length(); i++) {
+						if (prev_font->has_char(sample_base[i])) {
+							sample += sample_base[i];
+						}
+					}
+					if (sample.is_empty()) {
+						sample = prev_font->get_supported_chars().substr(0, 6);
+					}
+					if (sample.is_empty()) {
+						prev_ok = false;
+					} else {
+						prev_font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + prev_font->get_height(50)), sample, HORIZONTAL_ALIGNMENT_CENTER, get_size().x, 50, text_color);
+					}
+				}
+			}
+			if (!prev_ok) {
+				text_color.a *= 0.5;
+				font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + 2 * EDSCALE), TTR("Unable to preview font"), HORIZONTAL_ALIGNMENT_CENTER, get_size().x, font_size, text_color);
+			}
 		} break;
 		} break;
 	}
 	}
 }
 }
@@ -917,30 +945,11 @@ Size2 FontPreview::get_minimum_size() const {
 }
 }
 
 
 void FontPreview::set_data(const Ref<Font> &p_f) {
 void FontPreview::set_data(const Ref<Font> &p_f) {
-	line->clear();
-	if (p_f.is_valid()) {
-		name = vformat("%s (%s)", p_f->get_font_name(), p_f->get_font_style_name());
-		if (p_f->is_class("FontVariation")) {
-			name += " " + TTR(" - Variation");
-		}
-		String sample;
-		static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
-		for (int i = 0; i < sample_base.length(); i++) {
-			if (p_f->has_char(sample_base[i])) {
-				sample += sample_base[i];
-			}
-		}
-		if (sample.is_empty()) {
-			sample = p_f->get_supported_chars().substr(0, 6);
-		}
-		line->add_string(sample, p_f, 50);
-	}
-
+	prev_font = p_f;
 	update();
 	update();
 }
 }
 
 
 FontPreview::FontPreview() {
 FontPreview::FontPreview() {
-	line.instantiate();
 }
 }
 
 
 /*************************************************************************/
 /*************************************************************************/
@@ -964,6 +973,71 @@ bool EditorInspectorPluginFontPreview::parse_property(Object *p_object, const Va
 	return false;
 	return false;
 }
 }
 
 
+/*************************************************************************/
+/* EditorPropertyFontNamesArray                                          */
+/*************************************************************************/
+
+void EditorPropertyFontNamesArray::_add_element() {
+	Size2 size = get_size();
+	menu->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
+	menu->reset_size();
+	menu->popup();
+}
+
+void EditorPropertyFontNamesArray::_add_font(int p_option) {
+	if (updating) {
+		return;
+	}
+
+	Variant array = object->get_array();
+	int previous_size = array.call("size");
+
+	array.call("resize", previous_size + 1);
+	array.set(previous_size, menu->get_item_text(p_option));
+
+	emit_changed(get_edited_property(), array, "", false);
+	object->set_array(array);
+	update_property();
+}
+
+EditorPropertyFontNamesArray::EditorPropertyFontNamesArray() {
+	menu = memnew(PopupMenu);
+	menu->add_item("Sans-Serif", 0);
+	menu->add_item("Serif", 1);
+	menu->add_item("Monospace", 2);
+	menu->add_item("Fantasy", 3);
+	menu->add_item("Cursive", 4);
+
+	menu->add_separator();
+
+	if (OS::get_singleton()) {
+		Vector<String> fonts = OS::get_singleton()->get_system_fonts();
+		for (int i = 0; i < fonts.size(); i++) {
+			menu->add_item(fonts[i], i + 6);
+		}
+	}
+	add_child(menu);
+	menu->connect("id_pressed", callable_mp(this, &EditorPropertyFontNamesArray::_add_font));
+}
+
+/*************************************************************************/
+/* EditorInspectorPluginSystemFont                                       */
+/*************************************************************************/
+
+bool EditorInspectorPluginSystemFont::can_handle(Object *p_object) {
+	return Object::cast_to<SystemFont>(p_object) != nullptr;
+}
+
+bool EditorInspectorPluginSystemFont::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) {
+	if (p_path == "font_names") {
+		EditorPropertyFontNamesArray *editor = memnew(EditorPropertyFontNamesArray);
+		editor->setup(p_type, p_hint_text);
+		add_property_editor(p_path, editor);
+		return true;
+	}
+	return false;
+}
+
 /*************************************************************************/
 /*************************************************************************/
 /* FontEditorPlugin                                                */
 /* FontEditorPlugin                                                */
 /*************************************************************************/
 /*************************************************************************/
@@ -973,6 +1047,10 @@ FontEditorPlugin::FontEditorPlugin() {
 	fc_plugin.instantiate();
 	fc_plugin.instantiate();
 	EditorInspector::add_inspector_plugin(fc_plugin);
 	EditorInspector::add_inspector_plugin(fc_plugin);
 
 
+	Ref<EditorInspectorPluginSystemFont> fs_plugin;
+	fs_plugin.instantiate();
+	EditorInspector::add_inspector_plugin(fs_plugin);
+
 	Ref<EditorInspectorPluginFontPreview> fp_plugin;
 	Ref<EditorInspectorPluginFontPreview> fp_plugin;
 	fp_plugin.instantiate();
 	fp_plugin.instantiate();
 	EditorInspector::add_inspector_plugin(fp_plugin);
 	EditorInspector::add_inspector_plugin(fp_plugin);

+ 29 - 2
editor/plugins/font_config_plugin.h

@@ -34,6 +34,7 @@
 #include "core/io/marshalls.h"
 #include "core/io/marshalls.h"
 #include "editor/editor_plugin.h"
 #include "editor/editor_plugin.h"
 #include "editor/editor_properties.h"
 #include "editor/editor_properties.h"
+#include "editor/editor_properties_array_dict.h"
 
 
 /*************************************************************************/
 /*************************************************************************/
 
 
@@ -225,8 +226,7 @@ protected:
 	void _notification(int p_what);
 	void _notification(int p_what);
 	static void _bind_methods();
 	static void _bind_methods();
 
 
-	String name;
-	Ref<TextLine> line;
+	Ref<Font> prev_font;
 
 
 public:
 public:
 	virtual Size2 get_minimum_size() const override;
 	virtual Size2 get_minimum_size() const override;
@@ -249,6 +249,33 @@ public:
 
 
 /*************************************************************************/
 /*************************************************************************/
 
 
+class EditorPropertyFontNamesArray : public EditorPropertyArray {
+	GDCLASS(EditorPropertyFontNamesArray, EditorPropertyArray);
+
+	PopupMenu *menu = nullptr;
+
+protected:
+	virtual void _add_element() override;
+
+	void _add_font(int p_option);
+	static void _bind_methods(){};
+
+public:
+	EditorPropertyFontNamesArray();
+};
+
+/*************************************************************************/
+
+class EditorInspectorPluginSystemFont : public EditorInspectorPlugin {
+	GDCLASS(EditorInspectorPluginSystemFont, EditorInspectorPlugin);
+
+public:
+	virtual bool can_handle(Object *p_object) override;
+	virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false) override;
+};
+
+/*************************************************************************/
+
 class FontEditorPlugin : public EditorPlugin {
 class FontEditorPlugin : public EditorPlugin {
 	GDCLASS(FontEditorPlugin, EditorPlugin);
 	GDCLASS(FontEditorPlugin, EditorPlugin);
 
 

+ 3 - 0
platform/ios/os_ios.h

@@ -92,6 +92,9 @@ public:
 
 
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
 
 
+	virtual Vector<String> get_system_fonts() const override;
+	virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override;
+
 	virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override;
 	virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override;
 	virtual Error close_dynamic_library(void *p_library_handle) override;
 	virtual Error close_dynamic_library(void *p_library_handle) override;
 	virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override;
 	virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override;

+ 75 - 0
platform/ios/os_ios.mm

@@ -44,6 +44,7 @@
 #import "view_controller.h"
 #import "view_controller.h"
 
 
 #import <AudioToolbox/AudioServices.h>
 #import <AudioToolbox/AudioServices.h>
+#import <CoreText/CoreText.h>
 #import <UIKit/UIKit.h>
 #import <UIKit/UIKit.h>
 #import <dlfcn.h>
 #import <dlfcn.h>
 #include <sys/sysctl.h>
 #include <sys/sysctl.h>
@@ -302,6 +303,80 @@ String OS_IOS::get_processor_name() const {
 	ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
 	ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
 }
 }
 
 
+Vector<String> OS_IOS::get_system_fonts() const {
+	HashSet<String> font_names;
+	CFArrayRef fonts = CTFontManagerCopyAvailableFontFamilyNames();
+	if (fonts) {
+		for (CFIndex i = 0; i < CFArrayGetCount(fonts); i++) {
+			CFStringRef cf_name = (CFStringRef)CFArrayGetValueAtIndex(fonts, i);
+			if (cf_name && (CFStringGetLength(cf_name) > 0) && (CFStringCompare(cf_name, CFSTR("LastResort"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) && (CFStringGetCharacterAtIndex(cf_name, 0) != '.')) {
+				NSString *ns_name = (__bridge NSString *)cf_name;
+				font_names.insert(String::utf8([ns_name UTF8String]));
+			}
+		}
+		CFRelease(fonts);
+	}
+
+	Vector<String> ret;
+	for (const String &E : font_names) {
+		ret.push_back(E);
+	}
+	return ret;
+}
+
+String OS_IOS::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
+	String ret;
+
+	String font_name = p_font_name;
+	if (font_name.to_lower() == "sans-serif") {
+		font_name = "Helvetica";
+	} else if (font_name.to_lower() == "serif") {
+		font_name = "Times";
+	} else if (font_name.to_lower() == "monospace") {
+		font_name = "Courier";
+	} else if (font_name.to_lower() == "fantasy") {
+		font_name = "Papyrus";
+	} else if (font_name.to_lower() == "cursive") {
+		font_name = "Apple Chancery";
+	};
+
+	CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
+
+	CTFontSymbolicTraits traits = 0;
+	if (p_bold) {
+		traits |= kCTFontBoldTrait;
+	}
+	if (p_italic) {
+		traits |= kCTFontItalicTrait;
+	}
+
+	CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
+	CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+	CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
+
+	CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+	CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
+	CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
+
+	CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
+	if (font) {
+		CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(font, kCTFontURLAttribute);
+		if (url) {
+			NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
+			ret = String::utf8([font_path UTF8String]);
+			CFRelease(url);
+		}
+		CFRelease(font);
+	}
+
+	CFRelease(attributes);
+	CFRelease(traits_dict);
+	CFRelease(sym_traits);
+	CFRelease(name);
+
+	return ret;
+}
+
 void OS_IOS::vibrate_handheld(int p_duration_ms) {
 void OS_IOS::vibrate_handheld(int p_duration_ms) {
 	if (ios->supports_haptic_engine()) {
 	if (ios->supports_haptic_engine()) {
 		ios->vibrate_haptic_engine((float)p_duration_ms / 1000.f);
 		ios->vibrate_haptic_engine((float)p_duration_ms / 1000.f);

+ 6 - 0
platform/linuxbsd/detect.py

@@ -298,6 +298,12 @@ def configure(env):
 
 
     ## Flags
     ## Flags
 
 
+    if os.system("pkg-config --exists fontconfig") == 0:  # 0 means found
+        env.Append(CPPDEFINES=["FONTCONFIG_ENABLED"])
+        env.ParseConfig("pkg-config fontconfig --cflags --libs")
+    else:
+        print("Warning: fontconfig libraries not found. Disabling the system fonts support.")
+
     if os.system("pkg-config --exists alsa") == 0:  # 0 means found
     if os.system("pkg-config --exists alsa") == 0:  # 0 means found
         env["alsa"] = True
         env["alsa"] = True
         env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])
         env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])

+ 89 - 0
platform/linuxbsd/os_linuxbsd.cpp

@@ -52,6 +52,10 @@
 #include <sys/types.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
+#ifdef FONTCONFIG_ENABLED
+#include <fontconfig/fontconfig.h>
+#endif
+
 void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) {
 void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) {
 	const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
 	const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
 
 
@@ -327,6 +331,91 @@ uint64_t OS_LinuxBSD::get_embedded_pck_offset() const {
 	return off;
 	return off;
 }
 }
 
 
+Vector<String> OS_LinuxBSD::get_system_fonts() const {
+#ifdef FONTCONFIG_ENABLED
+	HashSet<String> font_names;
+	Vector<String> ret;
+
+	FcConfig *config = FcInitLoadConfigAndFonts();
+	ERR_FAIL_COND_V(!config, ret);
+
+	FcObjectSet *object_set = FcObjectSetBuild(FC_FAMILY, nullptr);
+	ERR_FAIL_COND_V(!object_set, ret);
+
+	static const char *allowed_formats[] = { "TrueType", "CFF" };
+	for (size_t i = 0; i < sizeof(allowed_formats) / sizeof(const char *); i++) {
+		FcPattern *pattern = FcPatternCreate();
+		ERR_CONTINUE(!pattern);
+
+		FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+		FcPatternAddString(pattern, FC_FONTFORMAT, reinterpret_cast<const FcChar8 *>(allowed_formats[i]));
+
+		FcFontSet *font_set = FcFontList(config, pattern, object_set);
+		if (font_set) {
+			for (int j = 0; j < font_set->nfont; j++) {
+				char *family_name = nullptr;
+				if (FcPatternGetString(font_set->fonts[j], FC_FAMILY, 0, reinterpret_cast<FcChar8 **>(&family_name)) == FcResultMatch) {
+					if (family_name) {
+						font_names.insert(String::utf8(family_name));
+					}
+				}
+			}
+			FcFontSetDestroy(font_set);
+		}
+		FcPatternDestroy(pattern);
+	}
+	FcObjectSetDestroy(object_set);
+
+	for (const String &E : font_names) {
+		ret.push_back(E);
+	}
+	return ret;
+#else
+	ERR_FAIL_COND_V_MSG(Vector<String>(), "Godot was compiled without fontconfig, system font support is disabled.")
+#endif
+}
+
+String OS_LinuxBSD::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
+#ifdef FONTCONFIG_ENABLED
+	String ret;
+
+	FcConfig *config = FcInitLoadConfigAndFonts();
+	ERR_FAIL_COND_V(!config, ret);
+
+	FcObjectSet *object_set = FcObjectSetBuild(FC_FAMILY, FC_FILE, nullptr);
+	ERR_FAIL_COND_V(!object_set, ret);
+
+	FcPattern *pattern = FcPatternCreate();
+	if (pattern) {
+		FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+		FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>(p_font_name.utf8().get_data()));
+		FcPatternAddInteger(pattern, FC_WEIGHT, p_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL);
+		FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
+
+		FcConfigSubstitute(0, pattern, FcMatchPattern);
+		FcDefaultSubstitute(pattern);
+
+		FcResult result;
+		FcPattern *match = FcFontMatch(0, pattern, &result);
+		if (match) {
+			char *file_name = nullptr;
+			if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) {
+				if (file_name) {
+					ret = String::utf8(file_name);
+				}
+			}
+
+			FcPatternDestroy(match);
+		}
+		FcPatternDestroy(pattern);
+	}
+	FcObjectSetDestroy(object_set);
+#else
+	ERR_FAIL_COND_V_MSG(Vector<String>(), "Godot was compiled without fontconfig, system font support is disabled.")
+#endif
+	return ret;
+}
+
 String OS_LinuxBSD::get_config_path() const {
 String OS_LinuxBSD::get_config_path() const {
 	if (has_environment("XDG_CONFIG_HOME")) {
 	if (has_environment("XDG_CONFIG_HOME")) {
 		if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) {
 		if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) {

+ 3 - 0
platform/linuxbsd/os_linuxbsd.h

@@ -80,6 +80,9 @@ public:
 
 
 	virtual uint64_t get_embedded_pck_offset() const override;
 	virtual uint64_t get_embedded_pck_offset() const override;
 
 
+	virtual Vector<String> get_system_fonts() const override;
+	virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override;
+
 	virtual String get_config_path() const override;
 	virtual String get_config_path() const override;
 	virtual String get_data_path() const override;
 	virtual String get_data_path() const override;
 	virtual String get_cache_path() const override;
 	virtual String get_cache_path() const override;

+ 2 - 0
platform/macos/os_macos.h

@@ -97,6 +97,8 @@ public:
 
 
 	virtual String get_locale() const override;
 	virtual String get_locale() const override;
 
 
+	virtual Vector<String> get_system_fonts() const override;
+	virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override;
 	virtual String get_executable_path() const override;
 	virtual String get_executable_path() const override;
 	virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
 	virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
 	virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
 	virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;

+ 74 - 0
platform/macos/os_macos.mm

@@ -303,6 +303,80 @@ String OS_MacOS::get_locale() const {
 	return String([locale_code UTF8String]).replace("-", "_");
 	return String([locale_code UTF8String]).replace("-", "_");
 }
 }
 
 
+Vector<String> OS_MacOS::get_system_fonts() const {
+	HashSet<String> font_names;
+	CFArrayRef fonts = CTFontManagerCopyAvailableFontFamilyNames();
+	if (fonts) {
+		for (CFIndex i = 0; i < CFArrayGetCount(fonts); i++) {
+			CFStringRef cf_name = (CFStringRef)CFArrayGetValueAtIndex(fonts, i);
+			if (cf_name && (CFStringGetLength(cf_name) > 0) && (CFStringCompare(cf_name, CFSTR("LastResort"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) && (CFStringGetCharacterAtIndex(cf_name, 0) != '.')) {
+				NSString *ns_name = (__bridge NSString *)cf_name;
+				font_names.insert(String::utf8([ns_name UTF8String]));
+			}
+		}
+		CFRelease(fonts);
+	}
+
+	Vector<String> ret;
+	for (const String &E : font_names) {
+		ret.push_back(E);
+	}
+	return ret;
+}
+
+String OS_MacOS::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
+	String ret;
+
+	String font_name = p_font_name;
+	if (font_name.to_lower() == "sans-serif") {
+		font_name = "Helvetica";
+	} else if (font_name.to_lower() == "serif") {
+		font_name = "Times";
+	} else if (font_name.to_lower() == "monospace") {
+		font_name = "Courier";
+	} else if (font_name.to_lower() == "fantasy") {
+		font_name = "Papyrus";
+	} else if (font_name.to_lower() == "cursive") {
+		font_name = "Apple Chancery";
+	};
+
+	CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
+
+	CTFontSymbolicTraits traits = 0;
+	if (p_bold) {
+		traits |= kCTFontBoldTrait;
+	}
+	if (p_italic) {
+		traits |= kCTFontItalicTrait;
+	}
+
+	CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
+	CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+	CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
+
+	CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+	CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
+	CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
+
+	CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
+	if (font) {
+		CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(font, kCTFontURLAttribute);
+		if (url) {
+			NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
+			ret = String::utf8([font_path UTF8String]);
+			CFRelease(url);
+		}
+		CFRelease(font);
+	}
+
+	CFRelease(attributes);
+	CFRelease(traits_dict);
+	CFRelease(sym_traits);
+	CFRelease(name);
+
+	return ret;
+}
+
 String OS_MacOS::get_executable_path() const {
 String OS_MacOS::get_executable_path() const {
 	char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
 	char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
 	int pid = getpid();
 	int pid = getpid();

+ 2 - 0
platform/windows/detect.py

@@ -270,6 +270,7 @@ def configure_msvc(env, manual_msvc_config):
         "bcrypt",
         "bcrypt",
         "Avrt",
         "Avrt",
         "dwmapi",
         "dwmapi",
+        "dwrite",
     ]
     ]
 
 
     if env["vulkan"]:
     if env["vulkan"]:
@@ -441,6 +442,7 @@ def configure_mingw(env):
             "avrt",
             "avrt",
             "uuid",
             "uuid",
             "dwmapi",
             "dwmapi",
+            "dwrite",
         ]
         ]
     )
     )
 
 

+ 130 - 0
platform/windows/os_windows.cpp

@@ -48,6 +48,7 @@
 #include <avrt.h>
 #include <avrt.h>
 #include <bcrypt.h>
 #include <bcrypt.h>
 #include <direct.h>
 #include <direct.h>
+#include <dwrite.h>
 #include <knownfolders.h>
 #include <knownfolders.h>
 #include <process.h>
 #include <process.h>
 #include <regstr.h>
 #include <regstr.h>
@@ -621,6 +622,135 @@ Error OS_Windows::set_cwd(const String &p_cwd) {
 	return OK;
 	return OK;
 }
 }
 
 
+Vector<String> OS_Windows::get_system_fonts() const {
+	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);
+		ERR_CONTINUE(FAILED(hr) || family.is_null());
+
+		ComAutoreleaseRef<IDWriteLocalizedStrings> family_names;
+		hr = family->GetFamilyNames(&family_names.reference);
+		ERR_CONTINUE(FAILED(hr) || family_names.is_null());
+
+		UINT32 index = 0;
+		BOOL exists = false;
+		UINT32 length = 0;
+		Char16String name;
+
+		hr = family_names->FindLocaleName(L"en-us", &index, &exists);
+		ERR_CONTINUE(FAILED(hr));
+
+		hr = family_names->GetStringLength(index, &length);
+		ERR_CONTINUE(FAILED(hr));
+
+		name.resize(length + 1);
+		hr = family_names->GetString(index, (WCHAR *)name.ptrw(), length + 1);
+		ERR_CONTINUE(FAILED(hr));
+
+		font_names.insert(String::utf16(name.ptr(), length));
+	}
+
+	for (const String &E : font_names) {
+		ret.push_back(E);
+	}
+	return ret;
+}
+
+String OS_Windows::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
+	String font_name = p_font_name;
+	if (font_name.to_lower() == "sans-serif") {
+		font_name = "Arial";
+	} else if (font_name.to_lower() == "serif") {
+		font_name = "Times New Roman";
+	} else if (font_name.to_lower() == "monospace") {
+		font_name = "Courier New";
+	} else if (font_name.to_lower() == "cursive") {
+		font_name = "Comic Sans MS";
+	} else if (font_name.to_lower() == "fantasy") {
+		font_name = "Gabriola";
+	}
+
+	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());
+
+	ComAutoreleaseRef<IDWriteFontCollection> font_collection;
+	hr = dwrite_factory->GetSystemFontCollection(&font_collection.reference, false);
+	ERR_FAIL_COND_V(FAILED(hr) || font_collection.is_null(), String());
+
+	UINT32 index = 0;
+	BOOL exists = false;
+	font_collection->FindFamilyName((const WCHAR *)font_name.utf16().get_data(), &index, &exists);
+	if (FAILED(hr)) {
+		return String();
+	}
+
+	ComAutoreleaseRef<IDWriteFontFamily> family;
+	hr = font_collection->GetFontFamily(index, &family.reference);
+	if (FAILED(hr) || family.is_null()) {
+		return String();
+	}
+
+	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);
+	if (FAILED(hr) || dwrite_font.is_null()) {
+		return String();
+	}
+
+	ComAutoreleaseRef<IDWriteFontFace> dwrite_face;
+	hr = dwrite_font->CreateFontFace(&dwrite_face.reference);
+	if (FAILED(hr) || dwrite_face.is_null()) {
+		return String();
+	}
+
+	UINT32 number_of_files = 0;
+	hr = dwrite_face->GetFiles(&number_of_files, nullptr);
+	if (FAILED(hr)) {
+		return String();
+	}
+	Vector<ComAutoreleaseRef<IDWriteFontFile>> files;
+	files.resize(number_of_files);
+	hr = dwrite_face->GetFiles(&number_of_files, (IDWriteFontFile **)files.ptrw());
+	if (FAILED(hr)) {
+		return String();
+	}
+
+	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;
+		}
+		return String::utf16((const char16_t *)&file_path[0]);
+	}
+	return String();
+}
+
 String OS_Windows::get_executable_path() const {
 String OS_Windows::get_executable_path() const {
 	WCHAR bufname[4096];
 	WCHAR bufname[4096];
 	GetModuleFileNameW(nullptr, bufname, 4096);
 	GetModuleFileNameW(nullptr, bufname, 4096);

+ 23 - 0
platform/windows/os_windows.h

@@ -62,6 +62,26 @@
 #define WINDOWS_DEBUG_OUTPUT_ENABLED
 #define WINDOWS_DEBUG_OUTPUT_ENABLED
 #endif
 #endif
 
 
+template <class T>
+class ComAutoreleaseRef {
+public:
+	T *reference = nullptr;
+
+	_FORCE_INLINE_ T *operator->() { return reference; }
+	_FORCE_INLINE_ const T *operator->() const { return reference; }
+	_FORCE_INLINE_ T *operator*() { return reference; }
+	_FORCE_INLINE_ const T *operator*() const { return reference; }
+	_FORCE_INLINE_ bool is_valid() const { return reference != nullptr; }
+	_FORCE_INLINE_ bool is_null() const { return reference == nullptr; }
+	ComAutoreleaseRef() {}
+	~ComAutoreleaseRef() {
+		if (reference != nullptr) {
+			reference->Release();
+			reference = nullptr;
+		}
+	}
+};
+
 class JoypadWindows;
 class JoypadWindows;
 class OS_Windows : public OS {
 class OS_Windows : public OS {
 #ifdef STDOUT_FILE
 #ifdef STDOUT_FILE
@@ -147,6 +167,9 @@ public:
 	virtual String get_environment(const String &p_var) const override;
 	virtual String get_environment(const String &p_var) const override;
 	virtual bool set_environment(const String &p_var, const String &p_value) const override;
 	virtual bool set_environment(const String &p_var, const String &p_value) const override;
 
 
+	virtual Vector<String> get_system_fonts() const override;
+	virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override;
+
 	virtual String get_executable_path() const override;
 	virtual String get_executable_path() const override;
 
 
 	virtual String get_locale() const override;
 	virtual String get_locale() const override;

+ 4 - 2
scene/gui/text_edit.cpp

@@ -443,8 +443,10 @@ void TextEdit::_notification(int p_what) {
 		case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
 		case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
 		case NOTIFICATION_TRANSLATION_CHANGED:
 		case NOTIFICATION_TRANSLATION_CHANGED:
 		case NOTIFICATION_THEME_CHANGED: {
 		case NOTIFICATION_THEME_CHANGED: {
-			_update_caches();
-			_update_wrap_at_column(true);
+			if (is_inside_tree()) {
+				_update_caches();
+				_update_wrap_at_column(true);
+			}
 		} break;
 		} break;
 
 
 		case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
 		case NOTIFICATION_WM_WINDOW_FOCUS_IN: {

+ 1 - 0
scene/register_scene_types.cpp

@@ -870,6 +870,7 @@ void register_scene_types() {
 	GDREGISTER_ABSTRACT_CLASS(Font);
 	GDREGISTER_ABSTRACT_CLASS(Font);
 	GDREGISTER_CLASS(FontFile);
 	GDREGISTER_CLASS(FontFile);
 	GDREGISTER_CLASS(FontVariation);
 	GDREGISTER_CLASS(FontVariation);
+	GDREGISTER_CLASS(SystemFont);
 
 
 	GDREGISTER_CLASS(Curve);
 	GDREGISTER_CLASS(Curve);
 
 

+ 371 - 0
scene/resources/font.cpp

@@ -2693,3 +2693,374 @@ FontVariation::FontVariation() {
 FontVariation::~FontVariation() {
 FontVariation::~FontVariation() {
 	reset_state();
 	reset_state();
 }
 }
+
+/*************************************************************************/
+/*  SystemFont                                                           */
+/*************************************************************************/
+
+void SystemFont::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &SystemFont::set_antialiased);
+	ClassDB::bind_method(D_METHOD("is_antialiased"), &SystemFont::is_antialiased);
+
+	ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &SystemFont::set_generate_mipmaps);
+	ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &SystemFont::get_generate_mipmaps);
+
+	ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &SystemFont::set_force_autohinter);
+	ClassDB::bind_method(D_METHOD("is_force_autohinter"), &SystemFont::is_force_autohinter);
+
+	ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &SystemFont::set_hinting);
+	ClassDB::bind_method(D_METHOD("get_hinting"), &SystemFont::get_hinting);
+
+	ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &SystemFont::set_subpixel_positioning);
+	ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &SystemFont::get_subpixel_positioning);
+
+	ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &SystemFont::set_oversampling);
+	ClassDB::bind_method(D_METHOD("get_oversampling"), &SystemFont::get_oversampling);
+
+	ClassDB::bind_method(D_METHOD("get_font_names"), &SystemFont::get_font_names);
+	ClassDB::bind_method(D_METHOD("set_font_names", "names"), &SystemFont::set_font_names);
+
+	ClassDB::bind_method(D_METHOD("set_font_style", "style"), &SystemFont::set_font_style);
+
+	ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "font_names"), "set_font_names", "get_font_names");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic"), "set_font_style", "get_font_style");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "get_generate_mipmaps");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), "set_subpixel_positioning", "get_subpixel_positioning");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling");
+	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks");
+}
+
+void SystemFont::_update_rids() const {
+	Ref<Font> f = _get_base_font_or_default();
+
+	rids.clear();
+	if (fallbacks.is_empty() && f.is_valid()) {
+		RID rid = _get_rid();
+		if (rid.is_valid()) {
+			rids.push_back(rid);
+		}
+
+		const TypedArray<Font> &base_fallbacks = f->get_fallbacks();
+		for (int i = 0; i < base_fallbacks.size(); i++) {
+			_update_rids_fb(base_fallbacks[i], 0);
+		}
+	} else {
+		_update_rids_fb(const_cast<SystemFont *>(this), 0);
+	}
+	dirty_rids = false;
+}
+
+void SystemFont::_update_base_font() {
+	if (base_font.is_valid()) {
+		base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+		base_font.unref();
+	}
+
+	face_indeces.clear();
+	ftr_weight = 0;
+	ftr_italic = 0;
+	for (const String &E : names) {
+		if (E.is_empty()) {
+			continue;
+		}
+
+		String path = OS::get_singleton()->get_system_font_path(E, style & TextServer::FONT_BOLD, style & TextServer::FONT_ITALIC);
+		if (path.is_empty()) {
+			continue;
+		}
+		Ref<FontFile> file;
+		file.instantiate();
+		Error err = file->load_dynamic_font(path);
+		if (err != OK) {
+			continue;
+		}
+
+		// If it's a font collection check all faces to match requested style.
+		for (int i = 0; i < file->get_face_count(); i++) {
+			file->set_face_index(0, i);
+			if (((file->get_font_style() & TextServer::FONT_BOLD) == (style & TextServer::FONT_BOLD)) && ((file->get_font_style() & TextServer::FONT_ITALIC) == (style & TextServer::FONT_ITALIC))) {
+				face_indeces.push_back(i);
+			}
+		}
+		if (face_indeces.is_empty()) {
+			face_indeces.push_back(0);
+		}
+		file->set_face_index(0, face_indeces[0]);
+
+		// If it's a variable font, apply weight and italic coordinates to match requested style.
+		Dictionary ftr = file->get_supported_variation_list();
+		if ((style & TextServer::FONT_BOLD) && ftr.has(TS->name_to_tag("weight"))) {
+			ftr_weight = 700;
+		}
+		if ((style & TextServer::FONT_ITALIC) && ftr.has(TS->name_to_tag("italic"))) {
+			ftr_italic = 1;
+		}
+
+		// Apply font rendering settings.
+		file->set_antialiased(antialiased);
+		file->set_generate_mipmaps(mipmaps);
+		file->set_force_autohinter(force_autohinter);
+		file->set_hinting(hinting);
+		file->set_subpixel_positioning(subpixel_positioning);
+		file->set_oversampling(oversampling);
+
+		base_font = file;
+	}
+
+	if (base_font.is_valid()) {
+		base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+	}
+
+	_invalidate_rids();
+	notify_property_list_changed();
+}
+
+void SystemFont::reset_state() {
+	if (base_font.is_valid()) {
+		base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+		base_font.unref();
+	}
+
+	if (theme_font.is_valid()) {
+		theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+		theme_font.unref();
+	}
+
+	names.clear();
+	face_indeces.clear();
+	ftr_weight = 0;
+	ftr_italic = 0;
+	style = 0;
+	antialiased = true;
+	mipmaps = false;
+	force_autohinter = false;
+	hinting = TextServer::HINTING_LIGHT;
+	subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
+	oversampling = 0.f;
+
+	Font::reset_state();
+}
+
+Ref<Font> SystemFont::_get_base_font_or_default() const {
+	if (theme_font.is_valid()) {
+		theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids));
+		theme_font.unref();
+	}
+
+	if (base_font.is_valid()) {
+		return base_font;
+	}
+
+	// Check the project-defined Theme resource.
+	if (Theme::get_project_default().is_valid()) {
+		List<StringName> theme_types;
+		Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
+
+		for (const StringName &E : theme_types) {
+			if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) {
+				Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+				if (f.is_valid()) {
+					theme_font = f;
+					theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+				}
+				return f;
+			}
+		}
+	}
+
+	// Lastly, fall back on the items defined in the default Theme, if they exist.
+	if (Theme::get_default().is_valid()) {
+		List<StringName> theme_types;
+		Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
+
+		for (const StringName &E : theme_types) {
+			if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) {
+				Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+				if (f.is_valid()) {
+					theme_font = f;
+					theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+				}
+				return f;
+			}
+		}
+
+		// If they don't exist, use any type to return the default/empty value.
+		Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
+		if (f.is_valid()) {
+			theme_font = f;
+			theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+		}
+		return f;
+	}
+
+	return Ref<Font>();
+}
+
+void SystemFont::set_antialiased(bool p_antialiased) {
+	if (antialiased != p_antialiased) {
+		antialiased = p_antialiased;
+		if (base_font.is_valid()) {
+			base_font->set_antialiased(antialiased);
+		}
+		emit_changed();
+	}
+}
+
+bool SystemFont::is_antialiased() const {
+	return antialiased;
+}
+
+void SystemFont::set_generate_mipmaps(bool p_generate_mipmaps) {
+	if (mipmaps != p_generate_mipmaps) {
+		mipmaps = p_generate_mipmaps;
+		if (base_font.is_valid()) {
+			base_font->set_generate_mipmaps(mipmaps);
+		}
+		emit_changed();
+	}
+}
+
+bool SystemFont::get_generate_mipmaps() const {
+	return mipmaps;
+}
+
+void SystemFont::set_force_autohinter(bool p_force_autohinter) {
+	if (force_autohinter != p_force_autohinter) {
+		force_autohinter = p_force_autohinter;
+		if (base_font.is_valid()) {
+			base_font->set_force_autohinter(force_autohinter);
+		}
+		emit_changed();
+	}
+}
+
+bool SystemFont::is_force_autohinter() const {
+	return force_autohinter;
+}
+
+void SystemFont::set_hinting(TextServer::Hinting p_hinting) {
+	if (hinting != p_hinting) {
+		hinting = p_hinting;
+		if (base_font.is_valid()) {
+			base_font->set_hinting(hinting);
+		}
+		emit_changed();
+	}
+}
+
+TextServer::Hinting SystemFont::get_hinting() const {
+	return hinting;
+}
+
+void SystemFont::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) {
+	if (subpixel_positioning != p_subpixel) {
+		subpixel_positioning = p_subpixel;
+		if (base_font.is_valid()) {
+			base_font->set_subpixel_positioning(subpixel_positioning);
+		}
+		emit_changed();
+	}
+}
+
+TextServer::SubpixelPositioning SystemFont::get_subpixel_positioning() const {
+	return subpixel_positioning;
+}
+
+void SystemFont::set_oversampling(real_t p_oversampling) {
+	if (oversampling != p_oversampling) {
+		oversampling = p_oversampling;
+		if (base_font.is_valid()) {
+			base_font->set_oversampling(oversampling);
+		}
+		emit_changed();
+	}
+}
+
+real_t SystemFont::get_oversampling() const {
+	return oversampling;
+}
+
+void SystemFont::set_font_names(const PackedStringArray &p_names) {
+	if (names != p_names) {
+		names = p_names;
+		_update_base_font();
+	}
+}
+
+PackedStringArray SystemFont::get_font_names() const {
+	return names;
+}
+
+void SystemFont::set_font_style(BitField<TextServer::FontStyle> p_style) {
+	if (style != p_style) {
+		style = p_style;
+		_update_base_font();
+	}
+}
+
+BitField<TextServer::FontStyle> SystemFont::get_font_style() const {
+	return style;
+}
+
+int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const {
+	if (base_font.is_valid()) {
+		return base_font->get_spacing(p_spacing);
+	} else {
+		return 0;
+	}
+}
+
+RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
+	Ref<Font> f = _get_base_font_or_default();
+	if (f.is_valid()) {
+		Dictionary var = p_variation_coordinates;
+		if (ftr_weight > 0 && !var.has(TS->name_to_tag("weight"))) {
+			var[TS->name_to_tag("weight")] = ftr_weight;
+		}
+		if (ftr_italic > 0 && !var.has(TS->name_to_tag("italic"))) {
+			var[TS->name_to_tag("italic")] = ftr_italic;
+		}
+
+		if (!face_indeces.is_empty()) {
+			int face_index = CLAMP(p_face_index, 0, face_indeces.size() - 1);
+			return f->find_variation(var, face_indeces[face_index], p_strength, p_transform);
+		} else {
+			return f->find_variation(var, 0, p_strength, p_transform);
+		}
+	}
+	return RID();
+}
+
+RID SystemFont::_get_rid() const {
+	Ref<Font> f = _get_base_font_or_default();
+	if (f.is_valid()) {
+		if (!face_indeces.is_empty()) {
+			Dictionary var;
+			if (ftr_weight > 0) {
+				var[TS->name_to_tag("weight")] = ftr_weight;
+			}
+			if (ftr_italic > 0) {
+				var[TS->name_to_tag("italic")] = ftr_italic;
+			}
+			return f->find_variation(var, face_indeces[0]);
+		} else {
+			return f->_get_rid();
+		}
+	}
+	return RID();
+}
+
+int64_t SystemFont::get_face_count() const {
+	return face_indeces.size();
+}
+
+SystemFont::SystemFont() {
+	/* NOP */
+}
+
+SystemFont::~SystemFont() {
+	reset_state();
+}

+ 71 - 1
scene/resources/font.h

@@ -41,7 +41,7 @@ class TextLine;
 class TextParagraph;
 class TextParagraph;
 
 
 /*************************************************************************/
 /*************************************************************************/
-/*  Font                                                             */
+/*  Font                                                                 */
 /*************************************************************************/
 /*************************************************************************/
 
 
 class Font : public Resource {
 class Font : public Resource {
@@ -381,4 +381,74 @@ public:
 	~FontVariation();
 	~FontVariation();
 };
 };
 
 
+/*************************************************************************/
+/*  SystemFont                                                           */
+/*************************************************************************/
+
+class SystemFont : public Font {
+	GDCLASS(SystemFont, Font);
+
+	PackedStringArray names;
+	BitField<TextServer::FontStyle> style = 0;
+
+	mutable Ref<Font> theme_font;
+
+	Ref<FontFile> base_font;
+	Vector<int> face_indeces;
+	int ftr_weight = 0;
+	int ftr_italic = 0;
+
+	bool antialiased = true;
+	bool mipmaps = false;
+	bool force_autohinter = false;
+	TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+	TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+	real_t oversampling = 0.f;
+
+protected:
+	static void _bind_methods();
+
+	virtual void _update_base_font();
+	virtual void _update_rids() const override;
+
+	virtual void reset_state() override;
+
+public:
+	virtual Ref<Font> _get_base_font_or_default() const;
+
+	virtual void set_antialiased(bool p_antialiased);
+	virtual bool is_antialiased() const;
+
+	virtual void set_generate_mipmaps(bool p_generate_mipmaps);
+	virtual bool get_generate_mipmaps() const;
+
+	virtual void set_force_autohinter(bool p_force_autohinter);
+	virtual bool is_force_autohinter() const;
+
+	virtual void set_hinting(TextServer::Hinting p_hinting);
+	virtual TextServer::Hinting get_hinting() const;
+
+	virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel);
+	virtual TextServer::SubpixelPositioning get_subpixel_positioning() const;
+
+	virtual void set_oversampling(real_t p_oversampling);
+	virtual real_t get_oversampling() const;
+
+	virtual void set_font_names(const PackedStringArray &p_names);
+	virtual PackedStringArray get_font_names() const;
+
+	virtual void set_font_style(BitField<TextServer::FontStyle> p_style);
+	virtual BitField<TextServer::FontStyle> get_font_style() const override;
+
+	virtual int get_spacing(TextServer::SpacingType p_spacing) const override;
+
+	virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override;
+	virtual RID _get_rid() const override;
+
+	int64_t get_face_count() const override;
+
+	SystemFont();
+	~SystemFont();
+};
+
 #endif // FONT_H
 #endif // FONT_H