Xterm.js: Unterstützt Schriftligaturen

Erstellt am 8. Sept. 2017  ·  54Kommentare  ·  Quelle: xtermjs/xterm.js

Unterstützung wird unter https://github.com/sourcelair/xterm.js/pull/938 entfernt

Es ist sicherlich immer noch möglich, der Renderer muss jedoch wissen, welche Zeichen verbunden werden sollen.

areapi arerenderer help wanted typenhancement

Hilfreichster Kommentar

Ich denke, wir interessieren uns nicht für die regulären Ligaturen (in der Tat wäre es schlecht, Ligaturen für li und dergleichen zu aktivieren), sondern für Dinge wie == , !== , => und so weiter. Hier ist eine schöne Liste von Ligaturen, die von der Fira-Code-Monospace-Schriftart unterstützt werden:
https://github.com/tonsky/FiraCode/blob/master/showcases/all_ligatures.png

Alle 54 Kommentare

Ich denke, wir interessieren uns nicht für die regulären Ligaturen (in der Tat wäre es schlecht, Ligaturen für li und dergleichen zu aktivieren), sondern für Dinge wie == , !== , => und so weiter. Hier ist eine schöne Liste von Ligaturen, die von der Fira-Code-Monospace-Schriftart unterstützt werden:
https://github.com/tonsky/FiraCode/blob/master/showcases/all_ligatures.png

Was würde passieren, wenn die Hälfte der Ligatur eine andere Farbe hat? Wie würden Sie damit umgehen?

@LoganDark funktioniert das überhaupt mit regulären Ligaturen? Oder sind sie aufgeteilt, wenn sie unterschiedliche Farben haben?

Nein und nein.

Ich frage mich, ob es möglich wäre, Rendercode aus txtjs zu ziehen, da sie herausgefunden haben, wie man Ligaturen rendert, obwohl ich denke, dass sie den Text manuell zeichnen. http://txtjs.com/examples/Text/ligatures.html

@devsnek Ich glaube nicht, dass es sich um ein Problem beim Rendern des Textes handelt. Das Problem besteht darin, zu wissen, welche Zeichen verbunden sind, damit sie zusammen gezeichnet werden können (Derzeit wird "==" als "=" und "=" gezeichnet, nicht als "==").

@ Tyriar würde sich ohne unser Eingreifen nicht darum kümmern

@devsnek ja, aber jede Zelle im Raster wird mit wenigen Ausnahmen einzeln gezeichnet (Emojis, breite Unicode-Zeichen). Ligaturen müssen irgendwie in diese Liste aufgenommen werden. Weitere Informationen finden Sie unter https://github.com/sourcelair/xterm.js/pull/938

@devsnek ES

Haftungsausschluss: völlig unangebracht, nur ein zufälliger Kommentar

@LoganDark funktioniert das überhaupt mit regulären Ligaturen? Oder sind sie aufgeteilt, wenn sie unterschiedliche Farben haben?

Wenn wir gehen , um wie andere Emulatoren mit ihnen umgehen, sie verteilen sich erheben , wenn sie unterschiedliche Farben sind, wie man es erwarten würde. Dies ist fast eine Voraussetzung, da Symbole beispielsweise von einigen Sprachmarkern in ViM korrekt dargestellt werden können.

Ich denke, es ist völlig akzeptabel, die einzelnen Symbole anzuzeigen, wenn der Rendermodus nicht für alle zugrunde liegenden Zeichen gleich ist.

@ Qix- Mein Vorschlag wäre dann, den gesamten Text auf einmal zu zeichnen und dann in der Post zu färben. Dies würde alle Probleme mit Ligaturen beseitigen und würde nicht die Erkennung von Ligaturpaaren erfordern (obwohl dies die Kompatibilität mit Schriftarten mit variabler Breite oder sogar Monospace-Schriftarten beeinträchtigen würde, die geringfügig abweichen / keine ganzzahligen Breiten haben).

@LoganDark mehrfarbige Ligaturen würden bizarr aussehen und es gäbe keine klare Möglichkeit, sie IMO zu färben.

Ja, ich glaube nicht, dass Mehrfarbenligaturen funktionieren würden. Es widerspricht auch der Funktionsweise darunter, wo zu Beginn eine einzelne Glyphe gezeichnet wird, nicht mehrere.

Zur Verdeutlichung wartet dies auf eine gute Lösung, um festzustellen, welche Zeichensequenzen Ligaturen aufweisen. Um dies richtig zu machen, würde es wahrscheinlich Code auf niedriger Ebene beinhalten, der Schriftdateien prüft (und daher ein natives Knotenmodul sein müsste und nicht für Webkonsumenten funktioniert). Ich glaube nicht, dass diese Informationen für die Webplattform verfügbar sind.

Jetzt, da Hyper Release 2.0.0 stabil ist, benötigen Ligatur-Workarounds möglicherweise eine höhere Priorität.

Das manuelle Bestimmen der Glyphenzuordnungen ist eine schwierige Nuss. Soweit ich das beurteilen kann, würde eine anständige Erfahrung Folgendes erfordern:

  1. Ordnen Sie die ausgewählte Schriftfamilie wieder der Datei / dem Puffer zu, die / der die tatsächlichen Schriftdaten enthält (otf / ttf / woff / etc).
  2. Analysieren Sie die Daten aus der GSUB- Tabelle der Schriftart und übersetzen Sie diese in ein sinnvolles Regelwerk für das Ersetzen von Glyphen
  3. Übergeben Sie eine Art Map oder Mapping-Funktion an xterm, um zu bestimmen, was für eine bestimmte Zeichenfolge gerendert werden soll

