रेंडर करने के लिए, डीप-डाइव की सुविधा: LayoutNG ब्लॉक फ़्रैगमेंटेशन

Morten Stenshorne
Morten Stenshorne

ब्लॉक फ़्रैगमेंटेशन, सीएसएस ब्लॉक-लेवल बॉक्स (जैसे, सेक्शन या पैराग्राफ़) को कई फ़्रैगमेंट में बांटता है. ऐसा तब होता है, जब वह एक फ़्रैगमेंट कंटेनर में पूरा फ़िट न हो. इस कंटेनर को फ़्रैगमेंटर कहा जाता है. फ़्रैगमेंटर कोई एलिमेंट नहीं है. हालांकि, यह कई कॉलम वाले लेआउट में कॉलम या पेज किए गए मीडिया में पेज को दिखाता है.

फ़्रैगमेंटेशन के लिए, कॉन्टेंट को फ़्रैगमेंटेशन कॉन्टेक्स्ट में होना चाहिए. फ़्रैगमेंटेशन कॉन्टेक्स्ट, आम तौर पर कई कॉलम वाले कंटेनर (कॉन्टेंट को कॉलम में बांटा जाता है) या प्रिंट करते समय (कॉन्टेंट को पेजों में बांटा जाता है) सेट किया जाता है. कई लाइनों वाले लंबे पैराग्राफ़ को कई फ़्रैगमेंट में बांटना पड़ सकता है, ताकि पहली लाइनों को पहले फ़्रैगमेंट में रखा जा सके और बाकी लाइनों को बाद के फ़्रैगमेंट में रखा जा सके.

टेक्स्ट का एक पैराग्राफ़, जिसे दो कॉलम में बांटा गया है.
इस उदाहरण में, कई कॉलम वाले लेआउट का इस्तेमाल करके, पैराग्राफ़ को दो कॉलम में बांटा गया है. हर कॉलम एक फ़्रैगमेंटर होता है, जो फ़्रैगमेंट किए गए फ़्लो का एक फ़्रैगमेंट दिखाता है.

ब्लॉक फ़्रैगमेंटेशन, फ़्रैगमेंटेशन के एक अन्य टाइप से मिलता-जुलता है: लाइन फ़्रैगमेंटेशन, जिसे "लाइन ब्रेकिंग" भी कहा जाता है. किसी भी इनलाइन एलिमेंट को कई फ़्रैगमेंट में बांटा जा सकता है. ऐसा तब किया जा सकता है, जब उसमें एक से ज़्यादा शब्द (कोई भी टेक्स्ट नोड, कोई भी <a> एलिमेंट वगैरह) हों और लाइन ब्रेक की सुविधा हो. हर फ़्रैगमेंट को एक अलग लाइन बॉक्स में रखा जाता है. लाइन बॉक्स, कॉलम और पेजों के लिए फ़्रैगमेंटर के बराबर इनलाइन फ़्रैगमेंटेशन है.

LayoutNG ब्लॉक फ़्रैगमेंटेशन

LayoutNGBlockFragmentation, LayoutNG के लिए फ़्रैगमेंटेशन इंजन को फिर से लिखा गया है. इसे पहली बार Chrome 102 में लॉन्च किया गया था. डेटा स्ट्रक्चर के मामले में, यह एनजी से पहले के कई डेटा स्ट्रक्चर को एनजी फ़्रैगमेंट से बदल देता है. ये फ़्रैगमेंट सीधे फ़्रैगमेंट ट्री में दिखते हैं.

उदाहरण के लिए, अब हम 'break-before' और 'break-after' सीएसएस प्रॉपर्टी के लिए 'avoid' वैल्यू का इस्तेमाल कर सकते हैं. इससे लेखकों को हेडर के ठीक बाद ब्रेक से बचने में मदद मिलती है. जब किसी पेज पर आखिरी चीज़ हेडर होती है और सेक्शन का कॉन्टेंट अगले पेज पर शुरू होता है, तो अक्सर यह अजीब लगता है. हेडर से पहले ब्रेक लगाना बेहतर होता है.

हेडिंग के अलाइनमेंट का उदाहरण.
पहली इमेज. पहले उदाहरण में, हेडिंग को पेज के सबसे नीचे दिखाया गया है. दूसरे उदाहरण में, हेडिंग को उसके कॉन्टेंट के साथ अगले पेज पर सबसे ऊपर दिखाया गया है.

Chrome, फ़्रैगमेंटेशन ओवरफ़्लो की सुविधा भी देता है, ताकि मोनोलिथिक (जिसे तोड़ा नहीं जा सकता) कॉन्टेंट को कई कॉलम में न काटा जाए. साथ ही, शैडो और ट्रांसफ़ॉर्म जैसे पेंट इफ़ेक्ट सही तरीके से लागू किए जा सकें.

