Sfoglia il codice sorgente

[Complex Text Layouts] Adds missing Font::SPACING_* to the Label, LineEdit, TextEdit, TextLine and TextParagraph.

Fixes oversized editor control height (default editor spacing is negative) and control size changing when text is set.
bruvzg 4 anni fa
parent
commit
a458e90179

+ 2 - 2
modules/text_server_adv/text_server_adv.cpp

@@ -1874,7 +1874,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 	}
 	}
 
 
 	if (fd == nullptr) {
 	if (fd == nullptr) {
-		// Add fallback glyohs
+		// Add fallback glyphs
 		for (int i = p_start; i < p_end; i++) {
 		for (int i = p_start; i < p_end; i++) {
 			if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
 			if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
 				TextServer::Glyph gl;
 				TextServer::Glyph gl;
@@ -2026,7 +2026,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 				}
 				}
 				for (int j = 0; j < w[i].count; j++) {
 				for (int j = 0; j < w[i].count; j++) {
 					if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
 					if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
-						p_sd->ascent = MAX(p_sd->ascent, MAX(fd->get_ascent(w[i + j].font_size), w[i + j].y_off));
+						p_sd->ascent = MAX(p_sd->ascent, MAX(fd->get_ascent(w[i + j].font_size), -w[i + j].y_off));
 						p_sd->descent = MAX(p_sd->descent, MAX(fd->get_descent(w[i + j].font_size), w[i + j].y_off));
 						p_sd->descent = MAX(p_sd->descent, MAX(fd->get_descent(w[i + j].font_size), w[i + j].y_off));
 					} else {
 					} else {
 						p_sd->ascent = MAX(p_sd->ascent, fd->get_advance(w[i + j].index, w[i + j].font_size).x * 0.5);
 						p_sd->ascent = MAX(p_sd->ascent, fd->get_advance(w[i + j].index, w[i + j].font_size).x * 0.5);

+ 12 - 9
scene/gui/label.cpp

@@ -64,16 +64,17 @@ bool Label::is_uppercase() const {
 }
 }
 
 
 int Label::get_line_height(int p_line) const {
 int Label::get_line_height(int p_line) const {
+	Ref<Font> font = get_theme_font("font");
 	if (p_line >= 0 && p_line < lines_rid.size()) {
 	if (p_line >= 0 && p_line < lines_rid.size()) {
-		return TS->shaped_text_get_size(lines_rid[p_line]).y;
+		return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
 	} else if (lines_rid.size() > 0) {
 	} else if (lines_rid.size() > 0) {
 		int h = 0;
 		int h = 0;
 		for (int i = 0; i < lines_rid.size(); i++) {
 		for (int i = 0; i < lines_rid.size(); i++) {
-			h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y);
+			h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
 		}
 		}
 		return h;
 		return h;
 	} else {
 	} else {
-		return get_theme_font("font")->get_height(get_theme_font_size("font_size"));
+		return font->get_height(get_theme_font_size("font_size"));
 	}
 	}
 }
 }
 
 
@@ -138,6 +139,7 @@ void Label::_shape() {
 void Label::_update_visible() {
 void Label::_update_visible() {
 	int line_spacing = get_theme_constant("line_spacing", "Label");
 	int line_spacing = get_theme_constant("line_spacing", "Label");
 	Ref<StyleBox> style = get_theme_stylebox("normal", "Label");
 	Ref<StyleBox> style = get_theme_stylebox("normal", "Label");
+	Ref<Font> font = get_theme_font("font");
 	int lines_visible = lines_rid.size();
 	int lines_visible = lines_rid.size();
 
 
 	if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
 	if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
@@ -147,7 +149,7 @@ void Label::_update_visible() {
 	minsize.height = 0;
 	minsize.height = 0;
 	int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
 	int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
 	for (int64_t i = lines_skipped; i < last_line; i++) {
 	for (int64_t i = lines_skipped; i < last_line; i++) {
-		minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+		minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
 		if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 		if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 			break;
 			break;
 		}
 		}
@@ -197,7 +199,7 @@ void Label::_notification(int p_what) {
 
 
 		// Get number of lines to fit to the height.
 		// Get number of lines to fit to the height.
 		for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
 		for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
-			total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+			total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
 			if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 			if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 				break;
 				break;
 			}
 			}
@@ -213,7 +215,7 @@ void Label::_notification(int p_what) {
 		// Get real total height.
 		// Get real total height.
 		total_h = 0;
 		total_h = 0;
 		for (int64_t i = lines_skipped; i < last_line; i++) {
 		for (int64_t i = lines_skipped; i < last_line; i++) {
-			total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+			total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
 		}
 		}
 
 
 		int vbegin = 0, vsep = 0;
 		int vbegin = 0, vsep = 0;
@@ -263,7 +265,7 @@ void Label::_notification(int p_what) {
 		ofs.y = style->get_offset().y + vbegin;
 		ofs.y = style->get_offset().y + vbegin;
 		for (int i = lines_skipped; i < last_line; i++) {
 		for (int i = lines_skipped; i < last_line; i++) {
 			ofs.x = 0;
 			ofs.x = 0;
-			ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
+			ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(Font::SPACING_TOP);
 			switch (align) {
 			switch (align) {
 				case ALIGN_FILL:
 				case ALIGN_FILL:
 				case ALIGN_LEFT: {
 				case ALIGN_LEFT: {
@@ -337,7 +339,7 @@ void Label::_notification(int p_what) {
 				}
 				}
 			}
 			}
 
 
-			ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing;
+			ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(Font::SPACING_BOTTOM);
 		}
 		}
 	}
 	}
 
 
@@ -381,12 +383,13 @@ int Label::get_line_count() const {
 }
 }
 
 
 int Label::get_visible_line_count() const {
 int Label::get_visible_line_count() const {
+	Ref<Font> font = get_theme_font("font");
 	Ref<StyleBox> style = get_theme_stylebox("normal");
 	Ref<StyleBox> style = get_theme_stylebox("normal");
 	int line_spacing = get_theme_constant("line_spacing");
 	int line_spacing = get_theme_constant("line_spacing");
 	int lines_visible = 0;
 	int lines_visible = 0;
 	float total_h = 0;
 	float total_h = 0;
 	for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
 	for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
-		total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+		total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
 		if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 		if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 			break;
 			break;
 		}
 		}

+ 3 - 2
scene/gui/line_edit.cpp

@@ -732,6 +732,7 @@ void LineEdit::_notification(int p_what) {
 				style = get_theme_stylebox("read_only");
 				style = get_theme_stylebox("read_only");
 				draw_caret = false;
 				draw_caret = false;
 			}
 			}
+			Ref<Font> font = get_theme_font("font");
 
 
 			style->draw(ci, Rect2(Point2(), size));
 			style->draw(ci, Rect2(Point2(), size));
 
 
@@ -742,7 +743,7 @@ void LineEdit::_notification(int p_what) {
 			int x_ofs = 0;
 			int x_ofs = 0;
 			bool using_placeholder = text.empty() && ime_text.empty();
 			bool using_placeholder = text.empty() && ime_text.empty();
 			float text_width = TS->shaped_text_get_size(text_rid).x;
 			float text_width = TS->shaped_text_get_size(text_rid).x;
-			float text_height = TS->shaped_text_get_size(text_rid).y;
+			float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
 
 
 			switch (align) {
 			switch (align) {
 				case ALIGN_FILL:
 				case ALIGN_FILL:
@@ -1570,7 +1571,7 @@ Size2 LineEdit::get_minimum_size() const {
 		min_size.width = MAX(min_size.width, full_width + space_size);
 		min_size.width = MAX(min_size.width, full_width + space_size);
 	}
 	}
 
 
-	min_size.height = MAX(TS->shaped_text_get_size(text_rid).y, font->get_height(font_size));
+	min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM), font->get_height(font_size));
 
 
 	// Take icons into account.
 	// Take icons into account.
 	bool using_placeholder = text.empty() && ime_text.empty();
 	bool using_placeholder = text.empty() && ime_text.empty();

+ 1 - 1
scene/gui/text_edit.cpp

@@ -1141,7 +1141,7 @@ void TextEdit::_notification(int p_what) {
 
 
 					// Draw line.
 					// Draw line.
 					RID rid = ldata->get_line_rid(line_wrap_index);
 					RID rid = ldata->get_line_rid(line_wrap_index);
-					float text_height = TS->shaped_text_get_size(rid).y;
+					float text_height = TS->shaped_text_get_size(rid).y + cache.font->get_spacing(Font::SPACING_TOP) + cache.font->get_spacing(Font::SPACING_BOTTOM);
 
 
 					if (rtl) {
 					if (rtl) {
 						char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x;
 						char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x;

+ 10 - 10
scene/resources/font.cpp

@@ -595,41 +595,41 @@ Dictionary Font::get_feature_list() const {
 float Font::get_height(int p_size) const {
 float Font::get_height(int p_size) const {
 	float ret = 0.f;
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_height(p_size);
+		ret = MAX(ret, data[i]->get_height(p_size));
 	}
 	}
-	return (ret / data.size()) + spacing_top + spacing_bottom;
+	return ret + spacing_top + spacing_bottom;
 }
 }
 
 
 float Font::get_ascent(int p_size) const {
 float Font::get_ascent(int p_size) const {
 	float ret = 0.f;
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_ascent(p_size);
+		ret = MAX(ret, data[i]->get_ascent(p_size));
 	}
 	}
-	return (ret / data.size()) + spacing_top;
+	return ret + spacing_top;
 }
 }
 
 
 float Font::get_descent(int p_size) const {
 float Font::get_descent(int p_size) const {
 	float ret = 0.f;
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_descent(p_size);
+		ret = MAX(ret, data[i]->get_descent(p_size));
 	}
 	}
-	return (ret / data.size()) + spacing_bottom;
+	return ret + spacing_bottom;
 }
 }
 
 
 float Font::get_underline_position(int p_size) const {
 float Font::get_underline_position(int p_size) const {
 	float ret = 0.f;
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_underline_position(p_size);
+		ret = MAX(ret, data[i]->get_underline_position(p_size));
 	}
 	}
-	return (ret / data.size());
+	return ret;
 }
 }
 
 
 float Font::get_underline_thickness(int p_size) const {
 float Font::get_underline_thickness(int p_size) const {
 	float ret = 0.f;
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_underline_thickness(p_size);
+		ret = MAX(ret, data[i]->get_underline_thickness(p_size));
 	}
 	}