Ich habe mich zunächst mit Fira Code (insbesondere seiner Nerd-Schriftvariante) beschäftigt, um herauszufinden, wie schwierig jeder Schritt sein könnte. Ich habe noch nicht entschieden, ob ich mich ehrgeizig genug fühle (oder mich genug um Schriftligaturen kümmere), um dies zu übernehmen, aber ich habe Folgendes gefunden, damit das Wissen nicht verloren geht:

  • Es gibt keine Möglichkeit, die Schriftdaten mithilfe von Browser-APIs abzurufen. Dies funktioniert also nicht als direkte Funktion von xterm.js, sondern eher als separates Paket / Erweiterung mit einem von xterm.js bereitgestellten Hook

  • Das Zuordnen des CSS font-family -Namens einer Schriftart zu ihrer Schriftartdatei in Windows ist schmerzhaft, erscheint jedoch machbar. Bisher habe ich nur gefunden, alles in %WINDIR%\Fonts abzurufen und jede gefundene Datei zu analysieren (Spoiler: Es ist sehr langsam). Ich habe noch keine anderen Plattformen ausprobiert. (Hinweis: Ich habe auch versucht, den Namen aus der Registrierung zu entfernen, aber die Benennung stimmt nicht mit einigen Schriftarten überein, z. B. mit denen von Nerd-Schriftarten. Sie verwenden eine "bevorzugte" Familie und Unterfamilie, die nicht im Namen der Registrierung enthalten sind wird aber in der CSS font-family . Wenn Sie neugierig sind, ist der Registrierungsschlüssel in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts )

  • Es gibt eine Bibliothek namens opentype.js, die OpenType-Schriftarten-Tabellen vollständig analysiert und sogar eine font.stringToGlyphs(str) -Funktion hat, die grundlegende Ligaturen verarbeitet, aber die Ligaturen für Fira Code (und mehrere, wenn nicht alle anderen gängigen Ligatur-Schriftarten). Verwenden Sie eine Funktion namens Kontextalternativen, die von opentype.js (unter Geplant aufgeführt ) noch nicht unterstützt wird. Die erforderliche GSUB-Tabelle wird jedoch für Sie in JSON analysiert. Theoretisch fehlt also nur die Interpretation der Tabellendaten.

  • Fira-Code-Ligaturen (und ich denke andere) ersetzen die ursprünglichen Glyphen tatsächlich durch die gleiche Anzahl von Glyphen und nicht durch eine einzige extra breite (um die Eigenschaften der Monospace-Schriftart beizubehalten). Bei einer Zeichenfolge wie ==> fordert Sie die Schriftart auf, sie durch zwei "LIG" -Zeichen (im Wesentlichen ein leeres Leerzeichen) zu ersetzen, gefolgt von der tatsächlichen Glyphe für die Ligatur, die technisch gesehen immer noch nur ein Monospace ist zeichenweit. Obwohl der Pfad des letzten Zeichens angeblich nur eine Breite hat, erstreckt er sich über die linke Seite des Begrenzungsrahmens des Zeichens hinaus und deckt den Platz ab, den die beiden LIG-Zeichen davor einnehmen. Siehe unten für eine visuelle Darstellung (0 und 600 sind die Seiten des Feldes des Charakters). Ich weiß nicht, ob dies die Aufgabe des Renderers kompliziert oder ob es transformiert werden müsste, bevor es an xterm.js übergeben wird, aber etwas, das man beachten muss.

image

  • Eine weitere Falte dabei ist die Bestimmung, wann die Ligatur neu bewertet werden muss. Wenn ich beispielsweise viermal hintereinander = eingebe, würde ich das gewünschte Verhalten sehen, wenn eine einzelne gleich, dann eine doppelte gleich Ligatur, dann eine dreifach gleich Ligatur, dann vier separate Gleichheitszeichen. Es gibt tatsächlich Zuordnungen in den kontextbezogenen alternativen Regeln zum Löschen der Ligatur (dh wenn die aktuelle Eingabe '=' ist und die vorherigen drei Zeichen '===' sind, ordnen Sie sie überhaupt nicht neu zu), aber wir hätten sie um herauszufinden, wie diese Regeln angewendet werden.

  • OpenType ist kompliziert. Zugegeben, ich bin kein Experte für das Rendern von Schriftarten, aber die Anzahl der möglichen Variationen, die zu unterschiedlichen Arten des Renderns führen, ist ziemlich groß. Ohne eine eingebaute Bibliothek, die das Mapping für uns übernimmt, ist der vernünftigste Weg, dies anzugreifen, meiner Meinung nach inkrementell. Fira Code verwendet speziell das Chaining Context Substitution Format 3 , aber ich bin sicher, dass es andere beliebte Schriftarten gibt, die andere verwenden. Da jede Semantik etwas anders ist, ist es wahrscheinlich sinnvoll, mit einer zu beginnen und von dort fortzufahren.