LayoutNG में ब्लॉक फ़्रैगमेंटेशन की सुविधा अब पूरी हो गई है

Chrome 102 में शिप किया गया मुख्य फ़्रैगमेंटेशन (लाइन लेआउट, फ़्लोट, और आउट-ऑफ़-फ़्लो पोज़िशनिंग वाले ब्लॉक कंटेनर). फ़्लेक्स और ग्रिड फ़्रैगमेंटेशन की सुविधा, Chrome 103 में लॉन्च की गई थी. वहीं, टेबल फ़्रैगमेंटेशन की सुविधा, Chrome 106 में लॉन्च की गई थी. आखिर में, Chrome 108 में प्रिंट करने की सुविधा जोड़ी गई. ब्लॉक फ़्रैगमेंटेशन, लेआउट के लिए लेगसी इंजन पर निर्भर रहने वाली आखिरी सुविधा थी.

Chrome 108 से, लेआउट बनाने के लिए लेगसी इंजन का इस्तेमाल नहीं किया जाता.

इसके अलावा, LayoutNG डेटा स्ट्रक्चर, पेंटिंग और हिट-टेस्टिंग के साथ काम करते हैं. हालांकि, हम JavaScript API के लिए कुछ लेगसी डेटा स्ट्रक्चर पर भरोसा करते हैं, जो लेआउट की जानकारी पढ़ते हैं. जैसे, offsetLeft और offsetTop.

NG के साथ सब कुछ व्यवस्थित करने से, नई सुविधाओं को लागू और शिप किया जा सकेगा. इन सुविधाओं में सिर्फ़ LayoutNG लागू होता है, न कि लेगसी इंजन का कोई दूसरा वर्शन. जैसे, सीएसएस कंटेनर क्वेरी, ऐंकर पोज़िशनिंग, MathML, और कस्टम लेआउट (Houdini). कंटेनर क्वेरी के लिए, हमने इसे थोड़ा पहले ही लॉन्च कर दिया था. साथ ही, डेवलपर को चेतावनी दी थी कि फ़िलहाल प्रिंटिंग की सुविधा काम नहीं कर रही है.

हमने LayoutNG का पहला हिस्सा 2019 में लॉन्च किया था. इसमें रेगुलर ब्लॉक कंटेनर लेआउट, इनलाइन लेआउट, फ़्लोट, और आउट-ऑफ़-फ़्लो पोज़िशनिंग शामिल थी. हालांकि, इसमें फ़्लेक्स, ग्रिड या टेबल के साथ काम करने की सुविधा नहीं थी. साथ ही, ब्लॉक को अलग-अलग हिस्सों में बांटने की सुविधा भी नहीं थी. हम फ़्लेक्स, ग्रिड, टेबल, और ब्लॉक फ़्रैगमेंटेशन से जुड़ी किसी भी चीज़ के लिए, लेगसी लेआउट इंजन का इस्तेमाल करेंगे. यह फ़्रैगमेंट किए गए कॉन्टेंट में ब्लॉक, इनलाइन, फ़्लोटिंग, और आउट-ऑफ़-फ़्लो एलिमेंट के लिए भी सही था. जैसा कि आप देख सकते हैं, इस तरह के जटिल लेआउट इंजन को अपग्रेड करना बहुत मुश्किल है.

इसके अलावा, साल 2019 के मध्य तक, LayoutNG ब्लॉक फ़्रैगमेंटेशन लेआउट की ज़्यादातर मुख्य सुविधाएं पहले से ही लागू थीं. तो, इसे शिप होने में इतना समय क्यों लगा? इसका छोटा जवाब यह है: फ़्रैगमेंटेशन को सिस्टम के अलग-अलग लेगसी हिस्सों के साथ सही तरीके से काम करना चाहिए. जब तक सभी डिपेंडेंसी को अपग्रेड नहीं किया जाता, तब तक उन्हें हटाया या अपग्रेड नहीं किया जा सकता.

लेगसी इंजन इंटरैक्शन

लेगसी डेटा स्ट्रक्चर अब भी उन JavaScript एपीआई के लिए ज़रूरी हैं जो लेआउट की जानकारी पढ़ते हैं. इसलिए, हमें लेगसी इंजन में डेटा को इस तरह से वापस लिखना होगा कि वह उसे समझ सके. इसमें LayoutMultiColumnFlowThread जैसे लेगसी मल्टी-कॉलम डेटा स्ट्रक्चर को सही तरीके से अपडेट करना शामिल है.

लेगसी इंजन फ़ॉलबैक का पता लगाना और उसे मैनेज करना

