ഒരു PDF-ൽ നിന്ന് ബോൾഡ് (bold) അല്ലെങ്കിൽ ഇറ്റാലിക് (italic) ടെക്സ്റ്റ് വേർതിരിച്ചെടുക്കാൻ നിങ്ങൾ ശ്രമിച്ചിട്ടുണ്ടെങ്കിൽ, നിങ്ങൾ ഒരു regex ഉപയോഗിച്ചായിരിക്കാം അത് തുടങ്ങിയിട്ടുണ്ടാവുക. അത് വളരെ ലളിതമായ ഒരു വഴിയായി തോന്നും. ഫോണ്ട് പേരിൽ “Bold” എന്ന വാക്ക് തിരയുക, ആ ടെക്സ്റ്റ് അടയാളപ്പെടുത്തുക, എന്നിട്ട് മുന്നോട്ട് പോവുക. ഈ തന്ത്രം നിങ്ങൾക്ക് തെറ്റായ ആത്മവിശ്വാസം നൽകുന്നതുവരെ മാത്രമേ പ്രവർത്തിക്കുകയുള്ളൂ. പിന്നീട് ആരെങ്കിലും അതേ ഡോക്യുമെന്റ് Acrobat-ന്റെ മറ്റൊരു വേർഷനിൽ നിന്നോ, LibreOffice-ൽ നിന്നോ, അല്ലെങ്കിൽ ഒരു print-to-PDF ഡ്രൈവർ ഉപയോഗിച്ചോ എക്സ്‌പോർട്ട് ചെയ്യുമ്പോൾ, നിങ്ങൾ കോഡ് ചെയ്ത എല്ലാ അനുമാനങ്ങളും തകർന്നടിയും.

ഫോണ്ട് പേരുകൾ എന്തുകൊണ്ട് കള്ളം പറയുന്നു

PDF.js നിങ്ങൾക്ക് ABCDEF+TimesNewRomanPS-BoldMT പോലുള്ള സ്ട്രിംഗുകൾ നൽകും. ആദ്യത്തെ ആറ് അക്ഷരങ്ങൾ എക്സ്‌പോർട്ട് സമയത്ത് ചേർക്കപ്പെടുന്ന റാൻഡം പ്രിഫിക്സുകളാണ് (random prefix), ഫയൽ വീണ്ടും നിർമ്മിക്കുമ്പോ마다 അവ മാറിക്കൊണ്ടിരിക്കും. നിങ്ങളുടെ പാഴ്സറിനെ (parser) ആ പ്രിഫിക്സിനെ ആശ്രയിപ്പിക്കുന്നത് വെറുതെ സമയം കളയുന്നതിന് തുല്യമാണ്. മറ്റ് എക്സ്‌പോർട്ടറുകൾ ഇതിലും മോശമാണ്. ചിലത് Font12 അല്ലെങ്കിൽ F1 പോലുള്ള വെറും ഐഡന്റിഫയറുകൾ മാത്രമാണ് നൽകുന്നത്. ഈ ലേബലുകൾക്ക് അർത്ഥപരമായ യാതൊരു പ്രസക്തിയുമില്ല; ഫയൽ എഴുതപ്പെട്ട സമയത്ത് ലഭ്യമായ ആന്തരിക റിസോഴ്സ് ടാഗുകൾ മാത്രമാണിവ.