@princjef Danke, dass du deine Erkundungen geteilt hast, wirklich sehr, sehr hilfreich! Ich habe vor ein paar Tagen auch einige Gedanken zu diesem Thema gemacht und bin zu folgendem Schluss gekommen:

  • Das Erkennen von Ligaturen in Webfonts mithilfe einer integrierten API scheint unmöglich (genau wie Sie es beschreiben).
  • Es gibt bestimmte Ligaturen, die in einem Terminal keinen Sinn ergeben, auch wenn die Schriftart dies unterstützt (z. B. li ).
  • Es gibt eine sehr kleine Untergruppe von Ligaturen, deren Unterstützung sinnvoll ist, nämlich die meisten Fira-Code-Ligaturen.
  • Das Hinzufügen von Unterstützung für das Zeichnen und Löschen eines Zeichens, das sich über mehrere Zellen erstreckt, ist mit unserem aktuellen Rendering-Ansatz (CharAtlas) auf Einzelzeichenbasis sehr schwer zu implementieren.

TBH, ich denke nicht, dass es die Mühe wert ist, Ligaturen im aktuellen Zustand zu unterstützen 😔

@mofux Ich glaube tatsächlich, ich habe einen (etwas schmackhaften) Weg gefunden, die Ligaturen in xterm.js zu rendern, indem ich sie aus einem anderen Blickwinkel betrachte. Ich habe irgendwie übersehen, dass Canvas bei meinen ersten Untersuchungen automatisch die Ligaturen für Sie rendert. Bei der Unterstützung von Ligaturen muss sichergestellt werden, dass die relevanten Zeichen zusammen gerendert werden.

Zu diesem Zweck habe ich den Textrenderer so angepasst, dass Zeichen mit identischen Attributen (fg, bg usw.) als einzelne Gruppe gerendert werden. Dies wird nicht alle Ligaturen korrekt rendern (und möglicherweise einige, die nicht sein sollten), aber es sollte eine Ligatur überall dort rendern, wo jemand eine erwarten würde. Hier ist ein Screenshot von NeoVIM in der Demo-App mit Fira Code (in Firefox gezeigt, funktioniert auch in Chrome, aber nicht in Edge):

image

Branch ist hier, wenn Leute einen Blick darauf werfen möchten: https://github.com/princjef/xterm.js/tree/ligature-support

Ein paar Anmerkungen dazu:

  • Ich habe noch kein Benchmarking durchgeführt, aber ich wette, dass dies nicht gut für die Leistung ist. Durch die Gruppierung aller gleich gestalteten Charaktere wird der Charakteratlas im Grunde genommen aus dem Fenster geworfen, selbst an Orten, an denen es keine Ligaturen gibt (da wir nicht wissen, wo sie sein werden / nicht sein werden). Wenn ich mehrere Zeichen zusammen rendere, setze ich den Zeichencode einfach auf Infinity um sicherzustellen, dass kein Caching stattfindet.
  • Es gibt wahrscheinlich einige Randfälle um die Zeichenbreite und -überlappung, mit denen ich nicht richtig umgehe. Dies ist zu diesem Zeitpunkt meistens ein Proof-of-Concept.

Was ist mit dem Rendern des Texts mithilfe des Zeichenatlas und dem erneuten Rendern im Hintergrund als Textblock im Leerlauf? Wenn beide zu demselben Bild führen, können Sie den kombinierten Text wegwerfen und zum Atlas zurückkehren. Durch Aufteilen von Textzeichenfolgen im Hintergrund kann möglicherweise ermittelt werden, welche Textzeichenfolgen ligiert sind.

Das Knifflige dabei ist, dass das Rendern einer Ligatur nicht nur von den durch die Ligatur ersetzten Zeichen, sondern auch von deren Kontext abhängt. Zum Beispiel sollte '=== 1' die drei Gleichheitszeichen als Ligatur wiedergeben, aber '====' sollte die gleichen drei Gleichheitszeichen als separate Zeichen wiedergeben. Es gibt keine Begrenzung, wie groß dieser Kontext sein kann, daher wäre es schwierig und wahrscheinlich fehleranfällig, die Regeln zu bestimmen, wann eine Ligatur nur aus ihrer Ausgabe gerendert wird.

Ein zuverlässigerer (aber weniger portabler) Ansatz besteht darin, die Hinweise zu Ligaturbereichen durch eine separate Funktion zu erhalten, die die Schriftmetadaten kennt. Dann kann alles außer den von der externen Funktion bereitgestellten Bereichen unter Verwendung des Atlas gerendert werden, während die angegebenen Gruppen einen Ansatz wie den oben beschriebenen verwenden könnten. Das Bestimmen der Positionen von Ersetzungen in einer Textzeile sollte ziemlich schnell sein, weist jedoch einige der zuvor beschriebenen Probleme auf (hauptsächlich Geschwindigkeit / Zuverlässigkeit beim Finden der richtigen Schriftart bei der Initialisierung und Komplexität der Verarbeitung von OpenType-Kontextalternativen).

Wäre es sinnvoll, eine Einstellung zu haben, um Ligaturen zu aktivieren, die schreibt
eine ganze Linie auf einmal zum Terminal? Es scheint so
Gewährleisten Sie ein korrektes Rendering, und der Leistungseinbruch wäre ein Opt-In für
Leute, die sich mehr für Ligaturen als für Geschwindigkeit interessieren.

Am Sonntag, 22. April 2018, 16:21 Uhr schrieb Jeff Principe [email protected] :

Der schwierige Teil dabei ist, dass das Rendern einer Ligatur abhängig ist
nicht nur auf die durch die Ligatur ersetzten Zeichen, sondern auch auf deren Kontext.
Zum Beispiel sollte '=== 1' die drei Gleichheitszeichen als Ligatur aber wiedergeben
'====' sollte dieselben drei Gleichheitszeichen als separate Zeichen darstellen.
Es gibt keine Begrenzung, wie groß dieser Kontext sein kann, also wäre es schwierig und
wahrscheinlich fehleranfällig, um die Regeln zu bestimmen, wann eine Ligatur gerendert wird
nur von seiner Ausgabe.

