HTML থেকে PDF-এ রূপান্তর করা কাগজে-কলমে সহজ মনে হয়। আপনি একটি পরিমার্জিত টেমপ্লেট তৈরি করেন, তাতে আপনার ডেটা যুক্ত করেন এবং আশা করেন যে ডকুমেন্টটি ওয়েব পেজটির মতো হুবহু পিক্সেল বাই পিক্সেল দেখাবে। বাস্তবে, এই পাইপলাইনটি প্রায়শই ক্র্যাশ, মিসিং গ্লিফ (glyphs) এবং ভিজ্যুয়াল করাপশনের বিরুদ্ধে একটি দৈনন্দিন লড়াইয়ে পরিণত হয়। একটি সাম্প্রতিক প্রজেক্ট চলাকালীন তিনটি সমস্যা বারবার ফিরে আসছিল: নির্দিষ্ট কিছু SVG গ্রাফিক্সের ক্ষেত্রে iText সম্পূর্ণভাবে ক্র্যাশ করত, ইমোজিগুলো সাদা খালি চারকোণা ঘরে পরিণত হতো এবং সূক্ষ্ম ট্রান্সপারেন্ট ব্যাকগ্রাউন্ডগুলো অস্বচ্ছ কালো ব্লকে পরিণত হতো। প্রতিটি ব্যর্থতার পেছনে একটি সুনির্দিষ্ট কারণ ছিল এবং এই তিনটি সমস্যা সমাধানের জন্য PDF ইঞ্জিনটি কন্টেন্ট দেখার আগেই অ্যাপ্লিকেশনটি কীভাবে তা প্রস্তুত করে, তা নিয়ে নতুন করে ভাবার প্রয়োজন ছিল।

যখন SVG পাইপলাইন ভেঙে ফেলে

সুবিধার জন্য iText-এর সাথে একটি ইন্টারনাল SVG রেন্ডারার থাকে, কিন্তু এই ইন্টিগ্রেশন একটি মারাত্মক দুর্বলতা লুকিয়ে রাখে। যখন কোনো SVG-তে জটিল পাথ (paths), ভারী CSS স্টাইলিং বা নির্দিষ্ট কোঅর্ডিনেট ট্রান্সফরমেশন থাকে, তখন এমবেডেড পার্সারটি কোনো গোছানো এরর (exception) না দেখিয়ে কাজ থামিয়ে দেয়। এটি কার্যত বিস্ফোরিত হয়। এগুলো হলো সম্পূর্ণ সিস্টেম ক্র্যাশ যা কোনো সতর্কতা ছাড়াই PDF জেনারেশন থ্রেডটিকে বন্ধ করে দেয়, ফলে আপনার কাছে একটি আংশিক ফাইল এবং ভেক্টর পার্সারের গভীরে থাকা একটি স্ট্যাক ট্রেস (stack trace) অবশিষ্ট থাকে।

এর নির্ভরযোগ্য সমাধান হলো iText-কে SVG রেন্ডার করার কাজ থেকে পুরোপুরি সরিয়ে নেওয়া। পরিবর্তে, সেই কাজটি স্ট্যান্ডঅ্যালোন মোডে চলা Apache Batik-এর ওপর ছেড়ে দিন। Batik একই রকম জটিল পাথ এবং CSS রুলগুলো কোনো ভঙ্গুরতা ছাড়াই সামলাতে পারে, এবং এটিকে আলাদা রাখলে আপনার PDF ইঞ্জিন গ্রাফিক্স সংক্রান্ত অস্থিরতা থেকে সুরক্ষিত থাকে। কাজের ধারাটি খুবই সহজ: ডকুমেন্ট অ্যাসেম্বলি শুরু করার আগে, একটি PNG ডেটা URL তৈরি করতে SVG-টিকে Batik-এর মাধ্যমে চালান। র (raw) ভেক্টর মার্কআপের পরিবর্তে সেই রাস্টার ইমেজটি iText-এ পাঠান। একটি বড় লাইব্রেরির ভেতরে থাকা এবং স্থির হয়ে যাওয়া এমবেডেড রেন্ডারারের তুলনায় স্ট্যান্ডঅ্যালোন Batik SVG স্পেসিফিকেশনকে আরও নিবিড়ভাবে অনুসরণ করে, এবং এই বিচ্ছিন্নতা নিশ্চিত করে যে একটি ত্রুটিপূর্ণ গ্রাফিক্স পুরো ডকুমেন্ট কনভার্সন প্রক্রিয়াকে অচল করে দিতে পারবে না।