जब अंदर ऐसा कॉन्टेंट होता था जिसे LayoutNG ब्लॉक फ़्रैगमेंटेशन से मैनेज नहीं किया जा सकता था, तब हमें लेगसी लेआउट इंजन का इस्तेमाल करना पड़ता था. LayoutNG के मुख्य ब्लॉक को अलग-अलग हिस्सों में बांटने के दौरान, इसमें फ़्लेक्स, ग्रिड, टेबल, और प्रिंट की गई कोई भी चीज़ शामिल होती है. यह खास तौर पर मुश्किल था, क्योंकि हमें लेआउट ट्री में ऑब्जेक्ट बनाने से पहले, लेगसी फ़ॉलबैक की ज़रूरत का पता लगाना था. उदाहरण के लिए, हमें यह पता लगाना था कि क्या कई कॉलम वाला कोई पैरंट कंटेनर है या नहीं. साथ ही, यह भी पता लगाना था कि कौनसे DOM नोड फ़ॉर्मैटिंग कॉन्टेक्स्ट बनेंगे या नहीं. यह एक ऐसी समस्या है जिसका कोई सटीक समाधान नहीं है. हालांकि, जब तक इसकी गड़बड़ी सिर्फ़ गलत तरीके से सकारात्मक नतीजे दिखाने (जब ज़रूरत न हो, तब लेगसी पर फ़ॉलबैक) तक सीमित है, तब तक यह ठीक है. इसकी वजह यह है कि लेआउट के व्यवहार में मौजूद कोई भी गड़बड़ी, क्रोमियम में पहले से मौजूद है, न कि कोई नई गड़बड़ी.

पेड़ों के बीच में चलने का रास्ता

प्री-पेंट, हम लेआउट के बाद, लेकिन पेंट करने से पहले करते हैं. मुख्य समस्या यह है कि हमें अब भी लेआउट ऑब्जेक्ट ट्री को वॉक करने की ज़रूरत है, लेकिन अब हमारे पास NG फ़्रैगमेंट हैं—तो हम इस समस्या को कैसे हल करें? हम लेआउट ऑब्जेक्ट और NG फ़्रैगमेंट ट्री, दोनों को एक साथ वॉक करते हैं! यह काफ़ी मुश्किल है, क्योंकि दोनों ट्री के बीच मैपिंग करना आसान नहीं है.

लेआउट ऑब्जेक्ट ट्री का स्ट्रक्चर, डीओएम ट्री से काफ़ी मिलता-जुलता है. हालांकि, फ़्रैगमेंट ट्री, लेआउट का आउटपुट है, न कि उसका इनपुट. फ़्रैगमेंट ट्री, इनलाइन फ़्रैगमेंटेशन (लाइन फ़्रैगमेंट) और ब्लॉक फ़्रैगमेंटेशन (कॉलम या पेज फ़्रैगमेंट) जैसे किसी भी फ़्रैगमेंटेशन के असर को दिखाता है. साथ ही, इसमें कॉन्टेंट वाले ब्लॉक और उन डीओएम वंशजों के बीच सीधे तौर पर पैरंट-चाइल्ड संबंध होता है जिनके कॉन्टेंट वाले ब्लॉक के तौर पर वह फ़्रैगमेंट होता है. उदाहरण के लिए, फ़्रैगमेंट ट्री में, पूरी तरह से पोज़िशन किए गए एलिमेंट से जनरेट किया गया फ़्रैगमेंट, उसमें शामिल ब्लॉक फ़्रैगमेंट का सीधा चाइल्ड होता है. भले ही, फ़्लो से बाहर पोज़िशन किए गए वंश और उसके ब्लॉक के बीच, पैतृक चेन में अन्य नोड हों.

फ़्रैगमेंटेशन में, आउट-ऑफ़-फ़्लो पोज़िशन वाला एलिमेंट होने पर, यह और भी मुश्किल हो सकता है. ऐसा होने पर, आउट-ऑफ़-फ़्लो फ़्रैगमेंट, फ़्रैगमेंटर के डायरेक्ट चाइल्ड बन जाते हैं. साथ ही, वे उस ब्लॉक के चाइल्ड नहीं बनते जिसे सीएसएस, कॉन्टैनिंग ब्लॉक मानता है. यह एक ऐसी समस्या थी जिसे लेगसी इंजन के साथ काम करने के लिए हल करना ज़रूरी था. आने वाले समय में, हम इस कोड को आसान बना पाएंगे. ऐसा इसलिए, क्योंकि LayoutNG को सभी आधुनिक लेआउट मोड के साथ काम करने के लिए डिज़ाइन किया गया है.

लेगसी फ़्रैगमेंटेशन इंजन से जुड़ी समस्याएं

वेब के शुरुआती दौर में डिज़ाइन किए गए लेगसी इंजन में, फ़्रैगमेंटेशन का कोई कॉन्सेप्ट नहीं है. भले ही, प्रिंटिंग की सुविधा देने के लिए, फ़्रैगमेंटेशन तकनीकी तौर पर उस समय भी मौजूद था. फ़्रैगमेंटेशन की सुविधा को बाद में जोड़ा गया था. इसे प्रिंटिंग के लिए सबसे ऊपर या कई कॉलम के लिए बाद में जोड़ा गया था.