Ein zuverlässigerer (aber weniger portabler) Ansatz besteht darin, Hinweise zu haben
Ligaturbereiche, die durch eine separate Funktion bereitgestellt werden, die die Schriftart kennt
Metadaten. Dann alles außer den von der externen Funktion bereitgestellten Bereichen
kann mit dem Atlas gerendert werden, während die angegebenen Gruppen eine verwenden könnten
Ansatz wie oben. Bestimmen der Positionen von Substitutionen
gegeben eine Textzeile sollte ziemlich schnell sein, hat aber einige der Probleme, die ich
früher detailliert (hauptsächlich Geschwindigkeit / Zuverlässigkeit, um die richtige Schriftart zu finden
Initialisierung und Komplexität der Verarbeitung von OpenType-Kontextalternativen).

- -
Sie erhalten dies, weil Sie diesen Thread abonniert haben.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/xtermjs/xterm.js/issues/958#issuecomment-383420281 ,
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AAKyryMlayIQijY32GpWBmpvCUi13Wfbks5trRBigaJpZM4PRej6
.

@princjef Wow, deine Änderungen sind so einfach zu verfolgen und es scheint, dass xterm Ligaturen mit dem Renderer unterstützen kann. Der Trick besteht darin, herauszufinden, wie die Regeln zugeordnet werden. Das gibt mir Hoffnung.

@princjef tolle Untersuchung!

Es gibt keine Möglichkeit, die Schriftdaten mithilfe von Browser-APIs abzurufen. Dies funktioniert also nicht als direkte Funktion von xterm.js, sondern eher als separates Paket / Erweiterung mit einem von xterm.js bereitgestellten Hook

@princjef Ich sehe eventuelle

  • some-magical-package : Ein natives Knotenmodul ruft native Schriftinformationen ab und ordnet den Dateien nach Möglichkeit Web-Schriftzeichenfolgen zu. Dies könnte in einem AsyncWorker , um den Leistungseinbruch zu reduzieren. Es ist keine große Sache, wenn dies faul gemacht wird und beim ersten Start dauert es einige Sekunden, um zu scannen und eine Aktualisierung durchzuführen.
  • xtermjs/xterm-ligature-support : Ein Addon, das vom Knoten abhängt und some-magical-package , um Schriftinformationen abzurufen und zwischenzuspeichern. Dieses Addon kann einen Scan durchführen, wenn nicht erkannte Schriftarten im Schriftartenverzeichnis erkannt werden, und Schriftarten entfernen, wenn sie entfernt werden. Ich erwarte so etwas wie die grobe API:

    /** Returns a list of characters to be drawn together */
    export function getLigatures(fontFamily: string): string[] { ... }
    

Es gibt bestimmte Ligaturen, die in einem Terminal keinen Sinn ergeben, auch wenn die Schriftart dies unterstützt (z. B. li).

@mofux Ich bin nicht sicher, ob wir uns darüber Sorgen machen müssen? Wenn der Benutzer Ligaturen anfordert, sollten wir sie nicht alle rendern?

Das Hinzufügen von Unterstützung für das Zeichnen und Löschen eines Zeichens, das sich über mehrere Zellen erstreckt, ist mit unserem aktuellen Rendering-Ansatz (CharAtlas) auf Einzelzeichenbasis sehr schwer zu implementieren.

@mofux Wir sollten in der Lage sein, ziemlich einfach eine Funktion hinzuzufügen, um eine Reihe benachbarter Zeichen zusammen zu zeichnen.

@princjef : Ich habe noch kein Benchmarking durchgeführt, aber ich wette, dass dies nicht gut für die Leistung ist.

@wavebeem : Wäre es sinnvoll, eine Einstellung zum Aktivieren von Ligaturen zu haben, die jeweils eine ganze Zeile in das Terminal schreibt?

Ich erwarte, dass die mit der Verwendung der Leinwand verbundenen Leistungsverbesserungen fast vollständig zunichte gemacht werden. Außerdem möchte ich die Anzahl der Optionen auf ein Minimum beschränken. Wir sollten versuchen, an einen Ort zu gelangen, an dem Ligaturen nur funktionieren, wenn ein Addon enthalten ist, um Unterstützung hinzuzufügen.

Auch in Bezug auf die Leistung werde ich wahrscheinlich in den kommenden Monaten daran arbeiten, eine Fallback-DOM-Rendering-Option hinzuzufügen, da bei der GPU-Unterstützung in Chromium einfach zu viele Dinge schief gehen können. Siehe https://github.com/xtermjs/xterm.js/issues/1360. Ligaturen funktionieren in diesem Modus sofort.

Was ist mit dem Rendern des Texts mithilfe des Zeichenatlas und dem erneuten Rendern im Hintergrund als Textblock im Leerlauf? Wenn beide zu demselben Bild führen, können Sie den kombinierten Text wegwerfen und zum Atlas zurückkehren.

@ j-f1 das ist schwieriger als es scheint. Selbst wenn die Zeichen gleich gerendert werden, ist das zweite Zeichen unterschiedlich, da der Abstand im Zeichenatlas unterschiedlich ist (Zeichen werden immer auf einer runden Zahl gezeichnet). Wir müssten viel mehr rendern, damit dies funktioniert, und viele unterschiedliche Pixel, was teuer ist.

