momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Caractere (vechi şi noi), cu PostScript (II)

PostScript
2020 may

[1] partea I

Adevăratul „tabel de caractere”, cu dileme

Pe lângă cod, nume şi grafic (v. [1]), fiecărui caracter îi sunt specificate în dic­ţionarul fontului şi anumite caracteristici dimensionale—servind pentru poziţionarea lui pe rândul curent al paginii; selectăm aici două caractere din catalogul fontului "Times-Roman", produs printr-un program cunoscut (referit mai jos):

Fig.1

Numele "uni..." nu apare în tabelul "Encoding" iniţial (ci doar în subdicţionarul "CharStrings"); codurile Eh şi Fh ne arată că aceste caractere au fost cumva mapate ulterior, pe intrările (care iniţial conţineau /.notdef) 14 (=Eh) şi 15 din "Encoding". Numele acestor caractere provin din sistemul Unicode (în care U+021A codifică "LATIN CAPITAL LETTER T WITH COMMA BELOW"; v. T-comma).

Linia orizontală adăugată pe graficul caracterului indică linia de bază a rândului curent (servind pentru alinierea pe orizontală a caracterelor rândului); capătul din stânga al acestei linii reprezintă originea sistemului de coordonate în care sunt trasate curbele care împreună constituie graficul caracterului; capătul din dreapta este originea graficului care ar urma pe rândul respectiv. Distanţa dintre aceste două origini este lăţimea caracterului; scalând fontul la 24 puncte, "Ț" din Fig.1 va avea lăţimea ("width") de 0.611×24=14.664bp (unde "punct" este 1bp=1/72 inch).

Liniile verticale trasate au toate, o aceeaşi lungime – reprezentând valoarea la care a fost scalat fontul („mărimea fontului”)—adică, distanţa dintre liniile de bază ale rândurilor consecutive pe pagină (mai sus, 24 puncte)—plus „adâncimea” permisă caracterelor (cât se poate întinde graficul dedesubtul liniei de bază curente).

Ar fi de investigat aceste aspecte: de unde sunt preluate lăţimile înscrise în tabelul de caractere (0.611, 0.278 în Fig.1); cum recodificăm caracterele, încât să putem lucra şi cu cele neînscrise iniţial în "Encoding"; care este semnificaţia reală a „prelungirilor” liniei de bază a caracterului în afara liniilor verticale care îl încadrează şi care este dimensiunea corectă a acestora ? (în catalogul de pe care am preluat Fig.1, aceste linioare au aceeaşi lungime pentru toate caracterele)

Şi în fond, ce primează: să producem un „catalogul fontului” —paginat şi ordonat după anumite criterii, liniat frumos, cu un minimum de informaţii de identificare şi de poziţionare— sau, să vizualizăm anumite caractere, nu neapărat dintr-un acelaşi font, redând cât se poate de precis şi complet, informaţiile metrice specifice acestora ?

Catalogul fontului, cu prfont.ps din Ghostscript

Între programele utilitare furnizate de Ghostscript (în subdirectorul lib/) găsim prfont.ps ("Print font catalog"); conform documentaţiei aferente—de două sau trei rânduri—îl putem folosi interactiv, încărcându-l prin run şi aplicând DoFont pe fontul curent:


— Fig.2 —

Putem proceda analog pentru fişiere-font PS "*.pfb", folosind file şi ".loadfont":

vb@Home:~/20mai$ gs  -q
GS> (/usr/share/ghostscript/9.26/lib/prfont.ps)  run
GS> (/usr/share/fonts/type1/gsfonts/c059013l.pfb)  (r)  file  .loadfont
GS> /CenturySchL-Roma  DoFont
>>showpage, press <return> to continue<<

Desigur, întâi a trebuit să găsim numele de font pe care să-l indicăm procedurii DoFont; putem proceda şi foarte banal, deschizând fişierul respectiv, c059013l.pfb, fie şi în gedit: prima linie conţine %!PS-AdobeFont-1.0: CenturySchL-Roma 1.06, iar ceva mai jos avem şi definiţia de rigoare /FontName /CenturySchL-Roma def.
Tot la început, "% Generated by FontForge" spune ceva despre cum a fost construit fontul; FontForge este un editor de font, cu multiple posibilităţi de lucru interactiv:

De fapt, putem „citi” numele fontului şi fără intermedierea vreunui editor, utilizând operatori (".findfontname") definiţi de GS la iniţierea sesiunii de lucru (prin fişierul lib/gs_ini.ps) – cum vedem în următorul mic experiment:

%!PS-Adobe-2.0
/FF { (/usr/share/fonts/type1/gsfonts/c059013l.pfb)  
      (r)  file  % 'file' modelează un "stream" pentru citire din fişier
} bind def
% folosim procedura /FF pentru .loadfont şi apoi pentru .findfontname
FF .loadfont
FF .findfontname  % „reciteşte” fişierul şi extrage valoarea din /FontName
pop  % Stiva conţinea 'true' (eliminat acum) şi dedesubt, valoarea găsită

dup  % o copie de /FontName vom folosi pentru a seta fontul drept "curent" 

(/usr/share/ghostscript/9.26/lib/prfont.ps)  run  % "catalogul fontului"
DoFont  % numele fontului va fi preluat din stiva operanzilor

findfont 36 scalefont setfont  % fontul indicat în stivă devine "currentfont"
72 700 moveto  (abcfg\242) show  % \242 (octal) = 162 (codul caracterului /cent)
72 650 moveto    % pentru "unencoded characters" putem folosi 'glyphshow':
[ /abreve /tcommaaccent /scommaaccent /scedilla ] {
    glyphshow /space glyphshow
} forall

Precizăm că <tablou> <proc> forall aplică procedura proc elementelor tabloului.

Lansând acest mic program de pe linia de comandă (gs -q findFN.ps) obţinem (fără a furniza direct, numele fontului) rând pe rând paginile catalogului produs prin procedura DoFont din prfont.ps şi apoi (după setarea fontului respectiv ca font curent) pagina care conţine cele două rânduri de caractere—produse cu show, respectiv cu glyphshow—din imaginea bordată, anexată mai sus programului.

Folosind prfont.ps cum am arătat mai sus, apare totuşi un anumit inconvenient: în fereastra grafică, asociată sesiunii de lucru curent cu interpretorul GS (v. Fig.2), vom avea numai ultima pagină creată de program; "press <return> to continue" ejectează pagina afişată, făcând loc noii pagini (desigur, dacă „redirectăm” cumva ieşirea către o imprimantă, atunci vom putea tipări pagină după pagină, întregul catalog).

Putem evita acest inconvenient invocând prfont.ps (copiat pentru orice eventualitate, în directorul propriu) direct de pe linia de comandă:

gs -q -dNOPAUSE -dBATCH -sDEVICE=ps2write -sOutputFile=prf1.ps \
      prfont.ps  -c /Times-Roman DoFont quit

Rezultă astfel fişierul "prf1.ps", conţinând toate paginile catalogului fontului indicat – aici, Times-Roman (deschizând în evince fişierul obţinut, am putut copia de pe una dintre paginile derulate pe ecran imaginea din Fig.1 redată la început).

Cu -sDEVICE=pdfwrite (şi -sOutputFile=prf1.pdf) obţinem catalogul în format PDF, iar fişierul rezultat este şi de vreo cinci ori mai scurt, decât în cazul ps2write – numai că pentru multe caractere (spre deosebire de ps2write) sunt figurate doar linia de bază şi cele două verticale (plus lăţimea şi numele caracterului), lipsind graficul caracterului.

Modelarea caracterului, în prfont.ps

Rescriem uşor (imităm) procedurile DoChar şi DoGlyph din prfont.ps; între altele, prevedem şi fontul ca parametru – încât să putem reprezenta comod, chiar şi pe un acelaşi rând, caractere din fonturi diferite:

%!  # adaptare după Ghostscript lib/prfont.ps #
/DoChar {  % <font> <code> Dochar  (ex.: /Courier 103 Dochar)
    /C exch def  % preia de pe stivă codul caracterului
    /font exch findfont def  % fontul
    /N font /Encoding get C get def  % numele asociat codului
    /S (_) dup 0 C put def  % reprezentarea "şir octal" a codului (pt. stringwidth)
    /W font setfont S stringwidth pop def  % lăţimea caracterului
    /F24 font 24 scalefont def
    /Temp 64 string def
    font 6 scalefont setfont  % "print code name, width and char name" (*)
    0 -20 moveto N Temp cvs show
    0 -12 moveto C 16 Temp cvrs show  (  ) show
    W 0.0005 add Temp cvs 0 5 getinterval show
    % "print char with reference lines", exceptând caracterul '/.notdef'
    N /.notdef ne { DoGlyph } if
} bind def

% "output to rectangle ll=(0,-24) ur=(36,24)"  
/DoGlyph {	% "C, N, W set"
        3 0 translate  0 0 moveto 
        F24 setfont  N glyphshow  % produce graficul caracterului
        /W W 24 mul def
        0 -6 moveto 0 24 lineto  % verticala din stânga
        W -6 moveto W 24 lineto  % verticala din dreapta
        -3 0 moveto W 3 add 0 lineto  % linia de bază
        0 setlinewidth stroke
} bind def

/grChar {gsave DoChar grestore} bind def
% exemplificare:
72 600 translate  /Times-Roman 12 grChar
40 0 translate  /Times-Roman 64 grChar
40 0 translate  /Times-Italic 65 grChar
40 0 translate  /Helvetica 65 grChar
40 0 translate  /Courier 65 grChar

În DoChar de mai sus, se scrie "code name, width and char name" şi apoi, pentru cazul când caracterul respectiv nu este /.notdef se invocă şi DoGlyph (în lib/prfont.ps secvenţa de 4 linii marcată mai sus cu (*) era conţinută—împreună cu testul de "/.notdef"—chiar în DoGlyph, iar DoChar se rezuma la a seta parametrii "C, N, W" şi a invoca, cu aceştia, DoGlyph).

Desigur, putem imagina diverse alte „exemplificări” (înlocuind pe cea de mai sus):

/fonts [/Times-Roman /Times-Italic /Times-Bold /Times-BoldItalic 
        /Palatino-Roman /NewCenturySchlbk-Roman 
        /Helvetica /Bookman-Light /AvantGarde-Demi] def
72 600 translate
fonts {
    103 grChar
    40 0 translate    
} forall

DoGlyph produce graficul caracterului (folosind glyphshow), cele două verticale cu lungimea 24+6 (unde 24 este mărimea fontului), la abscisele 0 şi respectiv W (care este lăţimea caracterului, obţinută în DoChar prin operatorul general stringwidth) şi marchează „linia de bază” de la abscisa -3 până la abscisa W+3.

Desigur, prfont.ps avea ca scop producerea unui catalog elegant al caracterelor disponibile în fontul indicat.
Prelungirile fixe ±3 (pentru linia de bază, la oricare caracter) au natură decorativă („catalogul caracterelor” arată astfel mai bine), în loc să reflecte cumva anumite aspecte metrice ale caracterului. Tot o natură mai degrabă decorativă, are şi spaţiul de 6 puncte înălţime, lăsat dedesubtul liniei de bază; pentru caracterele (în majoritate) care au „adâncimea” zero, vizualizarea acestui spaţiu este inutilă, iar pentru celelalte – adâncimea diferă (ajungând la 6—cât s-a fixat pentru toate caracterele—numai în cazul caracterelor compuse cu virgulă, cum avem în Fig.1).

Să presupunem însă că nu să vedem catalogul tuturor caracterelor ne interesează, ci să vizualizăm doar anumite caractere (şi nu neapărat dintr-un acelaşi font), reprezentându-le cam la fel ca mai sus, dar evidenţiind mai precis (şi fără decorări inutile) aspectele metrice specifice acelor caractere; vom diseca o soluţionare, în „partea a III-a”.

vezi Cărţile mele (de programare)

docerpro | Prev | Next