फ़्रैगमेंट किए जा सकने वाले कॉन्टेंट को लेआउट करते समय, लेगसी इंजन हर चीज़ को एक लंबी स्ट्रिप में लेआउट करता है. इस स्ट्रिप की चौड़ाई, कॉलम या पेज के इनलाइन साइज़ के बराबर होती है और ऊंचाई, कॉन्टेंट के हिसाब से तय होती है. इस लंबी स्ट्रिप को पेज पर रेंडर नहीं किया जाता. इसे किसी वर्चुअल पेज पर रेंडर करने के तौर पर देखें, जिसे फ़ाइनल डिसप्ले के लिए फिर से व्यवस्थित किया जाता है. यह कॉन्सेप्ट, अखबार के किसी लेख को एक कॉलम में प्रिंट करने और फिर उसे काटकर कई कॉलम में बांटने जैसा है. (पहले के समय में, कुछ अखबारों ने इस तरह की तकनीकों का इस्तेमाल किया था!)

लेगसी इंजन, स्ट्रिप में किसी काल्पनिक पेज या कॉलम की सीमा का ट्रैक रखता है. इससे, उस कॉन्टेंट को अगले पेज या कॉलम में भेजा जा सकता है जो सीमाओं के अंदर नहीं आता. उदाहरण के लिए, अगर किसी लाइन का सिर्फ़ ऊपरी आधा हिस्सा, इंजन के हिसाब से मौजूदा पेज पर फ़िट होता है, तो वह "पेजेशन स्ट्रट" डाल देगा, ताकि उसे उस जगह पर ले जाया जा सके जहां इंजन को लगता है कि अगले पेज का सबसे ऊपरी हिस्सा है. इसके बाद, ज़्यादातर फ़्रैगमेंटेशन का काम (यानी "कैंची से काटना और प्लेसमेंट") लेआउट के बाद, प्री-पेंट और पेंटिंग के दौरान होता है. इसमें, कॉन्टेंट की लंबी स्ट्रिप को पेजों या कॉलम में काटकर और उनके हिस्सों का अनुवाद करके काटा जाता है. इस वजह से, कुछ काम करना मुश्किल हो गया था. जैसे, फ़्रैगमेंटेशन के बाद ट्रांसफ़ॉर्म और रिलेटिव पोज़िशनिंग लागू करना (जो कि स्पेसिफ़िकेशन के मुताबिक ज़रूरी है). इसके अलावा, लेगसी इंजन में टेबल के फ़्रैगमेंटेशन के लिए कुछ मदद मिलती है, लेकिन फ़्लेक्स या ग्रिड के फ़्रैगमेंटेशन के लिए कोई मदद नहीं मिलती.

यहां एक उदाहरण दिया गया है, जिसमें दिखाया गया है कि लेगसी इंजन में, कैंची, प्लेसमेंट, और गोंद का इस्तेमाल करने से पहले, तीन कॉलम वाले लेआउट को अंदरूनी तौर पर कैसे दिखाया जाता है. हमारे पास तय की गई ऊंचाई होती है, ताकि सिर्फ़ चार लाइनें फ़िट हो सकें. हालांकि, सबसे नीचे कुछ अतिरिक्त जगह होती है:

कॉन्टेंट के बीच में पेजेशन के लिए बने स्ट्रट के साथ, एक कॉलम के तौर पर इंटरनल रेप्रज़ेंटेशन और स्क्रीन पर तीन कॉलम के तौर पर रेप्रज़ेंटेशन

लेगसी लेआउट इंजन, लेआउट के दौरान कॉन्टेंट को फ़्रैगमेंट नहीं करता. इस वजह से, कई अजीब आर्टफ़ैक्ट दिखते हैं. जैसे, रिलेटिव पोज़िशनिंग और ट्रांसफ़ॉर्मेशन गलत तरीके से लागू होना और कॉलम के किनारों पर बॉक्स-शैडो का क्लिप होना.

यहां text-shadow का इस्तेमाल करने का उदाहरण दिया गया है:

देखें

लेगसी इंजन, इन मामलों में ठीक से काम नहीं करता:

दूसरे कॉलम में, क्लिप किए गए टेक्स्ट की परछाईयां दिखाई गई हैं.

क्या आपको दिख रहा है कि पहले कॉलम में मौजूद लाइन के टेक्स्ट-शैडो को कैसे क्लिप किया गया है और उसकी जगह दूसरे कॉलम में सबसे ऊपर रखा गया है? ऐसा इसलिए होता है, क्योंकि लेगसी लेआउट इंजन, फ़्रैगमेंटेशन को समझ नहीं पाता.

