Una Chiave per il Desktop Publishing

Corso di PostScript - Marco A. Calamari

Undicesima Puntata


Oggi parleremo del problema della retinatura nella stampa con periferiche PostScript; buona parte della puntata sarà dedicata quindi all'utilizzo dell'operatore setscreen.


Durante tutto il corso, ci siamo spesso serviti dell'operatore setgray per variare il tono di grigio delle varie parti delle nostre stampe; non ci siamo mai occupati però di che cosa accada esattamente nell'interprete PostScript quando devono essere prodotti i toni di grigio.

La stampa con retini

Come sappiamo, la maggior parte delle stampanti laser e delle unità di fotocomposizione sono capaci di riprodurre solo il bianco ed il nero; per produrre i toni di grigio è necessario adoperare il meccanismo della retinatura. Si tratta dello stesso procedimento che deve essere utilizzato per riprodurre le fotografie sui quotidiani; le macchine da stampa con cui essi sono realizzati possono infatti stampare solo il nero pieno. Nei tempi andati, quando la retinatura era eseguita con metodi fotografici, si riproduceva la fotografia da stampare attraverso un retino (di solito costituito da piccoli cerchi trasparenti su fondo nero); sviluppando poi l'immagine così ottenuta ad alto contrasto con due passaggi su pellicola fotomeccanica si otteneva un nuovo positivo.

Con questa tecnica, nelle zone più chiare dell'immagine originale si formano dei puntini neri più piccoli di quelli del retino, mentre nelle zone più scure i puntini adiacenti si impastano tra loro, dando luogo ad un retino nero con losanghe bianche, tanto più piccole quanto più scuro era l'originale. Questo effetto può essere notato anche nel retino che la LaserWriter utilizza per default. Si esamini ad esempio la scala dei toni di grigio stampata dal Programma 1 della Parte VIII; è facilmente riconoscibile l'effetto di transizione da pallini neri a losanghe bianche che abbiamo appena descritto.

La retinatura in PostScript

Il PostScript ha la possibilità di variare sia il retino che la scala dei toni di grigio; per poter introdurre i relativi operatori, è necessario prima spendere due parole descrivendo il meccanismo che l'interprete PostScript utilizza per generare il retino.

Si immagini di sovrapporre alla bitmap della pagina memorizzata nella stampante una griglia uniforme di celle quadrate, ottenute raggruppando n*n pixel. Questa griglia possiede una frequenza (numero di celle per pollice) ed un angolo (angolo di orientamento della griglia rispetto al sistema di coordinate, espresso in gradi). Ciascuna cella può essere resa di un particolare tono di grigio dipingendo di bianco un certo numero di pixel e di nero gli altri. Numericamente, la percentuale di nero che si ottiene è pari al rapporto tra il numero di pixel anneriti ed il numero dei pixel che formano ogni cella; questo significa che se una cella ha il lato di n pixel, e ne contiene quindi n*n, può riprodurre n*n+1 toni di grigio, percentualmente spaziati in modo uniforme fra 0 (nero pieno) e 1 (bianco pieno). Non è quindi in generale possibile riprodurre esattamente una particolare sfumatura di grigio, ma solo approssimarla con la più vicina consentita dalla grandezza della cella utilizzata. A parità di risoluzione della periferica PostScript, aumentando il numero di pixel che formano una cella si possono riprodurre un maggior numero di toni di grigio; questo va però a scapito della frequenza del retino, e quindi della risoluzione dell'immagine finale. In parole povere, si deve trovare un compromesso tra risoluzione dell'immagine e numero di toni di grigio, in modo da non avere quelle scalettature che spesso compaiono nelle zone che sfumano gradatamente da bianco al nero.

Supponiamo di far variare il tono di grigio di una cella dal nero al bianco. per far questo è necessario definire l'ordine in cui i pixel devono essere resi bianchi, che sarà poi sempre lo stesso; se quindi un certo tono di grigio corrisponde a dipingere di bianco un ben definito insieme di pixel della cella, un altro tono più chiaro sarà realizzato aggiungendo al precedente insieme di pixel qualche altro pixel bianco. L'ordine col quale viene eseguito questo sbiancamento si chiama spot function del retino.

