2
0
Эх сурвалжийг харах

Merge pull request #46721 from bruvzg/custom_word_break_punct

Rémi Verschelde 3 жил өмнө
parent
commit
5968653662

+ 3 - 0
doc/classes/TextParagraph.xml

@@ -278,6 +278,9 @@
 		<member name="align" type="int" setter="set_align" getter="get_align" enum="HAlign" default="0">
 		<member name="align" type="int" setter="set_align" getter="get_align" enum="HAlign" default="0">
 			Paragraph horizontal alignment.
 			Paragraph horizontal alignment.
 		</member>
 		</member>
+		<member name="custom_punctuation" type="String" setter="set_custom_punctuation" getter="get_custom_punctuation" default="&quot;&quot;">
+			Custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
+		</member>
 		<member name="direction" type="int" setter="set_direction" getter="get_direction" enum="TextServer.Direction" default="0">
 		<member name="direction" type="int" setter="set_direction" getter="get_direction" enum="TextServer.Direction" default="0">
 			Text writing direction.
 			Text writing direction.
 		</member>
 		</member>

+ 15 - 0
doc/classes/TextServer.xml

@@ -962,6 +962,13 @@
 				Returns shapes of the carets corresponding to the character offset [code]position[/code] in the text. Returned caret shape is 1 pixel wide rectangle.
 				Returns shapes of the carets corresponding to the character offset [code]position[/code] in the text. Returned caret shape is 1 pixel wide rectangle.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="shaped_text_get_custom_punctuation" qualifiers="const">
+			<return type="String" />
+			<argument index="0" name="shaped" type="RID" />
+			<description>
+				Returns custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
+			</description>
+		</method>
 		<method name="shaped_text_get_descent" qualifiers="const">
 		<method name="shaped_text_get_descent" qualifiers="const">
 			<return type="float" />
 			<return type="float" />
 			<argument index="0" name="shaped" type="RID" />
 			<argument index="0" name="shaped" type="RID" />
@@ -1212,6 +1219,14 @@
 				Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
 				Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="shaped_text_set_custom_punctuation">
+			<return type="void" />
+			<argument index="0" name="shaped" type="RID" />
+			<argument index="1" name="punct" type="String" />
+			<description>
+				Sets custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
+			</description>
+		</method>
 		<method name="shaped_text_set_direction">
 		<method name="shaped_text_set_direction">
 			<return type="void" />
 			<return type="void" />
 			<argument index="0" name="shaped" type="RID" />
 			<argument index="0" name="shaped" type="RID" />

+ 15 - 0
doc/classes/TextServerExtension.xml

@@ -969,6 +969,13 @@
 				Returns shapes of the carets corresponding to the character offset [code]position[/code] in the text. Returned caret shape is 1 pixel wide rectangle.
 				Returns shapes of the carets corresponding to the character offset [code]position[/code] in the text. Returned caret shape is 1 pixel wide rectangle.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="_shaped_text_get_custom_punctuation" qualifiers="virtual const">
+			<return type="String" />
+			<argument index="0" name="shaped" type="RID" />
+			<description>
+				Returns custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
+			</description>
+		</method>
 		<method name="_shaped_text_get_descent" qualifiers="virtual const">
 		<method name="_shaped_text_get_descent" qualifiers="virtual const">
 			<return type="float" />
 			<return type="float" />
 			<argument index="0" name="shaped" type="RID" />
 			<argument index="0" name="shaped" type="RID" />
@@ -1221,6 +1228,14 @@
 				Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
 				Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="_shaped_text_set_custom_punctuation" qualifiers="virtual">
+			<return type="void" />
+			<argument index="0" name="shaped" type="RID" />
+			<argument index="1" name="punct" type="String" />
+			<description>
+				Sets custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
+			</description>
+		</method>
 		<method name="_shaped_text_set_direction" qualifiers="virtual">
 		<method name="_shaped_text_set_direction" qualifiers="virtual">
 			<return type="void" />
 			<return type="void" />
 			<argument index="0" name="shaped" type="RID" />
 			<argument index="0" name="shaped" type="RID" />

+ 37 - 3
modules/text_server_adv/text_server_adv.cpp

@@ -3008,6 +3008,27 @@ TextServer::Direction TextServerAdvanced::shaped_text_get_direction(RID p_shaped
 	return sd->direction;
 	return sd->direction;
 }
 }
 
 
