Pembahasan mendalam RenderingNG: Fragmentasi blok LayoutNG

Fragmentasi blok di LayoutNG kini telah selesai. Pelajari cara kerjanya dan mengapa hal ini penting dalam artikel ini.

Morten Stenshorne
Morten Stenshorne

Saya Morten Stenshorne, insinyur tata letak dalam tim rendering Blink di Google. Saya telah terlibat dalam pengembangan mesin browser sejak awal tahun 2000-an, dan saya memiliki banyak kesenangan, seperti membantu lolos uji acid2 di mesin Presto (Opera 12 dan yang lebih lama), dan merekayasa balik browser lain untuk memperbaiki tata letak tabel di Presto. Saya juga telah menghabiskan lebih banyak dari tahun-tahun tersebut daripada yang ingin saya akui tentang fragmentasi blok, dan, khususnya, multicol di Presto, WebKit, dan Blink. Selama beberapa tahun terakhir di Google, fokus utama saya adalah memimpin pekerjaan penambahan dukungan fragmentasi blok ke LayoutNG. Bergabunglah bersama kami dalam pembahasan mendalam tentang penerapan fragmentasi blok, karena ini mungkin adalah kali terakhir saya mengimplementasikan fragmentasi blok. :)

Apa itu fragmentasi blok?

Fragmentasi blok adalah proses membagi kotak tingkat blok CSS (seperti bagian atau paragraf) menjadi beberapa fragmen jika tidak muat secara keseluruhan di dalam satu penampung fragmen yang disebut fragmentainer. Fragmentainer bukanlah elemen, tetapi mewakili kolom dalam tata letak multi-kolom, atau halaman dalam media yang di-page. Agar fragmentasi terjadi, konten harus berada di dalam konteks fragmentasi. Konteks fragmentasi biasanya ditetapkan oleh penampung multi-kolom (konten akan dibagi menjadi beberapa kolom), atau saat mencetak (konten akan dibagi menjadi beberapa halaman). Paragraf panjang dengan banyak baris mungkin perlu dipecah menjadi beberapa fragmen, sehingga baris pertama ditempatkan di fragmen pertama, dan baris sisanya ditempatkan di fragmen berikutnya.

Satu paragraf teks yang dipecah menjadi dua kolom.
Dalam contoh ini, sebuah paragraf telah dibagi menjadi dua kolom menggunakan tata letak multi-kolom. Setiap kolom adalah fragmentainer, yang mewakili fragmen flow yang terfragmentasi.

Fragmentasi blok mirip dengan jenis fragmentasi terkenal lainnya: fragmentasi garis (atau dikenal sebagai "pemutusan garis"). Semua elemen inline yang terdiri dari lebih dari satu kata (node teks apa pun, elemen <a> apa pun, dan sebagainya), dan memungkinkan jeda baris, dapat dipecah menjadi beberapa fragmen. Setiap fragmen ditempatkan ke dalam kotak garis yang berbeda. Kotak baris adalah fragmentasi inline yang setara dengan fragmentainer untuk kolom dan halaman.

Apa yang dimaksud dengan fragmentasi blok LayoutNG?

LayoutNGBlockFragmentation adalah penulisan ulang mesin fragmentasi untuk LayoutNG, dan setelah bertahun-tahun mengerjakan, bagian pertama akhirnya diluncurkan di Chrome 102 awal tahun ini. Perbaikan ini telah memperbaiki masalah lama yang pada dasarnya tidak dapat diperbaiki di mesin "lama" kami. Terkait struktur data, struktur ini menggantikan beberapa struktur data pra-NG dengan fragmen NG yang direpresentasikan langsung di hierarki fragmen.

Misalnya, sekarang kami mendukung nilai'Hindari' untuk properti CSS 'break-before' dan 'break-after', yang memungkinkan penulis menghindari jeda tepat setelah header. Biasanya tidak terlihat bagus jika hal terakhir yang ditempatkan di halaman adalah header, sementara konten bagian dimulai di halaman berikutnya. Sebagai gantinya, lebih baik pecah sebelum header. Lihat gambar di bawah sebagai contoh.

Contoh pertama menunjukkan judul di bagian bawah halaman, contoh kedua menunjukkannya di bagian atas halaman berikutnya dengan konten terkait.

Chrome 102 juga mendukung fragmentasi tambahan, sehingga konten monolitik (dianggap tidak dapat dipecah) tidak diiris menjadi beberapa kolom, dan efek warna seperti bayangan dan transformasi diterapkan dengan benar.