Si noti che il retino è definito nel sistema di coordinate della periferica, e che quindi non viene modificato dagli operatori di scalatura e rotazione del sistema di coordinate utente che modificano la CTM (scale, translate, ecc.).

È anche possibile agire sui toni di grigio di un'immagine, alterandone globalmente il valore, per mezzo della transfer function (funzione di trasferimento) del PostScript; la funzione di trasferimento mette in correlazione il tono di grigio di una zona dell'immagine (definito dall'operatore setgray) con la frazione di pixel bianchi nella cella. Questa funzione è per default una proporzionalità diretta; questo significa che un gray level di 0.2 fa stampare in bianco il 20% dei pixel di ogni cella, ed in nero il rimanente 80%; questa banale funzione di trasferimento può però essere sostituita con altre più complesse al fine di ottenere risultati particolari, come vedremo fra poco.

I nuovi operatori

Prima di procedere oltre, è necessario definire gli operatori che vengono utilizzati per modificare retino e funzione di trasferimento:

La spot function, come abbiamo detto, definisce l'ordine in cui i pixel di una cella devono essere resi bianchi, al fine di ottenere un certo tono di grigio; l'interprete PostScript la realizza in un modo indiretto, al fine di minimizzare l'interferenza con la frequenza e l'angolo del retino prescelto. Si consideri il sistema di riferimento che descrive le coordinate di ciascun pixel all'interno della cella; in tale sistema l'origine è nel centro della cella, e le coordinate x ed y variano fra -1 e +1. Il pixel al centro della cella ha quindi coordinate nulle, e quelli ai quattro angoli coordinate uguali a +1 o -1. Per stabilire l'ordine in cui rendere bianchi i pixel, l'interprete PostScript pone le coordinate di ciascuno di essi sullo stack ed esegue la spot function; essa deve ritornare un singolo numero reale compreso fra -1 ed 1. L'operazione viene ripetuta per ciascun pixel, e quindi questi vengono ordinati in modo crescente rispetto al valore della spot function posseduto. I singoli valori della spot function non sono quindi significativi; l'informazione importante è infatti solo l'ordine in cui essa pone i vari pixel. Nel caso che due o più pixel abbiano lo stesso valore della spot function, l'ordine viene scelto arbitrariamente dall'interprete stesso.

Quando l'interprete PostScript deve stampare un certo tono di grigio in una zona dell'immagine, calcola per mezzo della funzione di trasferimento la frazione di pixel da rendere bianchi, definisce il numero m di pixel bianchi moltiplicando questa frazione per il numero di pixel che costituiscono la cella (arrotondando in eccesso il risultato), calcola la bitmap della cella dipingendo di bianco i primi m pixel nell'ordine stabilito dalla spot function corrente ed in nero tutti gli altri, e stampa ripetutamente la bitmap così calcolata in tutta la zona da riempire.

Un esempio pratico

La spot function, stabilendo l'ordine con cui i pixel devono essere dipinti di bianco, definisce anche l'aspetto geometrico del retino. Vediamo quali valori essa assume, insieme agli altri parametri che definiscono retino e funzione di trasferimento, nel caso di una LaserWriter; per far questo colleghiamoci all'interprete PostScript in modalità interactive (ricordiamo che PS> è il prompt dell'interprete in modalità interattiva, e che il doppio carattere meno tra cui sono racchiusi alcuni operatori è solo una rappresentazione usata dall'operatore pstack per indicare una situazione particolare di sostituzione all'interno della procedura, che può essere tranquillamente ignorata per i nostri scopi).

PostScript(tm) Version 38.0
Copyright (c) 1985 Adobe Systems Incorporated.
PS>statusdict begin
PS>vmstatus pstack clear
239216
79016
2
PS>product pstack clear
(LaserWriter Plus)
PS>version pstack clear
(38.0)
PS>currenttransfer pstack clear
{}
PS>currentscreen pstack clear
{--dup----mul----exch----dup----mul----add--1.0 --exch----sub--}
45.0
60.0