@ Tyriar Ich denke, das allgemeine Design, das Sie beschrieben haben, macht Sinn. Wir sind möglicherweise in der Lage, mit etwas davonzukommen, das nicht vom nativen Code abhängt, um die Schriftarten in some-magical-package , aber es wird definitiv von der Plattform / dem Dateisystem abhängen. Ich habe angefangen, mit dem Parsen der kontextbezogenen alternativen Substitutionen herumzuspielen, aber es ist schwer zu sagen, wie viel mehr es braucht, um es richtig zu machen.

Ich denke auch, dass wir am Ende eine etwas andere Oberfläche als some-magical-package brauchen werden:

export function getSubstitutionRanges(fontFamily: string, text: string): Promise<[number, number][]>;

Die Regeln für die Bestimmung der Ligaturen selbst sind kompliziert und ziemlich tief in die Schrift selbst eingebettet. Anstatt die Schriftdaten selbst zu übergeben und die Last der Interpretation auf xterm.js zu legen, würde ich dies der anderen Bibliothek überlassen und xterm.js mitteilen lassen, welche Zeichen zusammen gerendert werden sollen. Die Lookahead / Lookbehind-Aspekte des Kontexts erschweren auch das Parsen. Zum Beispiel wird '===' einer Ligatur zugeordnet, aber nicht, wenn darauf ein anderes Gleichheitszeichen folgt.

Ein weiterer Hinweis zum Konzept der Substitutionsbereiche: Es gibt keine klare Abgrenzung der Grenzen einer einzelnen Ligatur nach dem, was ich sagen kann (zumindest bei Verwendung von kontextbezogenen Alternativen). Es gibt nur Sequenzen von Substitutionen, die auf einzelne Zeichen angewendet werden. Ich habe einige Tricks gefunden, um die Grenzen herauszufinden, wenn Sie aufeinanderfolgende Ligaturen haben, aber es ist wahrscheinlich nicht narrensicher. Ich würde mich wahrscheinlich irren, wenn ich zwei Ligaturen versehentlich als eine behandeln würde, anstatt sie versehentlich aufzuteilen, da sie immer noch korrekt gerendert werden sollten, wenn sie alle zusammen als eine Gruppe gerendert werden. Das einzige wirkliche Problem besteht darin, heterogene Stile darauf anzuwenden.

Das einzige wirkliche Problem besteht darin, heterogene Stile darauf anzuwenden.

Tu es einfach nicht. Übergeben Sie jede fortlaufende Zeichenfolge separat an die Funktion. Möglicherweise können Sie eine Ausnahme für unterstrichenen Text machen, wenn die Unterstreichung separat gezeichnet wird.

Möglicherweise können wir mit etwas davonkommen, das nicht vom nativen Code abhängt

👍

Die Regeln für die Bestimmung der Ligaturen selbst sind kompliziert und ziemlich tief in die Schrift selbst eingebettet. Anstatt die Schriftdaten selbst zu übergeben und die Last der Interpretation auf xterm.js zu legen, würde ich dies der anderen Bibliothek überlassen und xterm.js mitteilen lassen, welche Zeichen zusammen gerendert werden sollen.

@princjef das klingt noch besser, je weniger xterm.js in diesem Bereich zu tun hat, desto besser.

Das einzige wirkliche Problem besteht darin, heterogene Stile darauf anzuwenden.

Ich glaube nicht, dass irgendetwas anderes versucht, dies zu tun. Wir sollten nur Ligaturen für Text hinzufügen, der denselben Stil verwendet.

Dies scheint auf der Schriftseite machbar zu sein. Ich habe einen Code, der alle Fira-Code-Ligaturen erfolgreich analysiert und die richtigen Bereiche für die zu kombinierenden Zeichen bereitstellt. Wenn Leute eine oder zwei andere Schriftarten haben, für die sie Unterstützung suchen, kann ich versuchen, diese ebenfalls zu überprüfen. Bisher habe ich nur die Substitutionstypen implementiert, die ich für Fira Code benötigte, daher wäre es etwas abwechslungsreich, die anderen Substitutionstypen auszuüben.

Der Teil zur Suche nach Schriftarten muss noch herausgefunden werden. Ich werde mich als nächstes darauf konzentrieren. Es gibt einige Pakete da draußen, aber alle scheinen entweder fehlerhaft oder schlecht gewartet zu sein

@princjef Wenn Sie andere Schriftarten überprüfen möchten, verwende ich Iosevka .

Okay, ich habe ein Paket namens font-ligatures (auch bekannt als some-magical-package ) und einige zugehörige Pakete erstellt, damit wir effizient die richtige Schriftart finden und dann herausfinden können, wo sich die Ligaturen für eine bestimmte Texteingabe befinden.

Ich habe einige Zeit damit verbracht, den Prozess zum Auffinden der Schriftart zu optimieren. Auf einem Surface Pro 4 mit ~ 150 ttf / otf-Schriftarten kann ich die Schriftarten-Metadaten für alle in 300-400 ms abrufen. Es ist größtenteils E / A-gebunden und kann während des Ladens für die ersten Renderzyklen in den Hintergrund getreten werden. Es sollte jedoch schnell genug sein, um geladen zu werden, wenn eine Pty gestartet wurde und Text ausspuckt. Sobald es geladen ist, können wir ein Rendering auslösen, um den möglicherweise bereits vorhandenen Text zu aktualisieren. Dies kann wiederholt werden, wenn sich die Schriftart ändert, oder wir können die vollständige Liste beim ersten Mal zwischenspeichern (ich rufe die vollständige Liste trotzdem am Anfang ab).

