જો તમે ક્યારેય PDF માંથી બોલ્ડ (bold) અથવા ઇટાલિક (italic) ટેક્સ્ટ કાઢવાનો પ્રયત્ન કર્યો હોય, તો તમે કદાચ regex થી શરૂઆત કરી હશે. તે એક સ્પષ્ટ ઉપાય લાગે છે. ફોન્ટના નામમાં “Bold” શબ્દ શોધો, ટેક્સ્ટને ફ્લેગ કરો અને આગળ વધો. આ વ્યૂહરચના તમને ખોટો આત્મવિશ્વાસ આપવા માટે પૂરતી કામ કરે છે. પછી કોઈ એક જ દસ્તાવેજ Acrobat ના અલગ વર્ઝનમાંથી, અથવા LibreOffice માંથી, અથવા print-to-PDF ડ્રાઇવરમાંથી એક્સપોર્ટ કરે છે, અને તમે જે પણ ધારણાઓ કોડ કરી હશે તે બધું જ તૂટી પડે છે.

ફોન્ટના નામ શા માટે જૂઠું બોલે છે

PDF.js તમને ABCDEF+TimesNewRomanPS-BoldMT જેવી સ્ટ્રિંગ્સ આપશે. પ્રથમ છ અક્ષરો એ એક્સપોર્ટ દરમિયાન ઉમેરવામાં આવેલ રેન્ડમ પ્રીફિક્સ (prefix) છે, અને જ્યારે પણ ફાઇલ ફરીથી જનરેટ કરવામાં આવે ત્યારે તે બદલાઈ જાય છે. તમારા પાર્સર (parser) ને તે પ્રીફિક્સ પર આધારિત રાખવો એ માત્ર અવાજ (noise) પર દાવ લગાવવા જેવું છે. અન્ય એક્સપોર્ટર્સ તો તેનાથી પણ ઓછા મદદરૂપ છે. કેટલાક Font12 અથવા F1 જેવા સાદા આઇડેન્ટિફાયર (identifiers) આપે છે. આ લેબલ્સનો કોઈ અર્થ (semantic meaning) નથી; તેઓ આંતરિક રિસોર્સ ટેગ્સ છે જે ફાઇલ લખતી વખતે ત્યાં હાજર હતા.

કારણ કે Portable Document Format ને ડાઉનસ્ટ્રીમ ટેક્સ્ટ એક્સટ્રેક્શન (text extraction) સરળ બનાવવા માટે ક્યારેય ડિઝાઇન કરવામાં આવ્યું ન હતું, તેથી ફોન્ટના નામ એ માત્ર એમ્બેડેડ (embedded) અથવા સબસેટેડ (subsetted) રિસોર્સના સંદર્ભો છે. તેમને સ્ટાઇલ ડિટેક્શન માટે સ્ટેબલ API તરીકે કામ કરવા માટે ક્યારેય બનાવવામાં આવ્યા ન હતા. જ્યારે તમે Bold અથવા Italic સબસ્ટ્રિંગ શોધવા માટે regex લખો છો, ત્યારે તમે એવા લેબલને સ્ક્રેપ કરી રહ્યા છો જેને બનાવનાર એપ્લિકેશન પોતાની મરજી મુજબ ફોર્મેટ કરી શકે છે. તમે વાસ્તવિક ટાઇપોગ્રાફિક ગુણધર્મો (typographic properties) વાંચી રહ્યા નથી. તમે ફાઇલ-નામકરણની પદ્ધતિ (file-naming convention) વાંચી રહ્યા છો, અને પદ્ધતિઓ એ કરાર (contracts) નથી.

લેબલ નહીં, ડિસ્ક્રિપ્ટર (Descriptor) વાંચો