Da questa breve sessione interattiva, iniziata rendendo accessibile statusdict che contiene alcune delle informazioni necessarie, possiamo rilevare la memoria totale e utilizzata, il tipo di stampante a cui siamo collegati e la versione dell'interprete che stiamo usando.

L'operatore currenttransfer ci permette di vedere che la funzione di trasferimento corrente è una procedura nulla, che si limita a lasciare il livello di grigio sullo stack, facendolo direttamente utilizzare come frazione dei pixel da rendere bianchi.

L'operatore currentscreen ci consente invece di sapere che la LaserWriter utilizza per default un retino a 60 linee per pollice con angolo di 45 gradi. La spot function è definita, come si può vedere, dalla formula

1 - (X^2 +Y^2)

Poiché, come abbiamo visto, le coordinate dei pixel nella cella variano tra -1 e +1, questo ordina i pixel in modo tale che i primi ad essere resi bianchi sono quelli agli angoli della cella, mentre l'ultimo è quello al centro.

Nella pratica tipografica la frequenza del retino è il parametro fondamentale di lavoro, e può variare dalle 60 alle 150 linee per pollice. Come abbiamo appena detto, la LaserWriter, che ha una risoluzione di 300 punti per pollice, utilizza normalmente un retino di frequenza 60 linee; questo significa che la cella ha un lato di

300 / 60 = 5 pixel

e che il numero di differenti toni di grigio riproducibili è quindi di

5*5 + 1 = 26 toni

Questo valore piuttosto basso provoca di solito vistose scalettature delle sfumature, che possono essere fatte scomparire stampando lo stesso programma PostScript su una periferica a risoluzione maggiore. Ad una risoluzione di 2540 punti per pollice, comune nelle unità di fotocomposizione, queste scalettature scompaiono, perché a 60 linee per pollice il numero di toni di grigio riproducibili sale a

(2540/60) ^ 2 + 1 = 1793

Questo è un esempio di come il PostScript permetta di sfruttare al massimo le caratteristiche della periferica di stampa, potendo usare lo stesso identico programma di descrizione della pagina, ed ottenendo sempre il miglior risultato possibile.

A completamento di quanto esposto sopra dobbiamo aggiungere che, poiché il numero di pixel che costituiscono il lato di una cella è una variabile intera, non tutti i valori delle coppie frequenza/angolo sono ammissibili. Per questo motivo, quando si utilizza l'operatore setscreen, l'interprete può apportare correzioni ai suddetti valori senza però darne alcun avviso; è bene quindi esaminare, con l'ausilio dell'operatore currentscreen, i valori effettivamente adottati dall'interprete PostScript, specialmente nel caso che essi siano critici (ad esempio per una separazione in quadricromia).

Avanti con gli esercizi!

Nel programma del Listato 1, il cui risultato è mostrato in Figura 1, viene stampata per quattro volte una stringa; la prima con il retino di default e le altre con un retino a linee. Quest'ultimo viene ottenuto con una spot function che scarta la coordinata x ed ordina i pixel in base all'inverso del valore assoluto della coordinata y. La terza stringa mostra l'effetto di una variazione dell'angolo, mentre la quarta mostra come il retino cambia al variare del tono di grigio (si noti che le prime tre stringhe sono stampate con lo stesso valore del tono di grigio).

Il programma del Listato 2 (vedi Figura 2) è la modifica di uno degli esempi delle precedenti puntate, e mostra, nella zona della scala dei grigi, come varia la struttura del retino lineare all'aumentare della percentuale di grigio.