Portable Document Format രൂപകൽപ്പന ചെയ്തിരിക്കുന്നത് ടെക്സ്റ്റ് എക്സ്ട്രാക്ഷൻ എളുപ്പമാക്കാനല്ല എന്നതുകൊണ്ട്, ഫോണ്ട് പേരുകൾ എന്നത് എംബെഡഡ് (embedded) അല്ലെങ്കിൽ സബ്‌സെറ്റഡ് (subsetted) റിസോഴ്സുകൾക്കുള്ള റഫറൻസുകൾ മാത്രമാണ്. സ്റ്റൈൽ ഡിറ്റക്ഷനായി (style detection) ഉപയോഗിക്കാൻ അവ ഉദ്ദേശിച്ചിട്ടില്ല. നിങ്ങൾ Bold അല്ലെങ്കിൽ Italic എന്ന സബ്സ്ട്രിംഗിനായി ഒരു regex എഴുതുമ്പോൾ, നിർമ്മാണ ആപ്ലിക്കേഷന് ഇഷ്ടമുള്ള രീതിയിൽ ഫോർമാറ്റ് ചെയ്യാവുന്ന ഒരു ലേബൽ മാത്രമാണ് നിങ്ങൾ ശേഖരിക്കുന്നത്. നിങ്ങൾ യഥാർത്ഥ ടൈപ്പോഗ്രാഫിക് പ്രോപ്പർട്ടികളല്ല വായിക്കുന്നത്. പകരം ഒരു ഫയൽ നാമകരണ രീതിയാണ് (file-naming convention) നിങ്ങൾ വായിക്കുന്നത്, ഇത്തരം രീതികൾ ഒരിക്കലും ഉറപ്പായ കരാറുകളല്ല.

ലേബലല്ല, ഡിസ്ക്രിപ്റ്റർ വായിക്കുക

യഥാർത്ഥ വിവരങ്ങൾ മറ്റൊരിടത്താണ് ഉള്ളത്. PDF.js-ൽ, ഓരോ പേജ് ഒബ്‌ജക്റ്റും commonObjs എന്നൊരു മാപ്പ് (map) പ്രദർശിപ്പിക്കുന്നു, പേജ് റെൻഡർ ചെയ്യാൻ ആവശ്യമായ യഥാർത്ഥ ഫോണ്ട് ഡിസ്ക്രിപ്റ്ററുകൾ ഇതിൽ അടങ്ങിയിരിക്കുന്നു. ലൈബ്രറി ഒരു പേജ് പാഴ്സ് ചെയ്യുമ്പോൾ, യഥാർത്ഥ ഫോണ്ട് ഒബ്‌ജക്റ്റുകൾ ഉപയോഗിച്ച് ഈ മാപ്പ് പൂരിപ്പിക്കുന്നു. ആ ഒബ്‌ജക്റ്റുകൾ bold, italic എന്നിവയ്ക്കായി ബൂലിയൻ (boolean) പ്രോപ്പർട്ടികൾ നൽകുന്നു. ഈ ബൂലിയൻ മൂല്യങ്ങൾ പേര് സ്ട്രിംഗിൽ നിന്ന് അനുമാനിച്ചുള്ളതല്ല. അവ PDF-നുള്ളിൽ എംബെഡ് ചെയ്തിട്ടുള്ള ഫോണ്ട് ഡിസ്ക്രിപ്റ്ററിൽ നിന്നാണ് വരുന്നത്; ഗ്ലിഫ് മെട്രിക്സ് (glyph metrics), OS/2 ടേബിൾ ഫ്ലാഗുകൾ, ടൈപ്പ്സെറ്റർ ഡോക്യുമെന്റിൽ രേഖപ്പെടുത്തിയ സിംബോളിക് ഡിസ്ക്രിപ്റ്ററുകൾ എന്നിവയിൽ നിന്നാണ് ഇവ ലഭിക്കുന്നത്.

അതായത്, ഇനി ഊഹിച്ചു നടക്കേണ്ടതില്ല. ഒരു പേജിലെ ടെക്സ്റ്റ് ഐറ്റങ്ങൾ പരിശോധിക്കുന്നതിന് മുമ്പ്, page.commonObjs ഉപയോഗിച്ച് ഒരു fontStyleMap നിർമ്മിക്കുക. ഓരോ ഫോണ്ട് ഐഡിനും വേണ്ടി, ഫോണ്ട് ഒബ്‌ജക്റ്റ് നൽകുന്ന യഥാർത്ഥ .bold, .italic പ്രോപ്പർട്ടികൾ രേഖപ്പെടുത്തുന്ന ഒരു ഒബ്‌ജക്റ്റ് സൂക്ഷിക്കുക. പിന്നീട് ഓരോ ടെക്സ്റ്റ് ഐറ്റവും പ്രോസസ്സ് ചെയ്യുമ്പോൾ, നിങ്ങളുടെ മാപ്പിൽ അതിന്റെ ഫോണ്ട് റഫറൻസ് നോക്കി മുൻകൂട്ടി കണക്കാക്കിയ ഫ്ലാഗുകൾ വായിക്കുക. എക്സ്‌പോർട്ട് രീതികൾ മാറിയാലും മാറാത്ത കൃത്യമായ ഒരു ഉത്തരം നിങ്ങൾക്ക് പെട്ടെന്ന് ലഭിക്കും.