-	return (ret / data.size());
+	return ret;
 }
 }
 
 
 int Font::get_spacing(int p_type) const {
 int Font::get_spacing(int p_type) const {

+ 17 - 7
scene/resources/text_line.cpp

@@ -113,6 +113,8 @@ RID TextLine::get_rid() const {
 
 
 void TextLine::clear() {
 void TextLine::clear() {
 	TS->shaped_text_clear(rid);
 	TS->shaped_text_clear(rid);
+	spacing_top = 0;
+	spacing_bottom = 0;
 }
 }
 
 
 void TextLine::set_preserve_invalid(bool p_enabled) {
 void TextLine::set_preserve_invalid(bool p_enabled) {
@@ -166,6 +168,8 @@ void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) {
 
 
 bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
 bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
 	bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
 	bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+	spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
+	spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
 	dirty = true;
 	dirty = true;
 	return res;
 	return res;
 }
 }
@@ -233,17 +237,21 @@ float TextLine::get_width() const {
 
 
 Size2 TextLine::get_size() const {
 Size2 TextLine::get_size() const {
 	const_cast<TextLine *>(this)->_shape();
 	const_cast<TextLine *>(this)->_shape();
-	return TS->shaped_text_get_size(rid);
+	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+		return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
+	} else {
+		return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y);
+	}
 }
 }
 
 
 float TextLine::get_line_ascent() const {
 float TextLine::get_line_ascent() const {
 	const_cast<TextLine *>(this)->_shape();
 	const_cast<TextLine *>(this)->_shape();
-	return TS->shaped_text_get_ascent(rid);
+	return TS->shaped_text_get_ascent(rid) + spacing_top;
 }
 }
 
 
 float TextLine::get_line_descent() const {
 float TextLine::get_line_descent() const {
 	const_cast<TextLine *>(this)->_shape();
 	const_cast<TextLine *>(this)->_shape();
-	return TS->shaped_text_get_descent(rid);
+	return TS->shaped_text_get_descent(rid) + spacing_bottom;
 }
 }
 
 
 float TextLine::get_line_width() const {
 float TextLine::get_line_width() const {
@@ -291,10 +299,10 @@ void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) co
 
 
 	float clip_l;
 	float clip_l;
 	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
 	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
-		ofs.y += TS->shaped_text_get_ascent(rid);
+		ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top;
 		clip_l = MAX(0, p_pos.x - ofs.x);
 		clip_l = MAX(0, p_pos.x - ofs.x);
 	} else {
 	} else {
-		ofs.x += TS->shaped_text_get_ascent(rid);
+		ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top;
 		clip_l = MAX(0, p_pos.y - ofs.y);
 		clip_l = MAX(0, p_pos.y - ofs.y);
 	}
 	}
 	return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color);
 	return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color);
