اگر آپ نے کبھی PDF سے بولڈ (bold) یا ترچھی (italic) تحریر نکالنے کی کوشش کی ہے، تو غالباً آپ نے regex سے آغاز کیا ہوگا۔ یہ ایک فطری قدم محسوس ہوتا ہے۔ فونٹ کے نام میں "Bold" کا لفظ تلاش کریں، تحریر کو نشان زد کریں، اور آگے بڑھ جائیں۔ یہ حکمت عملی صرف اتنی دیر تک کام کرتی ہے کہ آپ کو غلط اعتماد مل جائے۔ پھر کوئی اسی دستاویز کو Acrobat کے کسی مختلف ورژن، یا LibreOffice، یا کسی print-to-PDF ڈرائیور سے ایکسپورٹ کرتا ہے، اور آپ کے کوڈ کردہ تمام مفروضے غلط ثابت ہو جاتے ہیں۔
فونٹ کے نام کیوں جھوٹ بولتے ہیں
PDF.js آپ کو ABCDEF+TimesNewRomanPS-BoldMT جیسی اسٹرنگز (strings) دے گا۔ پہلے چھ حروف ایک رینڈم پری فکس (prefix) ہیں جو ایکسپورٹ کے دوران شامل کیے جاتے ہیں، اور فائل کے دوبارہ بننے پر یہ ہر بار بدل جاتے ہیں۔ اپنے پارسر (parser) کا انحصار اس پری فکس پر کرنا محض شور (noise) پر جوا لگانے کے مترادف ہے۔ دوسرے ایکسپورٹرز اس سے بھی کم مددگار ہوتے ہیں۔ کچھ محض Font12 یا F1 جیسے سادہ شناختی نام (identifiers) دیتے ہیں۔ ان لیبلز کا کوئی معنوی مفہوم نہیں ہوتا؛ یہ محض اندرونی ریسورس ٹیگز (resource tags) ہیں جو فائل لکھتے وقت قریب موجود تھے۔
چونکہ Portable Document Format کو اس طرح ڈیزائن نہیں کیا گیا تھا کہ اس سے بعد میں ٹیکسٹ نکالنا آسان ہو، اس لیے فونٹ کے نام محض ایمبیڈڈ (embedded) یا سب سیٹ شدہ (subsetted) ریسورسز کے حوالے ہیں۔ انہیں اسٹائل کی شناخت کے لیے ایک مستحکم API کے طور پر استعمال کرنے کے لیے کبھی نہیں بنایا گیا تھا۔ جب آپ ایسا regex لکھتے ہیں جو Bold یا Italic کے سب اسٹرنگ (substring) کو تلاش کرتا ہے، تو آپ دراصل ایک ایسے لیبل کو اسکریپ (scrape) کر رہے ہوتے ہیں جسے بنانے والی ایپلی کیشن اپنی مرضی سے کسی بھی طرح فارمیٹ کرنے میں آزاد تھی۔ آپ اصل ٹائپوگرافک خصوصیات نہیں پڑھ رہے ہوتے، بلکہ آپ فائل کے نام رکھنے کا ایک طریقہ (convention) پڑھ رہے ہوتے ہیں، اور طریقہ کار (conventions) معاہدے (contracts) نہیں ہوتے۔
لیبل نہیں، ڈسکرپٹر (Descriptor) پڑھیں
اصل حقیقت کہیں اور چھپی ہے۔ PDF.js میں، ہر پیج آبجیکٹ commonObjs کو ظاہر کرتا ہے، جو ایک میپ (map) ہے اور اس میں پیج کو رینڈر کرنے کے لیے درکار اصل فونٹ ڈسکرپٹرز (font descriptors) موجود ہوتے ہیں۔ جب لائبریری کسی پیج کو پارس (parse) کرتی ہے، تو یہ اس میپ کو حقیقی فونٹ آبجیکٹس سے بھر دیتی ہے۔ وہ آبجیکٹس bold اور italic کے لیے بولین (boolean) پراپرٹیز فراہم کرتے ہیں۔ وہ بولینز نام کی اسٹرنگ سے اخذ نہیں کیے جاتے۔ وہ PDF کے اندر ایمبیڈڈ فونٹ ڈسکرپٹر سے حاصل ہوتے ہیں، جو گلِف میٹرکس (glyph metrics)، OS/2 ٹیبل فلیگز، اور ان علامتی ڈسکرپٹرز سے اخذ کیے جاتے ہیں جو ٹائپ سیٹر نے دستاویز میں لکھے ہوتے ہیں۔
اس کا مطلب ہے کہ اب آپ اندازے لگانا چھوڑ سکتے ہیں۔ پیج پر موجود ٹیکسٹ آئٹمز پر عمل کرنے سے پہلے، page.commonObjs پر لوپ (iterate) چلا کر ایک fontStyleMap بنائیں۔ ہر فونٹ آئی ڈی (font ID) کے لیے، ایک ایسا آبجیکٹ محفوظ کریں جو فونٹ آبجیکٹ کی طرف سے فراہم کردہ اصل .bold اور .italic پراپرٹیز کو ریکارڈ کرے۔ بعد میں، جب آپ ہر ٹیکسٹ آئٹم کو پراسیس کریں، تو اپنے میپ میں اس کے فونٹ ریفرنس کو تلاش کریں اور پہلے سے کیلکولیٹ شدہ فلیگز (flags) پڑھ لیں۔ اچانک آپ کے پاس ایک یقینی جواب ہوگا جو ایکسپورٹ کے درمیان تبدیل نہیں ہوتا۔
آپ ایک متبادل (fallback) رکھ سکتے ہیں۔ اگر ڈسکرپٹر کسی وجہ سے موجود نہ ہو یا نامکمل ہو، تو فونٹ کے نام کو صاف کریں، پری فکس کو ہٹا دیں، رینڈم ٹیگز کو نکال دیں، اور جو باقی بچ جائے اس پر ایک محتاط regex چلائیں۔ لیکن یہ آپ کا آخری حربہ ہونا چاہیے، آپ کا بنیادی منطق (logic) نہیں۔ قابل اعتماد ہونے میں فرق بہت واضح ہے۔ جہاں نام پر مبنی پارسنگ مختلف ایکسپورٹرز کے ساتھ ناکام ہو جاتی ہے، وہاں ڈسکرپٹر پر مبنی پارسنگ مستحکم رہتی ہے کیونکہ یہ فائل سے پوچھتی ہے کہ اس میں اصل میں کیا موجود ہے۔
جب فونٹ بھی جھوٹ بولے: مصنوعی اسٹائلز (Synthetic Styles)
ڈسکرپٹر بھی کبھی کبھی غلطی کر سکتا ہے۔ کچھ PDF بنانے والے الگ سے ترچھی (italic) ٹائپ فیس (typeface) ایمبیڈ کرنے کی زحمت نہیں کرتے۔ اس کے بجائے، وہ سیدھے رومن فونٹ کو لیتے ہیں اور اسے ایک ٹرانسفارم میٹرکس (transform matrix) کے ذریعے ترچھا کر دیتے ہیں۔ یہ ڈیزائن ٹولز یا پرانے ورڈ پروسیسرز سے تیار کردہ فائلوں میں عام ہے جو ٹائپوگرافک پاکیزگی کے مقابلے میں فائل کے سائز کو ترجیح دیتے ہیں۔
PDF.js میں ہر ٹیکسٹ آئٹم ایک transform ایرے (array) لے کر چلتا ہے، جو کہ چھ عناصر پر مشتمل ایک افائن میٹرکس (affine matrix) ہے جو گلِف کوآرڈینیٹ سسٹم کو پیج کوآرڈینیٹ سسٹم میں منتقل کرتا ہے۔ اس ایرے کا تیسرا عنصر افقی شیئر (horizontal shear) کو کنٹرول کرتا ہے۔ جب وہ ویلیو زیرو نہ ہو، تو رینڈرر کے ذریعے تحریر کو میکانیکی طور پر ترچھا کیا جا رہا ہوتا ہے۔ اگر آپ صرف فونٹ ڈسکرپٹر پر بھروسہ کریں گے، تو آپ اس تحریر کو سیدھا رومن قرار دیں گے۔ اگر آپ میٹرکس کا معائنہ کریں گے، تو آپ مصنوعی ترچھی (synthetic italic) کو پکڑ لیں گے اور اسے درست طریقے سے نشان زد کریں گے۔ یہی منطق اوور پرنٹنگ (overprinting) کے ذریعے بنائے گئے مصنوعی بولڈ پر بھی لاگو ہوتی ہے، اگرچہ صرف جیومیٹری سے اسے پہچاننا مشکل ہے۔ ترچھی تحریر کے لیے، شیئر ویلیو (shear value) آپ کا پکا ثبوت ہے۔
انڈر لائن (Underline) ڈرا کی جاتی ہے، ڈکلیئر نہیں
بولڈ اور ترچھی (italic) فونٹ کی خصوصیات ہیں۔ انڈر لائن ایسی نہیں ہے۔ PDF میں، انڈر لائن ایک ویکٹر پاتھ (vector path) ہوتی ہے۔ رینڈرر ٹیکسٹ بیس لائن کے قریب واقع ایک باریک افقی حصے کے لیے ڈرائنگ کمانڈ جاری کرتا ہے۔ یہ ایک گرافیکل عنصر ہے جو محض گلِفس کے نیچے واقع ہوتا ہے، یہ cmap میں محفوظ شدہ کیریکٹر ایٹریبیوٹ (character attribute) نہیں ہے۔
یہ فرق اس لیے اہم ہے کیونکہ فونٹ ڈسکرپٹر (font descriptor) پڑھنے سے انڈر لائن کا پتہ نہیں چلے گا۔ آپ کو پیج پر موجود خام ڈرائنگ آپریٹرز (raw drawing operators) یا جیومیٹری لیول کے آؤٹ پٹ میں ان مختصر افقی لائنوں کے ٹکڑوں کو تلاش کرنا ہوگا جو درست فاصلے پر بیس لائن (baseline) کے متوازی چلتی ہوں۔ جب آپ کا ایکسٹریکشن انجن کسی ٹیکسٹ رن (text run) کے نیچے ایسا کوئی ٹکڑا دیکھتا ہے، تو آپ اس رن کو انڈر لائنڈ (underlined) کے طور پر ٹیگ کر دیتے ہیں۔ اسے ایک الگ ڈیٹیکشن لیئر کے طور پر سمجھنا آپ کے ڈیٹا ماڈل کو درست رکھتا ہے: بولڈ (bold) اور اٹالک (italic) فونٹ کی اپنی خصوصیات ہیں، جبکہ انڈر لائن دستاویز کے ذریعے پیش کی جانے والی ایک بیرونی سجاوٹ ہے۔
ایک فعال پائپ لائن
ایک صاف ستھرا ایکسٹریکشن سسٹم مختلف ذمہ داریوں کو الگ الگ تہوں (layers) میں تقسیم کرتا ہے۔ سب سے پہلے، ایک جیومیٹری ورکر (geometry worker) پیج کا جائزہ لیتا ہے۔ یہ آپ کا fontStyleMap تیار کرنے کے لیے page.commonObjs سے معلومات حاصل کرتا ہے، مصنوعی اٹالک (synthetic italics) کو پکڑنے کے لیے ہر ٹیکسٹ آئٹم کے ٹرانسفارم ایرے (transform array) کا معائنہ کرتا ہے، اور انڈر لائنز تلاش کرنے کے لیے قریبی ویکٹر پاتھز (vector paths) کو اسکین کرتا ہے۔ اس کا آؤٹ پٹ ایک صاف ستھرا درمیانی ڈھانچہ ہوتا ہے جسے textMeta کہا جا سکتا ہے، جہاں ہر ٹیکسٹ رن میں تین سادہ بولینز (booleans) ہوتے ہیں: bold، italic، اور underline۔
اس کے بعد، ایک ٹیکسٹ ری بلڈر (text rebuilder) اس ڈھانچے کو استعمال کرتا ہے اور مارک اپ شدہ آؤٹ پٹ فراہم کرتا ہے۔ یہاں ترتیب (nesting order) بہت اہم ہے۔ درست درجہ بندی میں انڈر لائن سب سے باہر، پھر اٹالک، اور پھر بولڈ سب سے اندر ہوتا ہے۔ اس کا مطلب ہے کہ مکمل اسٹائل شدہ رن <u><i><b>text</b></i></u> بن جاتا ہے۔ یہ ترتیب غیر قانونی HTML اوورلیپس کو روکتی ہے اور براؤزرز اور دستاویز کنورٹرز میں رینڈرنگ کو مستقل رکھتی ہے۔ یہ ٹائپوگرافک منطق کی بھی عکاسی کرتا ہے: سجاوٹ (decoration) سیمنٹک زور (semantic emphasis) کو لپیٹتی ہے، اور سیمنٹک زور ساختی وزن (structural weight) کو لپیٹتا ہے۔
اس طریقہ کار کی طاقت یہ ہے کہ یہ صرف اسی چیز کا استعمال کرتا ہے جو PDF پہلے سے اپنے بارے میں جانتی ہے۔ اس میں کوئی OCR شامل نہیں ہے، کوئی کلاؤڈ ویژن سروس نہیں ہے، اور نہ ہی کوئی مشین لرننگ ماڈل راسٹرائزڈ پکسلز سے اسٹائل کا اندازہ لگا رہا ہے۔ آپ فائل کی اپنی سیمنٹک لیئر (semantic layer) پڑھ رہے ہیں، جو جیومیٹری اور میٹا ڈیٹا کے ذریعے ظاہر ہوتی ہے جسے بنانے والی ایپلی کیشن پہلے ہی کیلکولیٹ کر چکی ہوتی ہے۔ اس کا نتیجہ تیز، یقینی (deterministic) اور PDF جنریٹرز کے افراتفری بھرے منظر نامے میں بھی درست ہوتا ہے۔
اصل نتیجہ
فونٹ کے ناموں کے خلاف Regex کا استعمال ایک جال ہے۔ یہ ایک شارٹ کٹ محسوس ہوتا ہے کیونکہ یہ اس ایک فائل پر کام کرتا ہے جس کا آپ نے ٹیسٹ کیا ہے، لیکن دوسرے ایکسپورٹ کے معمولی دباؤ میں یہ ناکام ہو جاتا ہے۔ اصل معلومات پہلے سے ہی PDF کے اندر، ڈسکرپٹرز (descriptors)، میٹرسز (matrices) اور ویکٹر پاتھز (vector paths) میں موجود ہیں۔ اپنی پائپ لائن ان حقائق کے گرد بنائیں۔ فونٹ آبجیکٹ سے پوچھیں کہ کیا وہ بولڈ ہے۔ شیئر (shear) کے لیے ٹرانسفارم میٹرکس چیک کریں۔ انڈر لائنز کے لیے ڈرا کیے گئے سیگمنٹس کو دیکھیں۔ اگر آپ دستاویز کے سطحی لیبلز کو اسکریپ کرنے کے بجائے اس کی اپنی انجینئرنگ سے معلومات حاصل کرتے ہیں، تو آپ کو ایسے اسٹائلز ملتے ہیں جو ایک PDF جنریٹر سے دوسرے تک برقرار رہتے ہیں۔