একটি ছোট বিষয় নির্ধারণ করে দেয় যে আপনার চার্টটি পেশাদার দেখাবে নাকি একটি বাগ রিপোর্টের মতো। SVG তার কোঅর্ডিনেট সিস্টেম এবং স্কেলিং আচরণ নির্ধারণ করতে viewBox অ্যাট্রিবিউটের ওপর নির্ভর করে। যদি আপনার কনভার্সন কোড viewBox-কে উপেক্ষা করে, তবে একটি নিখুঁত চার্টও পড়া অসম্ভব ক্ষুদ্র বিন্দুতে পরিণত হতে পারে বা বিকৃত হয়ে যেতে পারে। এই অ্যাট্রিবিউটটি স্পষ্টভাবে পার্স (parse) করুন এবং সেই ডাইমেনশনগুলোকে আপনার আউটপুট সাইজের সাথে ম্যাপ করুন। এই ধাপটি বাদ দিলে লেআউট সংক্রান্ত সমস্যা ডিবাগ করতে ঘণ্টার পর ঘণ্টা নষ্ট হবে, যার রেন্ডারিং কোয়ালিটির সাথে কোনো সম্পর্ক নেই বরং এটি মূলত একটি মিসিং কোঅর্ডিনেট ডিক্লারেশনের কারণে ঘটে।

অদৃশ্য কালির সমস্যা

যেখানে ইমোজি থাকার কথা সেখানে খালি চারকোণা ঘর থাকা একটি সহজ গল্প বলে: বর্তমান ফন্টটি সেই ভাষা বোঝে না। Helvetica এবং অন্যান্য স্ট্যান্ডার্ড PDF ফন্টগুলো ইমোজির ব্যাপক ব্যবহারের অনেক আগের। এগুলোতে ইমোজি ইউনিকোড রেঞ্জের জন্য কোনো গ্লিফ (glyphs) নেই, তাই iText যখন সেই কোড পয়েন্টগুলোর সম্মুখীন হয়, তখন এটি কিছুই রেন্ডার করে না এবং পরবর্তী ধাপে চলে যায়। এর ফলে ডকুমেন্টটি খালি বক্স দিয়ে পূর্ণ হয়ে যায়, যা সোশ্যাল সেন্টিমেন্ট রিপোর্ট বা ইউজার ফিডব্যাক এক্সপোর্টগুলোকে ত্রুটিপূর্ণ করে তোলে।

এই ঘাটতি পূরণের জন্য আপনি ক্লায়েন্ট অপারেটিং সিস্টেমের ওপর নির্ভর করতে পারেন না। PDF-এর নিজস্ব ফন্ট রিসোর্স থাকে, এবং ব্রাউজারে যা সঠিক দেখায়, ফাইলটি সিস্টেম ফন্ট থেকে বিচ্ছিন্ন হয়ে গেলে তার কোনো মূল্য থাকে না। সমাধান হলো একটি সুনির্দিষ্ট ফন্ট রাউটিং লেয়ার তৈরি করা। Symbola-এর মতো একটি ডেডিকেটেড ইমোজি-সক্ষম ফন্ট রেজিস্টার করুন, যা ইমোজি ইউনিকোড ব্লকগুলোকে কভার করে এমন মনোক্রোম সিম্বল প্রদান করে। একটি সাদা-কালো হার্ট বা সতর্কবার্তা চিহ্ন হয়তো রঙিন গ্লিফ সেটের মতো চাকচিক্যময় হবে না, কিন্তু এটি অর্থ প্রকাশ করতে সক্ষম। একটি খালি আয়তক্ষেত্র কেবল ব্যর্থতা প্রকাশ করে। PDF ভিউয়ারের ভেতরে ফুল-কালার ইমোজি ফন্টগুলো ধারাবাহিকভাবে রেন্ডার করা কঠিন, এবং কালার সাপোর্টের পেছনে ছোটা প্রায়শই সমস্যার সমাধানের চেয়ে সামঞ্জস্যতা (compatibility) সংক্রান্ত আরও বেশি সমস্যা তৈরি করে।

