RenderingNG im Detail: LayoutNG-Blockfragmentierung

Die Blockfragmentierung in LayoutNG ist jetzt abgeschlossen. In diesem Artikel erfahren Sie, wie er funktioniert und warum er wichtig ist.

Morten Stenshorne
Morten Stenshorne

Ich bin Morten Stenshorne, Layout Engineer im Blink-Rendering-Team bei Google. Ich bin seit den frühen 2000er Jahren an der Entwicklung von Browser-Engines beteiligt und habe viel Spaß gemacht. So habe ich zum Beispiel mitgewirkt, dass der acid2-Test in der Presto-Engine (Opera 12 und früher) bestanden wurde, und andere Browser zurückentwickeln, um das Tabellenlayout in Presto zu korrigieren. Ich habe auch mehr dieser Jahre verbracht, als ich zugeben möchte, mit Blockfragmentierung und insbesondere mit Multicol in Presto, WebKit und Blink. In den letzten Jahren habe ich mich bei Google hauptsächlich darauf konzentriert, Unterstützung für die Blockfragmentierung in LayoutNG bereitzustellen. Begleiten Sie mich in diesem Detail der Implementierung der Blockfragmentierung. Es ist wahrscheinlich das letzte Mal, dass ich die Blockfragmentierung implementiere. :)

Was ist Blockfragmentierung?

Bei der Blockfragmentierung wird ein CSS-Feld auf Blockebene (z. B. ein Abschnitt oder Absatz) in mehrere Fragmente aufgeteilt, wenn das Ganze nicht in einen Fragmentcontainer, den sogenannten Fragmentainer, passt. Ein Fragment ist kein Element, sondern stellt eine Spalte in einem mehrspaltigen Layout oder eine Seite in seitenmedienierten Medien dar. Damit eine Fragmentierung erfolgen kann, muss sich der Inhalt in einem Fragmentierungskontext befinden. Ein Fragmentierungskontext entsteht meistens durch einen mehrspaltigen Container (Inhalt wird in Spalten aufgeteilt) oder beim Drucken (Inhalte werden in Seiten aufgeteilt). Ein langer Absatz mit vielen Zeilen muss möglicherweise in mehrere Fragmente aufgeteilt werden, sodass die ersten Zeilen im ersten Fragment und die verbleibenden Zeilen in nachfolgenden Fragmenten platziert werden.

Ein Absatz mit Text, der in zwei Spalten aufgeteilt ist.
In diesem Beispiel wurde ein Absatz mithilfe eines mehrspaltigen Layouts in zwei Spalten aufgeteilt. Jede Spalte ist ein Fragment, das ein Fragment des fragmentierten Datenflusses darstellt.

Die Blockfragmentierung ist analog zu einer anderen bekannten Art der Fragmentierung: der Linienfragmentierung, die auch als "Zeilenumbruch" bezeichnet wird. Jedes Inline-Element, das aus mehr als einem Wort besteht (beliebige Textknoten, beliebige <a>-Elemente usw.), das Zeilenumbrüche zulässt, kann in mehrere Fragmente aufgeteilt werden. Jedes Fragment wird in einem anderen Zeilenfeld platziert. Ein Zeilenfeld ist die Inline-Fragmentierung, die einem Fragmentainer für Spalten und Seiten entspricht.

Was ist LayoutNG-Blockfragmentierung?

LayoutNGBlockFragmentation ist eine Neuformulierung der Fragmentierungs-Engine für LayoutNG. Nach vielen Jahren Arbeit wurden die ersten Teile Anfang dieses Jahres schließlich in Chrome 102 ausgeliefert. Dadurch wurden seit Langem bestehende Probleme behoben, die in unserer „alten“ Engine im Wesentlichen nicht behoben werden konnten. In Bezug auf Datenstrukturen werden mehrere Pre-NG-Datenstrukturen durch NG-Fragmente ersetzt, die direkt im Fragmentbaum dargestellt werden.

