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

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

PostScript
2020 jun

Exerciţiu: tabelarea glifelor după font şi nume

Considerăm iarăşi – v. [1] – cele 33 de fonturi PS "Type 1" pentru care dicţionarul /CharStrings aferent are 1004 intrări (formate pe aceleaşi nume de caracter); alegem 8 dintre aceste fonturi (preferând totuşi, câte unul din fiecare familie):

%!PS    confCat.ps
(bbsort.ps) run  % /sort_cvs (ordonează alfabetic un tablou; v. „partea a V-a”)
/Fonts [/AvantGarde-Demi /Bookman-Light /Courier /Helvetica 
        /NewCenturySchlbk-Roman /Palatino-Roman /Times-Roman 
        /ZapfChancery-MediumItalic] def

Vrem să formulăm un tabel cu 8 coloane şi 1004 linii, conţinând glifele fonturilor din tabloul /Fonts – adică, redând aici produsul final:

Intrările pe coloană corespund fonturilor, iar cele pe linie – numelor de caracter (pe care le-am scris în dreapta tabelului fiindcă lungimea acestora variază).

Vom proceda cam la fel ca în glyphCat.ps din „partea a V-a” – program care tabela glifele unui font indicat (în ordinea alfabetică a numelor de caracter); în fond, acum vom „rescrie” cumva, acest program.

În exerciţiile anterioare formulam „din aproape în aproape” (sau „de jos în sus”), scriind o procedură sau alta numai după ce puneam la punct toate sub-procedurile pe care le implică aceasta; în particular, procedura „principală” era formulată la sfârşitul fişierului PS respectiv. De data aceasta, începem cu acele două proceduri care exprimă esenţa lucrului (amânând pe cele „ajutătoare”): constituim un tablou cu numele de caracter comune (ordonate alfabetic) şi îl parcurgem aplicând gliphshow fiecărui nume, pentru toate fonturile consemnate în tabloul /Fonts.

Prin următoarea procedură constituim tabloul PS Keys, conţinând cele 1004 nume de caracter, în ordine alfabetică (fără distincţie de caz pentru litera iniţială):

/storeKeys {
    /Times-Italic findfont /CharStrings get  % pe stivă: perechi <nume "-string-">
    {pop} forall  % descarcă "-string-", din toate perechile
    count array astore  % pe stivă: un tablou PS cu cheile din /CharString
    /Keys exch def  % „salvează” tabloul cheilor în 'Keys' (variabilă „globală”)
    Keys sort_cvs  % ordonează alfabetic după numele glifelor
} bind def

Tabelul dorit se va obţine scriind pentru fiecare nume de caracter din tabloul Keys, glifele corespunzătoare acestuia în fonturile specificate în tabloul Fonts (rămânând de văzut cum trecem de la un rând la cel următor şi de la o pagină la următoarea):

/glyphList {  % procedura „principală”
    storeKeys  % creează tabloul 'Keys' (numele glifelor, alfabetic)
    newPage  % iniţializează prima pagină (scriindu-i antetul)
    Keys { writeGlyphs } forall  % scrie cele 8×1004 glife, pagină după pagină
    showpage  % ejectează ultima pagină
} bind def 

Obs. Următorul mic experiment ne arată cumva, diferenţa dintre interpretare (sau „citirea programului” de către interpretorul PS) şi execuţie. Salvăm în acest moment, fişierul "confCat.ps" şi-l încărcăm într-o sesiune interactivă de lucru cu Ghostscript:

    vb@Home:~/20-iun$ gs -q
    GS> (confCat.ps) run
    GS> storeKeys
    GS> Keys length ==
    1004
    GS> glyphList
    Error: /undefined in newPage

Nu se produce nicio eroare la citirea programului (chit că se implică subproceduri care nu sunt încă, definite); s-a putut executa /storeKeys (care este complet definită), dar nu şi /glyphList (a cărei definiţie lipseşte în acest moment, din fişierul încărcat).

În ierarhia logică a importanţei, urmează să formulăm subprocedura writeGlyphs; aceasta produce glifele din fonturile specificate în tabloul /Fonts, corespunzătoare numelui de caracter care îi este transmis pe stivă:

/writeGlyphs {  % pe stivă: un nume de caracter din tabloul 'Keys'
    /key exch def  % salvează numele din stivă în variabila globală 'key'
    18 newLine
    id0
    Fonts {  % scrie pe un rând glifele din cele 8 fonturi, pentru 'key'
        findfont 24 scalefont setfont
        key glyphshow
        nextpos id1  % avansează 'currentpoint' la următoarea coloană a rândului
    } forall
    fontEntry setfont  % scrie pe ultima coloană, numele de caracter din 'key'
    -10 0 rmoveto
    key 20 string cvs show
    currentpoint exch
    pop neg 700 lt  % trece la un nou rând (dacă încape), sau la o nouă pagină
    {18 newLine} {showpage newPage} ifelse    
} bind def

De observat că după completarea unui rând de glife, se trece pe rândul următor dacă nu s-a ajuns încă la baza paginii curente; altfel, se ejectează pagina curentă (prin showpage) şi se iniţializează o nouă pagină. Aceasta înseamnă că dacă pe ultima pagină, după ce s-a scris ultimul rând, încă ar fi loc pentru un nou rând – atunci această ultimă pagină trebuie ejectată direct (vezi ultima instrucţiune din /glyphList).

Mai rămâne să scriem procedurile „ajutătoare”, prin care poziţionăm glifele una lângă alta pe un acelaşi rând, sau trecem pe rândul următor sau pe următoarea pagină:

/newLine {  % <dy> newLine
    /dy exch def
    currentpoint exch  % pe stivă: y x
    pop dy sub 0 exch moveto % y dy | y1=y-dy 0 | 0 y1 | moveto
} bind def

/fontEntry {/Times-Italic findfont 12 scalefont} bind def  % font chei şi antet
/Header {  % scrie (prescurtat) numele fonturilor, la începutul fiecărei pagini
    fontEntry setfont
    (AvG-De   Bkm-Li   Courier   Helvet   NCenSc-Ro   Palat-Ro   Times-Ro   ZapfCha) 
        show
    12 newLine
} bind def
/newPage {54 800 translate  0 0 moveto Header} bind def  % format "A4"

/id0 {/idx 0 def} def  % vizează prima glifă de pe rândul curent
/id1 {/idx idx 1 add def} def  % vizează glifa următoare celei curent scrise
/nextpos { % poziţia pe rând a următoarei glife (50 puncte pentru fiecare)
    currentpoint pop 50 idx 1 add mul exch sub 0 rmoveto
} bind def

glyphList  % pune în execuţie procedura principală a fişierului

Se poate constata că am păstrat definiţiile (schimbând cel mult denumirea) din glyphCat.ps pentru newLine, nextpos şi chiar (dar acum, cu alt rol) id0 şi id1; desigur, faptul că am denumit la fel procedurile principale ("glyphList") este o gafă (dar uşor de îndreptat, dacă ar fi cazul de a folosi împreună cele două fişiere).

Pentru că în finalul fişierului confCat.ps am invocat deja procedura principală glyphList, putem obţine fişierul final PDF prin comanda (vezi exerciţiile precedente):

    ps2pdf -dNOSAFER -dPDFSETTINGS=/prepress -dEmbedAllFonts=true confCat.ps 

Rezultă astfel fişierul confCat.pdf (redat aici la început), conţinând 51 de pagini (cu câte 20 rânduri de glife, exceptând ultima pagină, cu numai 4 rânduri).

vezi Cărţile mele (de programare)

docerpro | Prev | Next