Una Chiave per il Desktop Publishing

Corso di PostScript - Marco A. Calamari

Quinta Puntata


In questa puntata verranno introdotti gli operatori di controllo del PostScript, e la definizione di programma PostScript conforme; sarà inoltre precisato il concetto di incapsulamento, introdotto la nella precedente puntata. Il nostro senso estetico, messo a dura prova da una indigestione di teoria pura, sarà consolato da un esempio di generazione di forme frattali con l'uso ricorsivo del PostScript.


Siamo rimasti con un debito del mese scorso, cioè la soluzione dell'esercizio proposto che riguardava la modifica del Programma 3, in modo da ottenere che il fondo della cornice diventasse grigio, mentre il fondo delle lettere PS restasse bianco. L'esempio (riportato nel Listato 1, ed il cui output è mostrato in Figura 1) è stato svolto nella maniera semplice e stupida; cioè utilizzando le path già definite nel programma originale, salvandole con l'operatore gsave, riempiendo la prima di grigio e la seconda di bianco con l'operatore fill. Si è cioè sfruttata la proprietà del PostScript di scrivere con un inchiostro coprente, per ottenere il risultato voluto.

Un'altra possibile soluzione, più elegante ma più complessa, sarebbe stata di eseguire il primo fill (quello nella procedura tracciaCorniceModificata) con una path forata con il profilo delle lettere PS, eliminando la necessità di eseguire il secondo fill in bianco. Questo avrebbe però richiesto una modifica più estesa del programma, rendendo meno generale la procedura tracciaCorniceModificata, e aumentando la dipendenza fra le varie parti del programma.

I nuovi operatori PostScript

Introduciamo ora l'elenco dei nuovi operatori PostScript, divisi per tipologia, che dovrà ovviamente essere utilizzato anche per esaminare il funzionamento degli esempi proposti.

Operatori di Controllo - test logici

Operatori di Controllo - flusso del programma

Un esempio di ricorsività

Dopo la solita indigestione di nuovi operatori, ci rilasseremo con un esempio alla moda del PostScript; la generazione di una forma frattale autosimile. Come ricorderete, nella seconda puntata avevamo elencato, tra le caratteristiche fondamentali del PostScript, quella di permettere l'utilizzo della ricorsività; senza imbastire un trattatello di teoria della programmazione, ci limiteremo a ricordare che si definisce ricorsivo un linguaggio che permette di definire procedure (o subroutine) che hanno la possibilità di richiamare se stesse. Gli esempi più noti di questa classe di linguaggi sono il Pascal ed il Lisp, mentre ad esempio Basic, Cobol, C e Fortran sono esempi di linguaggi non-ricorsivi. La ricorsività non permette di risolvere problemi impossibili con la classica tecnica dell'iterazione; è anzi possibile dimostrare che qualunque programma esprimibile in forma ricorsiva può anche essere scritto in forma iterativa. Queste due formulazioni sono ovviamente possibili utilizzando il medesimo linguaggio; solitamente la formulazione iterativa produce un codice più elegante e compatto, ma spesso anche di esecuzione più lenta.