Beispielsweise unterstützen wir jetzt den Wert'avoid' für die CSS-Eigenschaften 'break-before' und 'break-after', mit denen Autoren Unterbrechungen direkt nach einer Überschrift vermeiden können. Im Allgemeinen sieht es nicht gut aus, wenn das letzte Element auf einer Seite eine Kopfzeile ist, während der Inhalt des Bereichs auf der nächsten Seite beginnt. Es ist besser, einen Umbruch vor dem Header zu platzieren. Die folgende Abbildung zeigt ein Beispiel.

Das erste Beispiel zeigt eine Überschrift am unteren Rand der Seite, das zweite zeigt sie mit dem zugehörigen Inhalt ganz oben auf der nächsten Seite an.

In Chrome 102 wird auch Fragmentierungsüberlauf unterstützt, sodass monolithische (angegebene, unverzerrbare) Inhalte nicht auf mehrere Spalten aufgeteilt werden und Farbeffekte wie Schatten und Transformationen korrekt angewendet werden.

Blockfragmentierung in LayoutNG ist jetzt abgeschlossen.

Zum Zeitpunkt der Erstellung dieses Dokuments hatten wir die vollständige Unterstützung für die Blockfragmentierung in LayoutNG abgeschlossen. Kernfragmentierung (Blockcontainer, einschließlich Linienlayout, Floats und Out-of-Flow-Positionierung) in Chrome 102. Flex- und Rasterfragmentierung in Chrome 103 und Tabellenfragmentierung in Chrome 106. Und schließlich wird das Drucken in Chrome 108 eingeführt. Die Blockfragmentierung war die letzte Funktion, für die die Legacy-Engine zur Layout-Erstellung erforderlich war. Das bedeutet, dass die alte Engine ab Chrome 108 nicht mehr für das Layout verwendet wird.

LayoutNG-Datenstrukturen unterstützen nicht nur das Layout des Inhalts, sondern auch das Painting und die Treffertests. Wir stützen uns jedoch weiterhin auf einige alte Datenstrukturen für JavaScript APIs, die Layoutinformationen lesen, z. B. offsetLeft und offsetTop.

Wenn Sie alles mit NG ausstatten, können Sie neue Funktionen implementieren und ausliefern, die nur LayoutNG-Implementierungen haben (und keine Entsprechung in Legacy-Engine). Dazu gehören CSS-Containerabfragen, Ankerpositionierung, MathML und benutzerdefiniertes Layout (Houdini). Für Containeranfragen haben wir es etwas im Voraus mit einer Warnung an Entwickler versendet, dass das Drucken noch nicht unterstützt wird.

Den ersten Teil von LayoutNG haben wir 2019 veröffentlicht. Er bestand aus einem normalen Block-Container-Layout, Inline-Layout, Floats und Out-of-Flow-Positionierung, aber ohne Unterstützung für Flex, Raster oder Tabellen und keine Unterstützung für Blockfragmentierung. Wir würden auf die Verwendung der alten Layout-Engine für Flex, Raster, Tabellen sowie für alles, was mit einer Blockfragmentierung zu tun hat, zurückgreifen. Das gilt selbst für Block-, Inline-, Floating- und Out-of-Flow-Elemente innerhalb von fragmentierten Inhalten. Wie Sie sehen, ist das Upgrade einer derart komplexen Layout-Engine an Ort und Stelle sehr komplex.

Darüber hinaus wurde Mitte 2019 die meisten Kernfunktionen des LayoutNG-Blockfragmentierungs-Layouts bereits implementiert (hinter einer Markierung). Warum hat es so lange gedauert, bis es versendet wurde? Die kurze Antwort lautet: Fragmentierung muss korrekt mit verschiedenen Legacy-Komponenten des Systems koexistieren, die erst entfernt oder aktualisiert werden können, wenn alle Abhängigkeiten aktualisiert wurden. Unten finden Sie eine ausführliche Antwort.

Legacy-Engine-Interaktion

Legacy-Datenstrukturen sind immer noch für JavaScript APIs verantwortlich, die Layoutinformationen lesen. Daher müssen wir die Daten so in die Legacy-Engine zurückschreiben, dass sie für sie verständlich sind. Dazu gehört auch die korrekte Aktualisierung der mehrspaltigen Legacy-Datenstrukturen wie LayoutMultiColumnFlowThread.

Fallback-Erkennung und -Verarbeitung von Legacy-Engines

