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

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

Morten Stenshorne
Morten Stenshorne

मैं मॉर्टन स्टेंसहॉर्न, Google में ब्लिंक रेंडरिंग टीम में लेआउट इंजीनियर हूं. मैं 2000 के दशक की शुरुआत से ब्राउज़र इंजन डेवलपमेंट का काम कर रहा हूं. इसमें मुझे काफ़ी आनंद आया. जैसे, Presto इंजन (Opera 12 और उससे पहले के वर्शन) में aसिड2 टेस्ट पास करने में मदद करना और Presto में टेबल लेआउट को ठीक करने के लिए अन्य ब्राउज़र की रिवर्स इंजीनियरिंग. मैंने ब्लॉक फ़्रैगमेंट की प्रोसेस पर, उन सालों से ज़्यादा समय बिताया है जिनके बारे में मुझे नहीं जानना है. खास तौर पर, Presto, WebKit, और Blink में मल्टीकॉल. पिछले कुछ सालों से Google में मैं मुख्य तौर पर LayoutNG में ब्लॉक फ़्रैगमेंटेशन सहायता को जोड़ने पर ध्यान दे रही हूं. ब्लॉक फ़्रैगमेंटेशन को लागू करने की पूरी जानकारी पाने के लिए मेरे साथ जुड़ें, क्योंकि यह आखिरी बार है जब मैंने ब्लॉक फ़्रैगमेंटेशन को लागू किया है. :)

ब्लॉक फ़्रैगमेंटेशन क्या है?

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

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

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

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

LayoutNGBlockFunnelation, LayoutNG के लिए फ़्रैगमेंटेशन इंजन का रीराइट है. यह कई सालों तक काम करने के बाद, इस साल की शुरुआत में Chrome 102 में शुरुआती हिस्से को भेजा गया. इससे लंबे समय से चली आ रही उन समस्याओं को ठीक किया गया है जिन्हें हमारे "लेगसी" इंजन में ठीक नहीं किया जा सकता था. डेटा स्ट्रक्चर की बात करें, तो यह प्री-एनजी डेटा स्ट्रक्चर के कई प्री-एनजी डेटा स्ट्रक्चर को एनजी फ़्रैगमेंट से बदल देता है. इन फ़्रैगमेंट को फ़्रैगमेंट ट्री में सीधे तौर पर दिखाया जाता है.

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

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

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

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

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

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

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

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

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

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

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

लेगसी इंजन फ़ॉलबैक की पहचान और हैंडलिंग

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

प्री-पेंट ट्री वॉक

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

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

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

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

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

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

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

पेज पर अचानक दिखने वाली जानकारी को एक कॉलम में दिखाने के लिए, पेजिनेशन स्ट्रेट का इस्तेमाल किया गया है. वहीं, स्क्रीन पर दिखाए गए हिस्से को तीन कॉलम के तौर पर दिखाया गया है.

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

यहां टेक्स्ट-शैडो का एक सामान्य उदाहरण दिया गया है:

पुराना इंजन इसे ठीक से हैंडल नहीं करता है:

टेक्स्ट की क्लिप को दूसरे कॉलम में रखा गया.

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

यह कुछ ऐसा दिखना चाहिए और NG के साथ यह ऐसा दिखता है:

टेक्स्ट के दो कॉलम, जिनमें परछाई सही तरीके से दिख रही हैं.

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

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

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

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

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

ALT_TEXT_HERE

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

खास जानकारी

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

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

पढ़ने के लिए धन्यवाद!

स्वीकार हैं

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