+void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+	_THREAD_SAFE_METHOD_
+	ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+	ERR_FAIL_COND(!sd);
+
+	if (sd->custom_punct != p_punct) {
+		if (sd->parent != RID()) {
+			full_copy(sd);
+		}
+		sd->custom_punct = p_punct;
+		invalidate(sd);
+	}
+}
+
+String TextServerAdvanced::shaped_text_get_custom_punctuation(RID p_shaped) const {
+	_THREAD_SAFE_METHOD_
+	const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+	ERR_FAIL_COND_V(!sd, String());
+	return sd->custom_punct;
+}
+
 void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) {
 void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) {
 	ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
 	ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
 	ERR_FAIL_COND(!sd);
 	ERR_FAIL_COND(!sd);
@@ -3307,6 +3328,7 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 
 
 	new_sd->orientation = sd->orientation;
 	new_sd->orientation = sd->orientation;
 	new_sd->direction = sd->direction;
 	new_sd->direction = sd->direction;
+	new_sd->custom_punct = sd->custom_punct;
 	new_sd->para_direction = sd->para_direction;
 	new_sd->para_direction = sd->para_direction;
 	new_sd->line_breaks_valid = sd->line_breaks_valid;
 	new_sd->line_breaks_valid = sd->line_breaks_valid;
 	new_sd->justification_ops_valid = sd->justification_ops_valid;
 	new_sd->justification_ops_valid = sd->justification_ops_valid;
@@ -3887,6 +3909,9 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
 	const char32_t *ch = sd->text.ptr();
 	const char32_t *ch = sd->text.ptr();
 	Glyph *sd_glyphs = sd->glyphs.ptrw();
 	Glyph *sd_glyphs = sd->glyphs.ptrw();
 
 
+	int c_punct_size = sd->custom_punct.length();
+	const char32_t *c_punct = sd->custom_punct.ptr();
+
 	for (i = 0; i < sd_size; i++) {
 	for (i = 0; i < sd_size; i++) {
 		if (sd_glyphs[i].count > 0) {
 		if (sd_glyphs[i].count > 0) {
 			char32_t c = ch[sd_glyphs[i].start - sd->start];
 			char32_t c = ch[sd_glyphs[i].start - sd->start];
@@ -3899,12 +3924,21 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
 			if (is_whitespace(c)) {
 			if (is_whitespace(c)) {
 				sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
 				sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
 			}
 			}
+			if (c_punct_size == 0) {
+				if (u_ispunct(c) && c != 0x005F) {
+					sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+				}
+			} else {
+				for (int j = 0; j < c_punct_size; j++) {
+					if (c_punct[j] == c) {
+						sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+						break;
+					}
+				}
+			}
 			if (is_underscore(c)) {
 			if (is_underscore(c)) {
 				sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
 				sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
 			}
 			}
-			if (u_ispunct(c) && c != 0x005F) {
-				sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
-			}
 			if (breaks.has(sd->glyphs[i].start)) {
 			if (breaks.has(sd->glyphs[i].start)) {
 				if (breaks[sd->glyphs[i].start]) {
 				if (breaks[sd->glyphs[i].start]) {
 					sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
 					sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;

+ 3 - 0
modules/text_server_adv/text_server_adv.h

@@ -463,6 +463,9 @@ public:
 
 
 	virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
 	virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
 
 
+	virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) override;
+	virtual String shaped_text_get_custom_punctuation(RID p_shaped) const override;
+
 	virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
 	virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
 	virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
 	virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
 
 

+ 45 - 9
modules/text_server_fb/text_server_fb.cpp

@@ -2107,6 +2107,27 @@ TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped
 	return TextServer::DIRECTION_LTR;
 	return TextServer::DIRECTION_LTR;
 }
 }
 
 
+void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+	_THREAD_SAFE_METHOD_
+	ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+	ERR_FAIL_COND(!sd);
+
+	if (sd->custom_punct != p_punct) {
+		if (sd->parent != RID()) {
+			full_copy(sd);
+		}
+		sd->custom_punct = p_punct;
+		invalidate(sd);
+	}
+}
+
+String TextServerFallback::shaped_text_get_custom_punctuation(RID p_shaped) const {
+	_THREAD_SAFE_METHOD_
+	const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+	ERR_FAIL_COND_V(!sd, String());
+	return sd->custom_punct;
+}
+
 void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
 void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
 	ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
 	ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
 	ERR_FAIL_COND(!sd);
 	ERR_FAIL_COND(!sd);
@@ -2409,6 +2430,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 
 
 	new_sd->orientation = sd->orientation;
 	new_sd->orientation = sd->orientation;
 	new_sd->direction = sd->direction;
 	new_sd->direction = sd->direction;
+	new_sd->custom_punct = sd->custom_punct;
 	new_sd->para_direction = sd->para_direction;
 	new_sd->para_direction = sd->para_direction;
 	new_sd->line_breaks_valid = sd->line_breaks_valid;
 	new_sd->line_breaks_valid = sd->line_breaks_valid;
 	new_sd->justification_ops_valid = sd->justification_ops_valid;
 	new_sd->justification_ops_valid = sd->justification_ops_valid;
@@ -2692,27 +2714,41 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
 	}
 	}
 
 
 	int sd_size = sd->glyphs.size();
 	int sd_size = sd->glyphs.size();
+	Glyph *sd_glyphs = sd->glyphs.ptrw();
+
+	int c_punct_size = sd->custom_punct.length();
+	const char32_t *c_punct = sd->custom_punct.ptr();
+
 	for (int i = 0; i < sd_size; i++) {
 	for (int i = 0; i < sd_size; i++) {
-		if (sd->glyphs[i].count > 0) {
-			char32_t c = sd->text[sd->glyphs[i].start];
-			if (is_punct(c)) {
-				sd->glyphs.write[i].flags |= GRAPHEME_IS_PUNCTUATION;
+		if (sd_glyphs[i].count > 0) {
+			char32_t c = sd->text[sd_glyphs[i].start];
+			if (c_punct_size == 0) {
+				if (is_punct(c)) {
+					sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+				}
+			} else {
+				for (int j = 0; j < c_punct_size; j++) {
+					if (c_punct[j] == c) {
+						sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+						break;
+					}
+				}
 			}
 			}
 			if (is_underscore(c)) {
 			if (is_underscore(c)) {
 				sd->glyphs.write[i].flags |= GRAPHEME_IS_UNDERSCORE;
 				sd->glyphs.write[i].flags |= GRAPHEME_IS_UNDERSCORE;
 			}
 			}
 			if (is_whitespace(c) && !is_linebreak(c)) {
 			if (is_whitespace(c) && !is_linebreak(c)) {
-				sd->glyphs.write[i].flags |= GRAPHEME_IS_SPACE;
-				sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+				sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
+				sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
 			}
 			}
 			if (is_linebreak(c)) {
 			if (is_linebreak(c)) {
-				sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_HARD;
+				sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
 			}
 			}
 			if (c == 0x0009 || c == 0x000b) {
 			if (c == 0x0009 || c == 0x000b) {
-				sd->glyphs.write[i].flags |= GRAPHEME_IS_TAB;
+				sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
 			}
 			}
 
 
-			i += (sd->glyphs[i].count - 1);
+			i += (sd_glyphs[i].count - 1);
 		}
 		}
 	}
 	}
 	sd->line_breaks_valid = true;
 	sd->line_breaks_valid = true;

+ 3 - 0
modules/text_server_fb/text_server_fb.h

@@ -374,6 +374,9 @@ public:
 
 
 	virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
 	virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
 
 
+	virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) override;
+	virtual String shaped_text_get_custom_punctuation(RID p_shaped) const override;
+
 	virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
 	virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
 	virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
 	virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
 
 

+ 14 - 0
scene/resources/text_paragraph.cpp

@@ -38,6 +38,11 @@ void TextParagraph::_bind_methods() {
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
 
 
+	ClassDB::bind_method(D_METHOD("set_custom_punctuation", "custom_punctuation"), &TextParagraph::set_custom_punctuation);
+	ClassDB::bind_method(D_METHOD("get_custom_punctuation"), &TextParagraph::get_custom_punctuation);
+
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_punctuation"), "set_custom_punctuation", "get_custom_punctuation");
+
 	ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation);
 	ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation);
 	ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation);
 	ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation);
 
 