વાસ્તવિક સત્ય બીજે ક્યાંક છે. PDF.js માં, દરેક પેજ ઓબ્જેક્ટ commonObjs ને એક્સપોઝ કરે છે, જે એક મેપ (map) છે જેમાં પેજ રેન્ડર કરવા માટે જરૂરી વાસ્તવિક ફોન્ટ ડિસ્ક્રિપ્ટર્સ હોય છે. જ્યારે લાઇબ્રેરી પેજ પાર્સ કરે છે, ત્યારે તે આ મેપને અસલી ફોન્ટ ઓબ્જેક્ટ્સથી ભરે છે. તે ઓબ્જેક્ટ્સ bold અને italic માટે બુલિયન (boolean) પ્રોપર્ટીઝ એક્સપોઝ કરે છે. તે બુલિયન્સ નામની સ્ટ્રિંગ પરથી અનુમાનિત કરવામાં આવતા નથી. તેઓ PDF ની અંદર એમ્બેડેડ ફોન્ટ ડિસ્ક્રિપ્ટરથી આવે છે, જે ગ્લિફ મેટ્રિક્સ (glyph metrics), OS/2 ટેબલ ફ્લેગ્સ અને ટાઇપસેટર દ્વારા દસ્તાવેજમાં લખવામાં આવેલા સિમ્બોલિક ડિસ્ક્રિપ્ટર્સ પરથી મેળવવામાં આવે છે.

તેનો અર્થ એ છે કે તમે અનુમાન કરવાનું બંધ કરી શકો છો. પેજ પરના ટેક્સ્ટ આઇટમ્સ પર કામ કરતા પહેલા, page.commonObjs પર ઇટરેટ કરીને fontStyleMap બનાવો. દરેક ફોન્ટ ID માટે, એક એવો ઓબ્જેક્ટ સ્ટોર કરો જે ફોન્ટ ઓબ્જેક્ટ દ્વારા આપવામાં આવેલી વાસ્તવિક .bold અને .italic પ્રોપર્ટીઝને રેકોર્ડ કરે છે. પછીથી, જ્યારે તમે દરેક ટેક્સ્ટ આઇટમ પર પ્રોસેસ કરો છો, ત્યારે તમારા મેપમાં તેનો ફોન્ટ રેફરન્સ શોધો અને પ્રી-કમ્પ્યુટેડ (precomputed) ફ્લેગ્સ વાંચો. અચાનક તમારી પાસે એક નિશ્ચિત (deterministic) જવાબ હશે જે એક્સપોર્ટ વચ્ચે બદલાશે નહીં.

તમે એક ફોલબેક (fallback) રાખી શકો છો. જો ડિસ્ક્રિપ્ટર કોઈ રીતે ગેરહાજર અથવા અધૂરું હોય, તો ફોન્ટના નામમાંથી પ્રીફિક્સ દૂર કરો, રેન્ડમ ટેગ્સ કાઢી નાખો અને જે બાકી રહે તેના પર કન્ઝર્વેટિવ (conservative) regex ચલાવો. પરંતુ આ તમારો છેલ્લો વિકલ્પ હોવો જોઈએ, પ્રાથમિક લોજિક નહીં. વિશ્વસનીયતામાં તફાવત ઘણો મોટો છે. જ્યાં નામ-આધારિત પાર્સિંગ એક્સપોર્ટર્સ વચ્ચે તૂટી જાય છે, ત્યાં ડિસ્ક્રિપ્ટર-આધારિત પાર્સિંગ સ્થિર રહે છે કારણ કે તે ફાઇલને પૂછે છે કે તેમાં ખરેખર શું છે.

જ્યારે ફોન્ટ પણ જૂઠું બોલે છે: સિન્થેટિક સ્ટાઇલ્સ (Synthetic Styles)

ડિસ્ક્રિપ્ટર પણ ક્યારેક ભૂલ કરી શકે છે. કેટલાક PDF ક્રિએટર્સ અલગ ઇટાલિક ટાઇપફેસ (italic typeface) એમ્બેડ કરવાની તસ્દી લેતા નથી. તેના બદલે, તેઓ સીધા રોમન ફોન્ટ લે છે અને તેને ટ્રાન્સફોર્મ મેટ્રિક્સ (transform matrix) સાથે નમેલો (slant) બનાવે છે. આ ડિઝાઇન ટૂલ્સ અથવા જૂના વર્ડ પ્રોસેસર્સ દ્વારા જનરેટ કરવામાં આવેલી ફાઇલોમાં સામાન્ય છે જે ટાઇપોગ્રાફિક શુદ્ધતા કરતા ફાઇલ સાઇઝને વધુ મહત્વ આપે છે.