iText লাইন ব্রেকিংয়ের মাধ্যমে দ্বিতীয় এবং আরও জটিল একটি সমস্যা তৈরি করে। লাইব্রেরিটি ইমোজি সারোগেট পেয়ারগুলোকে (surrogate pairs) ভুল সীমানায় বিভক্ত করে ফেলতে পারে, ফলে একটি একক ক্যারেক্টার দুটি অবৈধ অংশে বিভক্ত হয়ে যায়। যখন এটি ঘটে, টেক্সট স্ট্রিমটি করাপ্ট হয়ে যায় এবং যেখানে একটি একক গ্লিফ থাকার কথা সেখানে আপনি অপাঠ্য খণ্ডাংশ দেখতে পান। এটি প্রতিরোধ করতে একটি কাস্টম ISplitCharacter ইমপ্লিমেন্ট করুন যা সারোগেট পেয়ারগুলোকে চিনতে পারে এবং সেগুলোকে একটি অবিভাজ্য ইউনিট (atomic unit) হিসেবে বিবেচনা করে। এটি লেআউট ইঞ্জিনকে ইমোজির মাঝখানে লাইন ব্রেক দেওয়া থেকে বিরত রাখে এবং টেক্সটের অখণ্ডতা বজায় রাখে।

যখন ট্রান্সপারেন্সি কালো হয়ে যায়

একটি সফট rgba ব্যাকগ্রাউন্ড বা লেয়ার্ড fill-opacity ইফেক্টযুক্ত SVG ব্রাউজারে দেখতে চমৎকার লাগে। কিন্তু সেই একই মার্কআপ iText-এ দিলে ট্রান্সপারেন্সি প্রায়শই একটি নিরেট কালো আয়তক্ষেত্রে পরিণত হয়। ইঞ্জিনটি CSS কালার ফাংশন এবং অপাসিটি অ্যাট্রিবিউটগুলো ভুলভাবে হ্যান্ডেল করে এবং অপাসিটির পরিবর্তে ফুল-ডেনসিটি কালি ব্যবহার করে।

কনভার্টারে পৌঁছানোর আগেই SVG প্রি-প্রসেসিং করা হলো একমাত্র নির্ভরযোগ্য প্রতিরক্ষা। আলফা ব্লেন্ডিং-এর ওপর নির্ভরশীল যেকোনো এলিমেন্ট সরিয়ে ফেলুন বা পরিবর্তন করুন। rgba() ভ্যালুগুলোকে সলিড rgb() কালারে রূপান্তর করুন। যদি আপনাকে অপাসিটির (opacity) কোনো ধারণা বজায় রাখতে হয়, তবে ভ্যালুগুলোকে CSS shorthand থেকে সরিয়ে স্ট্যান্ডার্ড opacity অ্যাট্রিবিউটে নিয়ে যান, যদিও ট্রান্সপারেন্সি (transparency) পুরোপুরি সরিয়ে ফেলাই সবচেয়ে নিরাপদ। এই পরিবর্তনগুলো ওয়েব ডিজাইনের ক্ষেত্রে অনেকটা পিছিয়ে যাওয়ার মতো মনে হতে পারে, কিন্তু PDF একটি ভিন্ন ইমেজিং মডেল ব্যবহার করে যা আধুনিক CSS ট্রান্সপারেন্সির অনেক আগের। এই ফরম্যাটটি সুনির্দিষ্ট কালার ভ্যালু আশা করে, আর অস্পষ্ট ভ্যালু দিলে তা বিপর্যয় ডেকে আনতে পারে।

মার্কআপ স্যানিটাইজ করার সময় নিশ্চিত করুন যে প্রতিটি SVG-তে সঠিক xmlns নেমস্পেস ডিক্লারেশন রয়েছে। জেনারেট করা HTML এবং টেমপ্লেট ইঞ্জিনগুলো প্রায়ই মিনফিকেশন (minification) বা DOM সিরিয়ালাইজেশনের সময় নেমস্পেস অ্যাট্রিবিউটগুলো বাদ দিয়ে দেয়। সেই নেমস্পেস ছাড়া, SVG পার্সার এলিমেন্টগুলোকে ভুলভাবে শনাক্ত করতে পারে বা কোনো ত্রুটি ছাড়াই কাজ বন্ধ করে দিতে পারে, যার ফলে পার্সার এরর অথবা ত্রুটিপূর্ণ ভেক্টর ডেটা তৈরি হতে পারে যা পেজে পৌঁছাতে পারে না। এটি একটি সাধারণ পরীক্ষা যা মাত্র কয়েক সেকেন্ড সময় নেয় কিন্তু ঘণ্টার পর ঘণ্টা সময় বাঁচিয়ে দেয়।