@@ -330,10 +338,10 @@ void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_si
 
 
 	float clip_l;
 	float clip_l;
 	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
 	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
-		ofs.y += TS->shaped_text_get_ascent(rid);
+		ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top;
 		clip_l = MAX(0, p_pos.x - ofs.x);
 		clip_l = MAX(0, p_pos.x - ofs.x);
 	} else {
 	} else {
-		ofs.x += TS->shaped_text_get_ascent(rid);
+		ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top;
 		clip_l = MAX(0, p_pos.y - ofs.y);
 		clip_l = MAX(0, p_pos.y - ofs.y);
 	}
 	}
 	return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color);
 	return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color);
@@ -347,6 +355,8 @@ int TextLine::hit_test(float p_coords) const {
 
 
 TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 	rid = TS->create_shaped_text(p_direction, p_orientation);
 	rid = TS->create_shaped_text(p_direction, p_orientation);
+	spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
+	spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
 	TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
 	TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
 }
 }
 
 

+ 2 - 0
scene/resources/text_line.h

@@ -40,6 +40,8 @@ class TextLine : public Reference {
 	GDCLASS(TextLine, Reference);
 	GDCLASS(TextLine, Reference);
 
 
 	RID rid;
 	RID rid;
+	int spacing_top = 0;
+	int spacing_bottom = 0;
 
 
 	bool dirty = true;
 	bool dirty = true;
 
 

+ 36 - 21
scene/resources/text_paragraph.cpp

@@ -140,6 +140,8 @@ RID TextParagraph::get_line_rid(int p_line) const {
 }
 }
 
 
 void TextParagraph::clear() {
 void TextParagraph::clear() {
+	spacing_top = 0;
+	spacing_bottom = 0;
 	for (int i = 0; i < lines.size(); i++) {
 	for (int i = 0; i < lines.size(); i++) {
 		TS->free(lines[i]);
 		TS->free(lines[i]);
 	}
 	}
@@ -187,6 +189,8 @@ TextServer::Orientation TextParagraph::get_orientation() const {
 
 
 bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
 bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
 	bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
 	bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+	spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
+	spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
 	dirty_lines = true;
 	dirty_lines = true;
 	return res;
 	return res;
 }
 }
@@ -260,7 +264,11 @@ float TextParagraph::get_width() const {
 
 
 Size2 TextParagraph::get_non_wraped_size() const {
 Size2 TextParagraph::get_non_wraped_size() const {
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	const_cast<TextParagraph *>(this)->_shape_lines();
-	return TS->shaped_text_get_size(rid);
+	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+		return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
+	} else {
+		return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y);
+	}
 }
 }
 
 
 Size2 TextParagraph::get_size() const {
 Size2 TextParagraph::get_size() const {
@@ -270,9 +278,9 @@ Size2 TextParagraph::get_size() const {
 		Size2 lsize = TS->shaped_text_get_size(lines[i]);
 		Size2 lsize = TS->shaped_text_get_size(lines[i]);
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			size.x = MAX(size.x, lsize.x);
 			size.x = MAX(size.x, lsize.x);
-			size.y += lsize.y;
+			size.y += lsize.y + spacing_top + spacing_bottom;
 		} else {
 		} else {
-			size.x += lsize.x;
+			size.x += lsize.x + spacing_top + spacing_bottom;
 			size.y = MAX(size.y, lsize.y);
 			size.y = MAX(size.y, lsize.y);
 		}
 		}
 	}
 	}
