HomeMapIndexSearchNewsArchivesLinksAbout LF
[Top Bar]
[Bottom Bar]

von:Emre Demiralp

Inhalt:
Einführung
Grundlegendes
Zeichnen
Text
Farbgebung
Weitergehende Information

PostScript

Zusammenfassung: Dieser Artikel ist der erste Teil einer Reihe über die Programmiersprache PostScript. Wir wollen dabei weniger jedes Detail dieser Sprache herausarbeiten, als vielmehr eine Übersicht zur Verfügung stellen, die es Interessierten erlaubt, Dokumente direkt in dieser interessanten Sprache zu erstellen. Und obwohl wir dabei keine technische Abhandlung, ein Lehrbuch oder gar 'die Bibel' zu diesem Thema erstellen wollen, werden wir alle hierzu nötige Information abdecken.


Worst seen with Explorer. Try Netscape instead. 

Einführung

Die Grundkonzepte von PostScript wurden bereits vor 20 Jahren von John Gaffney, damals bei der Evans & Sutherland Computer Corporation, zunächst unter dem Namen "Design System" erarbeitet. Die eigentliche Entwicklung erfolgte durch John E. Warnock und Charles M. Geschke, den Gründern von Adobe Systems Inc., unter dem Namen PostScript als Plattform- und Geräteunabhängiges Layout-Werkzeug. Doug Brotz, Billi Paxton und Ed Taft leisteten wertvolle Beiträge. Heutzutage ist PostScript eines der weitverbreitetsten Instrumente zur Dokumenterstellung, auch wenn dies für die meisten Endbenutzer transparent bleibt. Die Möglichkeiten von PostScript sind enorm, trotzdem vergraben sich die wenigsten in seine Einzelheiten und verwenden eher Textformatierungsprogramme, die größtenteils nach dem Prinzip "What you see is what you get" arbeiten. Viele dieser Programme verwenden jedoch auf PostSript basierende Dateiformate, sei es zum Ablegen des Dokumentes oder als Schnittstelle zum Drucker. In dieser Hinsicht bleibt PostScript als zugrunde liegendes Format aktuell, auch wenn dies, wie bereits erwähnt, in der Regel vor dem Benutzer verborgen bleibt. Wirft man einen Blick hinter die Strukturen, wird man schnell feststellen, daß PostScript nicht allzu schwer zu erlernen ist.

PostScript Befehle werden von einem entsprechenden Interpreter ausgeführt. Weit verbreitete Interpreter sind GhostScript von der Free Software Foundation oder von Aladdin Enterprise. Den meisten Lesern ist wohl eher "GhostView" vertraut, das aber lediglich eine graphische Schnittstelle zwischen GhostScript und dem Benutzer realisiert. Um mit der Sprache PostScript zu experimentieren, bleiben wir bei GhostScript, das sich nach dem Start (via 'ghostscript' oder 'gs') beispielsweise meldet mit

Initializing... done. Ghostscript 2.6.2 (4/19/95) Copyright (C) 1990-1995 Aladdin Enterprises, Menlo Park, CA. All rights reserved. Ghostscript comes with NO WARRANTY: see the file COPYING for details.
GS>_


Gleichzeitig wird ein leeres Fenster, unser "Zeichenblatt", geöffnet. Beendet wird der Interpreter übrigens mit dem Befehl 'quit' - ein EOF (i.e. Ctrl-d) tut's aber auch.

Der Interpreter kann die Kommandos natürlich auch aus einer Datei einlesen, entweder durch Angabe des Dateinamens als Argument beim Aufruf von ghostscript oder direkt durch Eingabe von

GS> (dateiname.ps) run

Die Klammern sind wichtig! Sollte die PostScript-Datei, ab sofort wollen wir lieber von einem Programm sprechen, mehr als eine Seite darstellen, so zeigt der Interpreter die Meldung showpage und wartet auf die Eingabe von 'Return'. Soviel zunächst zur grundsätzlichen Bedienung von ghostscript.

Grundlegendes: Der Stapel (Stack)