Qualche parola in più merita il programma del Listato 3, il cui risultato è mostrato in Figura 3. Anche questo programma è una modifica di un vecchio esempio (rispetto alla precedente versione questa rispetta anche le regole di conformità PostScript), e mostra come si possa, con l'aggiunta di una sola riga che modifica la funzione di trasferimento, ottenere il negativo della pagina precedentemente stampata. Questo può essere molto utile per produrre direttamente l'output utilizzabile in fotocomposizione per quei sistemi di stampa che richiedono una pellicola negativa. In questo caso la procedura che definisce la funzione di trasferimento è semplicemente

{ 1 exch sub}

Possono essere facilmente realizzate procedure che consentono effetti più complessi; come esercizio potreste provare a scrivere le procedure delle funzioni di trasferimento che permettono di ottenere una stampa ad alto contrasto od una posterizzazione a sei livelli di un'immagine.

Saldiamo un debito

Due mesi fa, abbiamo fatto l'esempio (Parte IX, Programma 4) di un programma PostScript che permetteva di stampare del testo lungo una linea qualsiasi, rimandandone la spiegazione per motivi di spazio. È giunto ormai il momento di saldare questo debito, cominciando innanzitutto a segnalare che il precedente listato contiene un errore che ne può impedire il funzionamento. Le due (arcinote) procedure inch e tracciaCornice sono state erroneamente poste all'interno del dizionario stampaSuLineatDict, che è locale alla sola procedura stampaSuLinea; questo provoca un errore del tipo undefined, a meno che le due procedure non siano già state memorizzate nella stampante da un programma precedente compreso nello stesso job. Per correggere il problema, basta spostare le due procedure incriminate all'inizio del programma (subito dopo il %%EndProlog), in modo che non vengano incluse nel dizionario stampaSuLineatDict.

Il listato, che inizia con i soliti commenti di conformità, genera un dizionario, destinato a contenere tutte le sottoprocedure necessarie, la procedura stampaSuLinea, ed esegue poi il programma principale (che è come sempre incapsulato in una coppia gsave/grestore), terminando con il classico End-Of-File.

Il programma principale salva lo stato grafico, traccia come sempre la cornice della pagina e inizializza il font corrente (il solito Times-Bold); viene poi generata, come path corrente, la linea lungo la quale deve essere disposto il testo, avendo cura di resettare la path all'inizio per evitare di includerne una preesistente. Il programma pone quindi sullo stack la stringa contenente il testo da stampare ed un numero che fornisce l'offset, cioè la distanza, misurata dall'inizio della path, alla quale disporre il primo carattere della stringa. Da ultimo viene eseguita la chiamata alla procedura principale stampaSuLinea; seguono la rimozione dell'incapsulamento e la stampa della pagina.

La procedura stampaSuLinea apre per prima cosa il dizionario stampaSuLineaDict, nel quale sono contenute tutte le sottoprocedure necessarie al suo funzionamento, pone i due argomenti che le sono stati passati per mezzo dello stack in due variabili, ed inizializza altre tre variabili (la lunghezza percorsa sulla linea, la lunghezza di testo già stampata ed il contatore dei caratteri stampati). Dopo un altro incapsulamento, viene eseguito l'operatore flattenpath, che, come ricordiamo, modifica la path corrente, che in questo caso è formata da segmenti ed archi di circonferenza, approssimandola con una unica spezzata che consiste solo di segmenti (cioè di operatori moveto, lineto e closepath). Il loop principale della procedura viene ottenuto ponendo quattro sottoprocedure sullo stack ed eseguendo l'operatore pathforall; come ricordiamo esso esegue, per ogni elemento che costituisce la path corrente ed a seconda del suo tipo, una delle quattro procedure passate come argomenti. Prima di terminare, la procedura stampaSuLinea rimuove l'incapsulamento, cancella la path corrente e chiude il dizionario precedentemente aperto.

Il dizionario stampaSuLineaDict contiene (non in questo ordine) le seguenti sottoprocedure:

C'e' programma e programma !