यह इस तरह दिखना चाहिए:

टेक्स्ट के दो कॉलम, जिनमें शेडो सही तरीके से दिख रहे हैं.

इसके बाद, ट्रांसफ़ॉर्म और बॉक्स-शैडो की मदद से, इसे थोड़ा और मुश्किल बनाते हैं. ध्यान दें कि लेगसी इंजन में, क्लिपिंग और कॉलम ब्लीड गलत तरीके से कैसे हो रहा है. ऐसा इसलिए है, क्योंकि स्पेसिफ़िकेशन के मुताबिक ट्रांसफ़ॉर्म को पोस्ट-लेआउट, पोस्ट-फ़्रैगमेंटेशन इफ़ेक्ट के तौर पर लागू किया जाना चाहिए. LayoutNG फ़्रैगमेंटेशन के साथ, दोनों सही तरीके से काम करते हैं. इससे Firefox के साथ इंटरऑपरेबिलिटी बढ़ती है. इस ब्राउज़र में, फ़्रैगमेंटेशन के लिए कुछ समय से अच्छी सुविधाएं उपलब्ध हैं. इस क्षेत्र में, ज़्यादातर टेस्ट भी पास हो जाते हैं.

देखें

बॉक्स को दो कॉलम में गलत तरीके से बांटा गया है.

लेगसी इंजन में, बड़े मोनोलिथिक कॉन्टेंट से भी समस्याएं आती हैं. अगर कॉन्टेंट को कई फ़्रैगमेंट में बांटा नहीं जा सकता, तो उसे मॉनोलिथिक कॉन्टेंट कहा जाता है. ओवरफ़्लो स्क्रोलिंग वाले एलिमेंट, एक जैसे होते हैं. ऐसा इसलिए, क्योंकि उपयोगकर्ताओं के लिए किसी ऐसे क्षेत्र में स्क्रोल करना फ़ायदेमंद नहीं होता जो आयताकार न हो. लाइन बॉक्स और इमेज, मोनोलिथिक कॉन्टेंट के अन्य उदाहरण हैं. यहां एक उदाहरण दिया गया है:

देखें

अगर किसी कॉलम में मोनोलिथिक कॉन्टेंट का कोई हिस्सा फ़िट नहीं हो पा रहा है, तो लेगसी इंजन उसे काट देगा. इससे स्क्रोल किए जा सकने वाले कंटेनर को स्क्रोल करने पर, बहुत "दिलचस्प" व्यवहार दिखेगा:

पहले कॉलम में ओवरफ़्लो होने देने के बजाय (जैसा कि LayoutNG ब्लॉक फ़्रैगमेंटेशन के साथ होता है):

ALT_TEXT_HERE

लेगसी इंजन, फ़ोर्स किए गए ब्रेक के साथ काम करता है. उदाहरण के लिए, <div style="break-before:page;">, DIV से पहले पेज ब्रेक डालेगा. हालांकि, यह बिना किसी दबाव के ऑप्टिमाइज़ किए गए ब्रेक ढूंढने के लिए, सीमित सहायता देता है. यह break-inside:avoid और अनाथ और विधवा के साथ काम करता है. हालांकि, अगर break-before:avoid के ज़रिए अनुरोध किया जाता है, तो ब्लॉक के बीच के ब्रेक से बचने के लिए कोई सुविधा उपलब्ध नहीं है. इस उदाहरण पर ध्यान दें:

देखें

टेक्स्ट को दो कॉलम में बांटा गया है.

यहां, #multicol एलिमेंट में हर कॉलम में पांच लाइनें हो सकती हैं. इसकी वजह यह है कि इसकी ऊंचाई 100 पिक्सल है और लाइन-हाइट 20 पिक्सल है. इसलिए, #firstchild की सभी लाइनें पहले कॉलम में फ़िट हो सकती हैं. हालांकि, इसके सिबलिंग #secondchild में break-before:avoid है. इसका मतलब है कि कॉन्टेंट के बीच ब्रेक नहीं होना चाहिए. widows की वैल्यू 2 होने पर, हमें ब्रेक से बचने के सभी अनुरोधों को पूरा करने के लिए, #firstchild की दो लाइनें दूसरे कॉलम में डालनी होंगी. Chromium, ब्राउज़र इंजन का पहला ऐसा वर्शन है जो इन सुविधाओं के कॉम्बिनेशन के साथ पूरी तरह से काम करता है.

एनजी फ़्रैगमेंटेशन कैसे काम करता है