PostScript arbeitet nach dem Prinzip der umgekehrten polnischen Notation (UPN oder RPN = 'reverse polish notation'), das der eine oder andere Leser evtl. schon von HP Taschenrechnern her kennt. Das bedeutet, daß sich PostScript Befehle ihre Argumente vom Stapel holen un das Ergebnis wieder dort ablegen.
(Wer's nicht weiß: einen Stapel kann man sich als eine Art Kartenturm vorstellen, auf dem der Interpreter seine Informationen ablegt. Dabei wird auf den Wert, der zuletzt abgelegt wurde, als erstes wieder zugegriffen, ein Prinzip, das als LIFO (last in, first out) bezeichnet wird.)
Ein einfaches Beispiel macht's deutlich: Die Befehlsfolge

   GS> 20
   GS<1> 30
   GS<2> add
   GS<1> 10
   GS<2> sub
   GS<1> ==
   40

oder auch

   GS> 20 30 add 10 sub ==

führt zu einer Stapelabarbeitung der folgenden Form (dargestellt ist immer der Stapel (oder 'Stack') nach Eingabe des jeweilgen Wertes oder Operators):

    20        30        add       10        sub       ==

  -------   -------   -------   -------   -------   -------
  | 20  |   | 30  |   | 50  |   | 10  |   | 40  |   |     |
  -------   -------   -------   -------   -------   -------
  |     |   | 20  |   |     |   | 50  |   |     |   |     |
  -------   -------   -------   -------   -------   -------
  |     |   |     |   |     |   |     |   |     |   |     |
  -------   -------   -------   -------   -------   -------

Der Befehl 'add' holt sich also vom Stapel 2 Argumente, berechnet ihre Summe und legt das Ergebnis wieder auf dem Stapel ab. Der '==' Operator nimmt das oberste Element vom Stapel und gibt es aus. GhostScript gibt dabei in jedem Prompt an, wieviele Elemente der Stapel zum augenblicklichen Zeitpunkt enthält. Die Operatoren für Multiplikation und Division lauten 'mult' und 'div'.
Einige weitere Stackoperationen sind:

   clear:  löscht den Stapel
   dup:    dupliziert das oberste Element
   pop:    löscht das oberste Element
   pstack: stellt den Inhalt des Stacks dar

Wer mit Stackoperationen nicht vertraut ist, spielt am besten erst einmal etwas mit diesen Operatoren. Es sei vermerkt, daß auf dem Stapel nicht nur Zahlen, sondern auch beliebige PostScript Objekte, z.B. Zeichensätze, Felder und Zeichenketten, abgelegt werden können und sich PostScript auch nicht mit nur einem Stapel begnügt. Aber mehr dazu später.

Zeichnen: Koordinaten und Pfade

Bevor wir mit dem Zeichnen beginnen, müssen wir uns zunächst einmal mit dem Seitenaufbau beschäftigen: Der Ursprung (Koordinatenpaar 0,0) liegt unten links; Abstände werden in 'Punkten' == 1/72 inch gemessen (wer's in cm wissen möchte, kann ja gs bemühen), wobei dadurch natürlich nicht die maximale Auflösung festgelegt ist: Es können auch Bruchteile eines Punktes als Längenargument übergeben werden.
PostScript stellt ein paar Standard-Papierformate zur Verfügung: Die Befehle 'legal', 'note', 'a3' und 'a4' (letzteres entspricht 595x842 Punkten) setzen das Zeichenblatt auf die entsprechende Größe (s.a. gs_statd.ps unter /usr/lib/ghostscript).

Der nächste Schritt besteht darin, den Zeichencursor durch den Befehl

   20 200 moveto 

auf den Anfangspunkt 20,200 zu setzen. Wie vorhin schon dargelegt, holen sich die Operatoren ihre Argumente vom Stapel, selbige müssen also immer vorangestellt werden.
Um nun eine Linie nach schräg rechts unten zu ziehen, führen wir den Befehl

   100 150 lineto 

aus. Im Augenblick stellt das Zeichenblatt noch nichts dar, denn wir haben mit den beiden letzten Befehlen nur einen 'Pfad' definiert, der zu allem möglichen herhalten könnte - beispielsweise könnte man einen Text entlang des definierten Pfades setzen. Zum Zeichnen einer Linie entlang des Pfades dient der Befehl

   stroke. 

Nach Ausführung des Befehls vergißt der Interpreter den Pfad und wir können mit dem Befehl

   newpath 

einen neuen beginnen. Ein kleines Programm zum Zeichnen eines Rechteckes könnte also folgendemaßen aussehen:

   newpath          % beginne neuen Pfad
   100 100 moveto   % setze Cursor auf Position 100 100
   300 100 lineto   % Linie nach 300 100
   300 250 lineto   % von dort weiter nach 300 250
   100 250 lineto   % u.s.w.
   100 100 lineto
   stroke           % zeichne den definierten Pfad und vergiß ihn

Wie erwähnt häatte das ganze Programm auch in einer Zeile Platz gefunden: Worttrenner ist jeder Whitespace, d.h. Tab, Leerzeichen und Zeilenumbruch. Kommentare werden durch ein Prozentzeichen abgetrennt.

Koordinaten können auch relativ zur momentanen Position angegeben werden. Das folgende kleine Programm zeichnet ein Kreuz:

   showpage          % zum Löschen der Seite - 
   newpath           % vergiß den alten Pfad und beginne
                     % einen neuen
   200 200 moveto    % Aufpunkt 200 200
   50 100 rlineto    % 50 Punkte nach rechts, 100 nach oben
   0 -100 rmoveto    % 'relative move to': 100 nach unten
   -50 100 rlineto   % 50 Punkte nach links, 100 nach oben
   3.5 setlinewidth  % setze Linienbreite auf 3.5 Punkte
   stroke            % und zeichnen

Mit setlinewidth haben wir einen neuen Befehl kennengelernt, mit dem die Linienbreite gesetzt wird. Da ein Pfad ein abstraktes Objekt ist, ist das auch nach der Pfaddefinition noch möglich. Die Wirkung von setlinewidth hält solange an, bis sie explizit aufgehoben wird.

Da Geraden schnell langweilig werden, wollen wir uns an's Kurvenzeichnen machen:

   x y r a b arc 

zeichnet einen Kreisbogen mit Radius r um den Mittelpunkt x,y von Winkel a nach b, wobei 0 Grad rechts liegt und die Winkelgrade gegen den Uhrzeigersinn gezählt werden. Wer nichts sieht, hat stroke vergessen.
Entspricht der Anfangspunkt des Bogens nicht der aktuellen Cursorposition, wird zusätzlich eine Verbindungslinie zwischen diesen beiden Punkten definiert:

   showpage newpath  % neue Seite, neuer Pfad
   3 setlinewidth
   200 200 moveto
   100 200 100 0 75 arc
   stroke                   % das kennen wir alles schon
   300 300 moveto
   400 500 200 20 50 arc
   stroke    

Will man diese Verbindungslinie unterdrücken, muß der Cursor auf den Startpunkt des Bogens gesetzt werden. Nach dem newpath Befehl ist der aktuelle Punkt übrigens undefiniert und wird auf den ersten gegebenen Punkt (im Falle eines Kreisbogens dessen Startpunkt) gesetzt.

Der arc Befehl kann auch zum Zeichnen von Ellipsen verwendet werden, wie das folgende Beispiel zeigt:

  showpage newpath 2 setlinewidth
  200 100 moveto
  2 1 scale              % skaliere Punkte in x um Faktor 2
  50 100 50 0 360 arc
  0.5 1 scale            % setze Skalierung zurück
  stroke

Der Befehl scale skaliert alle Größen in x und y um den entsprechenden Faktor (genauer gesagt: er skaliert die Größe eines 'Punktes'). Die gegebenen Faktoren sind relativ: Der Befehl

 1 1 scale 

setzt also nicht etwa die Skalierung zurück, sondern bewirkt gar nichts.

   newpath
   200 200 100 0 360 arc
   3 setlinewidth
   stroke
   newpath
   2 1 scale
   200 300 50 0 360 arc
   stroke
   newpath
   1 4 scale
   100 150 40 0 360 arc
   stroke

Wie das vorangegangene Beispiel zeigt, wirkt sich die Skalierung auch auf die Linienbreite in der jeweiligen Achse aus. Es handelt sich also wirklich um eine Skalierung der Punktgröße, nicht der Koordinaten.

Postscript stellt zwei weitere Befehle zur Definition von Kreisbögen zur Verfügung: arcn entspricht arc und zeichnet den Bogen lediglich im Uhrzeigersinn statt entgegengesetzt.

   x1 y1 x2 y2 r arcto

definiert einen Kreisbogen mit Radius r, der an die Verbindungsgeraden vom aktuellen Punkt nach x1,y1 und x1,y1 nach x2,y2 eine Tangente beschreibt. Ist der aktuelle Punkt undefiniert (beispielsweise nach einem showpage oder newpath Befehl), reagiert der Interpreter mit einer Fehlermeldung.

Außerdem stellt PostScript einen Bezier-Algorithmus zur Verfügung:

   x1 y1 x2 y2 x3 y3 curveto

definiert eine Kurve, beginnend im aktuellen Punkt (nennen wir ihn x0/y0), die die Geraden x0y0/x1y1, x1y1/x2y2 und x2y2/x3y3 berührt.

Text

PostScript stellt diverse Standard-Fonts bereit und verfügt außerdem über die Möglichkeit, neue Zeichensätze zu generieren (worauf wir hier nicht näher eingehen wollen). Sehen wir uns das folgende Programm an:

   /Times-Roman findfont       % suche Zeichensatz und
                               % lege ihn auf den Stack
   15 scalefont                % skaliere Zeichensatz auf 15pt
   setfont                     % setze Zeichensatz
   100 500 moveto
   (I love PostScript!) show   % Textausgabe (hello world!)

Der findfont Befehl verwendet das oberste Objekt im Stapel als Fontnamen und legt den gefundenen Zeichensatz wieder auf dem Stack ab. Der "/" vor dem Namen des Zeichensatzes hindert den Interpreter daran, "Times-Roman" als Befehl zu interpretieren. Statt dessen legt er die Zeichenkette (incl. dem /) einfach auf dem Stapel ab.
Der Befehl scalefont holt sich vom Stapel einen Skalierungsfaktor und einen Zeichensatz, skaliert den Zeichensatz entsprechend und legt ihn wieder auf dem Stapel ab. Das Ergebnis ist also ein 15 Punkt Times-Roman Zeichensatz auf dem Stapel.
setfont schließlich nimmt den Font vom Stapel und macht ihn zum aktuellen Zeichensatz.
Einige weitere verfügbare Zeichensätze sind Times-Italic, Times-Bold, Helvetica, Helvetica-Bold, Courier und Ugly (der Default für den Fall, daß findfont keinen gültigen Zeichensatz findet).
Die Textausgabe erfolgt durch

   show.
Stringobjekte werden in PostScript in runde Klammern gefaßt.
Natürlich wirkt sich ein scale Befehl auch auf die Zeichendarstellung aus.

Farbgebung

Alle bisherigen Ausgaben erfolgten in der Default-Farbe schwarz. PostScript stellt 3 Befehle zur Farbeinstellung zur Verfügung, deren Wirkung bis zum nächsten Befehl zur Farbeinstellung anhält, oder eine alte Zeichenumgebung wiederhergestellt wird.

  r g b setrgbcolor

setzt die neue Farbe entsprechend ihrem rot, grün und blau-Anteil. Der Wertebereich von r, g und b liegt im Bereich von 0 bis 1. Eine zweite Möglichkeit, die Farbe zu setzen, besteht in der Angabe der Farbwerte von cyan, magenta, gelb und schwarz - jeweils wieder im Bereich zwischen 0 und 1:

c m g s setcmykcolor  % in manchen Implementationen nicht verfügbar

Schließlich kann die Farbsetzung auch durch die Angabe des Farbweites, der Sättigung, und der Helligkeit erfolgen. Wer dieses Prinzip nicht kennt, sollte sich beispielsweise den Farbeditor von xfig angucken:

   h s b sethsbcolor  % Farbwert Sättigung Helligkeit

Am einfachsten ist:

   g setgray          % 0 <= g <= 1 erklärt sich von selbst 
Um gefüllte Flächen darstellen zu können, benötigen wir zwei weitere Befehle:
   closepath
schließt (wie der Name vermuten läßt) den aktuellen Pfad.
   fill
ist mit stroke zu vergleichen, mit dem Unterschied, daß der eben definierte Pfad nicht gezeichnet, sondern die umschriebene Figur mit der aktuellen Farbe gefüllt wird. Wie nach stroke vergißt der Interpreter anschließend den Pfad. Will man der gefüllten Figur eine Einfassung durch stroke verpassen, muß der aktuelle Pfad mittels
   gsave
gespeichert und kann später durch
   grestore
wieder abgerufen werden (gsave speichert den aktuellen Graphikzustand auf dem Graphikstapel; grestore ruft ihn wieder ab. Auch kompliziertere Transformationen (wie durch scale) lassen sich dadurch rückgängig machen.) Mit diesen Erläuterungen stellen die letzten beiden Programme kein Problem mehr dar:
   1 1 0 0 setcmykcolor
   100 100 moveto
   300 100 lineto
   300 250 lineto
   100 250 lineto
   100 100 lineto
   stroke
   1 0.5 0.8 0 setcmykcolor
   5 setlinewidth
   200 200 moveto
   400 300 lineto
   300 300 lineto
   closepath
   gsave fill grestore stroke
   1 0 0 setrgbcolor
   3 setlinewidth
   200 200 moveto
   100 200 100 0 75 arc
   stroke
   newpath
   400 500 200 20 50 arc
   stroke
   0 0 1 0.2 setcmykcolor
   3 setlinewidth
   200 200 100 0 360 arc
   stroke
   1 0 0 setrgbcolor
   newpath
   2 1 scale
   200 300 50 0 360 arc
   gsave fill grestore stroke
   0 1 0 setrgbcolor
   newpath
   1 4 scale
   100 150 40 0 360 arc
   gsave fill grestore stroke
Das folgende Programm zeichnet einige Linien in verschiedenen Graustufen:
   0.2 setgray
   10 setlinewidth
   100 700 moveto 200 0 rlineto
   stroke
   newpath
   0.3 setgray
   100 600 moveto 200 0 rlineto
   stroke
   newpath
   0.4 setgray
   100 500 moveto 200 0 rlineto
   stroke
   newpath
   0.5 setgray
   100 400 moveto 200 0 rlineto
   stroke
   newpath
   0.6 setgray
   100 300 moveto 200 0 rlineto
   stroke
   newpath
   0.7 setgray
   100 200 moveto 200 0 rlineto
   stroke
   newpath
   0.8 setgray
   100 100 moveto 200 0 rlineto
   stroke
Tatsächlich ist die Handhabung der Farbgebung unter PostScript wesentlich komplexer, als es diese wenigen Befehle Glauben machen könnten. Interessierte seien auf Kap. 4.8 des Referenzhandbuches verwiesen. Alternativ könnte das letzte Programm auch folgendermaßen formuliert werden - mit unserem Wissen über Stapel und ein bißchen Intuition etwas für Rätselfreunde :-)
  /g 0.2 def      % setze Variable g auf den Wert 0.2
  /sy 700 def
  /doline { newpath moveto 200 0 rlineto stroke } def
  10 setlinewidth
  6 { g setgray /g g 0.1 add def 100 sy doline /sy sy 100 sub def } repeat

Der Autor dankt Oliver Thilmann für die zahlreichen Kommentare zum Inhalt und zur Organisation des Textes. (Die deutsche Version wurde während der Übersetzung nochmals überarbeitet und ergänzt.)

Weitere Information

Von Adobe Systems Inc. sind zu Postscript 3 Bücher im Addison-Wesley Verlag erschienen:
The PostScript Language:
Webpages maintained by Miguel Ángel Sepúlveda
© Emre Demiralp 1998
Übersetzt von:Oliver Thilmann
LinuxFocus 1998