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

Aspecte de programare în PostScript - partea a treia

AWK | Bash | LaTex | PostScript | perl | sed
2019 may

[1] Aspecte de programare în PostScript - partea întâia, a doua

4. Etichetarea directă, folosind un font obişnuit

Pentru a eticheta anumite elemente ale graficului poate fi suficient să angajăm un font obişnuit; adăugăm în grafic.eps (v. §3), de exemplu:

/Helvetica findfont 0.18 scalefont setfont
(F) tR X tR Y 0.04 add moveto show  % eticheta 'F' pentru punctul K(tR)
(F') tL X 0.16 sub tL Y 0.06 sub moveto show  % dar "prim" va arăta ca o virgulă…
(C) xC 0.06 sub yC 0.06 add moveto show

tR X tR Y lasă în stivă coordonatele punctului K(tR); am mărit puţin ordonata (cu 0.04 din unitatea de măsură curentă, prevăzută în "programul principal" din grafic.eps), am mutat aici "punctul curent" şi apoi show a afişat caracterul rămas în vârful stivei, (F).

Ar fi de făcut nişte precizări: am indicat numele fontului, "/Helvetica" (prefixat cu '/', încât să nu fie considerat ca invocare a unui operator); findfont caută în dicţionarul de fonturi (sau, după caz, într-un director de fonturi) cheia indicată şi depune pe stivă dicţionarul asociat ei; acesta conţine între altele câte un operator pentru fiecare caracter (denumit ca şi caracterul, 'A', 'B', etc.); acest operator "desenează" caracterul respectiv, folosind ca de obicei când este de trasat un grafic moveto, lineto şi curveto; este asumat sistemul de coordonate curent şi fiindcă unitatea de măsură setată în grafic.eps era "mare" (1 ≡ 90bp), am folosit scalefont - obţinând caractere cu o dimensiune "normală", 0.18×90bp ≈ 16bp. Dicţionarul astfel setat în stiva operanzilor este apoi transferat prin setfont în vârful stivei dicţionarelor de fonturi, devenind astfel "fontul curent" pentru viitoarele instrucţiuni "(text) show".

Pe de altă parte, ar fi de dorit ca etichetele să nu fie nişte simple repere, ci să fie astfel formulate încât să reflecte cât mai bine contextul matematic al figurii; de exemplu, notând Kt şi K1-t în loc de F' şi F - s-ar evidenţia faptul că focarele sunt puncte ale semicardioidei $\mathcal{K}$, deduse dintr-o parametrizare a acesteia cu valori t simetrice faţă de mijlocul 0.5 al intervalului [0, 1].

Pentru acest caz, când ar trebui doar să adăugăm nişte indici, este încă uşor să folosim un font obişnuit:

/Courier-Bold findfont 0.18 scalefont setfont
(K) tR X tR Y 0.04 add moveto show
0 -0.04 rmoveto  % coboară puţin punctul curent, pentru a adăuga indicele t
gsave
.75 .75 scale  % mărimea indicelui va fi 3/4 din mărimea curentă de caracter
(1-t) show
grestore

Însă dacă avem nevoie şi de alte tipuri de literă ("caligrafic" de exemplu, pentru a eticheta cardioida cu $\mathcal{K}\,$), sau de expresii matematice mai complicate (cu radicali, fracţii, etc.) - atunci în loc să ne jucăm ca mai sus cu un "font obişnuit", mai bine căutăm o metodă generală, pe care să o putem aplica într-un mod standard pentru oricare figură şi pentru oricare necesităţi de etichetare…

5. Încapsularea în figură a notaţiilor matematice

Bineînţeles că există o asemenea "metodă generală", pentru a integra în documente notaţia matematică, în toată complexitatea specifică acesteia şi cu asigurarea unei foarte bune calităţi tipografice: sistemul de tipografie digitală $\TeX$, folosit curent în mediile ştiinţifice (sistem creat prin 1970-80, de către Donald Knuth).

Dar vrând să obţinem o figură "independentă", trebuie să combinăm cumva fişierul nostru grafic.eps (fără liniile experimentate în §4) cu etichetele matematice pe care le-am obţine folosind direct TeX.
Vom proceda cam aşa: creem un document ".tex" minimal, conţinând exprimarea TeX a etichetei dorite; compilăm cu latex acest fişier, obţinând fişierul corespunzător în formatul DVI; de aici, cu programul dvips, ajungem la formatul ".eps" al etichetei respective, fişier pe care îl vom putea "combina" apoi cu grafic.eps.
Bineînţeles, vom căuta să automatizăm această succesiune de operaţii, având în vedere şi faptul că sunt de montat mai multe etichete.

Instituim un şablon minimal de fişier LaTeX, "mathLabels.tex", în care să putem scrie expresia TeX a unei etichete:

\documentclass{article}
\usepackage{xcolor}  % în eventualitatea că vrem să colorăm o etichetă
\pagestyle{empty}
\begin{document}
$ $  % aici se va scrie eticheta (între caracterele "dolar")
\end{document}

Nu este mai convenabil, să scriem în acest fişier toate formulele (nu doar una): ar fi complicat de identificat zona din pagină asociată fiecăreia (şi de stabilit dimensiunile acesteia) în cadrul fişierului ".eps" final.

Constituim un fişier-text "labels.txt", conţinând etichetele noastre în notaţia matematică TeX (fiecare pe câte o linie):

K_t
K_{1-t}
C_t
\\color{red}\\mathcal{K}
\\mathcal{E}
\\color{blue}\\mathcal{P}

Am avut grijă să dublăm caracterul "backslash" (folosit de TeX pentru a desemna o comandă), fiindcă acest caracter are de obicei semnificaţie specială - care se evită prefixând cu '\' - în diverse limbaje pe care le-am putea folosi mai departe.

Formulăm acum un program Bash (interpretorul de comenzi obişnuit pe Ubuntu-Linux), "buildEPS.sh", prin care se preia câte o linie de text din fişierul labels.txt, se scrie textul respectiv în fişierul mathLabels.tex în locul rezervat etichetei (am ales să folosim sed, pentru aceasta), apoi se lansează compilatorul latex şi se transmite rezultatul obţinut astfel, programului dvips:

#!/bin/bash
labels=(`cat labels.txt`)
i=1
for l in "${labels[@]}" 
do
    sed -i "s/\$.*\$/\$$l\$/g" mathLabels.tex
    latex mathLabels.tex
    dvips -E mathLabels.dvi -o Label$i.eps
    ((i++))
done

Să extragem şi să afişăm coordonatele colţului stânga-jos al boxei care conţine desenul etichetei, din cadrul declaraţiei "%%BoundingBox" existente în fişierele "Label*.eps" rezultate în urma execuţiei scriptului Bash de mai sus:

vb@Home:~/5mai$ awk '/%%BoundingBox:/ {print $2, $3}'  Label*.eps
148 655
148 654
148 655
148 656
148 656
148 656

Aceste coordonate (care în cazul de faţă, aproape că nu diferă de la o etichetă la alta) ne sunt necesare pentru a stabili ce translaţie avem de făcut pentru a poziţiona eticheta respectivă în locul cuvenit în cadrul paginii produse de grafic.eps; de exemplu, -148 -655 translate va aduce prima etichetă în colţul stânga-jos al paginii care conţine graficul produs de grafic.eps şi rămâne de estimat (prin câteva încercări) ce translaţie mai trebuie făcută, încât s-o aducem lângă focarul Kt.

Următorul fişier, "pack.eps", conţine minimul necesar pentru ca fişierele EPS obţinute mai sus să poată fi încapsulate în fişierul grafic.eps:

%!PS-Adobe-2.0 EPSF-2.0
%%BoundingBox: 74 80 320 248

/showpage {} def

(grafic.eps) run

gsave -148 -655  98 120 translate  translate  
    (Label1.eps) run  grestore

gsave -148 -654  203 208 translate  translate
    (Label2.eps) run  grestore

gsave -148 -655  151 172 translate  translate
    (Label3.eps) run  grestore

gsave -148 -656  299 150 translate  translate
    (Label4.eps) run  grestore

gsave -148 -656  231 150 translate  translate
    (Label5.eps) run  grestore

gsave -148 -656  185 150 translate  translate
    (Label6.eps) run  grestore

Acest "minimul necesar" constă în anularea efectului instrucţiunii showpage (prin redeclararea acesteia ca procedură "vidă"); altfel, de regulă, s-ar produce "ejectarea" paginii, la fiecare nouă "adăugare" de pagină (în loc de "încorporare" într-o aceeaşi pagină). Desigur, fişierul "pack.eps" redat mai sus se scrie aproape automat, în sensul că putem folosi obiceiul "Copy-Paste--reeditare".
Precizăm că (nume_fişier) run execută pe rând instrucţiunile PS existente în fişierul indicat - altfel spus (în cazul nostru) se redesenează (la "punctul curent" rezultat prin cele două translaţii consecutive precedente) eticheta care fusese iniţial trasată prin TeX.

În final însă (după atâta muncă), am putea fi şi dezamăgiţi: evince (sau vreun alt "Document Viewer") nu reuşeşte să interpreteze fişierul pack.eps, cum ne-am fi aşteptat; iar invocând direct Ghostscript (prin gs pack.eps) obţinem o pagină care conţine numai rezultatul din "grafic.eps" (nu şi etichetele încapsulate în "pack.eps")
Pe undeva este normal să fie aşa: este periculos să permiţi includerea într-un fişier a altor fişiere care conţin instrucţiuni executabile (sau care conţin coduri protejate prin "copyright", cum este şi cazul fonturilor comerciale).
Totuşi, GS permite şi evitarea restricţiilor de lucru cu fişierele şi cu fonturile, prin opţiunea -dNOSAFER, încât prin comanda gs -dNOSAFER pack.eps obţinem vizualizarea pe ecran a figurii complete (incluzând şi etichetele).

Desigur, nu vom putea folosi "pack.eps" decât dacă punem alături şi toate celelalte fişiere vizate (grafic.eps, Label1.eps, etc.); putem evita acest inconvenient (rezultând şi alte avantaje) sintetizând fişierele respective într-un document PDF:

vb@Home:~/5mai$ ps2pdf -dNOSAFER  pack.eps  grafic.pdf

Pentru a converti din EPS în PDF, ps2pdf angajează de fapt interpretorul GS, încât putem beneficia ca şi mai sus, de opţiunea -dNOSAFER.

Fişierul obţinut grafic.pdf măsoară numai 15.4kB (în timp ce fişierele "Label*.eps" obţinute prin dvips mai sus totalizează aproape 200kB); vizualizându-l de exemplu prin evince, obţinem o pagină care conţine figura noastră (cu tot cu etichete) în partea din stânga-jos a ei. Dar aceasta nu este chiar ce ne-am dorit: în grafic.eps (v. §3) stabilisem în declaraţia %%BoundingBox limitele boxei care conţine figura noastră - iar acum vedem că acestea n-au fost în final, respectate; vizualizând cumva ca text, fişierul grafic.pdf - găsim că pagina respectivă a fost setată cu /MediaBox [0 0 595 842] (ceea ce corespunde formatului de pagină "A4"); să înlocuim cu limitele noastre:

vb@Home:~/5mai$ perl -pi -e 's/\[0 0 595 842\]/\[75 80 320 248\]/'  grafic.pdf

Deschizând acum "grafic.pdf" în evince şi consultând meniul "File / Properties" găsim "Paper Size 86×59mm", ceea ce corespunde cu dimensiunile setate mai sus (avem 320 - 75 = 245bp ≈ 86mm, fiindcă 1bp ≈ 0.3527mm etc.); redăm aici (cu micşorare) o copie de pe ecran a boxei respective:

Desigur, scopul pentru care faci o figură sau alta, nu este (în general) acela de a o arăta pur şi simplu pe ecran, sau a o tipări pe hârtie - ci de a o integra într-un anumit text referitor de exemplu, la contextul matematic asociat figurii. În cazul de faţă, figura de mai sus (adică fişierul grafic.pdf) mi-a servit pentru redactarea într-un fişier LaTeX a soluţiei problemei L361 din revista Recreaţii Matematice nr. 1/2019 (se cerea demonstrarea unor anumite legături între cardioidă, elipse şi parabolă).

Este clar că "metoda" directă de etichetare din §4 este mult mai simplă decât cea (superioară ca rezultat) constituită în §5 - dar şi aceasta, odată însuşită pentru o figură, devine uşor de practicat pentru orice altă figură.

vezi Cărţile mele (de programare)

docerpro | Prev | Next