@@ -297,9 +305,9 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
 	for (int i = 0; i < p_line; i++) {
 	for (int i = 0; i < p_line; i++) {
 		Size2 lsize = TS->shaped_text_get_size(lines[i]);
 		Size2 lsize = TS->shaped_text_get_size(lines[i]);
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
-			xrect.position.y += lsize.y;
+			xrect.position.y += lsize.y + spacing_top + spacing_bottom;
 		} else {
 		} else {
-			xrect.position.x += lsize.x;
+			xrect.position.x += lsize.x + spacing_top + spacing_bottom;
 		}
 		}
 	}
 	}
 	return xrect;
 	return xrect;
@@ -308,7 +316,11 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
 Size2 TextParagraph::get_line_size(int p_line) const {
 Size2 TextParagraph::get_line_size(int p_line) const {
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Size2());
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Size2());
-	return TS->shaped_text_get_size(lines[p_line]);
+	if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
+		return Size2(TS->shaped_text_get_size(lines[p_line]).x, TS->shaped_text_get_size(lines[p_line]).y + spacing_top + spacing_bottom);
+	} else {
+		return Size2(TS->shaped_text_get_size(lines[p_line]).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(lines[p_line]).y);
+	}
 }
 }
 
 
 Vector2i TextParagraph::get_line_range(int p_line) const {
 Vector2i TextParagraph::get_line_range(int p_line) const {
@@ -320,13 +332,13 @@ Vector2i TextParagraph::get_line_range(int p_line) const {
 float TextParagraph::get_line_ascent(int p_line) const {
 float TextParagraph::get_line_ascent(int p_line) const {
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
-	return TS->shaped_text_get_ascent(lines[p_line]);
+	return TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 }
 }
 
 
 float TextParagraph::get_line_descent(int p_line) const {
 float TextParagraph::get_line_descent(int p_line) const {
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
-	return TS->shaped_text_get_descent(lines[p_line]);
+	return TS->shaped_text_get_descent(lines[p_line]) + spacing_bottom;
 }
 }
 
 
 float TextParagraph::get_line_width(int p_line) const {
 float TextParagraph::get_line_width(int p_line) const {
@@ -353,10 +365,10 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
 	for (int i = 0; i < lines.size(); i++) {
 	for (int i = 0; i < lines.size(); i++) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
 			ofs.x = p_pos.x;
-			ofs.y += TS->shaped_text_get_ascent(lines[i]);
+			ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
 		} else {
 		} else {
 			ofs.y = p_pos.y;
 			ofs.y = p_pos.y;
-			ofs.x += TS->shaped_text_get_ascent(lines[i]);
+			ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
 		}
 		}
 		float length = TS->shaped_text_get_width(lines[i]);
 		float length = TS->shaped_text_get_width(lines[i]);
 		if (width > 0) {
 		if (width > 0) {
@@ -389,10 +401,10 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
 		TS->shaped_text_draw(lines[i], p_canvas, ofs, clip_l, clip_l + width, p_color);
 		TS->shaped_text_draw(lines[i], p_canvas, ofs, clip_l, clip_l + width, p_color);
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
 			ofs.x = p_pos.x;
-			ofs.y += TS->shaped_text_get_descent(lines[i]);
+			ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
 		} else {
 		} else {
 			ofs.y = p_pos.y;
 			ofs.y = p_pos.y;
-			ofs.x += TS->shaped_text_get_descent(lines[i]);
+			ofs.x += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
 		}
 		}
 	}
 	}
 }
 }
