HTML को PDF में बदलना कागज़ पर आसान लगता है। आप एक पॉलिश किया हुआ टेम्पलेट बनाते हैं, उसमें अपना डेटा डालते हैं, और एक ऐसे दस्तावेज़ की उम्मीद करते हैं जो वेब पेज की हूबहू पिक्सेल-दर-पिक्सेल नकल हो। वास्तविकता में, यह पाइपलाइन अक्सर क्रैश, गायब होते ग्लिफ़ (glyphs) और विज़ुअल करप्शन के खिलाफ एक दैनिक संघर्ष बन जाती है। हाल ही में एक प्रोजेक्ट के दौरान, तीन समस्याएँ बार-बार सामने आती रहीं: कुछ खास SVG ग्राफिक्स आने पर iText पूरी तरह से क्रैश हो जाता था, इमोजी गायब होकर खाली सफेद चौकोर बक्सों में बदल जाते थे, और सूक्ष्म पारदर्शी (transparent) बैकग्राउंड ठोस काले ब्लॉकों में बदल जाते थे। प्रत्येक विफलता का एक अलग कारण था, और इन तीनों को ठीक करने के लिए इस बात पर पुनर्विचार करने की आवश्यकता थी कि PDF इंजन द्वारा देखे जाने से पहले एप्लिकेशन कंटेंट को कैसे तैयार करता है।
जब SVG पाइपलाइन को तोड़ देता है
iText सुविधा के लिए एक आंतरिक SVG रेंडरर के साथ आता है, लेकिन यह एकीकरण एक गंभीर कमजोरी को छिपाए रखता है। जब किसी SVG में जटिल पाथ (paths), भारी CSS स्टाइलिंग, या कुछ खास कोऑर्डिनेट ट्रांसफॉर्मेशन होते हैं, तो एम्बेडेड पार्सर कोई साफ-सुथरा एक्सेप्शन (exception) देकर आगे नहीं बढ़ता। बल्कि, वह पूरी तरह से क्रैश हो जाता है। ये पूर्ण सिस्टम क्रैश होते हैं जो बिना किसी चेतावनी के PDF जनरेशन थ्रेड को खत्म कर देते हैं, जिससे आपके पास केवल एक अधूरी फ़ाइल बचती है और एक स्टैक ट्रेस (stack trace) मिलता है जो वेक्टर पार्सर के भीतर कहीं गहराई की ओर इशारा करता है।
इसका भरोसेमंद समाधान यह है कि iText से SVG रेंडर करने के लिए कहना ही बंद कर दें। इसके बजाय, उस काम को स्टैंडअलोन मोड में चल रहे Apache Batik को सौंप दें। Batik उन्हीं जटिल पाथ और CSS नियमों को बिना किसी अस्थिरता के संभाल लेता है, और इसे अलग रखने से आपका PDF इंजन ग्राफिक्स से जुड़ी अस्थिरता से सुरक्षित रहता है। वर्कफ़्लो सीधा है: दस्तावेज़ असेंबली शुरू होने से पहले, PNG डेटा URL बनाने के लिए SVG को Batik के माध्यम से चलाएं। कच्चे वेक्टर मार्कअप के बजाय उस रास्टर इमेज को iText में पास करें। स्टैंडअलोन Batik, किसी बड़ी लाइब्रेरी के भीतर बंडल और फ्रीज किए गए एम्बेडेड रेंडरर की तुलना में SVG स्पेसिफिकेशन का अधिक बारीकी से पालन करता है, और इस अलगाव (isolation) का मतलब है कि एक खराब ग्राफ़िक पूरे दस्तावेज़ रूपांतरण को ठप नहीं कर सकता।
एक छोटा सा विवरण यह तय करता है कि आपका चार्ट पेशेवर दिखेगा या किसी बग रिपोर्ट जैसा। SVG अपने कोऑर्डिनेट सिस्टम और स्केलिंग व्यवहार को परिभाषित करने के लिए viewBox एट्रिब्यूट पर निर्भर करता है। यदि आपका कन्वर्जन कोड viewBox को अनदेखा करता है, तो एक पूरी तरह से वैध चार्ट एक अपठनीय बिंदु में सिकुड़ सकता है या एक विकृत ढेर में फैल सकता है। इस एट्रिब्यूट को स्पष्ट रूप से पार्स करें और उन आयामों (dimensions) को अपने आउटपुट आकार के साथ मैप करें। इस चरण को छोड़ने से लेआउट की समस्या को डीबग करने में घंटों बर्बाद हो जाते हैं, जिसका रेंडरिंग गुणवत्ता से कोई लेना-देना नहीं होता, बल्कि यह पूरी तरह से एक गायब कोऑर्डिनेट डिक्लेरेशन के कारण होता है।
अदृश्य स्याही की समस्या
जहाँ इमोजी होने चाहिए वहाँ खाली चौकोर बक्से एक सरल कहानी बताते हैं: वर्तमान फ़ॉन्ट वह भाषा नहीं जानता। Helvetica और अन्य मानक PDF फ़ॉन्ट इमोजी के व्यापक उपयोग से पहले के हैं। उनमें इमोजी यूनिकोड रेंज के लिए ग्लिफ़ (glyphs) शामिल नहीं हैं, इसलिए जब iText उन कोड पॉइंट्स का सामना करता है, तो वह कुछ भी रेंडर नहीं करता और आगे बढ़ जाता है। परिणाम एक ऐसा दस्तावेज़ होता है जो खाली बक्सों से भरा होता है, जिससे सोशल सेंटीमेंट रिपोर्ट या यूजर फीडबैक एक्सपोर्ट टूटे हुए दिखाई देते हैं।
आप इस कमी को पूरा करने के लिए क्लाइंट ऑपरेटिंग सिस्टम पर भरोसा नहीं कर सकते। PDF अपने स्वयं के फ़ॉन्ट रिसोर्स लेकर चलते हैं, और ब्राउज़र में जो सही दिखता है, उसका आपके सिस्टम फ़ॉन्ट से अलग होने के बाद कोई मतलब नहीं रह जाता। समाधान एक स्पष्ट फ़ॉन्ट राउटिंग लेयर बनाना है। Symbola जैसा एक समर्पित इमोजी-सक्षम फ़ॉन्ट रजिस्टर करें, जो इमोजी यूनिकोड ब्लॉक्स को कवर करने वाले मोनोक्रोम सिंबल प्रदान करता है। एक ब्लैक-एंड-व्हाइट दिल या चेतावनी का प्रतीक एक चमकदार रंगीन ग्लिफ़ सेट जैसी चमक तो नहीं दे सकता, लेकिन यह अर्थ स्पष्ट करता है। एक खाली आयत विफलता का संकेत देता है। फुल-कलर इमोजी फ़ॉन्ट्स को PDF व्यूअर्स के भीतर लगातार रेंडर करना कठिन बना रहता है, और कलर सपोर्ट के पीछे भागने से अक्सर समाधान से अधिक कम्पैटिबिलिटी की समस्याएँ पैदा होती हैं।
iText लाइन ब्रेकिंग के माध्यम से एक दूसरी, अधिक गंभीर समस्या पैदा करता है। लाइब्रेरी इमोजी सरोगेट पेयर्स (surrogate pairs) को गलत सीमा पर विभाजित कर सकती है, जिससे एक एकल वर्ण दो अमान्य हिस्सों में टूट जाता है। जब ऐसा होता है, तो टेक्स्ट स्ट्रीम करप्ट हो जाती है और जहाँ एक एकल ग्लिफ़ होना चाहिए, वहाँ आपको अपठनीय टुकड़े मिलते हैं। इसे रोकने के लिए, एक कस्टम ISplitCharacter लागू करें जो सरोगेट पेयर्स को पहचान सके और उन्हें परमाणु इकाइयों (atomic units) के रूप में माने। यह लेआउट इंजन को इमोजी के बीच में लाइन ब्रेक डालने से रोकता है और टेक्स्ट की अखंडता को बनाए रखता है।
जब पारदर्शिता काली हो जाती है
एक सॉफ्ट rgba बैकग्राउंड या लेयर्ड fill-opacity प्रभाव वाला SVG ब्राउज़र में परिष्कृत दिखता है। उसी मार्कअप को iText में डालें, और पारदर्शिता अक्सर एक ठोस काले आयत में बदल जाती है। इंजन CSS कलर फंक्शन और ओपेसिटी एट्रिब्यूट्स को गलत तरीके से संभालता है, और ओपेसिटी की जगह फुल-डेंसिटी इंक का उपयोग कर देता है।
कन्वर्टर तक पहुँचने से पहले SVG को प्री-प्रोसेस करना ही एकमात्र भरोसेमंद बचाव है। किसी भी ऐसे एलिमेंट को हटा दें या बदल दें जो अल्फा ब्लेंडिंग (alpha blending) पर निर्भर करता है। rgba() वैल्यूज़ को सॉलिड rgb() रंगों में बदलें। यदि आपको ओपेसिटी (opacity) का कुछ अंश बनाए रखना ही है, तो वैल्यूज़ को CSS शॉर्टहैंड से हटाकर स्टैंडर्ड ओपेसिटी एट्रिब्यूट्स में ले जाएँ, हालाँकि पारदर्शिता (transparency) को पूरी तरह से हटा देना ही सबसे सुरक्षित विकल्प है। ये बदलाव वेब डिज़ाइन के लिए एक कदम पीछे की ओर महसूस हो सकते हैं, लेकिन PDF एक अलग इमेजिंग मॉडल का उपयोग करता है जो आधुनिक CSS पारदर्शिता से भी पुराना है। यह फॉर्मेट ठोस रंग वैल्यूज़ की अपेक्षा करता है, और इसे अस्पष्ट वैल्यूज़ देना आपदा को निमंत्रण देना है।
जब आप मार्कअप को सैनिटाइज़ कर रहे हों, तो दोबारा जाँच लें कि प्रत्येक SVG में उचित xmlns नेमस्पेस डिक्लेरेशन है। जनरेट किया गया HTML और टेम्पलेट इंजन अक्सर मिनिफिकेशन या DOM सीरियलाइजेशन के दौरान नेमस्पेस एट्रिब्यूट्स को हटा देते हैं। उस नेमस्पेस के बिना, SVG पार्सर एलिमेंट्स की गलत पहचान कर सकता है या बिना किसी सूचना के विफल हो सकता है, जिससे या तो पार्सर एरर आता है या फिर खराब वेक्टर डेटा बनता है जो पेज तक कभी नहीं पहुँच पाता। यह एक बुनियादी जाँच है जिसमें कुछ ही सेकंड लगते हैं और घंटों की बचत होती है।
एक टेम्पलेट, दो दुनियाएँ
सबसे खराब दीर्घकालिक समाधान ब्राउज़र और PDF के लिए अलग-अलग HTML टेम्पलेट बनाए रखना है। लेबल इधर-उधर हो जाते हैं, मार्जिन बदल जाते हैं, और जल्द ही एक्सपोर्ट की गई रिपोर्ट डैशबोर्ड से मेल नहीं खाती। एक बेहतर आर्किटेक्चर एक ही टेम्पलेट पर निर्भर करता है और एक सिंगल फ्लैग के साथ रेंडरिंग लॉजिक को अलग करता है, जैसे कि context.isForPdf()।
जब वह फ्लैग false होता है, तो टेम्पलेट पूरा ब्राउज़र अनुभव प्रदान करता है। यह इन्फिनिट ज़ूम के लिए नेटिव SVG, आधुनिक CSS, और ब्राउज़र द्वारा समर्थित किसी भी कलर एसेट्स को सर्व करता है। जब फ्लैग true होता है, तो वही टेम्पलेट SVG एसेट्स को प्री-रेंडर किए गए PNGs से बदल देता है, इमोजी-सेफ फ़ॉन्ट स्टैक को सक्रिय करता है, और किसी भी असमर्थित पारदर्शिता प्रभावों को हटा देता है। टेक्स्ट और स्ट्रक्चर अपरिवर्तित रहते हैं; केवल एसेट पाइपलाइन और स्टाइलिंग नियम ही लक्षित माध्यम के अनुसार ढलते हैं।
यह डुअल-पाथ अप्रोच कोडबेस को सटीक रखती है। आप एक ही जगह कंटेंट अपडेट करते हैं, और रूटिंग लेयर स्क्रीन और पेपर के बीच के यांत्रिक अंतरों को संभाल लेती है। यह टेस्टिंग को भी सरल बनाता है। आप फुल डेवलपर टूल्स के साथ ब्राउज़र में टेम्पलेट लॉजिक को सत्यापित कर सकते हैं, फिर PDF फ्लैग को ट्रिगर कर सकते हैं और पुष्टि कर सकते हैं कि वही डेटा कन्वर्टर को क्रैश किए बिना एक साफ दस्तावेज़ तैयार करता है।
PDF जनरेशन के बारे में कड़वा सच
PDF कभी भी ब्राउज़र की तरह व्यवहार नहीं करेगा। रेंडरिंग मॉडल मौलिक रूप से भिन्न होते हैं, और iText जैसी लाइब्रेरीज़ स्पीड, फ़ाइल साइज़ और स्पेसिफिकेशन अनुपालन के बीच जानबूझकर समझौते करती हैं। सफलता इंजन से लड़ने और सबसे अच्छे की उम्मीद करने से नहीं मिलती। यह सीमाओं को जल्दी स्वीकार करने और उनके इर्द-गिर्द पाइपलाइन डिज़ाइन करने से आती है।
PDF स्टेज से पहले अपने वेक्टर्स को कन्वर्ट करें। अपने फ़ॉन्ट्स को स्पष्ट रूप से रूट करें ताकि प्रत्येक ग्लिफ (glyph) का एक फ़ॉलबैक हो। पारदर्शिता को हटाकर सॉलिड रंगों का उपयोग करें। अपने टेम्पलेट्स को वह कॉन्टेक्स्ट दें जिसकी उन्हें यह जानने के लिए आवश्यकता है कि वे किस दुनिया के लिए रेंडर कर रहे हैं। इसे निरंतरता के साथ करें, और आपके दस्तावेज़ रेंडरर से लड़ना बंद कर देंगे और बिल्कुल वैसे ही दिखने लगेंगे जैसा आपने चाहा था।