Bei der Ligaturzuordnung selbst nimmt die Bibliothek eine Zeichenfolge auf und gibt Metadaten zu den Schriftligaturen zurück, einschließlich der Zeichengruppen, die zusammen gerendert werden sollen. Das CI enthält Tests für jede Ligatur in Fira Code, Iosevka und Monoid, daher bin ich ziemlich sicher, dass es die Verarbeitung für die von ihm durchgeführten Substitutionstypen korrekt durchführt (obwohl ich sicher bin, dass es einige Schriftarten gibt, die andere Typen verwenden, die Ich habe nicht implementiert).

Ich habe jedoch keine Zeit damit verbracht, die Ligaturanalyse zu optimieren / abzustimmen. Ich habe einige schnelle Tests durchgeführt und es sieht so aus, als würde das Parsen von Ligaturen für eine Zeichenfolge mittlerer Länge 2-20 ms dauern (lesen Sie: 1 Zeile). Es gibt noch viel Raum für Optimierungen, sodass ich mir im Moment keine Sorgen mache. Ich wollte dies hauptsächlich herausbringen, um die Benutzeroberfläche zu demonstrieren und die Leute auf die Reifen treten zu lassen, wenn sie wollen.

Sieht ziemlich cool aus @princjef! Was halten Sie vom Hinzufügen von Tests zu Fira Code für 0xc0ffee , 0x1234 und 17x32 ? (Das x wird zu einem Zeitzeichen auf diesen)

@princjef super !

Ich habe einige schnelle Tests durchgeführt und es sieht so aus, als würde das Parsen von Ligaturen für eine Zeichenfolge mittlerer Länge 2 bis 20 ms dauern (lesen Sie: 1 Zeile).

Können Sie mich auf einen der kritischen Codes verweisen, die dies überprüfen?

@ j-f1 Ja, diese funktionieren auch: https://github.com/princjef/font-ligatures/blob/master/src/index.spec.ts#L136 -L137

@ Tyriar Ich habe gerade einen grundlegenden Benchmark hinzugefügt, mit dem Sie herumspielen können. Das Anrufmuster sieht aus wie node bench/lines.js <font-name> <input-text> [iterations] . Sie können auch eine mehrzeilige Zeichenfolge übergeben oder eine Eingabedatei für den Text erweitern. Dabei werden die Zeilen durchlaufen, um zu verhindern, dass unrealistische Optimierungen genau dieselbe Eingabe wiederholt ausführen.

Während ich das aufschrieb, bemerkte ich, dass selbst wenn ich mehrere verschiedene Eingabezeilen verwende, es nach dem ersten Durchlauf immer noch einen signifikanten Leistungsschub erfährt. Für ~ 10 Zeicheneingaben sehe ich Durchschnittswerte von einem Bruchteil einer Millisekunde für Iosevka und etwas mehr als eine halbe Millisekunde für Fira Code. Entweder treffe ich einen Sonderfall oder Turbofan arbeitet, es ist magisch. Es ist immer noch nicht effizient genug, um es so wie es ist zu verwenden, aber ich werde mich als nächstes auf Perf-Optimierungen konzentrieren (an diesem Punkt werde ich wahrscheinlich auch einen besseren Benchmark hinzufügen, um ein Gefühl für die Verbesserungen zu bekommen).

Ein paar Anmerkungen:

  • Aufgrund der Funktionsweise der Ersetzungen würde ich erwarten, dass die Ausführungszeit linear mit der Größe der Eingabe skaliert, obwohl ich dies nicht überprüft habe
  • Aufgrund der Anzahl der Ligaturen und der Funktionsweise der Ersetzungen ist Fira Code viel perfekter zu analysieren als Iosevka oder Monoid. Ich sehe regelmäßig, dass es 5-10x so lange dauert, Fira Code zu machen wie Iosevka.
  • Schriftarten ohne Ligaturen laufen viel schneller und können weiter optimiert werden. Wenn die Schriftart keine kontextbezogenen Alternativen enthält, kann ich mit einer leeren Ergebnismenge schnell beenden (oder wir können den Aufruf einfach auf einer höheren Ebene umgehen).