In questi mesi, abbiamo più volte sottolineato come sia importantissimo, programmando in PostScript, scrivere un codice leggibile e ben commentato; i programmi PostScript infatti, come e più di quelli scritti con altri linguaggi, tendono a diventare rapidamente illeggibili anche per chi li ha scritti. Un lettore ci ha detto Ho notato che i programmi PostScript contenuti nei preprocessor delle applicazioni o nello stesso LaserPrep Apple sono scritti in maniera ben diversa da quelli del corso; nessun commento e nomi brevi ed illeggibili. Per quale motivo?.

Le ragioni principali sono due; la prima è che l'utilizzo di nomi brevissimi abbrevia il programma, che è quindi downloadabile più rapidamente alla stampante, ed in ultima analisi è più efficiente. La seconda regione è che il codice PostScript di un preprocessor rappresenta un patrimonio della software house che lo ha scritto, ma deve essere reso di pubblico dominio, perché va incluso, in forma leggibile nell'applicazione stessa. E' logico quindi che si tenti di renderne il più difficile possibile l'interpretazione alla concorrenza; per questo motivo, prima di rilasciare il programma all'esterno, si rimuovono tutti i commenti dal sorgente originale, e si sostituiscono, con una serie di find & replace, tutti i nomi di variabili e procedure con nomi brevissimi ed incomprensibili, di solito formati da uno o due caratteri.

Per dimostrare l'efficacia di questo modo di operare, consideriamo il programma del Listato 4; si tratta dell'esempio che stampa il testo lungo una linea qualsiasi. La sua lunghezza si e' ridotta da 4500 a 1500 caratteri, e la struttura e' assolutamente incomprensibile, eppure (riuscendo a batterlo senza errori) funziona perfettamente, ed è leggermente più veloce dell'originale.

... to be continued

La prossima puntata, che sarà l'ultima del corso, introdurrà gli operatori di gestione del colore; dedicheremo inoltre la parte finale ad alcune considerazioni su cloni PostScript e su come è cambiato, nell'anno che abbiamo passato insieme, il panorama del PostScript e dei linguaggi di composizione della pagina in generale.


%!PS-Adobe-2.0
%%Title: Esempio di utilizzo di diversi retini con setscreen
%%Creator: Corso PostScript - parte XI - programma 1
%%For: Corso PostScript BIT
%%CreationDate: (4-10-1989) (15:56)
%%BoundingBox: 28 30 566 811
%%EndComments
%%EndProlog
%
% 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 stampaNormale
% Argomenti: stringa / -
%
/stampaNormale {
/Stringa exch def
gsave
gsave 0.02 inch setlinewidth 0 setgray
Stringa true charpath stroke grestore
Stringa show currentpoint
grestore moveto
} def
%
% Procedura stampaConRetino
% Argomenti: stringa frequenza angolo / -
%
/stampaConRetino {
/Angolo exch def
/Frequenza exch def
/Stringa exch def
gsave
gsave 0.02 inch setlinewidth 0 setgray
Stringa true charpath stroke grestore
Frequenza Angolo {exch pop abs 1 exch sub } setscreen
Stringa show currentpoint
grestore moveto
} def
%
%
% Programma principale
%
% Incapsulamento e tracciatura della cornice
gsave
tracciaCornice
%
% Inizializzazioni
/Times-Bold findfont 2.2 inch scalefont setfont
.6 setgray
%
% Prima stampa (normale)
1.0 inch 8.0 inch moveto
(Retino) stampaNormale
%
% Seconda stampa (retino 10 linee 45 gradi)
1.0 inch 6.0 inch moveto
(Retino) 10 45 stampaConRetino
%
% Terza stampa (retino 25 linee 0 gradi)
1.0 inch 4.0 inch moveto
(Retino) 25 -10 stampaConRetino
%
% Quarta stampa (retino 25 linee 0 gradi)
% e con tono di grigio più scuro
.2 setgray
1.0 inch 2.0 inch moveto
(Retino) 25 -10 stampaConRetino
%
% Rimozione incapsulamento e stampa
grestore
showpage
%
%%Trailer
%%EOF

