*This proposal is follow up to #4, to get some community feedback/preferences on a specific parts of CTL support implementation.* **Describe the project you are working on:** The Godot Engine --- **Describe the problem or limitation you are having in your project:** Currently, text display is extremely limited, and only supports simple, left-to-right scripts. --- **Describe the feature / enhancement and how it helps to overcome the problem or limitation:** Proper display of the text requires multiple steps to be done: 🔹 BiDi reordering (placing parts of the text as they are displayed), should be done on the whole paragraph of text, e.g. any part of the text that logically independent of the rest. <details> <summary>Click to expand</summary>  </details> 🔹 Shaping (choosing context dependent glyphs from the font and their relative positions). <details> <summary>Click to expand</summary>  </details> 🔹 Since text in each singe line should maintain logical order, breaking is done on non-reordered text (but it should be shaped, and shaping requires direction to be known, hence text is temporary reordered back for breaking). Then each line is reordered again (using slightly different algorithm), there's no need for shaping it again, results can be taken from the step 2. <details> <summary>Click to expand</summary>  </details> 🔹 Optionally some advanced technics can be using for line justification, but just expanding spaces should be OK in general. <details> <summary>Click to expand</summary>  </details> 🔹 For some types of data (urls/emails/source code) each part should be processed separately. <details> <summary>Click to expand</summary>  </details> 🔹 Doing these steps is quite expensive, and it's results probably should be cached, and results of the steps 1 and 2 can be reused for steps 3, 4 (e.g. resizing controls). 🔹 macOS and Windows have powerful built-in BiDi/shaping engines (CoreText and DirectWrite), and there are open source solutions for both, FreeBidi (LGPL) and ICU (MIT like license) for BiDi (ICU quite big, but also provides tons of potentially useful i18n stuff) and HarfBuzz for shaping (MIT, AFAIK there're no alternatives). 🔹 Most shapers only support widely used languages, for more exotic once SIL Graphite (MPL2 or LGPL) can be used (shaping engine for the font is integrated as bytecode into the font itself), which can be used as backend for HarfBuzz. 🔹 For the cross-platform engine ICU+Harfbuzz+Graphite seems to be the most logical choice, but we probably should have some way for custom platform specific implementations. 🔹 Majority of games do not need any dynamic text (neither dynamic fonts), everything can be pre-rendered as image, probably both (Dynamic font and CTL) should be optional modules to avoid waste of space. --- **Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:** What's the best way to implement it? ***Core only, module or GDNative?*** 🔹 Built-in CTL as the only way display text. 🔹 Built-in CTL module that can be disabled and the simple fallback module (handling text as it is done now) and GDNative for custom implementations. 🔹 Built-in simple fallback and GDNative only CTL (include dynamic library with the editor and export templates). ***Module or GDNative providing what?:*** 🔹 Only BiDi/shaping APIs. 🔹 Both BiDi/shaping and font implementations. (Simple + BitmapFont/CTL + DynamicFont) ***API (Base), How low or high level should it be?*** 🔹 Low level functions (do all the steps in the `Font->draw` and complex controls): ```c++ struct Run { Start, End; Script, Direction }; Vector<Run> bidi_reorder(String, Direction) Vector<Run> bidi_reorder_substr(String, Start, End, Direction) // Line reordering is different form full reordering and require full paragraph string as input. Vector<Grapheme> shape_run(String, Run, Font, Language) Vector<Pos + Type> get_safe_break_points(String) Vector<Pos + Type> get_justification_points(String) ``` Or have abstraction to expose all internal implementation structures (e.g. BiDi context, and shaping buffers) 🔹 Do BiDi and shaping in the one step, but expose results (see https://github.com/godotengine/godot-proposals/issues/4#issuecomment-636872816) ```c++ Vector<Grapheme> shape_string(String, Font, Language, Direction) Vector<Grapheme> shape_rich_string("Some rich string representation", Direction) Vector<Grapheme> justify_line(String, Vector<Grapheme>, Width) Vector<Vector<Grapheme>> break_line(String, /*full line*/ Vector<Grapheme>, Width) // Can reuse shaped grapheme and do BiDi steps only. or Vector<Vector<Grapheme>> shape_lines(String, Font, Language, Direction, Width) // Do full reshaping, it's not efficient way to do it, if line width for the same text is changing muliple times, but OK if it done once (or can ue internal cache, to reuse full line data). ``` 🔹 High level, use `ShapedString` structure containing both input and output data as the single entity, w/o need of directly accessing underling low-level stuff, with lazy BiDi/shaping and caching. (see https://github.com/godotengine/godot-proposals/issues/4#issuecomment-490202448) ```c++ ShapedAttributedString("Some rich string representation", Direction) ShapedString->set_string/font/language/direction Vector<ShapedString> ShapedString->break_lines(Width) // Can reuse preserved paragraph BiDi context, and shaped full line graphemes without extra actions. void shstr->justify(Width) ``` 🔹 High level, use `ShapedParagraph` structure handling all layout features for a whole paragraphs at once. 🔹 Use both `ShapedString` and `Paragraph` as higher level helper for multiline controls. (see https://github.com/bruvzg/godot_tl) 🔹 Which part of the API should handle caching? Controls and Font or module functions? 🔹 Something else? ***API (Text input, cursor/selection control), should be handled by controls or module?*** 🔹 Only complex, font specific functions (e.g. ligature cursors), do everything else in the controls. 🔹 Common cursor control API for all controls (e.g. `ShapedString->move_caret(CursorPos, +/- Magnitude, Type WORD/CHAR/LINE/PARA) -> CursorPos`, `ShapedStgring->hit_test(..., Coords) -> CursorPos`). 🔹 Something else? ***API (Font, Canvas)*** Currently, we have duplicate string drawing functions both in Canvas and Font, do we need both? --- **If this enhancement will not be used often, can it be worked around with a few lines of script?:** It will be used to draw all text in the editor and exported apps. --- **Is there a reason why this should be core and not an add-on in the asset library?:** Main implementation can and probably should be module, and have support for the custom GDNative implementations, but substantial changes to the core are required anyway.