Wir mussten auf die alte Layout-Engine zurückgreifen, wenn sich darin Inhalte befanden, die noch nicht von der LayoutNG-Blockfragmentierung verarbeitet werden konnten. Beim Versand der zentralen LayoutNG-Blockfragmentierung (Frühjahr 2022) umfasste dies Flex, Raster, Tabellen und alles, was gedruckt wurde. Dies war besonders schwierig, da wir erst feststellen mussten, ob ein Legacy-Fallback erforderlich war, bevor wir Objekte im Layoutbaum erstellt haben. Wir mussten beispielsweise herausfinden, ob es einen mehrspaltigen Container-Ancestor gibt, und bevor wir wussten, welche DOM-Knoten zu einem Formatierungskontext werden oder nicht. Es ist ein Huhn-Ei-Problem, für das keine perfekte Lösung gibt, aber solange es sich nur um falsch positive Ergebnisse handelt (eine Alternative, wenn dies keine Notwendigkeit ist), ist es in Ordnung, denn die Fehler in diesem Layout-Verhalten sind in Chromium bereits vorhanden und nicht auf neue.

Rundgang durch Baumbemalung

Das Voranstrichen erfolgt nach dem Layout, aber vor dem Streichen. Die größte Herausforderung besteht darin, dass wir den Layoutobjektbaum immer noch durchlaufen müssen, aber jetzt haben wir NG-Fragmente. Wie gehen wir damit um? Wir durchlaufen das Layout-Objekt und die NG-Fragmentbaum gleichzeitig! Das ist ziemlich kompliziert, da die Kartierung zwischen den beiden Bäumen nicht einfach ist. Während die Baumstruktur des Layoutobjekts der des DOM-Baums sehr ähnelt, ist der Fragmentbaum eine Ausgabe des Layouts, keine Eingabe dafür. Die Fragmentstruktur spiegelt nicht nur die Auswirkungen jeder Fragmentierung, einschließlich Inline-Fragmentierung (Zeilenfragmente) und Blockfragmentierung (Spalten- oder Seitenfragmente), sondern auch eine direkte Über-/Untergeordnet-Beziehung zwischen einem enthaltenden Block und den Nachfolgerelementen des DOMs wider, bei denen dieses Fragment als übergeordneten Block dient. Im Fragmentbaum ist ein Fragment, das von einem absolut positionierten Element generiert wurde, dem zugehörigen Blockfragment direkt untergeordnet, selbst wenn sich andere Knoten in der Herkunftskette zwischen dem positionierten Nachfolger aus dem Out-of-Flow und dem enthaltenden Block befinden.

Noch komplizierter wird es, wenn sich ein Out-of-Flow-Element innerhalb der Fragmentierung befindet, da die Out-of-Flow-Fragmente direkte untergeordnete Elemente des Fragmentainers werden und nicht ein untergeordnetes Element von dem Element werden, das CSS als enthaltenden Block betrachtet. Leider war dies ein Problem, das gelöst werden musste, um problemlos mit der alten Engine zusammenarbeiten zu können. In Zukunft sollten wir einen Großteil dieses Codes vereinfachen können, da LayoutNG so konzipiert ist, dass alle modernen Layoutmodi flexibel unterstützt werden.

Die Probleme mit der alten Fragmentierungs-Engine

Die Legacy-Engine, die in einer früheren Ära des Webs entwickelt wurde, hat nicht wirklich ein Konzept der Fragmentierung, auch wenn es damals auch technisch versiert war, um Druck auszuüben. Die Unterstützung von Fragmentierung wurde einfach hinzugefügt (drucken) oder nachgerüstet (mehrspaltig).

Beim Erstellen fragmentierter Inhalte legt die Legacy-Engine alles auf einen hohen Streifen an, dessen Breite der Inline-Größe einer Spalte oder Seite entspricht und die Höhe der für den Inhalt der Seite erforderlichen Höhe ist. Dieser lange Streifen wird nicht auf der Seite gerendert. Betrachten Sie ihn als Rendering auf einer virtuellen Seite, die dann für die endgültige Anzeige neu angeordnet wird. Das Konzept ähnelt dem Drucken eines kompletten Zeitungsartikels in einer Spalte und dem anschließenden Zerteilen des Artikels mit einer Schere im zweiten Schritt. (Früher wurden in Zeitungen ähnliche Techniken eingesetzt.)