आम तौर पर, NG लेआउट इंजन, सीएसएस बॉक्स ट्री को डीप-फ़र्स्ट तरीके से ट्रैवर्स करके दस्तावेज़ को लेआउट करता है. जब किसी नोड के सभी वंशजों को लेआउट किया जाता है, तो उस नोड का लेआउट पूरा किया जा सकता है. इसके लिए, NGPhysicalFragment बनाकर, पैरंट लेआउट एल्गोरिदम पर वापस जाना होगा. यह एल्गोरिदम, फ़्रैगमेंट को चाइल्ड फ़्रैगमेंट की सूची में जोड़ता है. सभी चाइल्ड फ़्रैगमेंट बन जाने के बाद, यह अपने लिए एक फ़्रैगमेंट जनरेट करता है. इसमें सभी चाइल्ड फ़्रैगमेंट शामिल होते हैं. इस तरीके से, यह पूरे दस्तावेज़ के लिए फ़्रैगमेंट ट्री बनाता है. हालांकि, यह बहुत आसान है: उदाहरण के लिए, फ़्लो से बाहर पोज़िशन किए गए एलिमेंट को लेआउट करने से पहले, उन्हें डीओएम ट्री में मौजूद जगह से, उनके ब्लॉक में ले जाना होगा. हम यहां इस बेहतर जानकारी को अनदेखा कर रहे हैं, ताकि इसे आसानी से समझा जा सके.

LayoutNG, सीएसएस बॉक्स के साथ-साथ लेआउट एल्गोरिदम के लिए भी जगह उपलब्ध कराता है. इससे एल्गोरिदम को जानकारी मिलती है, जैसे कि लेआउट के लिए उपलब्ध जगह, नया फ़ॉर्मैटिंग कॉन्टेक्स्ट सेट किया गया है या नहीं, और पिछले कॉन्टेंट से मार्जिन को छोटा करने के बीच के नतीजे. कंस्ट्रेंट स्पेस में, फ़्रैगमेंटर के लेआउट किए गए ब्लॉक का साइज़ और उसमें मौजूदा ब्लॉक का ऑफ़सेट भी होता है. इससे पता चलता है कि ब्रेक कहां लेना है.

जब ब्लॉक को अलग-अलग हिस्सों में बांटा जाता है, तो ब्रेक पर वंश के लेआउट को रोकना पड़ता है. पेज या कॉलम में जगह खत्म होने या फ़ोर्स ब्रेक की वजह से, टेक्स्ट में ब्रेक लग सकता है. इसके बाद, हम उन नोड के लिए फ़्रैगमेंट बनाते हैं जिन्हें हमने विज़िट किया है. साथ ही, फ़्रैगमेंटेशन कॉन्टेक्स्ट रूट (मल्टीकॉल कंटेनर या प्रिंटिंग के मामले में, दस्तावेज़ रूट) तक वापस आ जाते हैं. इसके बाद, फ़्रैगमेंटेशन कॉन्टेक्स्ट रूट पर, हम नए फ़्रैगमेंटर के लिए तैयारी करते हैं और फिर से ट्री में नीचे जाते हैं. साथ ही, ब्रेक से पहले जहां से हमने छोड़ा था वहां से फिर से शुरू करते हैं.

ब्रेक के बाद लेआउट को फिर से शुरू करने का तरीका बताने वाले अहम डेटा स्ट्रक्चर को NGBlockBreakToken कहा जाता है. इसमें अगले फ़्रैगमेंटर में लेआउट को सही तरीके से फिर से शुरू करने के लिए ज़रूरी सारी जानकारी होती है. NGBlockBreakToken किसी नोड से जुड़ा होता है और यह NGBlockBreakToken ट्री बनाता है, ताकि जिस नोड को फिर से शुरू करना है उसे दिखाया जा सके. NGBlockBreakToken, अंदर ब्रेक करने वाले नोड के लिए जनरेट किए गए NGPhysicalBoxFragment से जुड़ा होता है. ब्रेक टोक़न, पैरंट टोक़न में भेजे जाते हैं. इससे ब्रेक टोक़न का ट्री बनता है. अगर हमें किसी नोड के अंदर के बजाय, उससे पहले ब्रेक करना है, तो कोई फ़्रैगमेंट नहीं बनाया जाएगा. हालांकि, पैरंट नोड को अब भी नोड के लिए "break-before" ब्रेक टोकन बनाना होगा, ताकि अगले फ़्रैगमेंटर में नोड ट्री में उसी पोज़िशन पर पहुंचने पर, हम उसे लेआउट करना शुरू कर सकें.

ब्रेक तब डाले जाते हैं, जब फ़्रैगमेंटेनर में जगह खत्म हो जाती है (बिना किसी वजह के ब्रेक) या जब ब्रेक का अनुरोध किया जाता है.

स्पेसिफ़िकेशन में, ब्रेक के लिए नियम होते हैं. इसलिए, सिर्फ़ उस जगह पर ब्रेक डालना सही नहीं है जहां स्पेस खत्म हो गया है. उदाहरण के लिए, break-before जैसी कई सीएसएस प्रॉपर्टी हैं, जो ब्रेक की जगह को चुनने पर असर डालती हैं.