@@ -403,10 +415,10 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
 	for (int i = 0; i < lines.size(); i++) {
 	for (int i = 0; i < lines.size(); i++) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
 			ofs.x = p_pos.x;
-			ofs.y += TS->shaped_text_get_ascent(lines[i]);
+			ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
 		} else {
 		} else {
 			ofs.y = p_pos.y;
 			ofs.y = p_pos.y;
-			ofs.x += TS->shaped_text_get_ascent(lines[i]);
+			ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
 		}
 		}
 		float length = TS->shaped_text_get_width(lines[i]);
 		float length = TS->shaped_text_get_width(lines[i]);
 		if (width > 0) {
 		if (width > 0) {
@@ -439,10 +451,10 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
 		TS->shaped_text_draw_outline(lines[i], p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color);
 		TS->shaped_text_draw_outline(lines[i], p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color);
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
 			ofs.x = p_pos.x;
-			ofs.y += TS->shaped_text_get_descent(lines[i]);
+			ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
 		} else {
 		} else {
 			ofs.y = p_pos.y;
 			ofs.y = p_pos.y;
-			ofs.x += TS->shaped_text_get_descent(lines[i]);
+			ofs.x += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
 		}
 		}
 	}
 	}
 }
 }
@@ -462,10 +474,12 @@ int TextParagraph::hit_test(const Point2 &p_coords) const {
 			if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines[i]).y)) {
 			if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines[i]).y)) {
 				return TS->shaped_text_hit_test_position(lines[i], p_coords.x);
 				return TS->shaped_text_hit_test_position(lines[i], p_coords.x);
 			}
 			}