%!PS-Adobe-2.0
%%Title: Programma di stampa di una bitmap con retino lineare
%%Creator: Corso PostScript - parte XI - programma 2
%%For: Corso PostScript BIT (modifica parte VIII programma 1)
%%CreationDate: (4-10-1989) (15:56)
%%BoundingBox: 28 30 566 811
%%EndComments
%%EndProlog
%
% 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 stampaBitmap
% Argomenti: - / -
% Variabili Globali:
% LarghezzaBitmap
% AltezzaBitmap
% NumeroBitBitmap
% StringaBitmap
% Procedura per la lettura e stampa di una
% bitmap con toni di grigio
/stampaBitmap {
gsave
LarghezzaBitmap
AltezzaBitmap
NumeroBitBitmap
[LarghezzaBitmap 0 0 AltezzaBitmap neg 0 AltezzaBitmap]
{currentfile StringaBitmap readhexstring pop}
image
grestore
}def
%
%
% Programma principale
%
% Incapsulamento e tracciatura della cornice
gsave
tracciaCornice
%
% Retino lineare a 15 linee e -20 gradi di angolo
15 -20 {exch pop abs 1 exch sub } setscreen
%
% Inizializzazione parametri bitmap
% (si noti che il pixel può non essere quadrato)
/LarghezzaBitmap 20 def
/AltezzaBitmap 15 def
/NumeroBitBitmap 8 def
/LarghezzaPixel 0.3 inch def
/AltezzaPixel 0.3 inch def
/StringaBitmap LarghezzaBitmap NumeroBitBitmap mul
8 div ceiling cvi string def
%
% Traslazione e scalatura assi per stampa
1.2 inch 3.5 inch translate
LarghezzaPixel LarghezzaBitmap mul
AltezzaPixel AltezzaBitmap mul
scale
%
% Esecuzione procedura; le righe seguenti alla chiamata
% della procedura stampaBitmap vengono lette dall'operatore
% readhexstring fino al completamento dei dati necessari
% alla definizione della bitmap.
stampaBitmap
0000000000000000000000000000000000000000
00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
00FFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFF00
00FFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFF00
000000000000FFFFFFFFFF000000000000FFFF00
00FFFFFFFF0000FFFFFFFF00FFFFFFFFFFFFFF00
00DDDDDDDDDD0000FFFFFF00FFFFFFFFFFFFFF00
00BBBBBBBBBBBB0000FFFFFFFFFFFFFFFFFFFF00
00999999999999990000FFFFFFFFFFFFFFFFFF00
0077777777777777770000FFFFFFFFFFFFFFFF00
005555555555555555550000FFFFFFFFFFFFFF00
00333333333333333333330000FFFFFFFFFFFF00
0011111111111111111111110000FFFFFFFFFF00
000000000000000000000000000000FFFFFFFF00
0000000000000000000000000000000000000000
%
% Rimozione incapsulamento e stampa
grestore
showpage
%
%%Trailer
%%EOF