Die Legacy-Engine verfolgt eine imaginäre Seiten- oder Spaltengrenze im Streifen. Dadurch können Inhalte, die nicht über die Begrenzung hinausgehen, auf die nächste Seite oder Spalte verschoben werden. Wenn beispielsweise nur die obere Hälfte einer Zeile auf das passt, was die Suchmaschine für die aktuelle Seite hält, wird ein „Paginierungs-Strut“ eingefügt, um sie an die Position zu verschieben, an der die Suchmaschine annimmt, dass der obere Rand der nächsten Seite ist. Der größte Teil der Fragmentierung (das „Schneiden mit der Schere und Platzierung“) erfolgt nach dem Layout während der Vormalerei und dem Gemälde. Dadurch wurden einige Dinge praktisch unmöglich gemacht, z. B. das Anwenden von Transformationen und die relative Positionierung nach der Fragmentierung (was die Spezifikation erfordert). Darüber hinaus wird die Tabellenfragmentierung in der Legacy-Engine zwar in gewissem Umfang unterstützt, jedoch wird keine flexible oder Rasterfragmentierung unterstützt.

Hier sehen Sie eine Abbildung, wie ein dreispaltiges Layout intern in der alten Engine dargestellt wird, bevor eine Schere, ein Placement und Klebstoff verwendet werden. Wir haben eine bestimmte Höhe, sodass nur vier Zeilen passen, aber am unteren Rand noch etwas überflüssiger Platz ist:

Die interne Darstellung als eine Spalte mit Paginierungsstruts, an denen der Inhalt unterbrochen wird, und die Bildschirmdarstellung in drei Spalten.

Da die Legacy-Layout-Engine den Inhalt während des Layouts nicht tatsächlich fragmentiert, treten viele seltsame Artefakte auf, z. B. die relative Positionierung, Transformationen, die nicht korrekt angewendet werden, oder Boxschatten, die an den Spaltenrändern abgeschnitten werden.

Hier ist ein einfaches Beispiel mit Textschatten:

In der alten Engine ist das nicht möglich:

Beschnittene Textschatten werden in der zweiten Spalte platziert.

Sehen Sie, wie der Textschatten der Linie in der ersten Spalte abgeschnitten und stattdessen oben in der zweiten Spalte platziert wird? Das liegt daran, dass die alte Layout-Engine Fragmentierung nicht versteht.

Sie sollte so aussehen (und so sieht es mit NG aus):

Zwei Textspalten, wobei die Schatten korrekt angezeigt werden.

Als Nächstes machen wir es etwas komplizierter, mit Transformationen und „box-shadow“. Beachten Sie, dass es in der Legacy-Engine falsches Beschneiden und zu Spaltenabstände gibt. Das liegt daran, dass Transformationen gemäß Spezifikation als Post-Layout- und Post-Fragmentierungs-Effekt angewendet werden sollen. Mit der LayoutNG-Fragmentierung funktionieren beide ordnungsgemäß. Dies verbessert die Interoperabilität mit Firefox, das bereits seit einiger Zeit gute Fragmentierung unterstützt und die meisten Tests in diesem Bereich ebenfalls erfolgreich abgeschlossen werden.

Felder sind fälschlicherweise in zwei Spalten aufgeteilt.

Die Legacy-Engine hat auch Probleme mit hohen monolithischen Inhalten. Inhalte sind monolithisch, wenn sie nicht in mehrere Fragmente aufgeteilt werden können. Elemente mit Überlauf-Scrolling sind monolithisch, da es für Nutzer nicht sinnvoll ist, in einem nicht rechteckigen Bereich zu scrollen. Zeilenfelder und Bilder sind weitere Beispiele für monolithische Inhalte. Beispiel:

Wenn ein monolithischer Inhalt zu hoch ist, um in eine Spalte zu passen, wird er von der Legacy-Engine brutal unterteilt. Dies führt zu einem sehr "interessanten" Verhalten beim Scrollen durch den scrollbaren Container:

Anstatt die erste Spalte überlaufen zu lassen (wie dies bei der LayoutNG-Blockfragmentierung der Fall ist):

ALT_TEXT_HERE

Die Legacy-Engine unterstützt erzwungene Unterbrechungen. Beispielsweise fügt <div style="break-before:page;"> vor dem DIV-Element einen Seitenumbruch ein. Die Unterstützung für optimale nicht erzwungene Umbrüche ist jedoch nur eingeschränkt möglich. break-inside:avoid sowie Waisen und Witwen werden unterstützt. Es gibt jedoch keine Unterstützung, um Pausen zwischen Blöcken zu vermeiden, beispielsweise auf Anfrage über break-before:avoid. Betrachten Sie dieses Beispiel:

Auf zwei Spalten aufgeteilter Text.

Hier hat das #multicol-Element Platz für fünf Zeilen in jeder Spalte, da es 100 Pixel hoch ist und die Linienhöhe 20 Pixel beträgt. Daher könnte die gesamte #firstchild in die erste Spalte passen. #secondchild in seinem gleichgeordneten Element enthält jedoch „break-before:avoid“, d. h., der Inhalt wünscht, dass zwischen ihnen keine Pause eintritt. Da der Wert von widows 2 ist, müssen 2 Zeilen von #firstchild in die zweite Spalte verschoben werden, um alle Anfragen zur Vermeidung von Unterbrechungen zu berücksichtigen. Chromium ist die erste Browser-Engine, die diese Kombination von Funktionen vollständig unterstützt.

So funktioniert NG-Fragmentierung

Die NG-Layout-Engine erstellt im Allgemeinen das Layout des Dokuments, indem sie zuerst die Tiefe des CSS-Feldbaums durchläuft. Wenn alle Nachfolger eines Knotens angelegt sind, kann das Layout dieses Knotens fertiggestellt werden, indem ein NGPhysicalFragment erstellt und zum übergeordneten Layout-Algorithmus zurückgekehrt wird. Dieser Algorithmus fügt das Fragment zu seiner Liste der untergeordneten Fragmente hinzu und generiert, sobald alle untergeordneten Fragmente abgeschlossen sind, ein Fragment für sich selbst, in dem alle untergeordneten Fragmente enthalten sind. Mit dieser Methode wird ein Fragmentbaum für das gesamte Dokument erstellt. Dies ist jedoch eine zu große Vereinfachung: Zum Beispiel müssen sich außerhalb des Flows positionierte Elemente von ihrer Position im DOM-Baum bis zum zugehörigen Block aufsteigen, bevor sie angeordnet werden können. Der Einfachheit halber ignoriere ich dieses komplexe Detail.

Zusammen mit dem CSS-Feld selbst bietet LayoutNG einen Beschränkungsraum für einen Layout-Algorithmus. Dadurch erhält der Algorithmus Informationen wie den verfügbaren Platz für das Layout, ob ein neuer Formatierungskontext erstellt wird, und wie sich der Zwischenrand aus dem vorherigen Inhalt minimieren lässt. Der Einschränkungsbereich kennt auch die angelegte Blockgröße des Fragments und den aktuellen Blockversatz darin. Damit wird angegeben, wo die Unterbrechung erfolgen soll.

Wenn eine Blockfragmentierung eine Rolle spielt, muss das Layout der Nachfolger an einer Unterbrechung enden. Zu den Gründen für Probleme gehören ein Mangel an Platz auf der Seite oder Spalte oder eine erzwungene Unterbrechung. Anschließend erstellen wir Fragmente für die besuchten Knoten und kehren bis zum Fragment-Kontextstamm, also zum Multicol-Container oder im Fall der Ausgabe zum Dokumentstamm, zurück. Dann bereiten wir uns auf der Wurzel des Fragmentierungskontexts auf einen neuen Fragmentierungspunkt vor und steigen wieder in den Baum ab. Dabei werden wir dort fortfahren, wo wir vor der Pause aufgehört haben.