+			ofs.y += TS->shaped_text_get_size(lines[i]).y + spacing_bottom + spacing_top;
 		} else {
 		} else {
 			if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines[i]).x)) {
 			if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines[i]).x)) {
 				return TS->shaped_text_hit_test_position(lines[i], p_coords.y);
 				return TS->shaped_text_hit_test_position(lines[i], p_coords.y);
 			}
 			}
+			ofs.y += TS->shaped_text_get_size(lines[i]).x + spacing_bottom + spacing_top;
 		}
 		}
 	}
 	}
 	return TS->shaped_text_get_range(rid).y;
 	return TS->shaped_text_get_range(rid).y;
@@ -477,9 +491,9 @@ void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, co
 
 
 	Vector2 ofs = p_pos;
 	Vector2 ofs = p_pos;
 	if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
 	if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
-		ofs.y += TS->shaped_text_get_ascent(lines[p_line]);
+		ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 	} else {
 	} else {
-		ofs.x += TS->shaped_text_get_ascent(lines[p_line]);
+		ofs.x += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 	}
 	}
 	return TS->shaped_text_draw(lines[p_line], p_canvas, ofs, -1, -1, p_color);
 	return TS->shaped_text_draw(lines[p_line], p_canvas, ofs, -1, -1, p_color);
 }
 }
@@ -490,9 +504,9 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_
 
 
 	Vector2 ofs = p_pos;
 	Vector2 ofs = p_pos;
 	if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
 	if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
-		ofs.y += TS->shaped_text_get_ascent(lines[p_line]);
+		ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 	} else {
 	} else {
-		ofs.x += TS->shaped_text_get_ascent(lines[p_line]);
+		ofs.x += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 	}
 	}
 	return TS->shaped_text_draw_outline(lines[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color);
 	return TS->shaped_text_draw_outline(lines[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color);
 }
 }
@@ -500,8 +514,9 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_
 TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 	rid = TS->create_shaped_text(p_direction, p_orientation);
 	rid = TS->create_shaped_text(p_direction, p_orientation);
 	TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
 	TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+	spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
+	spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
 	width = p_width;
 	width = p_width;
-	dirty_lines = true;
 }
 }
 
 
 TextParagraph::TextParagraph() {
 TextParagraph::TextParagraph() {

+ 2 - 0
scene/resources/text_paragraph.h

@@ -41,6 +41,8 @@ class TextParagraph : public Reference {
 
 
 	RID rid;
 	RID rid;
 	Vector<RID> lines;
 	Vector<RID> lines;
+	int spacing_top = 0;
+	int spacing_bottom = 0;
 
 
 	bool dirty_lines = true;
 	bool dirty_lines = true;