നിങ്ങൾക്ക് ഒരു ഫാള்பാക്ക് (fallback) ഉപയോഗിക്കാം. ഡിസ്ക്രിപ്റ്റർ ലഭ്യമല്ലെങ്കിലോ അപൂർണ്ണമാണെങ്കിലോ, ഫോണ്ട് പേര് വൃത്തിയാക്കി പ്രിഫിക്സും റാൻഡം ടാഗുകളും ഒഴിവാക്കി ബാക്കിയുള്ളതിൽ ഒരു regex ഉപയോഗിക്കാം. എന്നാൽ ഇത് നിങ്ങളുടെ അവസാന മാർഗ്ഗമായിരിക്കണം, പ്രധാന ലോജിക്കായിരിക്കരുത്. ഇതിന്റെ വിശ്വാസ്യതയിൽ വലിയ വ്യത്യാസമുണ്ട്. പേര് അടിസ്ഥാനമാക്കിയുള്ള പാഴ്സിംഗ് എക്സ്‌പോർട്ടറുകൾ മാറുന്നതിനനുസരിച്ച് പരാജയപ്പെടുമ്പോൾ, ഡിസ്ക്രിപ്റ്റർ അടിസ്ഥാനമാക്കിയുള്ള പാഴ്സിംഗ് കൃത്യമായി പ്രവർത്തിക്കുന്നു; കാരണം അത് ഫയലിൽ യഥാർത്ഥത്തിൽ എന്താണുള്ളതെന്ന് നേരിട്ട് ചോദിക്കുന്നു.

ഫോണ്ടും കള്ളം പറഞ്ഞാൽ: സിന്തറ്റിക് സ്റ്റൈലുകൾ (Synthetic Styles)

ഡിസ്ക്രിപ്റ്ററിനും ചിലപ്പോൾ തെറ്റുപറ്റാം. ചില PDF ക്രിയേറ്ററുകൾ പ്രത്യേക ഇറ്റാലിക് ടൈപ്പ്ഫേസ് എംബെഡ് ചെയ്യാൻ ശ്രമിക്കാറില്ല. പകരം, അവർ നേരായ റോമൻ ഫോണ്ട് എടുത്ത് ഒരു ട്രാൻസ്ഫോം മാട്രിക്സ് (transform matrix) ഉപയോഗിച്ച് ചരിച്ച് കാണിക്കുന്നു. ടൈപ്പോഗ്രാഫിക് ശുദ്ധിയേക്കാൾ ഫയൽ സൈസിന് പ്രാധാന്യം നൽകുന്ന ഡിസൈൻ ടൂളുകളിൽ നിന്നോ പഴയ വേർഡ് പ്രോസസറുകളിൽ നിന്നോ നിർമ്മിക്കപ്പെടുന്ന ഫയലുകളിൽ ഇത് സാധാരണമാണ്.

