Cet article présente PostScript du point de vue du développeur.
Vous avez probablement déjà entendu parler de PostScript. Non ? d’EPS alors. Toujours pas ? donc de PDF (ne répondez pas non, je sais que vous mentez).
Pour poser le décor, PostScript a été créé en 1982 et son développement stoppé en 2007 au profit de PDF :
PostScript est un langage de programmation. Oui, oui, vous avez bien lu : PostScript est un langage de programmation. Plus précisément,PostScript est un langage de programmation orienté pile à typage dynamique fort influencé par le langage Forth.
Voici quelques ressources utiles sur PostScript :
Ce langage offre les fonctionnalités suivantes :
Étant donnée la destination de ce langage, l’interface utilisateur est réduite à la portion congrue : PostScript peut seulement afficher des informations textuelles ou graphiques, il n’est pas possible d’interagir avec un programme PostScript depuis une souris ou un clavier.
Les exemples donnés sur cette page nécessite un interpréteur PostScript. Vous pouvez utiliser GhostScript. Sous Debian/Ubuntu, vous pouvez l’installer avec la commande :
sudo apt-get install ghostscript
Pour lancer l’interpréteur, il suffit de taper dans un terminal :
gs
PostScript est un langage orienté pile. Pour les programmeurs habitués aux langages traditionnels, cela oblige à une gymnastique de l’esprit particulière qui rappelle la notation en polonaise inversée. Par exemple, le Hello world ! en PostScript s’écrit de la façon suivante :
(Hello world !\n) print
L’exemple donné ci-dessus se comprend comme :
Notes :
PostScript manipule plusieurs types de données :
(chaîne de caractères)
/ValeurLitérale
2387
<< /Clé valeur >>
[ 2 4 8 16 32 64 ]
{ (Hello world !) print }
Les commentaires sont signalés par un%
et se poursuivent jusqu’à la fin de la ligne à l’instar de//
en C++.
Vous pouvez saisir ces exemples dans l’interpréteur de GhostScript.
1 2 3 4 5 6 add sub add sub add =
Cet exemple doit afficher la valeur 7. Le calcul laisse une unique valeur au sommet de la pile. La fonction=
permet de dépiler cette valeur et de l’afficher. Le pseudo-code suivant explique le fonctionnement de cet exemple :
(1 (2 (3 (4 (5 6 add) sub) add) sub) add) % Mise en évidence de la portée
(1+(2-(3+(4-(5+6))))) % notation in-fixe traditionnelle
(1+(2-(3+(4-11))))
(1+(2-(3+-7)))
(1+(2--4))
(1+6)
7
Il existe deux fonctions de test en PostScript :if
etifelse
.if
consomme deux éléments (une valeur booléenne et le code à exécuter si celle-ci est àtrue
) de la pile tandis queifelse
en consomme trois (une valeur booléenne, le code à exécuter si la valeur booléenne est àtrue
et le code à exécuter si elle est àfalse
). Exemple :
3 4 lt { (3 < 4 !) = } if
La fonctionlt
teste si 3 est inférieur à 4.lt
retournanttrue
,if
exécute le code entre accolades et affiche la chaîne3 < 4 !
.
En mettant en forme, le code pourrait ressembler à :
3 4 lt {
(3 < 4 !) =
} if
En PostScript, la bouclefor
prend 4 paramètres sur la pile : la valeur initiale, l’incrément, la valeur finale (incluse !) et enfin le code à exécuter à chaque itération. À chaque itération, for va placer la valeur courante au sommet de la pile à disposition du code. Il est de la responsabilité de la boucle de dépiler cette valeur. Saurez-vous dire le résultat affiché par la ligne suivante ?
0 1 1 10 { add } for =
Cette fonction est l’équivalent en PHP de :
$sum = 0;
for($i = 0; $i <= 10; $i++) {
$sum += $i;
}
echo $sum;
L’utilisation intensive de la pile en PostScript a tendance à faire disparaître les variables intermédiaires. Vous aurez sûrement remarqué qu’il y a 5 éléments et non 4 avant l’appel à for. Le premier 0 placé au sommet de la pile correspond à la valeur initiale de la somme. La fonctionadd
consomme deux nombres sur la pile et en replace un (le résultat). Sans empiler une valeur au sommet de la pile avant la première itération,add
n’aurait pas suffisamment de valeur. Après la dernière itération,add
aura laissé la dernière valeur au sommet de la pile et=
permet de l’afficher.
Au fait, vous devriez voir 55 ;-)
Une version un peu plus lisible de l’exemple :
% Initial value
0
% Compute the sum of 1 to 10
1 1 10 {
add
} for
À l’instar de Lisp ou Scheme, le formatage du code source et les commentaires sont primordiaux à une lecture aisée du code PostScript.
En PostScript, les variables sont gérées à l’aide de dictionnaires.
Définir ou affecter une valeur à une variable se fait avec la fonctiondef
:
% total = 0
/total 0 def
% total = total + 1
/total total 1 add def
Pour être plus précis, la fonctiondef
définit un couple clé-valeur dans le dictionnaire courant. Quand l’interpréteur rencontretotal
, il recherche dans le dictionnaire courant la clétotal
et empile sa valeur.
Le dictionnaire courant n’est pas limité à de simples valeurs. Il est également possible d’y stocker du code.
% Define x addthree x+3
/addthree { 3 add } def
% Display 5+3 = 8
5 addthree =
Note : il existe 2 méthodes pour la création de fonctions avec ou sansbind
.bind
permet de précompiler la fonction. Démonstration :
/addthree1 { 3 add } def
/addthree2 { 3 add } bind def
5 addthree1 = % print 8
5 addthree2 = % print 8
/add { sub } def % add becomes sub !
5 addthree1 = % print 2 !
5 addthree2 = % print 8
L’exécution deaddthree1
est complètement dynamique tandis que l’édition de liens deaddthree2
a été réalisée lors de sa définition.
Voici un exemple un peu plus élaboré :
%!PS-Adobe-3.0
(===========================) =
(Postscript language example) =
(===========================) =
% Initializes random seed
realtime usertime add srand
% Binary random value generator
/brand {
rand 2 mod
} def
% Create a line of 10 characters
/line (0123456789) def
% Create 10 lines
0 1 9 {
pop % discard counter (we don't use it)
% Fill a line with 10 X or O character
0 1 9 {
% Test binary value
brand 0 eq {
line exch 79 put % display an O
} {
line exch 88 put % display an X
} ifelse
} for
% Display a 10 characters line
line =
} for
% Display the remaining stack (it should print nothing)
pstack
% Explicit exit. The interpreter would otherwise for user input
quit
Pour l’exécuter, il suffit d’utiliser la commande :
gs -dQUIET exemple01.ps
L’option-dQUIET
demande à l’interpréteur de ne pas afficher d’information sur sa version.
Après exécution, la sortie devrait ressembler à cela :
===========================
Postscript language example
===========================
XXOOXXXOXX
OOXOXOOXOO
OOXXXXOXOO
OOXXXOXXOX
OOOXXOOXOO
OXOOXXXOXO
OOOOOXOXOO
XXOXOOOOXO
OXXOXOOOXO
XXXXXXOXOO
Les fichiers PostScript (.ps) commencent par l’entête :
%!PS-Adobe-3.0
Affichage du grand titre :
(===========================) =
(Postscript language example) =
(===========================) =
Initialisation du générateur de nombres aléatoires. On utilise les fonctionsrealtime
etusertime
pour initialiser (srand
) le générateur afin de générer un nombre réellement aléatoire mais le but de PostScript est de reproduire, non pas de créer, la séquence sera souvent la même.
% Initializes random seed
realtime usertime add srand
Création d’une fonction de génération de nombres binaires aléatoires (0 ou 1). Pour cela on utilise la fonctionrand
qui retourne un nombre aléatoire entre 0 et 2^31-1. La valeur retournée est calculée modulo 2, ce qui ramène les valeurs entre 0 et 1.
% Binary random value generator
/brand {
rand 2 mod
} def
Création d’une chaîne de caractèresline
de 10 caractères
% Create a line of 10 characters
/line (0123456789) def
Bouclefor
pour l’affichage de 10 lignes
% Create 10 lines
0 1 9 {
Comme on n’utiliseras pas la valeur d’itération de cette boucle, on la dépile avecpop
sinon les valeurs successives viendront polluer la pile.
pop % discard counter (we don't use it)
Remplissage de la ligne (10 caractères) avec des X et des O.
% Fill a line with 10 X or O character
0 1 9 {
Génère une valeur binaire aléatoire et teste les résultat.
% Test binary value
brand 0 eq {
La valeur binaire aléatoire est 0, on place la chaîne sur la pile et on échange sa place avec la valeur d’itération qui correspond à la position du caractère à modifier. L’échange est obligatoire car après l’empilement de la variableline
, la pile est dans l’état "position line" alors que la fonctionput
requiert 3 éléments dans l’ordre "line position valeur". Le nombre 79 correspond au O majuscule en Ascii.
line exch 79 put % display an O
} {
On fait la même chose pour l’autre possibilité avec un X majuscule (88 en Ascii).
line exch 88 put % display an X
} ifelse
} for
Affichage de la ligne générée
% Display a 10 characters line
line =
} for
La ligne suivante permet d’afficher l’état de la pile après notre code afin de nous assurer qu’il n’y a aucune fuite mémoire.
% Display the remaining stack (it should print nothing)
pstack
Appel de la fonctionquit
pour sortir de l’interpréteur. Si cet appel n’est pas fait, l’interpréteur reste en mode interactif.
% Explicit exit. The interpreter would otherwise for user input
quit