একটি টেমপ্লেট, দুটি জগত

দীর্ঘমেয়াদী সবচেয়ে খারাপ সমাধান হলো ব্রাউজার এবং PDF-এর জন্য আলাদা HTML টেমপ্লেট বজায় রাখা। লেবেলগুলো সরে যায়, মার্জিন পরিবর্তিত হয় এবং শীঘ্রই এক্সপোর্ট করা রিপোর্টটি ড্যাশবোর্ডের সাথে আর মেলে না। একটি পরিচ্ছন্ন আর্কিটেকচার একটি মাত্র টেমপ্লেটের ওপর নির্ভর করে এবং একটি ফ্ল্যাগ (flag) দিয়ে রেন্ডারিং লজিককে আলাদা করে, যেমন context.isForPdf()

যখন সেই ফ্ল্যাগটি false থাকে, তখন টেমপ্লেটটি ব্রাউজারের পূর্ণ অভিজ্ঞতা প্রদান করে। এটি ইনফিনিট জুমের জন্য নেটিভ SVG, আধুনিক CSS এবং ব্রাউজার যে কালার অ্যাসেটগুলো সাপোর্ট করে সেগুলো প্রদান করে। যখন ফ্ল্যাগটি true হয়, তখন একই টেমপ্লেট SVG অ্যাসেটগুলোকে প্রি-রেন্ডার করা PNG দিয়ে পরিবর্তন করে, ইমোজি-সেফ (emoji-safe) ফন্ট স্ট্যাক সক্রিয় করে এবং যেকোনো আনসাপোর্টেড ট্রান্সপারেন্সি ইফেক্ট সরিয়ে ফেলে। টেক্সট এবং স্ট্রাকচার অপরিবর্তিত থাকে; শুধুমাত্র অ্যাসেট পাইপলাইন এবং স্টাইলিং রুলগুলো টার্গেট মিডিয়ামের সাথে মানিয়ে নেয়।

এই ডুয়াল-পাথ (dual-path) পদ্ধতি কোডবেসকে নির্ভুল রাখে। আপনি এক জায়গাতেই কন্টেন্ট আপডেট করেন এবং রাউটিং লেয়ার স্ক্রিন ও কাগজের মধ্যকার যান্ত্রিক পার্থক্যগুলো সামলে নেয়। এটি টেস্টিংকেও সহজ করে তোলে। আপনি ফুল ডেভেলপার টুলস দিয়ে ব্রাউজারে টেমপ্লেট লজিক যাচাই করতে পারেন, তারপর PDF ফ্ল্যাগটি ট্রিগার করে নিশ্চিত করতে পারেন যে একই ডেটা কনভার্টার ক্র্যাশ না করেই একটি পরিচ্ছন্ন ডকুমেন্ট তৈরি করছে।

PDF জেনারেশন সম্পর্কে কঠিন সত্য

PDF কখনোই ব্রাউজারের মতো আচরণ করবে না। রেন্ডারিং মডেলগুলো মৌলিকভাবে আলাদা, এবং iText-এর মতো লাইব্রেরিগুলো গতি, ফাইলের আকার এবং স্পেসিফিকেশন কমপ্লায়েন্সের মধ্যে সচেতনভাবে ভারসাম্য বজায় রাখে। ইঞ্জিনের সাথে লড়াই করে এবং ভালো কিছুর আশা করে সাফল্য আসে না। সাফল্য আসে শুরুতেই সীমাবদ্ধতাগুলো মেনে নিয়ে সেই অনুযায়ী পাইপলাইন ডিজাইন করার মাধ্যমে।

PDF স্টেজের আগেই আপনার ভেক্টরগুলো রূপান্তর করুন। আপনার ফন্টগুলোকে স্পষ্টভাবে রুট করুন যাতে প্রতিটি গ্লিফ (glyph)-এর একটি ফলব্যাক (fallback) থাকে। ট্রান্সপারেন্সি সরিয়ে সলিড কালার ব্যবহার করুন। আপনার টেমপ্লেটগুলোকে প্রয়োজনীয় কনটেক্সট দিন যাতে তারা বুঝতে পারে তারা কোন জগতের জন্য রেন্ডার করছে। এটি ধারাবাহিকভাবে করুন, তাহলে আপনার ডকুমেন্টগুলো রেন্ডারারের সাথে লড়াই করা বন্ধ করবে এবং ঠিক আপনি যেভাবে চেয়েছিলেন সেভাবেই দেখাবে।