@@ -304,6 +309,15 @@ TextServer::Direction TextParagraph::get_direction() const {
 	return TS->shaped_text_get_direction(rid);
 	return TS->shaped_text_get_direction(rid);
 }
 }
 
 
+void TextParagraph::set_custom_punctuation(const String &p_punct) {
+	TS->shaped_text_set_custom_punctuation(rid, p_punct);
+	lines_dirty = true;
+}
+
+String TextParagraph::get_custom_punctuation() const {
+	return TS->shaped_text_get_custom_punctuation(rid);
+}
+
 void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
 void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
 	TS->shaped_text_set_orientation(rid, p_orientation);
 	TS->shaped_text_set_orientation(rid, p_orientation);
 	TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
 	TS->shaped_text_set_orientation(dropcap_rid, p_orientation);

+ 3 - 0
scene/resources/text_paragraph.h

@@ -96,6 +96,9 @@ public:
 
 
 	void set_bidi_override(const Array &p_override);
 	void set_bidi_override(const Array &p_override);
 
 
+	void set_custom_punctuation(const String &p_punct);
+	String get_custom_punctuation() const;
+
 	bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
 	bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
 	void clear_dropcap();
 	void clear_dropcap();
 
 

+ 15 - 0
servers/text/text_server_extension.cpp