PDF.js-ലെ ഓരോ ടെക്സ്റ്റ് ഐറ്റത്തിലും ഒരു transform അറേ ഉണ്ട്; ഇത് ഗ്ലിഫ് കോർഡിനേറ്റ് സിസ്റ്റത്തെ പേജ് കോർഡിനേറ്റ് സിസ്റ്റവുമായി ബന്ധിപ്പിക്കുന്ന ആറ് എലമെന്റുകളുള്ള ഒരു അഫിൻ മാട്രിക്സ് (affine matrix) ആണ്. ആ അറേയിലെ മൂന്നാമത്തെ എലമെന്റ് ആണ് ഹൊറിസോണ്ടൽ ഷിയർ (horizontal shear) നിയന്ത്രിക്കുന്നത്. ആ മൂല്യം പൂജ്യമല്ലെങ്കിൽ, റെൻഡറർ ടെക്സ്റ്റിനെ മെക്കാനിക്കലായി ചരിച്ച് കാണിക്കുകയാണ് ചെയ്യുന്നത്. നിങ്ങൾ ഫോണ്ട് ഡിസ്ക്രിപ്റ്ററിനെ മാത്രം വിശ്വസിക്കുകയാണെങ്കിൽ, ഈ ടെക്സ്റ്റിനെ നേരായ റോമൻ ഫോണ്ട് എന്ന് നിങ്ങൾ കരുതും. എന്നാൽ നിങ്ങൾ മാട്രിക്സ് പരിശോധിച്ചാൽ, അത് ഒരു സിന്തറ്റിക് ഇറ്റാലിക് ആണെന്ന് തിരിച്ചറിയാനും ശരിയായി അടയാളപ്പെടുത്താനും സാധിക്കും. ഓവർപ്രിന്റിംഗ് (overprinting) വഴി നിർമ്മിക്കുന്ന സിന്തറ്റിക് ബോൾഡിനും ഇതേ ലോജിക് ബാധകമാണ്, എങ്കിലും ജ്യാമിതീയമായി മാത്രം ഇത് കണ്ടെത്തുന്നത് പ്രയാസമാണ്. ചരിഞ്ഞ ടെക്സ്റ്റുകളെ സംബന്ധിച്ചിടത്തോളം, ഷിയർ മൂല്യം (shear value) ആണ് നിങ്ങളുടെ പ്രധാന തെളിവ്.

അണ്ടർലൈനുകൾ വരയ്ക്കപ്പെട്ടവയാണ്, പ്രഖ്യാപിക്കപ്പെട്ടവയല്ല

ബോൾഡ്, ഇറ്റാലിക് എന്നിവ ഫോണ്ട് പ്രോപ്പർട്ടികളാണ്. എന്നാൽ അണ്ടർലൈൻ അങ്ങനെയല്ല. PDF-ൽ, ഒരു അണ്ടർലൈൻ എന്നത് ഒരു വെക്റ്റർ പാത്ത് (vector path) ആണ്. ടെക്സ്റ്റ് ബേസ്‌ലൈനിന് സമീപം ഒരു നേർത്ത തിരശ്ചീന വര വരയ്ക്കാൻ റെൻഡറർ ഒരു ഡ്രോയിംഗ് കമാൻഡ് നൽകുന്നു. ഇത് ഗ്ലിഫുകൾക്ക് താഴെയായി കാണപ്പെടുന്ന ഒരു ഗ്രാഫിക്കൽ എലമെന്റ് മാത്രമാണ്, ഒരു cmap-ൽ സൂക്ഷിച്ചിട്ടുള്ള ക്യാരക്ടർ അറ്റ്രിബ്യൂട്ട് അല്ല.

ഈ വ്യത്യാസം പ്രധാനമാണ്, കാരണം ഫോണ്ട് ഡിസ്ക്രിപ്റ്ററുകൾ (font descriptors) വായിച്ചതുകൊണ്ട് മാത്രം ഒരു അണ്ടർലൈൻ കണ്ടെത്താൻ കഴിയില്ല. പേജിലെ റോ (raw) ഡ്രോയിംഗ് ഓപ്പറേറ്ററുകളിലോ അല്ലെങ്കിൽ ജിയോമെട്രി-ലെവൽ ഔട്ട്‌പുട്ടിലോ നോക്കിയാൽ മാത്രമേ ബേസ്‌ലൈനിന് സമാന്തരമായി കൃത്യമായ അകലത്തിൽ കാണപ്പെടുന്ന ചെറിയ തിരശ്ചീന രേഖകൾ (horizontal line segments) കണ്ടെത്താൻ കഴിയൂ. നിങ്ങളുടെ എക്സ്ട്രാക്ഷൻ എഞ്ചിൻ ഒരു ടെക്സ്റ്റ് റണ്ണിന് താഴെ അത്തരം ഒരു സെഗ്‌മെന്റ് കണ്ടെത്തുമ്പോൾ, ആ റണ്ണിനെ അണ്ടർലൈൻ ചെയ്തതായി നിങ്ങൾ ടാഗ് ചെയ്യുന്നു. ഇതിനെ ഒരു പ്രത്യേക ഡിറ്റക്ഷൻ ലെയർ ആയി പരിഗണിക്കുന്നത് നിങ്ങളുടെ ഡാറ്റാ മോഡലിന്റെ കൃത്യത ഉറപ്പാക്കുന്നു: ബോൾഡ് (bold), ഇറ്റാലിക് (italic) എന്നിവ ഫോണ്ടിന്റെ ഭാഗമാണ്, എന്നാൽ അണ്ടർലൈൻ എന്നത് ഡോക്യുമെന്റ് നൽകുന്ന ഒരു പുറമെ നിന്നുള്ള അലങ്കാരമാണ് (extrinsic decoration).