PDF.js માં દરેક ટેક્સ્ટ આઇટમમાં transform એરે હોય છે, જે છ-એલિમેન્ટ ધરાવતું એફાઇન મેટ્રિક્સ (affine matrix) છે જે ગ્લિફ કોઓર્ડિનેટ સિસ્ટમને પેજ કોઓર્ડિનેટ સિસ્ટમમાં મેપ કરે છે. તે એરેનું ત્રીજું એલિમેન્ટ હોરિઝોન્ટલ શિયર (horizontal shear) ને નિયંત્રિત કરે છે. જ્યારે તે વેલ્યુ નોન-ઝીરો (non-zero) હોય, ત્યારે ટેક્સ્ટને રેન્ડરર દ્વારા મિકેનિકલી નમેલો બનાવવામાં આવે છે. જો તમે ફક્ત ફોન્ટ ડિસ્ક્રિપ્ટર પર વિશ્વાસ કરો છો, તો તમે આ ટેક્સ્ટને સીધા રોમન તરીકે વર્ગીકૃત કરશો. જો તમે મેટ્રિક્સનું નિરીક્ષણ કરો છો, તો તમે સિન્થેટિક ઇટાલિકને પકડી શકો છો અને તેને યોગ્ય રીતે માર્ક કરી શકો છો. સમાન લોજિક ઓવરપ્રિન્ટિંગ દ્વારા બનાવવામાં આવેલ સિન્થેટિક બોલ્ડને પણ લાગુ પડે છે, જોકે તેને માત્ર ભૂમિતિ (geometry) પરથી શોધવું મુશ્કેલ છે. નમેલા ટેક્સ્ટ માટે, શિયર વેલ્યુ (shear value) એ તમારો નિર્ણાયક પુરાવો છે.

અંડરલાઇન દોરવામાં આવે છે, જાહેર કરવામાં આવતી નથી

બોલ્ડ અને ઇટાલિક એ ફોન્ટ પ્રોપર્ટીઝ છે. અંડરલાઇન નથી. PDF માં, અંડરલાઇન એ વેક્ટર પાથ (vector path) છે. રેન્ડરર ટેક્સ્ટ બેઝલાઇન પાસે સ્થિત પાતળા આડા સેગમેન્ટ માટે ડ્રોઇંગ કમાન્ડ આપે છે. તે એક ગ્રાફિકલ એલિમેન્ટ છે જે ગ્લિફ્સની નીચે હોય છે, તે cmap માં સંગ્રહિત કેરેક્ટર એટ્રિબ્યુટ નથી.

આ તફાવત મહત્વનો છે કારણ કે ફોન્ટ ડિસ્ક્રિપ્ટર (font descriptor) વાંચવાથી અંડરલાઇન (underline) વિશે ખબર નહીં પડે. તમારે પેજ પરના રો (raw) ડ્રોઈંગ ઓપરેટર્સ અથવા ભૂમિતિ-સ્તરના (geometry-level) આઉટપુટમાં એવા ટૂંકા આડા રેખાના ભાગો શોધવા પડશે જે બેઝલાઇન (baseline) ની સમાંતર અને યોગ્ય અંતરે હોય. જ્યારે તમારું એક્સટ્રેક્શન એન્જિન ટેક્સ્ટ રન (text run) ની નીચે આવો કોઈ ભાગ શોધે છે, ત્યારે તમે તે રનને અંડરલાઇન તરીકે ટેગ કરો છો. આને અલગ ડિટેક્શન લેયર તરીકે ગણવાથી તમારું ડેટા મોડલ સચોટ રહે છે: bold અને italic એ ફોન્ટના આંતરિક ગુણધર્મો છે, જ્યારે underline એ દસ્તાવેજ દ્વારા રજૂ કરવામાં આવેલી બાહ્ય સુશોભન છે.

એક કાર્યકારી પાઇપલાઇન