Utilizzeremo una applicazione della ricorsività per generare una forma frattale autosimile, cioè una figura geometrica (in questo caso una freccia) che è collegata a copie di se stessa sempre più piccole (teoricamente all'infinito, in pratica fino al limite della risoluzione dei normali device PostScript, cioè 300 d.p.i.).

I commenti inseriti nel Listato 2 descrivono i dettagli del funzionamento della procedura ricorsiva frecciaFrattale; come è possibile vedere rimuovendo questi ultimi, il programma è estremamente breve ed elegante. Il risultato grafico può essere variato entro ampi limiti agendo sulle variabili globali LivelloMassimo e RapportoDiRiduzione; ad esempio, in Figura 3 è mostrato il cambiamento che si ottiene modificando il valore di RapportoDiRiduzione da 0.6 a 0.707. Se modificate il valore di LivelloMassimo , prestate attenzione al fatto che ogni aumento di una unità implica all'incirca un raddoppio del tempo di esecuzione, e che, con i valori del Listato 2 (LivelloMassimo=12 e RapportoDiRiduzione=0.6), la dodicesima generazione di linee tracciate è lunga un solo pixel.

Se desiderate fare delle ulteriori prove, vi conviene lavorare con un valore di LivelloMassimo di circa 8, per poi portarlo al valore desiderato quando il risultato vi soddisferà; il tempo di esecuzione del programma con 12 livelli di ricorsione è infatti di circa dieci minuti.

Nel caso che abbiate eseguito il programma coi valori modificati, noterete che il risultato ottenuto differisce in un importante dettaglio da quello mostrato in Figura 3; non preoccupatevi e proseguite nella lettura, il mistero sarà spiegato, come in ogni giallo che si rispetti, alla fine.

Ancora sull'incapsulamento

Come si può notare dagli esempi di questa puntata, continuiamo a racchiudere i programmi principali di ogni esempio fra una coppia di istruzioni gsave e grestore. Questo ci permette di modificare allegramente qualunque valore dello stato grafico senza che i cambiamenti si ripercuotano all'esterno del nostro programma.

È necessario spendere una qualche parola di incoraggiamento, perché la tentazione di scrivere programmi senza prestare attenzione alle regole di buona programmazione come questa è sempre forte; per poter limitare il tempo perso alla caccia di bachi nei vostri programmi, è indispensabile che le singole parti in cui suddividete il programma si influenzino reciprocamente solo dove strettamente necessario. In particolare, è bene avere sempre presenti queste regole (che, come tutte le regole, ammettono anche rare e motivate eccezioni):

  1. una procedura deve lasciare inalterato lo stato grafico che ha trovato all'inizio;
  2. gli stack degli operandi e degli stati grafici non devono contenere oggetti lasciati da altre procedure, se non per il tempo strettamente necessario a passarli da una procedura all'altra (ad esempio contatori di loop for non utilizzati);
  3. ciascun programma deve, nei limiti del possibile, evitare di fare assunzioni sullo stato grafico iniziale, ma considerarlo ignoto, e modificare tutti i parametri non impostandoli con valori fissi, ma con variazioni di quelli esistenti. Il modo di operare deve essere analogo al funzionamento degli operatori PostScript come scale, infatti 2 2 scale non fissa la scala a 2 ma al doppio del valore preesistente.

La fatica dedicata a scrivere programmi che seguono le regole è come un investimento a lungo termine; costa all'inizio, ma viene ricompensata alla fine da un risparmio nel debugging del programma e soprattutto dall'ottenimento di un codice facilmente riutilizzabile.

I programmi conformi

L'utilizzo più comune del PostScript è quello di permettere alle applicazioni che girano sui più diversi tipi di computer di stampare in loro output su una stampante laser; ne consegue che, a parte l'eventuale differenza del collegamento fisico fra computer e stampante (RS-232 seriale, Centronics parallelo, AppleTalk ecc.), il computer invia alla stampante un programma PostScript scritto in caratteri ASCII, esattamente come facciamo noi. Ci sono tuttavia delle importanti differenze, la principale delle quali è che i programmi generati dall'applicazione devono essere più brevi possibile, per diminuire i tempi di trasmissione, ma anche semplici da generare, per poter essere prodotti automaticamente da driver di stampante relativamente semplici. Le due esigenze sono ovviamente in contrasto; i programmi semplici e stupidi tendono ad essere sempre più lunghi di quelli intelligenti.

La soluzione che viene adottata permette di soddisfare tutte e due le esigenze; il programma che viene inviato alla stampante è di solito diviso in due parti.

In ogni ambiente PostScript esiste un metodo per poter salvare, esaminare e modificare i file di stampa; sui sistemi Macintosh consiste nel premere Command-F appena chiusa la finestra di stampa. In questo caso l'applicazione non invia il programma PostScript alla stampante, ma lo salva in un file denominato Postscript0 (1, 2, ecc.). Nel far questo l'applicazione suppone che il preprocessor sia già stato caricato sulla stampante, e non lo inserisce quindi nel file; lo si può includere eseguendo la stessa procedura, ma premendo Command-K.

Dal semplice documento di testo di Word 3.0 mostrato in Figura 4, sono stati ottenuti i Listati 3 (Command-F) e 4 (Command-K).

Nel documento del Listato 3 possiamo individuare un notevole numero di commenti, detti commenti conformi, che iniziano con i caratteri %! oppure %%:

%!PS-Adobe-2.0
%%Title: Prova
%%Creator: Microsoft Word
%%CreationDate: Domenica, 3 dicembre 1989
%%Pages: (atend)
%%BoundingBox: ? ? ? ?
%%PageBoundingBox: 28 30 566 811
%%For: Marco
%%IncludeProcSet: "(AppleDict md)" 68 0
%%EndComments
%%EndProlog
%%BeginDocumentSetup
md begin
......(altri comandi)......
F T cp
%%Trailer
cd
end
%%Pages: 1 0
%%EOF

I programmi PostScript che contengono commenti di questo tipo sono detti programmi conformi; tali commenti sono riconosciuti al di fuori del contesto PostScript da particolari implementazioni dell'interprete stesso, o da apposite interfacce, che possono estrarne informazioni utili a preparare l'elaborazione del programma. L'esempio riportato contiene, nell'ordine, le seguenti informazioni:

  1. inizio del programma conforme (include il livello di conformità PostScript);
  2. titolo del documento;
  3. nome dell'applicazione generatrice;
  4. data di creazione del documento;
  5. numero totale delle pagine (informazione rinviata alla fine del programma);
  6. dimensione del rettangolo che contiene il documento grafico (non applicabile in questo caso, poiché il documento è di solo testo);
  7. dimensione del rettangolo che contiene la pagina;
  8. destinatario del documento.
  9. preprocessor utilizzato dal documento.

Altre informazioni possono essere incluse nel corpo di un programma conforme; alcune di esse sono:

  1. font utilizzati nel programma;
  2. font inclusi nel programma;
  3. font non inclusi nel programma (che devono quindi essere forniti dalla stampante);
  4. documenti (testo, bitmap o grafica) utilizzati nel programma;
  5. documenti inclusi nel programma;
  6. documenti non inclusi nel programma (che devono quindi essere rintracciati esternamente su disco).

Ogni documento PostScript conforme può essere suddiviso in quattro parti principali, che sono:

Il Listato 4, che contiene sia il preprocessor che il documento da stampare, è quindi costituito da due distinti programmi PostScript, ciascuno dei quali termina con %%EOF, ed è suddiviso nelle parti sopraelencate; si noti che alcune parti possono mancare, come nel caso del prologue del documento di stampa.

Un esempio di utilizzo delle informazioni contenute nei commenti dei programmi PostScript conformi è quello del rettangolo grigio che programmi come PageMaker rappresentano per indicare una immagine EPSF importata da un'altra applicazione; le dimensioni di questo rettangolo e le informazioni (titolo, data, ecc.) su di esso riportate sono appunto ricavate dai commenti del file EPSF conforme che è stato incluso nel documento.

Per maggiori dettagli sui programmi conformi potete consultare i testi elencati nella bibliografia fornita alla fine della terza puntata, ed in particolare:

- ref. 1, appendice C, per le regole di conformità minimali e complete nella versione Adobe-1.0;

- ref. 5, capitolo 2 e appendice B, per le regole di conformità complete nella versione Adobe-2.0.

Ritorneremo sull'argomento dei programmi conformi quando tratteremo delle implementazioni PostScript in ambienti di rete e multiutenza.

To be continued .....

Prima di lasciarci vi proponiamo tre esercizi da svolgere.

- Il primo è correlato con il giallo lasciato in sospeso all'inizio di questa puntata; il risultato ottenuto dal programma del Listato 2 modificato differisce da quello mostrato in Figura 3 perché in quest'ultima il frattale non deborda oltre la cornice. Provate quindi a modificare il programma, utilizzando l'operatore clip, in modo tale che il risultato sia identico a quello di figura.

- Il secondo è di modificare ulteriormente il Programma 1 di questa puntata al fine di creare una path forata ed eseguire il riempimento con un unico fill.

- Il terzo è di scrivere una versione non ricorsiva del Programma 2, che faccia uso degli operatori di controllo presentati in questa puntata; si tratta di sostituire la ricorsività con l'iterazione.

Nella prossima puntata riprenderemo a parlare di testi e di font, lasciandovi così il tempo di svolgere gli esercizi sulla grafica; tratteremo per esteso le problematiche dell'utilizzo degli operatori dedicati al trattamento dei testi e forniremo anche qualche cenno sulle principali tecniche di impaginazione.


% Corso PostScript - parte V - programma 1
%
% Possibile svolgimento dell'esercizio 1
% incluso nella IV parte.
%
% Esempio di utilizzo dell'operatore clip
% (Parte IV - programma 3)
% modificato in modo da stampare in grigio
% chiaro lo spazio compreso tra la cornice
% ed il profilo delle lettere 'PS'.
%
% Procedura inch
% Argomenti: XInches / XPoints
% Converte misure da pollici
% a punti tipografici.
/inch { 72 mul } def
%
% Procedura tracciaCorniceModificata
% Argomenti: - / -
% Traccia una cornice su pagina
% A4 a mezzo pollice dal margine
% e riempie l'interno di grigio chiaro.
/tracciaCorniceModificata {
gsave newpath
0.5 inch 0.5 inch moveto
7.3 inch 0 inch rlineto
0 inch 10.5 inch rlineto
-7.3 inch 0 inch rlineto
closepath
%
% MODIFICA ----------------------
% Modifica per riempimento in grigio:
% salva la path corrente in uno stato
% grafico, la riempie con un fill, ed
% infine ripristina lo stato grafico
% precedente.
gsave
0.9 setgray fill
grestore
%
0.05 inch setlinewidth
0 setgray stroke
grestore
} def
%
% Programma principale
% Viene tracciata la cornice della pagina
tracciaCorniceModificata
%
% Incapsulamento
gsave
%
% Raddoppia la scala sugli assi e pone flat=4
2 2 scale
4.0 setflat
%
% Definisce le variabili globali X e Y
/X .5 inch def /Y 2.5 inch def
%
% Sposta il punto corrente a X,Y
X Y moveto
%
% Definisce il font corrente come
% Times-Bold a corpo 200
/Times-Bold findfont 200 scalefont setfont
%
% Definisce la path corrente uguale al
% contorno delle lettere PS
(PS) true charpath
%
% MODIFICA ----------------------
% Modifica per riempimento in bianco:
% salva la path corrente in uno stato
% grafico, la riempie con un fill, ed
% infine ripristina lo stato grafico
% precedente.
gsave
1.0 setgray fill
grestore
%
% Salvo lo stato grafico (per salvare la path
% corrente), la traccia sulla pagina ed infine
% ripristina lo stato grafico
gsave stroke grestore
%
% Definisce la clipping path uguale al
% contorno delle lettere PS
clip
%
% Annulla la path corrente (clip non lo fa)
newpath
%
% Definisce il font corrente come
% Times-Roman a corpo 7
/Times-Roman findfont 7 scalefont setfont
%
% Riempie la clipping path precedentemente
% definita con un pattern formato dalla parola
% PostScript, ottenuto utilizzando un ciclo
% per mezzo dell'operatore for, il quale ripete
% la procedura fra parentesi graffe, ponendo ogni
% volta il contatore aggiornato sullo stack, per
% i valori da 0 a 136 a passi di 8.
0 8 136
{ Y add X exch moveto
(PostScript PostScript PostScript PostScript\
PostScript PostScript PostScript PostScript ) show }
for
%
% Rimozione incapsulamento
grestore
%
% Stampa pagina
showpage

% Corso PostScript - parte V - programma 2
% Esempio di utilizzo ricorsivo del PostScript
%
% Procedura inch
% Argomenti: XInches / XPoints
%
% Converte misure da pollici
% a punti tipografici.
/inch { 72 mul } def
%
% Procedura tracciaCornice
% Argomenti: - / -
%
% Traccia una cornice su pagina
% A4 a mezzo pollice dal margine.
/tracciaCornice {
gsave newpath
0.5 inch 0.5 inch moveto
7.3 inch 0 inch rlineto
0 inch 10.5 inch rlineto
-7.3 inch 0 inch rlineto
closepath
.05 inch setlinewidth
0 setgray stroke
grestore
} def
%
% Procedura giuUnLivello
% Argomenti: - / -
% Variabili Globali: Livello
%
% Incrementa di 1 il valore del
% livello di ricorsione.
/giuUnLivello {
/Livello Livello 1 add def
} def
%
% Procedura suUnLivello
% Argomenti: - / -
% Variabili Globali: Livello
%
% Decrementa di 1 il valore del
% livello di ricorsione.
/suUnLivello {
/Livello Livello 1 sub def
} def
%
% Procedura faiUnaLinea
% Argomenti: - / -
%
% Traccia una linea verticale di
% due pollici nel sistema di
% riferimento corrente, salva
% sullo stack il punto corrente,
% imprime la linea sulla pagina
% (resettando la path ed il punto
% correnti) e ripristina infine
% quest'ultimo.
/faiUnaLinea {
0 inch 2 inch rlineto
currentpoint stroke
translate
0 inch 0 inch moveto
} def
%
% Procedura frecciaFrattale
% Argomenti: - / -
% Variabili Globali: Livello,
% RapportoDiRiduzione,
% SpessoreDellaLinea,
% LivelloMassimo
%
% Salva lo stato grafico, scala
% il sistema di riferimento sui
% due assi, aumenta di uno il
% livello di ricorsione, traccia la linea,
% controlla che il livello
% di ricorsione sia minore di quello
% massimo, ed infine richiama
% due volte se stessa, dopo aver
% ruotato il sistema di riferimento
% prima a destra e poi a sinistra.
/frecciaFrattale {
gsave
RapportoDiRiduzione
RapportoDiRiduzione scale
SpessoreDellaLinea setlinewidth
giuUnLivello faiUnaLinea
Livello LivelloMassimo le
{
+135 rotate frecciaFrattale
-270 rotate frecciaFrattale
} if
suUnLivello grestore
} def
%
%
% Programma principale
%
% Incapsulamento
gsave
%
% Si assegnano i valori iniziali
% alle variabili globali.
/Livello 0 def
/LivelloMassimo 12 def
/RapportoDiRiduzione 0.6 def
/SpessoreDellaLinea 0.12 inch def
%
% Si traccia la cornice della pagina
tracciaCornice
%
% Si fissa il punto iniziale e la scala
4 inch 4 inch moveto
0 inch 0 inch translate
4 4 scale
1 setlinecap
%
% Viene richiamata la procedura ricorsiva
frecciaFrattale
%
% Rimozione incapsulamento e stampa
grestore
showpage

%!PS-Adobe-2.0
%%Title: Prova
%%Creator: Microsoft Word
%%CreationDate: Domenica, 3 dicembre 1989
%%Pages: (atend)
%%BoundingBox: ? ? ? ?
%%PageBoundingBox: 28 30 566 811
%%For: Marco
%%IncludeProcSet: "(AppleDict md)" 68 0
%%EndComments
%%EndProlog
%%BeginDocumentSetup
md begin
T T -30 -28 811 566 100 72 72 3 F F F F T T F psu
(Marco; document: Prova)jn
0 mf
od
%%EndDocumentSetup
%%Page: ? 1
op
30 28 xl
1 1 pen
99 85 gm
(nc 30 28 811 566 6 rc)kp
1 setTxMode
0 fs
bu fc
{}mark T /Times-Roman /|______Times-Roman 0 rf
bn
18 fz
bu fc
2 F /|______Times-Roman fnt
bn
0.32135 0. 32 0.03213 0.(Testo normale in Times 18)awidthshow
117 85 gm
1 fs
bu fc
{}mark T /Times-Bold /|______Times-Bold 0 rf
bn
bu fc
2 F /|______Times-Bold fnt
bn
2.66677 0. 32 0.26667 0.(Testo neretto in Times 18)awidthshow
135 85 gm
2 fs
bu fc
{}mark T /Times-Italic /|______Times-Italic 0 rf
bn
bu fc
2 F /|______Times-Italic fnt
bn
0.78582 0. 32 0.07858 0.(Testo corsivo in Times 18)awidthshow
F T cp
%%Trailer
cd
end
%%Pages: 1 0
%%EOF
%%Title: "Laser Prep -- The Apple PostScript Dictionary (md)"
%%Creator: Apple Software Engineering
%%CreationDate: Thursday, March 19, 1987
%{appledict version #68 0
% © CopyRight Apple Computer, Inc. 1984,1985,1986,1987,1988 All Rights Reserved.
%%EndComments
%%BeginProcSet: "(AppleDict md)" 68 0
statusdict begin waittimeout 300 lt{0 60 300
.
. (un sacco di altra roba)
.
cleartomark
%%EndProcSet
%%EOF
%!PS-Adobe-2.0
%%Title: Prova
%%Creator: Microsoft Word
%%CreationDate: Domenica, 3 dicembre 1989
%%Pages: (atend)
%%BoundingBox: ? ? ? ?
%%PageBoundingBox: 28 30 566 811
%%For: Marco
%%IncludeProcSet: "(AppleDict md)" 68 0
%%EndComments
%%EndProlog
%%BeginDocumentSetup
md begin
T T -30 -28 811 566 100 72 72 3 F F F F T T F psu
(Marco; document: Prova)jn
0 mf
od
%%EndDocumentSetup
%%Page: ? 1
op
30 28 xl
1 1 pen
99 85 gm
(nc 30 28 811 566 6 rc)kp
1 setTxMode
0 fs
bu fc
{}mark T /Times-Roman /|______Times-Roman 0 rf
bn
18 fz
bu fc
2 F /|______Times-Roman fnt
bn
0.32135 0. 32 0.03213 0.(Testo normale in Times 18)awidthshow
117 85 gm
1 fs
bu fc
{}mark T /Times-Bold /|______Times-Bold 0 rf
bn
bu fc
2 F /|______Times-Bold fnt
bn
2.66677 0. 32 0.26667 0.(Testo neretto in Times 18)awidthshow
135 85 gm
2 fs
bu fc
{}mark T /Times-Italic /|______Times-Italic 0 rf
bn
bu fc
2 F /|______Times-Italic fnt
bn
0.78582 0. 32 0.07858 0.(Testo corsivo in Times 18)awidthshow
F T cp
%%Trailer
cd
end
%%Pages: 1 0
%%EOF

Copyright © 1985: Marco A. Calamari