Ich habe begonnen, den Leistungsaspekt in einem Problem in dem von mir erstellten Paket (https://github.com/princjef/font-ligatures/issues/2) zu verfolgen. Hier sind die Anfangsnummern, die von dort für einige Konfigurationen kopiert wurden (alle Nummern sind Zeiten in ms). Die ersten fünf Zahlenspalten befinden sich pro Eingabezeile und die letzten beiden pro Zeichen. Die vorletzte Spalte ist die, der ich am meisten Aufmerksamkeit schenke. Ich fotografiere für ca. 0,5 Mikrosekunden pro Zeichen (0,0005 in Millisekunden):

NAME | AVG | STDEV | 5% | 50% | 95% | AVG (CHAR) | STDEV (CHAR)
----------------------- | ------- | --------- | ------ | -------- | ------- | ------------- | ----------------
Fira Code: code.txt | 5,9570 | 12.6951 | 0,5270 | 5.1020 | 12.2330 | 0,1443531 | 0,2091743
Fira Code: noLigatures.txt | 12.1932 | 3.4402 | 8.1420 | 12.1205 | 15.5900 | 0,1321094 | 0,0334362
Iosevka: code.txt | 0,5571 | 1,4722 | 0,0485 | 0,3215 | 1.6155 | 0,0135005 | 0,0333483
Iosevka: noLigatures.txt | 0,7476 | 0,4230 | 0,4365 | 0,6030 | 1,7725 | 0,0080998 | 0,0044501
Monoid: code.txt | 0,8896 | 1,6637 | 0,0910 | 0,6625 | 1,9225 | 0,0215566 | 0,0482166
Monoid: noLigatures.txt | 1,6661 | 0,6935 | 1,0695 | 1,4450 | 2.6910 | 0,0180521 | 0,0071922
Ubuntu Mono: code.txt | 0,0402 | 0,3935 | 0,0080 | 0,0220 | 0,0605 | 0,0009735 | 0,0090228
Ubuntu Mono: noLigatures.txt | 0,0356 | 0,0644 | 0,0120 | 0,0280 | 0,0805 | 0,0003858 | 0,0006891

Jetzt, da es gute Basisdaten gibt, haben wir eine bessere Vorstellung von den Gewinnen, die mit jeder Leistungsoptimierung erzielt werden.

@ Tyriar Haben Sie eine Präferenz dafür, wo das xterm-ligature-support -Paket lebt und wie es dort ankommt?

Es sieht so aus, als ob Sie denken, es sollte unter die xtermjs-Organisation fallen (was für mich vernünftig klingt), aber ich habe nicht die Möglichkeit, ein Repository in der Organisation zu erstellen / zu pushen. Möchten Sie es lieber in ein Repo unter meinem Benutzer einfügen, das in die Organisation migriert wird, oder möchten Sie ein Stub-Repo erstellen, gegen das ich eine PR einreichen kann?

Aus Sicht der Überprüfung ist es sinnvoll, zuerst # 1460 zu finalisieren, also keine Eile, aber ich möchte herausfinden, wo der Code abgelegt werden soll, sobald er fertig ist.

@princjef Ich habe https://github.com/xtermjs/xterm-ligature-support erstellt und Ihnen Administratorzugriff gewährt . Ich gehe davon aus, dass Sie gesehen haben, wie die anderen Addons funktionieren, aber dies wird das erste offizielle externe Addon sein. Verwenden Sie daher die internen Addons und https://github.com/CoderPad/xterm-webfont als Referenz für die Implementierung 😄

Ich werde bald die PR überprüfen 👍

Habe ein Update im vscode-Repo unter https://github.com/microsoft/vscode/issues/34103 veröffentlicht. Hier ist die verbleibende Arbeit, bevor wir dies in xterm.js als geschlossen bezeichnen können:

  • Integrieren Sie xtermjs / xterm-addon-ligaturen in das Repo xtermjs / xterm.js . Dies ermöglicht die automatische Veröffentlichung und stellt sicher, dass die Tests weiterhin bestanden werden
  • Schließen Sie Ligaturen an die Demo an
  • Richten Sie einige Puppenspielertests ein, die auf allen Renderern (dom, canvas, webgl) überprüfen, ob Ligaturen funktionieren
  • Reduzieren Sie die Abhängigkeiten so weit wie möglich

Vielen Dank für die harte Arbeit hier an alle! 😄 👍

Wenn ich fragen darf, @Tyriar , gibt es Pläne für die Unterstützung von Webbrowsern?

Ich habe auch gesehen, dass dies im Browser funktionieren könnte, aber es wäre super langsam. Wenn ich fragen darf, wie langsam war diese Version, die im Browser funktioniert hat?

Vielen Dank!

@ torch2424 Ich glaube nicht, dass es möglich ist, wie die Renderer funktionieren, da die

@ Tyriar Danke für die schnelle Antwort!

Das ist bedauerlich, aber es war eher schön zu haben. Wir haben über eine Elektronenversion nachgedacht, also haben wir diese vielleicht als enthaltenes Feature 😄 Danke!

Hmm, ich denke, es sollte möglich sein, entweder einen Webfont zu verwenden und dieselben Schriftdateien (urgh) in JS-Bereiche zu laden, um die Ligaturdaten zu extrahieren. Ich bin mir nicht sicher, ob dies machbar ist / es sich lohnt, dies zu tun, da die Ligaturextraktion eine ziemlich große Beeinträchtigung der Perfektion darstellt. Sie sind sich nicht sicher über die Verwendung von Mem, eine Schriftartdatei kann ziemlich groß werden. Vielleicht hat @princjef hier einige Zahlen zum Laufzeitverhalten?

Es ist schon eine Weile her, seit ich mich damit befasst habe, aber ich erinnere mich, dass es keine eingebauten APIs gab, um rohe Schriftinformationen im Browser zu extrahieren, selbst für Web-Schriftarten.

Wenn Sie Ihre eigene Webschrift manuell aus einer Datei laden und den Inhalt derselben Datei separat in den Code zum Parsen von Ligaturen einfügen, ist dies theoretisch möglich, ohne die Optimierungen für das Glyphen-Caching im Renderer aufgeben zu müssen. Das vorhandene Addon wurde jedoch nicht für diesen Anwendungsfall entwickelt, da es mehr Koordination und manuelle Verkabelung erfordert als der Fall ohne Browser.

Ich bin mir nicht sicher, ob dies machbar ist / es sich lohnt, dies zu tun, da die Ligaturextraktion eine ziemlich große Beeinträchtigung der Perfektion darstellt. Eine Schriftartdatei ist nicht sicher, ob sie verwendet wird. Sie kann sehr groß werden

Dies kann für bestimmte Schriftarten definitiv problematisch sein. Das Ligaturverarbeitungspaket ist stark für die Effizienz der Suche / Erkennung optimiert, was vorerst zu Lasten längerer Ladezeiten und Speicherverbrauch geht. Es wird von Schriftart zu Schriftart erheblich variieren, ist jedoch eine gute Überlegung, die Sie berücksichtigen sollten.

Selbst wenn das Extrahieren von Ligaturinformationen möglich war, verwendet die Web-API Text- / Zeichencodepunkte, während Ligaturen in der Schriftart schriftartenspezifische Glyphenindizes verwenden, die zum Zeichnen nicht direkt an Web-APIs übergeben werden können.

@princjef Danke für die Heads-Ups, das klingt viel zu umständlich, imho nicht wert für den Nutzen, den es danach gibt.

@khaledhosny Die Idee war mehr, die abzurufen, während noch mit Codepunkten gearbeitet wird, und den Browser-Font-Renderer die Low-Level-

Update zu diesem Thema, ich habe gerade das Ligatur-Addon in die Codebasis (https://github.com/xtermjs/xterm.js/pull/2847) eingefügt. Es ist möglicherweise immer noch ein wenig kaputt, aber ohne die Einrichtung von Integrationstests ist es schwer zu sagen ( https://github.com/xtermjs/xterm.js/issues/2896).

Derzeit läuft das Addon nur in einer Browser + Knoten-Laufzeit (dh Elektron). Ich habe einen Vorschlag gemacht, der voraussichtlich nur im Browser funktioniert, mit dem wir die Knoten-Deps löschen und in VS Code (https: / /github.com/microsoft/vscode/issues/34103). Es ist mit Hilfe gekennzeichnet, die gesucht wird, wenn jemand es versuchen möchte https://github.com/xtermjs/xterm.js/issues/2897.

Wir sollten die Verwendung der Font Access-API (derzeit in der Ursprungsstudie) für das Ligatur-Addon prüfen.

Ich benutze ttyd, das Terminal über das Web teilt und xterm.js verwendet. Wie Sie im Bild unten sehen, werden die Symbole nicht richtig geladen. Sie sind im Originalterminal sichtbar. aber im Web fehlen sie.

image

@UziTech oh wow, das sieht toll aus! Ich denke, jede Arbeit, die in diesem Bereich erledigt wird, sollte vorausschauend sein, damit wir sie mit der API für den Schriftzugriff nutzen können.

@UziTech @Tyriar @princjef
Ich denke, der Code für die Schriftzugriffs-API kann in font-ligatures -Paketen abgelegt werden.
Nachdem festgestellt wurde, ob sich der Prozess im Browser oder im Knoten / Elektron befindet, kann er entscheiden, ob die API für den Schriftzugriff aufgerufen oder die aktuelle Methode verwendet werden soll.

Ich habe einen hackigen Proof of Concept erstellt und ihn in der Demo an meiner Gabelung (Zweigstelle localFonts) zum Laufen gebracht.
Screenshot 2021-01-17 at 18 39 27

Sie können es ausprobieren, nachdem Sie #font-access in chrome://flags aktiviert haben (müssen nach dem Zugriff auf Schriftarten einmal aktualisiert werden).

Ich kann versuchen, PRs zu öffnen, wenn das für dich gut aussieht.

Nachdem festgestellt wurde, ob sich der Prozess im Browser oder im Knoten / Elektron befindet, kann er entscheiden, ob die API für den Schriftzugriff aufgerufen oder die aktuelle Methode verwendet werden soll.

Bei dieser Lösung treten Probleme auf, da Projekte, die nur im Web ausgeführt werden, Probleme beim Bündeln der Knoten-Deps haben. Ich habe keine Antwort darauf, aber ich denke auch, wir sollten die Standardeinstellungen umdrehen (eventuell?) Und standardmäßig den Schriftzugriff verwenden, wenn die Feature-Erkennung erfolgreich ist, andernfalls Fallback.

Auch tolle Arbeit, damit es funktioniert! Das ist großartig 😍

Wir können sie nach der Feature-Erkennung nur bei Bedarf require .
Abhängige Web-Only-Projekte können sie beim Bündeln als extern markieren.

Wir können die API für den Schriftzugriff standardmäßig verwenden, sobald sie für alle Browser verfügbar ist.

👍 Ich überdenke möglicherweise die Bedenken des Bundlers, da Sie erwähnen, dass es Problemumgehungen gibt. Ich gehe davon aus, dass wir die Electron-Option ablehnen / auslaufen lassen würden, sobald der Schriftzugriff in Electron ohnehin aktiviert ist.

@ Tyriar können wir bald eine Veröffentlichung des Ligaturen-Addons mit dem Update im Font-Finder bekommen?

@UziTech [email protected] sollte das haben, ich habe eine Erinnerung erstellt, um die Version für 4.10 https://github.com/xtermjs/xterm.js/issues/3220 zu stoßen

Ich habe als ersten Schritt ein pr princjef / font-ligatures # 22 geöffnet, um dies zu erreichen

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

chris-tse picture chris-tse  ·  4Kommentare

LB-J picture LB-J  ·  3Kommentare

fabiospampinato picture fabiospampinato  ·  4Kommentare

tandatle picture tandatle  ·  3Kommentare

jestapinski picture jestapinski  ·  3Kommentare