ഒരു വർക്കിംഗ് പൈപ്പ്‌ലൈൻ

ഒരു മികച്ച എക്സ്ട്രാക്ഷൻ സിസ്റ്റം വിവിധ കാര്യങ്ങളെ വ്യത്യസ്ത ലെയറുകളായി തിരിക്കുന്നു. ആദ്യം, ഒരു ജിയോമെട്രി വർക്കർ പേജ് പരിശോധിക്കുന്നു. നിങ്ങളുടെ fontStyleMap തയ്യാറാക്കാൻ അത് page.commonObjs ഉപയോഗിക്കുന്നു, സിന്തറ്റിക് ഇറ്റാലിക്സ് (synthetic italics) കണ്ടെത്താൻ ഓരോ ടെക്സ്റ്റ് ഐറ്റത്തിന്റെയും ട്രാൻസ്ഫോം അറേ (transform array) പരിശോധിക്കുന്നു, കൂടാതെ അണ്ടർലൈനുകൾ കണ്ടെത്താൻ അടുത്തുള്ള വെക്റ്റർ പാത്തുകൾ (vector paths) സ്കാൻ ചെയ്യുന്നു. ഇതിന്റെ ഔട്ട്‌പുട്ട് textMeta എന്ന് വിളിക്കാവുന്ന ഒരു ക്ലീൻ ഇന്റർമീഡിയറ്റ് സ്ട്രക്ചർ ആണ്, അവിടെ ഓരോ ടെക്സ്റ്റ് റണ്ണിലും bold, italic, underline എന്നിങ്ങനെ മൂന്ന് ലളിതമായ ബൂളിയൻ (boolean) മൂല്യങ്ങൾ ഉണ്ടായിരിക്കും.

അടുത്തതായി, ഒരു ടെക്സ്റ്റ് റീബിൽഡർ ആ സ്ട്രക്ചർ ഉപയോഗിച്ച് മാർക്ക്-അപ്പ് ചെയ്ത ഔട്ട്‌പുട്ട് നൽകുന്നു. ഇവിടെ നെസ്റ്റിംഗ് ഓർഡർ (nesting order) വളരെ പ്രധാനമാണ്. അണ്ടർലൈൻ ഏറ്റവും പുറത്തും, പിന്നെ ഇറ്റാലിക്, ഏറ്റവും ഉള്ളിലായി ബോൾഡ് എന്നിങ്ങനെയാണ് ശരിയായ ശ്രേണി (hierarchy). അതായത്, പൂർണ്ണമായി സ്റ്റൈൽ ചെയ്ത ഒരു റൺ <u><i><b>text</b></i></u> എന്ന രീതിയിലാകും. ഈ ക്രമം തെറ്റായ HTML ഓവർലാപ്പുകൾ ഒഴിവാക്കാനും ബ്രൗസറുകളിലും ഡോക്യുമെന്റ് കൺവെർട്ടറുകളിലും ഒരേപോലെ റെൻഡറിംഗ് ഉറപ്പാക്കാനും സഹായിക്കുന്നു. ഇത് ടൈപ്പോഗ്രാഫിക് ലോജിക്കിനെ (typographic logic) പ്രതിഫലിപ്പിക്കുന്നു കൂടിയ: അലങ്കാരങ്ങൾ സെമാന്റിക് എംഫസിസിനെ (semantic emphasis) പൊതിയുന്നു, സെമാന്റിക് എംഫസിസ് സ്ട്രക്ചറൽ വെയ്റ്റിനെ (structural weight) പൊതിയുന്നു.

