Diese Seite dokumentiert die vollständige, aus dem Original-Quellcode zeilengenau nachvollzogene Tag-Sprache innerhalb von caption-Werten sowie die Pipeline, die daraus tatsächlich gezeichnete Glyphen macht. Module: Nui/Text (Tag-Parser + Layout, keine GPU-Abhängigkeit), Renderer/Text (DirectWrite, Glyph-Atlas).
Die Tag-Sprache ist nicht verschachtelt wie HTML. Stattdessen hält der Parser einen einzigen veränderlichen "aktuellen Textabschnitt" (Font, Größe, Farbe, aktive Effekt-Flags). Jede zustandsändernde Tag schließt zunächst den bisher gesammelten Text als fertigen Abschnitt ab, bevor die Änderung auf den nächsten Text angewendet wird:
<B>abc<U>def</B>ghi</U>
wird zu drei Abschnitten:
| Text | Fett | Unterstrichen |
|---|---|---|
abc |
✅ | ❌ |
def |
✅ | ✅ |
ghi |
❌ | ✅ |
Effekt-Flags verhalten sich also wie einfache An/Aus-Bits, die sich per UND/ODER verknüpfen — nicht wie ein Baum aus verschachtelten Bereichen.
| Tag | Wirkung |
|---|---|
<font:name> |
Wechselt die Schriftart (Alias, siehe Font-Auflösung unten) |
<size:N> |
Schriftgröße in Pixeln |
<#RRGGBB> / <#RRGGBBAA> |
Textfarbe (Alpha optional, Standard: undurchsichtig) |
<@RRGGBB> / <@RRGGBBAA> |
Effektfarbe (für Schatten/Kontur/Glow) — unabhängig von der Textfarbe |
<hcenter>, <right>, <left> |
Horizontale Ausrichtung — gilt für die gesamte Caption, nicht pro Abschnitt |
<vcenter>, <top>, <bottom> |
Vertikale Ausrichtung — ebenfalls für die gesamte Caption |
<B> / </B> |
Fett an/aus |
<U> / </U> |
Unterstrichen an/aus |
<STRIKE> / </STRIKE> |
Durchgestrichen an/aus — wird geparst, aber nicht gezeichnet (siehe unten) |
<INV> / </INV> |
Invertiert (für Markierungs-/Caret-Darstellung) |
<shadow> / </shadow>, <SHD> / </SHD> |
Schlagschatten — beide Schreibweisen sind Synonyme |
<OUT> / </OUT> |
Kontur |
<GLOW> / </GLOW> |
Leuchten |
<2XGLOW> / </2XGLOW> |
Verstärktes Leuchten |
<BR> |
Erzwungener Zeilenumbruch |
<P> |
Absatzumbruch |
<OFFSET:N> |
Zusätzlicher horizontaler Pixel-Vorschub vor dem nächsten Abschnitt |
<OFFSETY:N> |
Vertikaler Pixel-Versatz (für Icon-Ausrichtung) |
<$ID:fallback> |
Lokalisierungs-Referenz — siehe unten |
<%ID>, <%%ID> |
Icon-/Emoticon-Referenz — wird erkannt, reserviert Platz, aber bewusst nicht gezeichnet (siehe unten) |
<CARET> / </CARET> |
Wirkungslos (No-Op) |
<$ID:fallback> — Lokalisierung ohne echte Übersetzungstabellecaption = "<font:font_01><size:8><hcenter><vcenter><$6606:Umtauschen>";
Das Original registriert in seiner tatsächlich laufenden Editor-Instanz nie eine echte Übersetzungstabelle. Das bedeutet: Der Text nach dem Doppelpunkt (Umtauschen) ist genau das, was tatsächlich angezeigt wird — nicht ein per ID nachgeschlagener, übersetzter String. NUIStudio bildet exakt dieses Verhalten nach: Ohne Doppelpunkt wird die numerische ID selbst als Text angezeigt (<$1234> → 1234), mit Doppelpunkt wird der Fallback-Text unverändert übernommen.
Der äußere Tag-Scanner entfernt jedes Leerzeichen, das innerhalb von <...> auftaucht, bevor der eigentliche Tag-Interpreter den Inhalt überhaupt zu sehen bekommt. Das betrifft auch den Fallback-Text eines <$ID:...>-Tags:
<$6606:Item Name> → wird zu "ItemName" (Leerzeichen verschwindet)
Das ist keine Einschränkung von NUIStudio, sondern eine bestätigte, reale Eigenart des Original-Tokenizers — Fallback-Texte mit Leerzeichen sind im Original genauso betroffen.
links → kein Versatz, rechts → max(Kastenbreite − Zeilenbreite, 0), zentriert → die Hälfte davon.KFLAG_SINGLE_LINE — Kürzung statt UmbruchIst das globale flag-Bit KFLAG_SINGLE_LINE (1<<4) gesetzt, wird nicht umgebrochen — stattdessen wird der Text bei Bedarf mit einer Ellipse ("…") gekürzt, bis er in die einzelne Zeile passt. Ohne dieses Bit wird bei Bedarf ganz normal mehrzeilig umgebrochen (Wortgrenzen-basiert, in NUIStudios Implementierung bewusst vereinfacht gegenüber der sehr viel komplexeren Blocksatz-/Silbentrennungs-Logik des Originals — siehe Grenzen der aktuellen Implementierung).
<shadow>/<SHD>, <OUT>, <GLOW> und <2XGLOW> werden über zwei Zeichen-Durchgänge pro Glyphe umgesetzt, statt eines neuen Shaders: zuerst eine Effekt-Ebene (in der Effektfarbe aus <@RRGGBB>, Standard: undurchsichtiges Schwarz), danach die normale Glyphe obendrüber (in der Textfarbe) — genau die Reihenfolge, mit der auch das Original einen Schatten/Glow unter den eigentlichen Buchstaben compositet. Konkret, zeilengenau aus XFreeType.cpps applyEffect() übernommen (dem Pfad, den der tatsächlich aktive FreeType-Renderpfad benutzt — nicht der ungenutzte GDI-Fallback im selben Original-File):
0 − Schriftgröße/20 − 1 Pixel diagonal nach unten-rechts versetzt gezeichnet.<OUT>): die Alpha-Maske der Glyphe wird einmal "aufgeweicht" (Dilatation) — ein dünner, symmetrischer Rand um die Buchstabenform.Eine reale, nicht offensichtliche Eigenart, 1:1 übernommen: Im Original werden diese vier Flags nacheinander in genau dieser Reihenfolge ausgewertet (Schatten → Glow → 2xGlow → Kontur → Fett), wobei jeder Schritt das Ergebnis des vorherigen überschreibt. Sind in einer Caption mehrere dieser Flags gleichzeitig gesetzt, gewinnt also immer nur eines — sie werden nie kombiniert/überlagert. <B> (Fett) steht in dieser Kette ganz am Ende und unterdrückt daher alle vier Effekte vollständig, falls beide zusammen gesetzt sind. NUIStudio bildet exakt diese Priorität nach, nicht eine "sinnvollere" Kombination.
Architektur-Hinweis: Das Original rastert und verarbeitet einen ganzen Textabschnitt als eine Bitmap; NUIStudios Glyph-Atlas cached dagegen pro einzelner Glyphe (siehe Die Rendering-Pipeline im Überblick unten). Da Dilatation und Versatz beides rein lokale, 1–2 Pixel weit reichende Operationen sind, die nie in den normalen Abstand zwischen zwei Buchstaben hineinreichen, ist das Ergebnis pro-Glyphe identisch zum Original — eine bewusste, bestätigt gleichwertige Anpassung an die andere Architektur, kein Kompromiss.
Zwei weitere, bestätigte Fakten aus dem Original-Quellcode, die NUIStudio bewusst genauso nachbildet statt sie "richtiger" zu machen:
<STRIKE> hat im Original keinen sichtbaren Effekt. Das Bit wird zwar gesetzt und in der Breitenberechnung berücksichtigt, aber der tatsächlich aktive Rendering-Pfad des Originals zeichnet nie eine Durchstreichungslinie dafür. NUIStudio parst das Tag identisch, zeichnet aber ebenfalls keine Linie — Bildtreue zum Original hat hier Vorrang vor "es sollte doch eigentlich...".<CARET>/</CARET> sind reine No-Ops — im Original wie in NUIStudio.<%ID>/<%%ID> (Icons/Emoticons) werden im Original selbst nie gezeichnet. Der Original-Quellcode reserviert dafür zwar Platz (Schriftgröße × 2, quadratisch) und lässt diesen Platz korrekt in den Zeilenumbruch einfließen, besitzt aber an keiner Stelle eine Icon-ID→Bild-Zuordnungstabelle — ein Kommentar im Original-Quellcode markiert das explizit als zurückgestellt ("Icon braucht eine eigene Icon-Ebene... aktuell nicht die Priorität... auf später verschoben"). NUIStudio bildet exakt dieses Verhalten nach: Tag erkannt, Platz reserviert, nichts gezeichnet — es gibt in der Referenzimplementierung schlicht kein "richtiges" Icon-Rendering, das nachgebaut werden könnte.<B>) — reine Alpha-Verstärkung, keine zusätzliche EbeneAnders als Schatten/Kontur/Glow/2xGlow ist Fett im Original keine zusätzliche Zeichen-Ebene, sondern eine einmalige Nachbearbeitung der bereits gerasterten Haupt-Glyphe: jedes Pixel mit Alpha > 0 wird mit 1,8 multipliziert und auf 255 begrenzt (abgeschnitten, nicht gerundet — XAlphaImage::MultiplyAlpha). Keine Größenänderung, kein Versatz, keine Farbänderung (Fett wirkt ausschließlich auf den Alpha-Kanal, nachdem die RGB-Einfärbung bereits passiert ist) — und, bestätigt über KTextRender::GetStringSizes FreeType-Zweig, keine Auswirkung auf die gemessene Breite/Vorschubbreite der Glyphe. NUIStudio setzt das exakt so um: derselbe Cache-Eintrag-Mechanismus wie bei Kontur/Glow, nur ohne Größenänderung der gepackten Bitmap, und als Ersatz für die normale Glyphe gezeichnet (nicht als zusätzlicher Layer).
Font-Aliase (font_default, font_01, font_02, font_03) werden über denselben ResourceResolver-Mechanismus wie Sprites aufgelöst — reale, im Asset-Ordner vorhandene Font-Dateien:
| Alias | Datei |
|---|---|
font_01 |
nanumgothic.otf |
font_02 |
nanumgothicextrabold.otf |
font_03 |
nanummyungjoextrabold.otf |
font_default |
nanumgothic.otf (Ersatz — die Original-Datei GASIIIB.ttf ist in keinem verfügbaren Asset-Korpus vorhanden) |
.nui-Text wird vom Parser als unveränderte Rohbytes gespeichert — für nicht-lateinischen Text bedeutet das CP949. Zwei Stellen müssen das explizit wissen:
FontCache::DecodeCaptionText): dekodiert Rohbytes explizit mit Codepage 949, bevor sie an DirectWrite gehen.?-Zeichen für jedes koreanische Zeichen — sowohl weil die Bytes kein gültiges UTF-8 sind, als auch weil ImGuis eigener Font-Atlas ursprünglich gar keine koreanischen Glyphen enthielt. Beide Ursachen sind behoben: Cp949ToUtf8/Utf8ToCp949 (Core/StringUtil) konvertieren beim Anzeigen/Speichern, und ein echter koreanischer Font (nanumgothic.otf) wird beim Setzen des Resource Roots zusätzlich in ImGuis eigenen Font-Atlas eingemischt.Jede rasterisierte Glyphe wird nach (Font-Alias, Größe, Codepoint) zwischengespeichert — ein wiederholtes Zeichen im selben Font/Größe wird nur einmal rasterisiert und danach aus dem Atlas wiederverwendet.
Diese Einschränkungen sind bewusste, dokumentierte Entscheidungen für den aktuellen Entwicklungsstand — keine übersehenen Lücken:
<%ID>/<%%ID> weiter oben) — es existiert dort schlicht keine Bild-Zuordnung, die man nachbauen könnte.# Ein Rang-Zähler, rechtsbündig, mit Schatten
caption = "<font:font_01><right><vcenter><size:8><shadow>000";
# Ein Mini-Button-Label mit Lokalisierungs-Fallback
caption = "<font:font_01><size:8><hcenter><vcenter><$6606:Anordnen>";
# Ein Hinweistext in Grau mit Lokalisierungs-ID
caption = "<font:font_01><size:8><#898989><$1153:[?] Rechtsklick auf Karte für Details>";