Fragmentasi blok di LayoutNG kini telah selesai

Pada saat penulisan ini, kami telah menyelesaikan dukungan fragmentasi blok penuh di LayoutNG. Fragmentasi inti (container blok, termasuk tata letak garis, float, dan positioning out-of-flow) yang diluncurkan di Chrome 102. Fragmentasi fleksibel dan petak diluncurkan di Chrome 103, dan fragmentasi tabel tersedia di Chrome 106. Terakhir, pencetakan dikirimkan di Chrome 108. Fragmentasi blok adalah fitur terakhir yang bergantung pada mesin lama untuk melakukan tata letak. Artinya, mulai Chrome 108, mesin lama tidak akan lagi digunakan untuk melakukan tata letak.

Selain benar-benar menata letak konten, struktur data LayoutNG mendukung paint dan hit-testing, tetapi kami masih mengandalkan beberapa struktur data lama untuk JavaScript API yang membaca informasi tata letak, seperti offsetLeft dan offsetTop.

Dengan menata letak semuanya menggunakan NG, Anda dapat mengimplementasikan dan mengirimkan fitur baru yang hanya memiliki implementasi LayoutNG (dan tidak memiliki implementasi LayoutNG (dan tanpa versi mesin lama), seperti kueri container CSS, penempatan anchor, MathML, dan tata letak kustom (Houdini). Untuk kueri penampung, kami mengirimkannya sedikit sebelumnya, dengan peringatan kepada developer bahwa pencetakan belum didukung.

Kami meluncurkan bagian pertama LayoutNG pada tahun 2019, yang terdiri dari tata letak container blok reguler, tata letak inline, float dan posisi out-of-flow, tetapi tidak ada dukungan untuk flex, grid, atau tabel, dan tidak ada dukungan fragmentasi blok sama sekali. Kita akan kembali menggunakan mesin tata letak lama untuk fleksibilitas, petak, tabel, serta apa pun yang melibatkan fragmentasi blok. Hal tersebut berlaku bahkan untuk elemen blok, inline, mengambang, dan keluar-aliran dalam konten yang terfragmentasi—seperti yang dapat Anda lihat, mengupgrade mesin tata letak yang kompleks seperti itu adalah tarian yang sangat halus.

Selain itu, percaya atau tidak, pada pertengahan 2019 sebagian besar fungsi inti tata letak fragmentasi blok LayoutNG sudah diimplementasikan (di belakang tanda). Jadi, mengapa perlu waktu lama untuk mengirim? Jawaban singkatnya adalah: fragmentasi harus hidup berdampingan dengan benar dengan berbagai bagian sistem yang lama, yang tidak dapat dihapus atau diupgrade sampai semua dependensi diupgrade. Untuk jawaban panjang, lihat detail berikut.

Interaksi mesin lama

Struktur data lama masih bertanggung jawab atas JavaScript API yang membaca informasi tata letak, jadi kita perlu menulis kembali data ke mesin lama dengan cara yang dapat dipahami. Hal ini termasuk memperbarui struktur data multi-kolom lama, seperti LayoutMultiColumnFlowThread, dengan benar.

Deteksi dan penanganan penggantian mesin lama

Kita harus kembali ke mesin tata letak lama ketika ada konten di dalamnya yang belum dapat ditangani oleh fragmentasi blok LayoutNG. Pada saat mengirimkan fragmentasi blok LayoutNG inti (musim semi 2022), yang mencakup desain fleksibel, petak, tabel, dan apa pun yang dicetak. Hal ini sangat rumit karena kami perlu mendeteksi kebutuhan penggantian lama sebelum membuat objek dalam hierarki tata letak. Misalnya, kita perlu mendeteksi sebelum tahu apakah ada ancestor penampung multi-kolom, dan sebelum kita tahu node DOM mana yang akan menjadi konteks pemformatan atau tidak. Ini adalah masalah ayam dan telur yang tidak memiliki solusi sempurna, tetapi selama satu-satunya kesalahan perilakunya adalah positif palsu (berganti ke yang lama ketika sebenarnya tidak diperlukan), tidak apa-apa, karena bug apa pun dalam perilaku tata letak tersebut adalah yang sudah dimiliki Chromium, bukan yang baru.

Jalan-jalan di pohon pra-penggambaran

Pre-paint adalah sesuatu yang kita lakukan setelah tata letak, tetapi sebelum menggambar. Tantangan utamanya adalah kita masih harus menjalankan hierarki objek tata letak, tetapi sekarang kita memiliki fragmen NG. Jadi, bagaimana cara mengatasinya? Kita berjalan menuju objek tata letak dan hierarki fragmen NG secara bersamaan. Hal ini cukup rumit, karena pemetaan antara dua pohon tidaklah mudah. Walaupun struktur hierarki objek tata letak sangat mirip dengan hierarki DOM, hierarki fragmen adalah output tata letak, bukan input untuk struktur tersebut. Selain benar-benar mencerminkan efek dari fragmentasi apa pun, termasuk fragmentasi inline (fragmen garis) dan fragmentasi blok (fragmen kolom atau halaman), hierarki fragmen juga memiliki hubungan induk-turunan langsung antara blok yang memuat dan turunan DOM yang memiliki fragmen tersebut sebagai blok penampungnya. Misalnya, dalam hierarki fragmen, fragmen yang dihasilkan oleh elemen yang diposisikan secara mutlak adalah turunan langsung dari fragmen blok yang memuatnya, meskipun ada node lain dalam rantai ancestor antara turunan yang diposisikan out-of-flow dan blok yang memuatnya.

Menjadi semakin rumit lagi ketika ada elemen yang diposisikan out-of-flow di dalam fragmentasi, karena fragmen out-of-flow akan menjadi turunan langsung dari fragmentainer (dan bukan turunan dari apa yang dianggap CSS sebagai blok penampungnya). Sayangnya, ini adalah masalah yang harus dipecahkan untuk dapat hidup berdampingan dengan mesin lama tanpa terlalu banyak masalah. Di masa mendatang, kita akan dapat menyederhanakan banyak kode ini, karena LayoutNG dirancang untuk mendukung semua mode tata letak modern secara fleksibel.

Masalah dengan mesin fragmentasi lama

Mesin lama, yang dirancang di era web sebelumnya, tidak benar-benar memiliki konsep fragmentasi, bahkan jika fragmentasi secara teknis juga ada saat itu (untuk mendukung pencetakan). Dukungan fragmentasi adalah fitur yang dibaut di atas (pencetakan) atau dipasang (multi-kolom).

Saat menata letak konten yang dapat difragmentasi, mesin lama menata letak semuanya ke dalam strip tinggi yang lebarnya sama dengan ukuran kolom atau halaman, dan tingginya setinggi yang diperlukan untuk menampung kontennya. Strip tinggi ini tidak dirender ke halaman—anggap saja sebagai rendering ke halaman virtual yang kemudian disusun ulang untuk tampilan akhir. Secara konseptual ini mirip dengan mencetak seluruh artikel koran kertas menjadi satu kolom, kemudian menggunakan gunting untuk memotongnya menjadi beberapa langkah sebagai langkah kedua. (Dahulu, beberapa surat kabar sebenarnya menggunakan teknik yang mirip dengan ini.)

Mesin lama melacak batas kolom atau halaman imajiner dalam strip. Dengan begitu, lapisan tersebut dapat mendorong konten yang tidak pas melewati batas ke halaman atau kolom berikutnya. Misalnya, jika hanya separuh bagian atas garis yang cocok dengan halaman yang dianggap mesin saat ini, maka akan dimasukkan "strut paging" untuk mendorongnya ke posisi yang mana mesin tersebut mengasumsikan bahwa bagian atas halaman berikutnya berada. Kemudian, sebagian besar pekerjaan fragmentasi yang sebenarnya ("pemotongan dengan gunting dan penempatan") dilakukan setelah tata letak selama pra-pengecatan dan pengecatan, menerjemahkan konten menjadi bagian-bagian yang tinggi atau mengecat. Tindakan ini membuat beberapa hal pada dasarnya menjadi tidak mungkin, seperti menerapkan transformasi dan posisi relatif setelah fragmentasi (yang diperlukan spesifikasi). Selain itu, meskipun ada dukungan untuk fragmentasi tabel di mesin lama, sama sekali tidak ada dukungan fragmentasi fleksibel atau petak.

Berikut adalah ilustrasi tentang bagaimana tata letak tiga kolom direpresentasikan secara internal di mesin lama, sebelum menggunakan gunting, penempatan, dan lem (kita memiliki tinggi yang ditentukan, sehingga hanya empat garis yang pas, tetapi ada ruang berlebih di bagian bawah):

Representasi internal sebagai satu kolom dengan penomoran halaman berada di tempat konten rusak, dan representasi di layar sebagai tiga kolom.

Karena mesin tata letak lama tidak benar-benar memecah konten selama tata letak, ada banyak artefak aneh, seperti posisi relatif dan transformasi yang diterapkan dengan tidak benar, serta bayangan kotak yang terpotong di tepi kolom.

Berikut ini contoh sederhana dengan bayangan teks:

Mesin lama tidak menangani hal ini dengan baik:

Bayangan teks yang dijepit ditempatkan di kolom kedua.

Apakah Anda melihat bagaimana bayangan teks dari baris di kolom pertama diklip, dan ditempatkan di bagian atas kolom kedua? Hal ini karena mesin tata letak lama tidak memahami fragmentasi.

Hasilnya akan terlihat seperti ini (dan ini ditampilkan dengan NG):

Dua kolom teks dengan bayangan ditampilkan dengan benar.

Selanjutnya, mari kita buat sedikit lebih rumit, dengan transformasi dan box-shadow. Perhatikan bagaimana di mesin lama, terjadi clipping yang salah dan column bleed. Itu karena transformasi menurut spesifikasi seharusnya diterapkan sebagai efek pasca-fragmentasi dan pasca-tata letak. Dengan fragmentasi LayoutNG, keduanya berfungsi dengan benar. Ini meningkatkan interop dengan Firefox, yang telah memiliki dukungan fragmentasi yang baik selama beberapa waktu dengan sebagian besar pengujian di area ini juga lulus di sana.

Kotak-kotak yang salah dipecah di dua kolom.

Mesin lama juga memiliki masalah dengan konten monolitik yang tinggi. Konten bersifat monolitik jika tidak memenuhi syarat untuk dipecah menjadi beberapa fragmen. Elemen dengan scroll tambahan bersifat monolitik, karena pengguna tidak harus men-scroll di area non-persegi panjang. Kotak garis dan gambar adalah contoh lain dari konten monolitik. Berikut contohnya:

Jika bagian konten monolitik terlalu tinggi untuk muat di dalam kolom, mesin lama akan memotongnya secara brutal (menghasilkan perilaku yang sangat "menarik" saat mencoba men-scroll container yang dapat di-scroll):

Daripada membiarkannya melebihi kolom pertama (seperti yang dilakukan dengan fragmentasi blok LayoutNG):

ALT_TEXT_HERE

Mesin lama mendukung pecah paksa. Misalnya, <div style="break-before:page;"> akan menyisipkan batas halaman sebelum DIV. Namun, fitur ini hanya memiliki dukungan terbatas untuk menemukan jeda unforced yang optimal. Metode ini mendukung break-inside:avoid serta anak yatim dan janda, tetapi tidak ada dukungan untuk menghindari jeda antar-blok, jika diminta melalui break-before:avoid, misalnya. Perhatikan contoh berikut:

Teks dipecah menjadi dua kolom.

Di sini, elemen #multicol memiliki ruang untuk 5 baris di setiap kolom (karena tingginya 100 piksel dan tinggi garis 20 piksel), sehingga semua #firstchild dapat muat di kolom pertama. Namun, #secondchild yang seinduknya memiliki break-before:before, yang berarti konten ingin agar tidak terjadi jeda di antara keduanya. Karena nilai widows adalah 2, kita perlu mendorong 2 baris #firstchild ke kolom kedua, untuk memenuhi semua permintaan pencegahan jeda. Chromium adalah mesin browser pertama yang sepenuhnya mendukung kombinasi fitur ini.

Cara kerja fragmentasi NG

Mesin tata letak NG umumnya menata letak dokumen dengan menjelajahi hierarki kotak CSS terlebih dahulu. Saat semua turunan node disusun, tata letak node tersebut dapat diselesaikan dengan menghasilkan NGPhysicalFragment dan kembali ke algoritme tata letak induk. Algoritma tersebut menambahkan fragmen ke daftar fragmen turunannya, dan setelah semua turunan selesai, menghasilkan fragmen untuk dirinya sendiri dengan semua fragmen turunannya di dalamnya. Dengan metode ini, akan dibuat pohon fragmen untuk seluruh dokumen. Namun, ini adalah penyederhanaan yang berlebihan: misalnya, elemen yang diposisikan di luar alur harus menggelembung dari tempatnya berada di hierarki DOM ke blok penampungnya sebelum dapat ditata. Saya mengabaikan detail lanjutan ini di sini agar lebih praktis.

Bersama dengan kotak CSS itu sendiri, LayoutNG memberikan ruang batasan untuk algoritma tata letak. Hal ini memberikan informasi kepada algoritma seperti ruang yang tersedia untuk tata letak, apakah konteks pemformatan baru telah dibuat, dan margin menengah yang menyusut hasil dari konten sebelumnya. Ruang batasan juga mengetahui ukuran blok yang ditata dari fragmentainer, dan offset blok saat ini ke dalamnya. Menunjukkan di mana harus memecah.

Ketika fragmentasi blok terlibat, tata letak turunan harus berhenti pada jeda. Penyebab error termasuk kehabisan ruang di halaman atau kolom, atau jeda paksa. Kemudian kita membuat fragmen untuk node yang dikunjungi, dan menampilkannya hingga ke root konteks fragmentasi (container multikol, atau, untuk pencetakan, root dokumen). Kemudian, di root konteks fragmentasi, kita bersiap untuk fragmentainer baru, dan turun ke pohon lagi, melanjutkan dari tempat yang terakhir kita tinggalkan sebelum jeda.

Struktur data yang penting untuk menyediakan sarana guna melanjutkan tata letak setelah jeda disebut NGBlockBreakToken. File ini berisi semua informasi yang diperlukan untuk melanjutkan tata letak dengan benar di fragmentainer berikutnya. NGBlockBreakToken dikaitkan dengan node dan membentuk hierarki NGBlockBreakToken, sehingga setiap node yang perlu dilanjutkan akan ditampilkan. NGBlockBreakToken ditambahkan ke NGPhysicalBoxFragment yang dihasilkan untuk node yang rusak di dalamnya. Token istirahat disebarkan ke induk, sehingga membentuk pohon token pemutusan. Jika kita perlu memecah node sebelum node (bukan di dalamnya), tidak ada fragmen yang akan dihasilkan, tetapi node induk masih perlu membuat token jeda "break-before" untuk node tersebut, sehingga kita dapat mulai menata letaknya saat mencapai posisi yang sama dalam hierarki node di fragmentainer berikutnya.

Jeda iklan disisipkan saat kita kehabisan ruang fragmentainer (jeda tidak paksa), atau saat jeda paksa diminta.

Ada aturan dalam spesifikasi untuk jeda tidak paksa yang optimal, dan menyisipkan jeda persis di tempat kita kehabisan ruang tidak selalu merupakan hal yang tepat untuk dilakukan. Misalnya, ada berbagai properti CSS seperti break-before yang memengaruhi pilihan lokasi jeda. Oleh karena itu, selama tata letak, untuk menerapkan bagian spesifikasi jeda tidak paksa dengan benar, kita perlu melacak titik henti sementara yang mungkin baik. Data ini berarti kita dapat kembali dan menggunakan titik henti sementara terbaik yang terakhir ditemukan, jika kita kehabisan ruang pada titik di mana kita akan melanggar permintaan pencegahan jeda (misalnya, break-before:avoid atau orphans:7). Setiap titik henti sementara yang memungkinkan akan diberi skor, mulai dari "hanya lakukan ini sebagai upaya terakhir" hingga "tempat sempurna untuk merusak", dengan beberapa nilai di antaranya. Jika lokasi jeda mencapai skor "sempurna", artinya tidak ada aturan yang melanggar yang dilanggar jika kita melanggarnya (dan jika kita mendapatkan skor ini tepat pada saat kita kehabisan ruang, tidak perlu mencari yang lebih baik). Jika skornya adalah "last-resort", titik henti sementara bahkan bukan yang valid, tetapi kita mungkin masih akan memecahkannya jika tidak menemukan sesuatu yang lebih baik, untuk menghindari fragmentainer.

Titik henti sementara yang valid umumnya hanya terjadi di antara elemen yang setara (kotak baris atau blok), dan bukan, misalnya, antara induk dan turunan pertamanya (titik henti sementara class C merupakan pengecualian, tetapi kita tidak perlu membahasnya di sini). Ada titik henti sementara yang valid, misalnya sebelum blok seinduk dengan break-before:verify, tetapi titik henti sementara berada di antara "sempurna" dan "last-resort".

Selama tata letak, kita melacak titik henti sementara terbaik yang ditemukan sejauh ini dalam struktur yang disebut NGEarlyBreak. Jeda awal adalah kemungkinan titik henti sementara sebelum atau di dalam node blok, atau sebelum baris (baris penampung blok atau baris fleksibel). Kita dapat membentuk rantai atau jalur objek NGEarlyBreak, jika titik henti sementara terbaik ada di suatu tempat yang jauh di dalam sesuatu yang kita lewati sebelumnya saat ruang penyimpanan habis. Berikut contohnya:

Dalam hal ini, kita kehabisan ruang tepat sebelum #second, tetapi memiliki "break-before:verify", yang mendapatkan skor lokasi jeda "melanggar hindari jeda". Pada saat itu, kita memiliki rantai NGEarlyBreak "di dalam #outer > di dalam #middle > di dalam #inner > sebelum "baris 3"', dengan "sempurna", jadi kita lebih baik berhenti di sana. Jadi, kita perlu mengembalikan dan menjalankan ulang tata letak dari awal #outer (dan kali ini teruskan NGEarlyBreak yang kita temukan), sehingga kita bisa berhenti sebelum "baris 3" di #inner. (Kita berhenti sebelum "baris 3", sehingga 4 baris yang tersisa berakhir di fragmentainer berikutnya, dan untuk mematuhi widows:4.)

Algoritma ini dirancang untuk selalu merusak titik henti sementara terbaik—seperti yang ditentukan dalam spec—dengan menjatuhkan aturan dalam urutan yang benar, jika tidak semuanya dapat dipenuhi. Perhatikan bahwa kita hanya perlu menata ulang paling banyak sekali per alur fragmentasi. Pada saat kita berada di penerusan tata letak kedua, lokasi jeda terbaik telah diteruskan ke algoritma tata letak, ini adalah lokasi jeda yang ditemukan di penerusan tata letak pertama, dan disediakan sebagai bagian dari output tata letak di ronde tersebut. Di tahap tata letak kedua, kita tidak membuat tata letak sampai kehabisan ruang—sebenarnya, kita tidak diharapkan kehabisan ruang (yang sebenarnya akan menjadi error), karena kita diberi tempat yang sangat manis (yah, semanis yang tersedia) untuk memasukkan jeda lebih awal, agar tidak melanggar aturan yang tidak perlu. Jadi kami menatap ke titik itu, dan berhenti.

Dengan catatan itu, terkadang kita perlu melanggar beberapa permintaan penghindaran jeda, jika hal itu membantu menghindari fragmentainer overflow. Contoh:

Di sini, kita kehabisan ruang tepat sebelum #second, tetapi kode tersebut memiliki "break-before:result". Itu diterjemahkan menjadi "melanggar istirahat menghindarkan", seperti contoh terakhir. Kita juga memiliki NGEarlyBreak dengan "melanggar anak yatim dan janda" (di dalam #first > sebelum "baris 2"), yang masih belum sempurna, tetapi lebih baik daripada "melanggar jeda istirahat". Jadi kita akan jeda sebelum "baris 2", yang melanggar permintaan anak yatim / janda. Spesifikasi tersebut berkaitan dengan hal ini di 4.4. Unforced Breaks, yang menentukan aturan yang melanggar mana yang akan diabaikan terlebih dahulu jika kita tidak memiliki cukup titik henti sementara untuk menghindari fragmentainer overflow.

Ringkasan

Tujuan fungsional utama pada project fragmentasi blok LayoutNG adalah untuk menyediakan implementasi yang mendukung arsitektur LayoutNG dari semua yang didukung mesin lama, dan sesedikit mungkin lainnya, selain perbaikan bug. Pengecualian utama di sini adalah dukungan penghindaran error yang lebih baik (misalnya break-before:avoid), karena ini adalah bagian inti dari mesin fragmentasi, sehingga harus ada di sana sejak awal, karena menambahkannya nanti akan berarti penulisan ulang lainnya.

Setelah fragmentasi blok LayoutNG selesai, kita dapat mulai menambahkan fungsi baru, seperti mendukung ukuran halaman campuran saat mencetak, @page kotak margin saat mencetak, box-decoration-break:clone, dan lainnya. Dan seperti halnya LayoutNG secara umum, kami memperkirakan tingkat bug dan beban pemeliharaan sistem baru akan jauh lebih rendah dari waktu ke waktu.

Terima kasih sudah membaca.

Ucapan terima kasih