ഈ രീതിയുടെ പ്രത്യേകത, PDF-ൽ നിലവിലുള്ള വിവരങ്ങൾ മാത്രം ഉപയോഗിക്കുന്നു എന്നതാണ്. ഇതിൽ OCR ഉപയോഗിക്കുന്നില്ല, ക്ലൗഡ് വിഷൻ സർവീസോ മെഷീൻ ലേണിംഗ് മോഡലുകളോ ഉപയോഗിച്ച് റാസ്റ്ററൈസ് ചെയ്ത പിക്സലുകളിൽ നിന്ന് സ്റ്റൈലുകൾ ഊഹിച്ചെടുക്കുന്നില്ല. പകരം, ക്രിയേറ്റർ ആപ്ലിക്കേഷൻ നേരത്തെ തന്നെ കണക്കാക്കിയ ജിയോമെട്രിയും മെറ്റാഡേറ്റയും വഴി ലഭ്യമാകുന്ന ഫയലിന്റെ സ്വന്തം സെമാന്റിക് ലെയർ ആണ് നിങ്ങൾ വായിക്കുന്നത്. ഇതിന്റെ ഫലം വേഗതയേറിയതും, കൃത്യതയുള്ളതും, വിവിധതരം PDF ജനറേറ്ററുകൾക്കിടയിലും വിശ്വസനീയവുമാണ്.

യഥാർത്ഥ പാഠം

ഫോണ്ട് പേരുകൾ ഉപയോഗിച്ചുള്ള Regex ഒരു കെണിയാണ്. നിങ്ങൾ പരിശോധിച്ച ഒരു ഫയലിൽ അത് പ്രവർത്തിക്കുന്നത് കൊണ്ട് തന്നെ അതൊരു എളുപ്പവഴിയാണെന്ന് തോന്നാം, എന്നാൽ രണ്ടാമതൊരു എക്സ്‌പോർട്ട് വരുമ്പോൾ അത് പരാജയപ്പെടും. യഥാർത്ഥ വിവരങ്ങൾ ഇതിനകം തന്നെ PDF-നുള്ളിൽ, അതായത് ഡിസ്ക്രിപ്റ്ററുകളിലും (descriptors), മാട്രിക്സുകളിലും (matrices), വെക്റ്റർ പാത്തുകളിലും (vector paths) അടങ്ങിയിരിക്കുന്നു. നിങ്ങളുടെ പൈപ്പ്‌ലൈൻ ഈ വസ്തുതകളെ അടിസ്ഥാനമാക്കി നിർമ്മിക്കുക. ഫോണ്ട് ഒബ്ജക്റ്റ് ബോൾഡ് ആണോ എന്ന് ചോദിക്കുക. ഷിയറിനായി (shear) ട്രാൻസ്ഫോം മാട്രിക്സ് പരിശോധിക്കുക. അണ്ടർലൈനുകൾക്കായി വരച്ച സെഗ്‌മെന്റുകൾ നോക്കുക. ഡോക്യുമെന്റിന്റെ ഉപരിതലത്തിലുള്ള ലേബലുകൾ മാത്രം പരിശോധിക്കുന്നതിന് പകരം അതിന്റെ എൻജിനീയറിംഗ് വിവരങ്ങൾ ഉപയോഗിച്ചാൽ, ഒരു PDF ജനറേറ്ററിൽ നിന്ന് മറ്റൊന്നിലേക്ക് മാറുമ്പോഴും നിലനിൽക്കുന്ന സ്റ്റൈലുകൾ നിങ്ങൾക്ക് ലഭിക്കും.