%!PS-Adobe-2.0
%%Title: Programma di stampa di un paesaggio stilizzato in negativo
%%Creator: Corso PostScript - parte XI - programma 3
%%For: Corso PostScript BIT (modifica parte III programma 2)
%%CreationDate: (4-10-1989) (15:56)
%%BoundingBox: 28 30 566 811
%%EndComments
%%EndProlog
%
%
% Creiamo il nuovo dizionario e
% poniamolo sullo stack.
/paesaggiodict 10 dict def
paesaggiodict begin
%
% 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 tracciaSfondo
% Argomenti: - / -
% Traccia lo sfondo del paesaggio
/tracciaSfondo {
newpath
0.5 inch 0.5 inch moveto
7.3 inch 0 inch rlineto
0 inch 4.5 inch rlineto
-7.3 inch 0 inch rlineto
closepath
.3 setgray fill
0.5 inch 4.5 inch moveto
7.3 inch 0 inch rlineto
0 inch 6.5 inch rlineto
-7.3 inch 0 inch rlineto
closepath
.9 setgray fill
} def
%
% Procedura tracciaTriangolo
% Argomenti Base, TonoGrigio / -
% Variabili locali: BaseTriangolo,
% TonoTriangolo
% Traccia un triangolo pieno a
% partire dal punto corrente.
/tracciaTriangolo {
/TonoTriangolo exch def
/BaseTriangolo exch def
currentpoint newpath moveto
BaseTriangolo 0 rlineto
BaseTriangolo 2 div neg dup neg
rlineto closepath
TonoTriangolo setgray fill
} def
%
% Procedura tracciaSole
% Argomenti Raggio, TonoGrigio / -
% Variabili locali: RaggioSole,
% TonoSole
% Traccia un cerchio pieno a
% partire dal punto corrente.
/tracciaSole{
/TonoSole exch def
/RaggioSole exch def
currentpoint newpath moveto
currentpoint RaggioSole 0 180 arc
closepath
TonoSole setgray fill
} def
%
% Togliamo il dizionario completo
% dallo stack
end
%
%
% Inizio programma principale
%
% Si rende disponibile il
% contenuto del dizionario.
paesaggiodict begin
%
% Si modifica la funzione di trasferimento
{ 1 exch sub} settransfer
%
% Si traccia il paesaggio
tracciaSfondo
tracciaCornice
4 inch 4.5 inch moveto
.5 inch 1 inch tracciaSole
1.2 inch 4.4 inch moveto
2.5 inch .2 tracciaTriangolo
4 inch 4.5 inch moveto
2 inch .1 tracciaTriangolo
2.5 inch 4.3 inch moveto
2 inch .1 tracciaTriangolo
1.5 inch 4.3 inch moveto
1 inch .05 tracciaTriangolo
2.2 inch 4.2 inch moveto
1 inch .05 tracciaTriangolo
5 inch 4.3 inch moveto
1.5 inch .05 tracciaTriangolo
%
% Rimozione del dizionario dallo
% stack dei dizionari
end
%
% Rimozione incapsulamento e stampa
grestore
showpage
%
%%Trailer
%%EOF

/c { 72 mul } def /d { gsave newpath 0.5 c 0.5 c moveto 7.3 c
0 c rlineto 0 c 10.5 c rlineto -7.3 c 0 c rlineto closepath
.05 c setlinewidth 0 setgray stroke grestore } def /a 30 dict
def a begin /e { /j exch def /i exch def /k i def /l j def
/m 0 def i j transform /q exch def /p exch def } def /f { /n
i def /o j def /j exch def /i exch def /r i n sub def /s j o
sub def /v r dup mul s dup mul add sqrt def v 0 ne { /t r v
div m mul def /u s v div m mul def n t add o u add transform
/q exch def /p exch def /y y v add def { x y le {z ad length
lt {aa} {exit} ifelse } { /m x y sub def exit } ifelse }
loop } if } def /h { (Non ci possono essere curveto dopo un
flattenpath!\n) (L'impossibile è avvenuto !\n) (ERRORE: \n)
print print print } def /g { k l f k l e } def /aa { /ab ad
z 1 getinterval def /z z 1 add def /ac ab stringwidth pop def
gsave p q itransform translate s r atan rotate 0 0 moveto ab
show currentpoint transform /q exch def /p exch def grestore
/x x ac add def } def end /b { a begin /Offset exch def /ad
exch def /y 0 def /x Offset def /z 0 def gsave flattenpath
{e} {f} {h} {g} pathforall grestore newpath end } def gsave
d /Times-Bold findfont 24 scalefont setfont newpath 2.25 c
5.5 c moveto 5.25 c 7.5 c 2 c 270 180 arc 3.25 c 3.5 c lineto
2.25 c 3.5 c 1 c 0 180 arcn 1.25 c 11.0 c lineto
(Programmare in PostScript è un pò contorto all'inizio, \
ma i superstiti poi si abituano. D'altronde, come diceva \
John Belushi, "... quando il gioco si fa duro, i duri \
cominciano a giocare".)
0 b grestore showpage

Copyright © 1985: Marco A. Calamari