लेआउट के दौरान, बिना किसी रुकावट के ब्रेक की जानकारी वाले सेक्शन को सही तरीके से लागू करने के लिए, हमें संभावित ब्रेकपॉइंट का ट्रैक रखना होगा. इस रिकॉर्ड का मतलब है कि अगर किसी ऐसे पॉइंट पर स्पेस खत्म हो जाता है जहां ब्रेक लेने से जुड़े अनुरोधों (उदाहरण के लिए, break-before:avoid या orphans:7) का उल्लंघन होता है, तो हम पिछले सबसे अच्छे ब्रेकपॉइंट का इस्तेमाल कर सकते हैं. हर ब्रेकपॉइंट को एक स्कोर दिया जाता है. यह स्कोर, "सिर्फ़ आखिरी विकल्प के तौर पर ऐसा करें" से लेकर "ब्रेक लेने के लिए सबसे सही जगह" तक होता है. अगर किसी ब्रेक की जगह को "बेहतरीन" के तौर पर स्कोर किया जाता है, तो इसका मतलब है कि उस जगह पर ब्रेक लेने पर, ब्रेक के नियमों का उल्लंघन नहीं होगा. अगर हमें यह स्कोर ठीक उसी जगह पर मिलता है जहां स्पेस खत्म हो जाता है, तो बेहतर जगह खोजने की ज़रूरत नहीं है. अगर स्कोर "लास्ट-रिज़ॉर्ट" है, तो ब्रेकपॉइंट मान्य भी नहीं है. हालांकि, अगर हमें कोई बेहतर विकल्प नहीं मिलता है, तो हम फ़्रैगमेंटर ओवरफ़्लो से बचने के लिए, वहां ब्रेक कर सकते हैं.

मान्य ब्रेकपॉइंट आम तौर पर सिर्फ़ सिबलिंग (लाइन बॉक्स या ब्लॉक) के बीच होते हैं. उदाहरण के लिए, पैरंट और उसके पहले चाइल्ड के बीच नहीं. हालांकि, क्लास C ब्रेकपॉइंट एक अपवाद हैं, लेकिन हमें यहां उनकी चर्चा करने की ज़रूरत नहीं है. उदाहरण के लिए, break-before:avoid वाले ब्लॉक सिबलिंग से पहले एक मान्य ब्रेकपॉइंट है, लेकिन यह "परफ़ेक्ट" और "लास्ट-रिज़ॉर्ट" के बीच कहीं है.

लेआउट के दौरान, हम NGEarlyBreak नाम के स्ट्रक्चर में, अब तक मिले सबसे अच्छे ब्रेकपॉइंट का ट्रैक रखते हैं. ब्लॉक नोड के पहले या अंदर या किसी लाइन (ब्लॉक कंटेनर लाइन या फ़्लेक्स लाइन) के पहले, ब्रेक होने की संभावना होती है. अगर सबसे अच्छा ब्रेकपॉइंट, किसी ऐसी जगह पर है जहां पहले हमने स्पेस खत्म होने पर, आगे बढ़ने से पहले रुकने का विकल्प नहीं चुना था, तो हम NGEarlyBreak ऑब्जेक्ट की चेन या पाथ बना सकते हैं. यहां एक उदाहरण दिया गया है:

देखें

इस मामले में, #second से ठीक पहले जगह खत्म हो जाती है. हालांकि, इसमें "break-before:avoid" है, जिसे ब्रेक की जगह का स्कोर "break avoid का उल्लंघन" मिलता है. उस समय हमारे पास "#outer के अंदर > #middle के अंदर > #inner के अंदर > "लाइन 3" से पहले"' की NGEarlyBreak चेन है, जिसमें "perfect" है. इसलिए, हम वहां ब्रेक लेना चाहेंगे. इसलिए, हमें #outer की शुरुआत से लेआउट को वापस लाकर फिर से चलाना होगा. साथ ही, इस बार हमें NGEarlyBreak को पास करना होगा, ताकि हम #inner में "लाइन 3" से पहले ब्रेक कर सकें. (हम "लाइन 3" से पहले ब्रेक करते हैं, ताकि बाकी चार लाइनें अगले फ़्रैगमेंटर में जा सकें. साथ ही, widows:4 को सम्मान दिया जा सके.)

