von:Emre Demiralp
|
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.
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.
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.
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.
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.
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 selbstUm gefüllte Flächen darstellen zu können, benötigen wir zwei weitere Befehle:
closepathschließt (wie der Name vermuten läßt) den aktuellen Pfad.
fillist 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
gsavegespeichert und kann später durch
grestorewieder 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 strokeDas 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 strokeTatsä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.)
Webpages maintained
by Miguel Ángel Sepúlveda
© Emre Demiralp 1998 Übersetzt von:Oliver Thilmann LinuxFocus 1998 |