એક સ્વચ્છ એક્સટ્રેક્શન સિસ્ટમ કાર્યોને અલગ-અલગ લેયરમાં વિભાજિત કરે છે. સૌ પ્રથમ, એક geometry worker પેજનું વિશ્લેષણ કરે છે. તે તમારા fontStyleMap ને તૈયાર કરવા માટે page.commonObjs ક્વેરી કરે છે, synthetic italics પકડવા માટે દરેક ટેક્સ્ટ આઇટમના transform array ની તપાસ કરે છે, અને અંડરલાઇન શોધવા માટે નજીકના vector paths સ્કેન કરે છે. તેનું આઉટપુટ એક સ્વચ્છ મધ્યવર્તી સ્ટ્રક્ચર છે જેને textMeta કહી શકાય, જ્યાં દરેક ટેક્સ્ટ રનમાં ત્રણ સરળ booleans હોય છે: bold, italic, અને underline.

ત્યારબાદ, એક text rebuilder તે સ્ટ્રક્ચરનો ઉપયોગ કરીને માર્ક-અપ આઉટપુટ આપે છે. અહીં nesting order મહત્વપૂર્ણ છે. સાચું પદાનુક્રમ અંડરલાઇનને સૌથી બહાર, પછી italic અને પછી bold ને સૌથી અંદર રાખે છે. તેનો અર્થ એ કે સંપૂર્ણ સ્ટાઇલ ધરાવતું રન <u><i><b>text</b></i></u> બની જાય છે. આ ક્રમ અમાન્ય HTML ઓવરલેપ્સને અટકાવે છે અને બ્રાઉઝર્સ તથા ડોક્યુમેન્ટ કન્વર્ટર પર રેન્ડરિંગને સુસંગત રાખે છે. તે ટાઇપોગ્રાફિક લોજિકને પણ પ્રતિબિંબિત કરે છે: decoration એ semantic emphasis ને આવરી લે છે, અને semantic emphasis એ structural weight ને આવરી લે છે.

આ અભિગમની શક્તિ એ છે કે તે ફક્ત તે જ માહિતીનો ઉપયોગ કરે છે જે PDF પહેલેથી જ પોતાની વિશે જાણે છે. તેમાં કોઈ OCR સામેલ નથી, કોઈ cloud vision service નથી, અને કોઈ machine learning model રાસ્ટરાઇઝ્ડ પિક્સેલ્સ (rasterized pixels) પરથી સ્ટાઇલનો અંદાજ લગાવતું નથી. તમે ફાઇલના પોતાના semantic layer ને વાંચી રહ્યા છો, જે geometry અને metadata દ્વારા ખુલ્લું પડે છે જે ક્રિએટર એપ્લિકેશને પહેલેથી જ ગણતરી કરી લીધી હોય છે. પરિણામ ઝડપી, deterministic અને PDF જનરેટર્સના અસ્તવ્યસ્ત લેન્ડસ્કેપમાં સચોટ હોય છે.

સાચો નિષ્કર્ષ

ફોન્ટના નામો સામે Regex નો ઉપયોગ કરવો એ એક જાળ છે. તે શોર્ટકટ જેવું લાગે છે કારણ કે તે તમે ટેસ્ટ કરેલી એક ફાઇલ પર કામ કરે છે, પરંતુ બીજા એક્સપોર્ટના સામાન્ય દબાણ હેઠળ તે નિષ્ફળ જાય છે. સાચી માહિતી પહેલેથી જ PDF ની અંદર, descriptors, matrices અને vector paths માં રહેલી છે. તમારી પાઇપલાઇન આ તથ્યોની આસપાસ બનાવો. ફોન્ટ ઓબ્જેક્ટને પૂછો કે તે bold છે કે નહીં. shear માટે transform matrix તપાસો. અંડરલાઇન માટે દોરેલા સેગમેન્ટ્સ જુઓ. જો તમે દસ્તાવેજના સપાટીના લેબલ્સ સ્ક્રેપ કરવાને બદલે તેના પોતાના એન્જિનિયરિંગને ક્વેરી કરો છો, તો તમને એવા સ્ટાઇલ્સ મળશે જે એક PDF જનરેટરથી બીજા સુધી ટકી રહેશે.