Die entscheidende Datenstruktur zum Fortsetzen des Layouts nach einer Unterbrechung heißt NGBlockBreakToken. Es enthält alle Informationen, die erforderlich sind, um das Layout im nächsten Fragmentierung korrekt fortzusetzen. Ein NGBlockBreakToken ist einem Knoten zugeordnet und bildet einen NGBlockBreakToken-Baum, sodass jeder Knoten, der fortgesetzt werden muss, dargestellt wird. Ein NGBlockBreakToken wird an das NGPhysicalBoxFragment angehängt, das für Knoten generiert wird, die in diesem Bereich beschädigt werden. Die Pausentokens werden an die übergeordneten Token weitergegeben und bilden eine Struktur aus Tokens. Wenn die Unterbrechung vor einem Knoten (statt darin) erfolgen muss, wird kein Fragment erzeugt. Der übergeordnete Knoten muss jedoch noch ein "Break-before"-Token für den Knoten erstellen, damit wir mit dessen Layout beginnen können, wenn wir die gleiche Position im Knotenbaum im nächsten Fragmentainer erreichen.

Unterbrechungen werden eingefügt, wenn entweder nicht mehr genug Fragmentierungsbereich vorhanden ist (eine nicht erzwungene Unterbrechung) oder wenn eine erzwungene Pause angefordert wird.

In der Spezifikation gibt es Regeln für optimale nicht erzwungene Unterbrechungen. Es ist aber nicht immer richtig, eine Unterbrechung einfach genau dort einzufügen, wo der Platz knapp wird. Beispielsweise gibt es verschiedene CSS-Eigenschaften wie break-before, die die Wahl der Stelle für die Unterbrechung beeinflussen. Um den Abschnitt mit der Spezifikation für unerzwungene Pausen korrekt zu implementieren, müssen wir daher während des Layouts möglicherweise fehlerfreie Haltepunkte im Auge behalten. Dieser Eintrag bedeutet, dass wir den letzten bestmöglichen Haltepunkt verwenden können, wenn an einer Stelle, an der wir gegen Anfragen zur Pausenvermeidung verstoßen, nicht genügend Speicherplatz vorhanden ist (z. B. break-before:avoid oder orphans:7). Jeder mögliche Haltepunkt erhält eine Bewertung, die von „nur als letzte Option“ bis „perfekter Ort für eine Pause“ mit einigen Werten dazwischen reicht. Wenn die Punktzahl für einen Ort „perfekt“ ist, bedeutet dies, dass keine brechenden Regeln verletzt werden, wenn wir dort brechen (und wenn wir diese Punktzahl genau an dem Punkt erhalten, an dem uns der Speicherplatz ausgeht, besteht kein Grund mehr, nach etwas Besserem zu suchen). Wenn die Punktzahl "Last-Resort" lautet, ist der Haltepunkt nicht einmal ein gültiger Haltepunkt. Wir können jedoch dort brechen, wenn wir nichts Besseres finden, um einen Überlauf der Fragmentierung zu vermeiden.

Gültige Haltepunkte treten in der Regel nur zwischen gleichgeordneten Elementen (Linienfelder oder Blöcke) auf, beispielsweise zwischen einem übergeordneten Element und seinem ersten untergeordneten Element. Haltepunkte der Klasse C sind eine Ausnahme, diese müssen wir hier jedoch nicht näher erläutern. Es gibt einen gültigen Haltepunkt für beispielsweise vor einem gleichgeordneten Block mit „break-before:avoid“, aber er liegt irgendwo zwischen „perfect“ und „last-resort“.

Beim Layout wird der bisher beste Haltepunkt in einer Struktur namens NGEarlyBreak erfasst. Ein vorzeitiger Abbruch ist ein möglicher Haltepunkt vor oder innerhalb eines Blockknotens oder vor einer Zeile (entweder einer Blockcontainerzeile oder einer flexiblen Linie). Wir können eine Kette oder einen Pfad von NGEarlyBreak-Objekten bilden, falls der beste Haltepunkt tief im Inneren eines Objekts liegt, an dem wir früher vorbeigegangen sind, als der Speicherplatz ausgeht. Beispiel:

In diesem Fall ist der Speicherplatz kurz vor #second aufgebraucht, aber es enthält „break-before:avoid“, wodurch der Wert „verstoßen bei Werbeunterbrechung vermeiden“ den Wert für die Werbeunterbrechung erhält. An dieser Stelle gibt es eine NGEarlyBreak-Kette von "inside #outer > in #middle > in #inner > vor "line 3"', mit "perfect", also würden wir lieber dort aufbrechen. Wir müssen also zurückkehren und das Layout noch einmal vom Anfang von #outer ausführen (und dieses Mal die gefundene NGEarlyBreak übergeben), damit wir vor „Zeile 3“ in #inner durchbrechen können. Wir unterbrechen vor „Zeile 3“, damit die verbleibenden vier Zeilen in der nächsten Fragmentierung landen und um widows:4 zu berücksichtigen.

Der Algorithmus ist so konzipiert, dass er immer beim bestmöglichen Haltepunkt (wie in der spec definiert) unterbrochen wird. Dazu werden Regeln in der richtigen Reihenfolge gelöscht, wenn nicht alle erfüllt werden können. Beachten Sie, dass wir das Layout nur einmal pro Fragmentierungsablauf neu erstellen müssen. Im zweiten Layoutpass wurde die beste Stelle für die Unterbrechung bereits an die Layoutalgorithmen übergeben. Das ist die Stelle für die Unterbrechung, die im ersten Layoutdurchlauf ermittelt und als Teil der Layoutausgabe in dieser Runde angegeben wurde. Beim zweiten Layout-Pass legen wir nicht so lange hin, bis uns der Platz ausgeht. Tatsächlich ist nicht zu erwarten, dass uns der Platz ausgeht (was eigentlich ein Fehler wäre), da wir einen superschönen (so schön wie möglich) Ort für eine frühe Pause erhalten haben, um nicht unnötig gegen Regeln zu verstoßen. Wir legen also erst einmal los und brechen ab.

Bei dieser Anmerkung müssen wir manchmal gegen einige Anfragen zur Vermeidung von Pausen verstoßen, wenn dies dazu beiträgt, einen fragmentierten Überlauf zu vermeiden. Beispiel:

Hier ist der Speicherplatz knapp vor #second, aber es enthält „break-before:avoid“. Das bedeutet, wie im letzten Beispiel „verletzende Pausenvermeidung“. Es gibt auch einen NGEarlyBreak mit „Verwaisen von Waisen und Witwen“ (in #first > vor „Zeile 2“), der zwar nicht perfekt ist, aber besser als „Verstoß gegen die Pausenvermeidung“. Wir machen also eine Unterbrechung vor „Zeile 2“, was gegen die Anfrage von Waisen / Witwen verstößt. In der Spezifikation wird dies in 4.4. Nicht erzwungene Unterbrechungen. Hier wird definiert, welche Regeln zuerst ignoriert werden, wenn nicht genügend Haltepunkte vorhanden sind, um einen Überlauf von Fragmenten zu vermeiden.

Zusammenfassung

Das wichtigste funktionale Ziel des Projekts „LayoutNG“ zur Blockfragmentierung bestand darin, die LayoutNG-Architektur zu implementieren, die alle von der Legacy-Engine unterstützten Implementierungen unterstützt, und zwar so wenig wie möglich, abgesehen von Fehlerkorrekturen. Die Hauptausnahme hier ist eine bessere Unterstützung zur Vermeidung von Unterbrechungen (z. B. break-before:avoid), da dies ein zentraler Bestandteil der Fragmentierungs-Engine ist und daher von Anfang an dabei sein musste, da ein späterer Hinzufügen eine weitere Überarbeitung bedeuten würde.

Nachdem die LayoutNG-Blockfragmentierung nun abgeschlossen ist, können wir neue Funktionen hinzufügen, z. B. die Unterstützung gemischter Seitengrößen beim Drucken, @page Randfelder beim Drucken, box-decoration-break:clone und mehr. Wie bei LayoutNG im Allgemeinen gehen wir davon aus, dass die Fehlerrate und der Wartungsaufwand des neuen Systems mit der Zeit deutlich geringer werden.

Vielen Dank, dass Sie sich die Zeit zum Lesen dieser E-Mail genommen haben.

Danksagungen