एल्गोरिदम को इस तरह से डिज़ाइन किया गया है कि वह हमेशा सबसे अच्छे ब्रेकपॉइंट पर रुक जाए. यह स्पेसिफ़िकेशन में बताया गया है. अगर सभी नियमों को पूरा नहीं किया जा सकता, तो एल्गोरिदम सही क्रम में नियमों को हटाकर, सबसे अच्छे ब्रेकपॉइंट पर रुक जाता है. ध्यान दें कि हमें हर फ़्रैगमेंटेशन फ़्लो के लिए, ज़्यादा से ज़्यादा एक बार ही री-लेआउट करना पड़ता है. दूसरे लेआउट पास के दौरान, लेआउट एल्गोरिदम को ब्रेक की सबसे अच्छी जगह पहले ही भेज दी जाती है. यह ब्रेक की वह जगह होती है जिसे पहले लेआउट पास में खोजा गया था और उस राउंड में लेआउट आउटपुट के हिस्से के तौर पर दिया गया था. दूसरे लेआउट पास में, हम तब तक लेआउट नहीं कर रहे हैं, जब तक कि हमारे पास जगह खत्म नहीं हो जाती. असल में, हमारे पास जगह खत्म नहीं होनी चाहिए (यह असल में एक गड़बड़ी होगी), क्योंकि हमें ब्रेक को जल्दी डालने के लिए एक बेहतरीन जगह (जहां तक उपलब्ध थी) दी गई है, ताकि ब्रेक के किसी भी नियम का गलत तरीके से उल्लंघन न हो. इसलिए, हम उस बिंदु तक ही प्लान बनाते हैं और फिर ब्रेक लेते हैं.

इस बात का ध्यान रखें कि अगर फ़्रैगमेंटेनर ओवरफ़्लो से बचने में मदद मिलती है, तो कभी-कभी हमें ब्रेक से बचने के कुछ अनुरोधों का उल्लंघन करना पड़ता है. उदाहरण के लिए:

देखें

यहां #second से ठीक पहले स्पेस खत्म हो गया है, लेकिन इसमें "break-before:avoid" है. इसका अनुवाद, पिछले उदाहरण की तरह ही "break avoid का उल्लंघन" के तौर पर किया गया है. हमारे पास "अनाथ और विधवाओं का उल्लंघन" (#first के अंदर > "लाइन 2" से पहले) वाला NGEarlyBreak भी है, जो अब भी सही नहीं है, लेकिन "ब्रेक से बचने का उल्लंघन" से बेहतर है. इसलिए, हम "लाइन 2" से पहले ब्रेक लगा देंगे. इससे, अनाथ / विधवा लाइन के अनुरोध का उल्लंघन होगा. स्पेसिफ़िकेशन में इस बारे में 4.4 में बताया गया है. अनफ़ोर्स्ड ब्रेक, जहां यह तय किया जाता है कि फ़्रैगमेंटर ओवरफ़्लो से बचने के लिए, हमारे पास ज़रूरत के मुताबिक ब्रेकपॉइंट न होने पर, ब्रेकिंग के कौनसे नियमों को पहले अनदेखा किया जाए.

नतीजा

LayoutNG ब्लॉक फ़्रैगमेंटेशन प्रोजेक्ट का फ़ंक्शनल लक्ष्य, लेगसी इंजन के साथ काम करने वाली सभी चीज़ों को LayoutNG-आर्किटेक्चर के साथ काम करने वाला बनाना था. साथ ही, बग ठीक करने के अलावा, इसमें कम से कम बदलाव करना था. हालांकि, ब्रेक से बचने के लिए बेहतर सहायता (उदाहरण के लिए, break-before:avoid) को शामिल नहीं किया गया है. इसकी वजह यह है कि यह फ़्रैगमेंटेशन इंजन का मुख्य हिस्सा है. इसलिए, इसे शुरू से ही शामिल करना ज़रूरी था. बाद में इसे जोड़ने का मतलब होगा कि इसे फिर से लिखना होगा.

LayoutNG ब्लॉक फ़्रैगमेंटेशन की प्रोसेस पूरी हो चुकी है. इसलिए, हम नई सुविधाएं जोड़ने पर काम कर सकते हैं. जैसे, प्रिंट करते समय अलग-अलग पेज साइज़ का इस्तेमाल करना, @page प्रिंट करते समय मार्जिन बॉक्स का इस्तेमाल करना, box-decoration-break:clone वगैरह. आम तौर पर, LayoutNG की तरह ही हमें उम्मीद है कि समय के साथ, नए सिस्टम में गड़बड़ी की दर और उसे मैनेज करने में लगने वाला समय काफ़ी कम हो जाएगा.

आभार

  • Una Kravets को, बेहतरीन "हाथ से बनाए गए स्क्रीनशॉट" के लिए.
  • क्रिस हैरलसन, प्रूफ़रीडिंग, सुझाव, और राय देने के लिए.
  • सुझाव, राय या शिकायत के लिए Philip Jägenstedt से संपर्क करें.
  • बदलाव करने और एक से ज़्यादा कॉलम वाले पहले उदाहरण के लिए, रेचल एंड्रयू.