This page documents Godot's text shaping infrastructure: the TextServer interface, its two built-in implementations (TextServerAdvanced and TextServerFallback), and the high-level TextLine and TextParagraph resource classes that sit on top of it. The scope covers how raw Unicode strings are converted into positioned, renderable glyphs, including BiDi reordering, font fallback, OpenType feature application, line breaking, and justification.
For how specific UI controls consume this infrastructure, see TextEdit & CodeEdit, RichTextLabel, and Label & LineEdit. For the Font resource hierarchy used as input to text shaping, that is also covered here in brief; the broader rendering pipeline is documented in Rendering System.
Godot decouples text shaping logic from the rest of the engine through the TextServer interface. All text UI controls talk only to this interface, never to a specific implementation. At runtime, exactly one TextServer is active, managed through TextServerManager.
Architectural Layers
Sources: servers/text/text_server_extension.h modules/text_server_adv/text_server_adv.h modules/text_server_fb/text_server_fb.h scene/resources/text_line.h scene/resources/text_paragraph.h
TextServer (declared in servers/text/text_server.h) is a RefCounted-derived abstract class. Implementations register through TextServerExtension (in servers/text/text_server_extension.h), which exposes all methods as overridable virtuals.
The global singleton shortcut TS (a macro expanding to TextServerManager::get_primary_interface()) is used throughout all text-related code to access the active server.
The Feature enum controls capability detection at runtime. The two implementations expose different subsets:
| Feature Constant | TextServerAdvanced | TextServerFallback |
|---|---|---|
FEATURE_SIMPLE_LAYOUT | ✓ | ✓ |
FEATURE_BIDI_LAYOUT | ✓ | ✗ |
FEATURE_VERTICAL_LAYOUT | ✓ | ✗ |
FEATURE_SHAPING | ✓ | ✗ |
FEATURE_KASHIDA_JUSTIFICATION | ✓ | ✗ |
FEATURE_BREAK_ITERATORS | ✓ | ✗ |
FEATURE_FONT_DYNAMIC | if FreeType | if FreeType |
FEATURE_FONT_MSDF | if msdfgen | if msdfgen |
FEATURE_FONT_VARIABLE | ✓ | ✗ |
FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION | ✓ | ✗ |
FEATURE_USE_SUPPORT_DATA | ✓ | ✗ |
FEATURE_UNICODE_IDENTIFIERS | ✓ | ✗ |
FEATURE_UNICODE_SECURITY | ✓ | ✗ |
Sources: modules/text_server_adv/text_server_adv.cpp353-398 modules/text_server_fb/text_server_fb.cpp87-122
The TextServer API is split into three groups of operations, each using RID-based handles:
Font management — create_font(), font_set_data(), font_set_face_index(), font_get_glyph_index(), font_draw_glyph(), font_get_ascent(), etc.
Shaped text buffers — create_shaped_text(), shaped_text_add_string(), shaped_text_add_object(), shaped_text_shape(), shaped_text_get_glyphs(), shaped_text_get_line_breaks(), shaped_text_fit_to_width(), shaped_text_overrun_trim_to_width(), etc.
String utilities — string_to_upper(), string_to_lower(), is_locale_right_to_left(), format_number(), parse_number(), percent_sign().
Sources: doc/classes/TextServer.xml servers/text/text_server_extension.h
TextServerAdvanced (in modules/text_server_adv/) is the full-featured implementation. Its name string is "ICU / HarfBuzz / Graphite (Built-in)".
modules/text_server_adv/text_server_adv.cpp380-386
| Library | Role | Header Entry Point |
|---|---|---|
ICU (ubidi, ubrk, uchar, uscript, uspoof) | BiDi analysis, break iteration, script detection, Unicode security | <unicode/ubidi.h> etc. |
HarfBuzz (hb.h, hb-icu.h, hb-ft.h, hb-ot.h) | OpenType shaping (substitution, positioning, ligatures) | <hb.h> |
FreeType (ft2build.h) | Vector glyph rasterization, font metric extraction | FT_FREETYPE_H |
| msdfgen | Multi-channel SDF glyph generation | <msdfgen.h> |
| Graphite2 (via HarfBuzz) | Graphite smart-font shaping for scripts like Nastaliq | via HarfBuzz |
Sources: modules/text_server_adv/text_server_adv.h95-140
TextServerAdvanced requires ICU break-iteration data. It can be loaded from a file (icudt_godot.dat) or compiled in statically when ICU_STATIC_DATA is defined. The load happens in _load_support_data():
modules/text_server_adv/text_server_adv.cpp440-477
Languages that require ICU support data include Burmese (my), Chinese (zh), Japanese (ja), Korean (ko), Khmer (km), Lao (lo), and Thai (th).
modules/text_server_adv/text_server_adv.cpp523-530
Because HarfBuzz normally expects a FreeType face, TextServerAdvanced includes a hand-crafted bridge for bitmap fonts (bmp_font_t). The static functions _bmp_get_nominal_glyph, _bmp_get_glyph_h_advance, _bmp_get_glyph_h_kerning, etc., are registered as a custom hb_font_funcs_t function table in _bmp_create_font_funcs().
modules/text_server_adv/text_server_adv.cpp92-262
TextServerAdvanced maintains a bidirectional map between human-readable feature names and 4-byte OpenType tags (HB_TAG). The function _insert_feature_sets() populates feature_sets (name→tag) and feature_sets_inv (tag→info). Over 100 features are registered, including contextual_alternates, ligatures, small_caps, kerning, all character_variant_XX entries, and Indic-script-specific features.
modules/text_server_adv/text_server_adv.cpp551-700
TextServerFallback (in modules/text_server_fb/) is a minimal implementation for platforms or build configurations where ICU/HarfBuzz are unavailable. Its name string is "Fallback (Built-in)".
It supports only:
FEATURE_SIMPLE_LAYOUT — left-to-right text, no BiDiFEATURE_FONT_DYNAMIC — FreeType rasterization (if compiled in)FEATURE_FONT_MSDF — MSDF glyph generation (if compiled in)FEATURE_FONT_BITMAP — raw bitmap fonts_is_locale_right_to_left() always returns false. _load_support_data() always returns false.
Sources: modules/text_server_fb/text_server_fb.cpp87-204
Shaping a single paragraph from string to glyphs:
Sources: scene/gui/label.cpp140-345 doc/classes/TextServer.xml
The LineBreakFlag bitfield controls wrapping behavior. Common combinations:
| Mode | Flags |
|---|---|
AUTOWRAP_OFF | BREAK_MANDATORY only |
AUTOWRAP_ARBITRARY | BREAK_GRAPHEME_BOUND | BREAK_MANDATORY |
AUTOWRAP_WORD | BREAK_WORD_BOUND | BREAK_MANDATORY |
AUTOWRAP_WORD_SMART | BREAK_WORD_BOUND | BREAK_ADAPTIVE | BREAK_MANDATORY |
Trim flags (BREAK_TRIM_START_EDGE_SPACES, BREAK_TRIM_END_EDGE_SPACES) are OR'd in separately and are set by the autowrap_trim_flags property on UI controls.
Sources: scene/gui/label.cpp211-226 scene/gui/rich_text_label.cpp610-624
JustificationFlag controls how shaped_text_fit_to_width() distributes extra space:
JUSTIFICATION_WORD_BOUND — expand word gapsJUSTIFICATION_KASHIDA — insert Arabic Kashida charactersJUSTIFICATION_SKIP_LAST_LINE — don't justify the final line of a paragraphJUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE — override skip-last-line for single-line paragraphsJUSTIFICATION_AFTER_LAST_TAB — allow justification in the final tab segmentSources: scene/gui/label.cpp265-330
structured_text_parser() is a free function that produces a Vector<Vector3i> BiDi override map. It is used to handle structured content within a single shaped text buffer (e.g., file paths, URIs, email addresses). The parser type is controlled by the StructuredTextParser enum (STRUCTURED_TEXT_DEFAULT, STRUCTURED_TEXT_URI, STRUCTURED_TEXT_FILE, STRUCTURED_TEXT_EMAIL, STRUCTURED_TEXT_LIST, STRUCTURED_TEXT_GDSCRIPT, STRUCTURED_TEXT_CUSTOM).
scene/gui/rich_text_label.cpp816-821
TextLine (scene/resources/text_line.h, scene/resources/text_line.cpp) is a RefCounted resource wrapping a single shaped text buffer RID. It provides a convenient API for single-line text, used primarily for prefix/bullet rendering in RichTextLabel.
Key methods:
| Method | Purpose |
|---|---|
add_string(text, font, size, lang, meta) | Append a text span |
add_object(key, size, align, length, baseline) | Embed an inline object |
set_direction(dir) | Set explicit text direction |
set_width(width) | Set maximum width |
tab_align(tab_stops) | Set tab stop positions |
get_size() | Return shaped size (Vector2) |
draw(canvas, pos, color) | Render to a canvas item |
hit_test(coords) | Map pixel position → character index |
Sources: scene/resources/text_line.h scene/resources/text_line.cpp
In RichTextLabel, TextLine objects are used for list prefixes (l.text_prefix) via add_string() then get_size().x to compute maximum prefix width.
scene/gui/rich_text_label.cpp366-369
TextParagraph (scene/resources/text_paragraph.h, scene/resources/text_paragraph.cpp) is a RefCounted resource wrapping both a paragraph-level shaped buffer and the set of line-break RIDs derived from it. It is the primary shaped-text object used by Label, RichTextLabel, and TextEdit.
Internal state:
| Field | Type | Purpose |
|---|---|---|
rid | RID | The paragraph-level shaped text buffer |
lines_rid | Vector<RID> | One RID per wrapped line (substrings of rid) |
width | float | Available width for line breaking |
alignment | HorizontalAlignment | Text alignment |
direction | TextServer::Direction | Explicit or auto BiDi direction |
orientation | TextServer::Orientation | Horizontal or vertical |
break_flags | BitField<LineBreakFlag> | Wrapping mode |
jst_flags | BitField<JustificationFlag> | Justification mode |
Sources: scene/resources/text_paragraph.h scene/resources/text_paragraph.cpp
Key methods beyond those of TextLine:
| Method | Purpose |
|---|---|
set_dropcap(text, font, size, margins) | Set a dropcap character for the paragraph |
get_dropcap_lines() | Number of lines the dropcap occupies |
get_line_count() | Total wrapped line count |
get_line_size(line) | Size of a specific wrapped line |
get_line_ascent(line) / get_line_descent(line) | Per-line metrics |
draw(canvas, pos, color, ...) | Render all lines |
draw_line(canvas, pos, line, color) | Render a specific line |
draw_dropcap(canvas, pos, color) | Render the dropcap |
hit_test(coords) | Map pixel → character |
Relationship between TextParagraph and TextServer calls:
Sources: scene/resources/text_paragraph.cpp doc/classes/TextParagraph.xml
Godot's font hierarchy lives in scene/resources/font.h / font.cpp. All font classes ultimately provide a TypedArray<RID> (via get_rids()) to the text server.
Fallback chain: Every Font instance holds a TypedArray<Font> fallbacks. The _update_rids() method walks this tree depth-first, building a flat Vector<RID> that shaped_text_add_string() receives. The text server tries each RID in order for each character.
scene/resources/font.cpp105-124
Glyph rendering types (controlled per-font in the text server):
| Type | Description |
|---|---|
Bitmap (FEATURE_FONT_BITMAP) | Pre-rendered pixel glyphs; no FreeType |
Dynamic (FEATURE_FONT_DYNAMIC) | FreeType-rasterized outlines, cached in textures |
MSDF (FEATURE_FONT_MSDF) | Multi-channel SDF, resolution-independent, GPU-rendered |
| SVG-in-OT | Color SVG glyphs embedded in OpenType (via thorvg, optional) |
Sources: scene/resources/font.h scene/resources/font.cpp modules/text_server_fb/text_server_fb.cpp420-519
Both implementations use a shelf-pack texture atlas (ShelfPackTexture) to cache rasterized glyphs. When a glyph is first needed, it is rasterized (via FreeType or msdfgen), written into the first texture slab with sufficient space, and recorded by index + UV rect. Subsequent draws use the cached entry.
modules/text_server_fb/text_server_fb.cpp260-338
The Glyph struct (defined in the engine's core types, used throughout text rendering code) carries:
| Field | Type | Meaning |
|---|---|---|
start | int32_t | Character index in source string |
end | int32_t | End character index |
count | int8_t | Cluster size |
repeat | int8_t | Number of repeated glyphs |
flags | uint16_t | GRAPHEME_IS_RTL, GRAPHEME_IS_VIRTUAL, GRAPHEME_IS_SPACE, etc. |
x_off | float | Horizontal offset from baseline |
y_off | float | Vertical offset from baseline |
advance | float | Advance width |
font_rid | RID | Which font face to use |
font_size | int32_t | Render size |
index | int32_t | Glyph index within the font |
Callers iterate this array to draw text, calling TS->font_draw_glyph(gl.font_rid, canvas, gl.font_size, pos + Vector2(gl.x_off, gl.y_off), gl.index, color) for each glyph with a valid font_rid.
TextServerAdvanced implements the Unicode BiDi Algorithm (UBA) using ICU's ubidi library. The process:
ubidi_setPara() determines the base direction and creates a visual-to-logical map.uscript_getScript() (via script_iterator.h) further splits runs by Unicode script block (Latin, Arabic, Devanagari, etc.).hb_shape() with the matching OpenType script and language tags.Override control is provided via shaped_text_set_bidi_override(), which accepts a Vector<Vector3i> where each entry is (start, end, direction). RichTextLabel computes this from structured_text_parser() before calling the method.
scene/gui/rich_text_label.cpp816-821
Right-to-left locales are detected via _is_locale_right_to_left(), which checks the language subtag against a hardcoded list (ar, dv, he, fa, ff, ku, ur).
modules/text_server_adv/text_server_adv.cpp532-539
TextServer defines VisibleCharactersBehavior to control how character count limits interact with shaping:
| Constant | Meaning |
|---|---|
VC_CHARS_BEFORE_SHAPING | Truncate the string before shaping; avoids partial cluster artifacts |
VC_CHARS_AFTER_SHAPING | Shape the full string; hide trailing glyphs by character count |
VC_GLYPHS_LTR | Hide trailing glyphs in LTR visual order |
VC_GLYPHS_RTL | Hide trailing glyphs in RTL visual order |
VC_GLYPHS_AUTO | Choose LTR or RTL based on layout direction |
RichTextLabel implements VC_CHARS_BEFORE_SHAPING by creating a duplicate TextParagraph (text_buf_disp) containing only the visible substring.
scene/gui/rich_text_label.cpp712-717
Third-party or GDExtension code can implement a custom text server by subclassing TextServerExtension and overriding the virtual methods. Required overrides are marked virtual required in TextServerExtension. Optional ones carry a default no-op or fallback. The extension class is in servers/text/text_server_extension.h.
Implementations register themselves via TextServerManager::add_interface(). The primary interface is selected by priority or by the rendering/text_server/primary_interface project setting.
Sources: servers/text/text_server_extension.h doc/classes/TextServerExtension.xml
The following diagram shows how Label and RichTextLabel map their internal state to text server calls:
Sources: scene/gui/label.cpp140-345 scene/gui/rich_text_label.cpp602-827
RichTextLabel additionally calls TS->shaped_get_span_count() and TS->shaped_get_span_meta() to associate shaped spans back to its internal ItemText items for per-span font updates (_update_line_font()).
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.