@@ -194,6 +194,9 @@ void TextServerExtension::_bind_methods() {
 
 
 	GDVIRTUAL_BIND(_shaped_text_set_bidi_override, "shaped", "override");
 	GDVIRTUAL_BIND(_shaped_text_set_bidi_override, "shaped", "override");
 
 
+	GDVIRTUAL_BIND(_shaped_text_set_custom_punctuation, "shaped", "punct");
+	GDVIRTUAL_BIND(_shaped_text_get_custom_punctuation, "shaped");
+
 	GDVIRTUAL_BIND(_shaped_text_set_orientation, "shaped", "orientation");
 	GDVIRTUAL_BIND(_shaped_text_set_orientation, "shaped", "orientation");
 	GDVIRTUAL_BIND(_shaped_text_get_orientation, "shaped");
 	GDVIRTUAL_BIND(_shaped_text_get_orientation, "shaped");
 
 
@@ -951,6 +954,18 @@ void TextServerExtension::shaped_text_set_bidi_override(RID p_shaped, const Arra
 	GDVIRTUAL_CALL(_shaped_text_set_bidi_override, p_shaped, p_override);
 	GDVIRTUAL_CALL(_shaped_text_set_bidi_override, p_shaped, p_override);
 }
 }
 
 
+void TextServerExtension::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+	GDVIRTUAL_CALL(_shaped_text_set_custom_punctuation, p_shaped, p_punct);
+}
+
+String TextServerExtension::shaped_text_get_custom_punctuation(RID p_shaped) const {
+	String ret;
+	if (GDVIRTUAL_CALL(_shaped_text_get_custom_punctuation, p_shaped, ret)) {
+		return ret;
+	}
+	return String();
+}
+
 void TextServerExtension::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
 void TextServerExtension::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
 	GDVIRTUAL_CALL(_shaped_text_set_preserve_invalid, p_shaped, p_enabled);
 	GDVIRTUAL_CALL(_shaped_text_set_preserve_invalid, p_shaped, p_enabled);
 }
 }

+ 5 - 0
servers/text/text_server_extension.h

@@ -316,6 +316,11 @@ public:
 	virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
 	virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
 	GDVIRTUAL2(_shaped_text_set_bidi_override, RID, const Array &);
 	GDVIRTUAL2(_shaped_text_set_bidi_override, RID, const Array &);
 
 
+	virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) override;
+	virtual String shaped_text_get_custom_punctuation(RID p_shaped) const override;
+	GDVIRTUAL2(_shaped_text_set_custom_punctuation, RID, String);
+	GDVIRTUAL1RC(String, _shaped_text_get_custom_punctuation, RID);
+
 	virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
 	virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
 	virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
 	virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
 	GDVIRTUAL2(_shaped_text_set_orientation, RID, Orientation);
 	GDVIRTUAL2(_shaped_text_set_orientation, RID, Orientation);

+ 3 - 0
servers/text_server.cpp

@@ -347,6 +347,9 @@ void TextServer::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::shaped_text_set_bidi_override);
 	ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::shaped_text_set_bidi_override);
 
 
+	ClassDB::bind_method(D_METHOD("shaped_text_set_custom_punctuation", "shaped", "punct"), &TextServer::shaped_text_set_custom_punctuation);
+	ClassDB::bind_method(D_METHOD("shaped_text_get_custom_punctuation", "shaped"), &TextServer::shaped_text_get_custom_punctuation);
+
 	ClassDB::bind_method(D_METHOD("shaped_text_set_orientation", "shaped", "orientation"), &TextServer::shaped_text_set_orientation, DEFVAL(ORIENTATION_HORIZONTAL));
 	ClassDB::bind_method(D_METHOD("shaped_text_set_orientation", "shaped", "orientation"), &TextServer::shaped_text_set_orientation, DEFVAL(ORIENTATION_HORIZONTAL));
 	ClassDB::bind_method(D_METHOD("shaped_text_get_orientation", "shaped"), &TextServer::shaped_text_get_orientation);
 	ClassDB::bind_method(D_METHOD("shaped_text_get_orientation", "shaped"), &TextServer::shaped_text_get_orientation);
 
 

+ 4 - 0
servers/text_server.h

@@ -150,6 +150,7 @@ protected:
 		int end = 0; // Substring end offset in the parent string.
 		int end = 0; // Substring end offset in the parent string.
 
 
 		String text;
 		String text;
+		String custom_punct;
 		TextServer::Direction direction = DIRECTION_LTR; // Desired text direction.
 		TextServer::Direction direction = DIRECTION_LTR; // Desired text direction.
 		TextServer::Orientation orientation = ORIENTATION_HORIZONTAL;
 		TextServer::Orientation orientation = ORIENTATION_HORIZONTAL;
 
 
@@ -369,6 +370,9 @@ public:
 
 
 	virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) = 0;
 	virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) = 0;
 
 
+	virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) = 0;
+	virtual String shaped_text_get_custom_punctuation(RID p_shaped) const = 0;
+
 	virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0;
 	virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0;
 	virtual Orientation shaped_text_get_orientation(RID p_shaped) const = 0;
 	virtual Orientation shaped_text_get_orientation(RID p_shaped) const = 0;