Dietrich Zawischa Kontakt |
PostScript, die Druck-Seitenbeschreibungssprache von Adobe, hat sich seit ihrer Einführung 1985 zum weltweiten Standard entwickelt. Gute Grafik- und Textverarbeitungsprogramme können PostScript-Code ausgeben, Druckereien nehmen Aufträge als Postscript-Dateien an, und im übrigen gibt es Programme, die solche Dateien in das (ebenfalls von der Firma Adobe stammende) PDF-Format (Portable Document File Format) umwandeln, so daß sie (z.B. mit Hilfe des Adobe Readers) auch auf nicht-PostScript-fähigen Druckern ausgegeben werden können. Er wird daher auch anderen so ergehen, wie es mir ergangen ist: man kommt mit PostScript in Berührung, ob man will oder nicht.
Damit keine Mißverständnisse aufkommem: Der Name "PostScript"
ist eingetragenes Warenzeichen der Firma Adobe Systems. Adobe Systems Inc.
ist Eigentümerin des Copyrights der Liste von Operatoren und der
schriftlichen Spezifikation von Adobes PostScript-Programmiersprache.
Da Adobe jedoch an der weiten Verbreitung der Sprache interessiert ist,
gestattet die Firma jedem,
- Programme in PostScript zu schreiben,
- Treiber zu schreiben, deren Output aus PostScript-Befehlen besteht
- Software zu schreiben, die in PostScript geschriebene Programme
interpretiert,
- die durch das Copyright geschützte Liste der Kommandos in dem
Ausmaß zu kopieren, wie es für die oben genannten
Zwecke erforderlich
ist.
Einzige Bedingung für diese allgemeine Erlaubnis ist, daß jeder,
der die geschützte Liste von Befehlen auf diese Weise benutzt, einen
entsprechenden Hinweis auf das Copyright gibt.
Dies hier soll einen leichten Einstieg ermöglichen, wird aber kein ausführliches Lehrbuch oder Tutorium ersetzen. Weitere Informationen finden Sie bei Adobe selbst und an vielen anderen Stellen – die Links, die ich hier ursprünglich hatte, sind inzwischen zerbrochen, aber Suchmaschinen finden genug.
Ein typisches Grafikprogramm macht einem manches sehr bequem, anderes aber unmöglich. Um bunte Bilder herzustellen, wie ich sie in der Vorlesung zeigen wollte, reichte die vom Grafikprogramm angebotene vorgestanzte Farbpalette nicht aus. Notlösung: man erzeugt das Bild erst mit den vorhandenen Farben und versucht dann, im PostScript-Output die Stellen zu finden, wo die Farbe gesetzt wird, und kann sie dort den eigenen Wünschen anpassen. PostScript-Code ist lesbarer ASCII-Code, daher kann man mit einem beliebigen Editor darin herumfummeln.
Der Eindruck, den man bei diesem Vorgehen von PostScript bekommt, kann verheerend sein. Der vom Grafik-Programm automatisch erzeugte Code enthält viel Redundanz und wird dadurch recht umfangreich. Um dem entgegenzuwirken, werden für die häufig vorkommenden Befehle oder Befehlsgruppen einbuchstabige Kurzformen definiert, was der – im Prinzip gegebenen – Lesbarkeit sehr abträglich ist. Manchmal sieht es sogar so aus, als ob die Ausgabe mit redundantem Schrott angereichert würde, um das Lesen zu erschweren.
Ich möchte im folgenden eine Lanze für PostScript brechen. Es ist eine richtige Programmiersprache, es kennt Laufanweisungen, Schleifen, Abfragen – alles, was man so braucht. In den Beispielprogrammen werde ich Abkürzungen zugunsten der Lesbarkeit möglichst vermeiden. Das Schwergewicht wird auf der Erstellung einfacher Grafiken liegen, wobei aber, meinen Vorlieben folgend, die Farbe eine große Rolle spielt, hier wird etwas mehr Aufwand getrieben werden.
Auf meinem Rechner ist Ghostscript (GS), das ist ein PostScript-Interpreter, und Ghostview (GSview ), das ist eine sehr komfortable Benutzeroberfläche, installiert. Die haben den Vorteil, daß sie kostenlos bzw. als Shareware erhältlich sind. Kommerzielle Programme bieten möglicherweise noch zusätzlichen Komfort.
Man kann in einem Editierfenster (mit einem beliebigen Editor, der allerdings in der Lage sein sollte, Dateien mit der Endung .ps oder .eps als Textdateien zu akzeptieren) das Programm basteln und sich im Fenster von Ghostview nach jedem Abspeichern das Ergebnis (oder die Fehlermeldungen) zeigen lassen. Man kann mit Ghostscript allein auch interaktiv arbeiten, das kann anfangs zum Kennenlernen der Art, wie die Kommandos abgearbeitet werden, hilfreich sein, ist aber eher umständlich.
Man muß sich vor Augen halten, daß PostScript-Code etwas ist, das zum Drucker geschickt wird um dort als Grafik ausgegeben zu werden. Der Drucker kann keine Daten-Dateien vorhalten (von den eingebauten Schriften abgesehen). Es gibt zwar Lesebefehle, wie die Daten zum Drucker kommen sollten, damit sie bei der Abarbeitung zur Verfügung stehen, weiß ich aber nicht; das hängt, wenn überhaupt möglich, sicher von der Hardware ab. Abgesehen von den Schriften müssen die Programme daher alle benötigten Daten in sich enthalten.
Wer längere Zeit mit einem Taschenrechner von Hewlett-Packard gearbeitet hat, dem ist die "umgekehrt polnische Notation" (reverse polish notation, RPN) vertraut und lieb. Auf diesen Taschenrechnern sucht man vergeblich nach der Taste mit dem Gleichheitszeichen, auch öffnende und schließende Klammern sind nicht vorhanden, statt dessen gibt es eine große Eingabetaste (ENTER).
Eine einfache Addition geht so vor sich:
14 (ENTER) 8.6 +
wird eingegeben, im Sichtfenster (vor meinem geistigen Auge sehe ich den
HP35 vor mir) erscheint das Ergebnis, 22.6.
Die wichtige Organisationsstruktur ist der Stapel (auf englisch: stack).
Das oberste Element des Stapels ist im Sichtfenster zu sehen, die darunter
liegenden Elemente sieht man nicht. Nachdem die Zahl 14 eingegeben ist, wird
sie durch (ENTER) auf dem Stapel um eine Stufe tiefer geschoben.
Die nächste Eingabe, 8.6, liegt dann oben und ist im Fenster zu
sehen. Durch die
Taste + wird das Rechenwerk angewiesen, die obersten beiden Elemente vom
Stapel zu nehmen, zu addieren und das Ergebnis als oberstes Element auf den
Stapel (ins Sichtfenster) zurück zu legen.
Soll 6*(14+8.6) berechnet werden, kann man dies mit der Eingabe
6 (ENTER) 14 (ENTER) 8.6 + * erreichen.
PostScript arbeitet mit einem Operandenstapel, der alle möglichen Variablentypen aufnehmen kann. Die Plätze auf dem Stapel sind numeriert, der oberste hat die Nummer 0. Das Leerzeichen dient als Trennzeichen. Werden mehrere durch ein Leerzeichen getrennte Zahlen eingegeben, so werden sie der Reihe nach auf den Stapel gelegt.
Die Kenntnis der folgenden Tabelle ermöglicht Ihnen schon, Ghostscript anstelle eines Taschenrechners zu benutzen:
Arithmetische Operationen | Standardfunktionen | ||
add | Addition | atan | Arcustangens |
div | Division | cos | Cosinus |
idiv | ganzzahlige Division | exp | allg. Exponentiation a hoch b |
mul | Multiplikation | ln | natürlicher Logarithmus |
sub | Subtraktion | log | dekadischer Logarithmus |
neg | dreht Vorzeichen um | sin | Sinus |
sqrt | Quadratwurzel |
Zu beachten ist, daß der Arcustangens zwei Argumente benötigt:
der Aufruf lautet
Gegenkathete Ankathete atan
und das ist sehr praktisch: -1 -1 atan =
liefert ein anderes Ergebnis als 1 1 atan =.
Probieren Sie es aus! Suchen Sie in dem Ordner, in dem Ghostscript installiert ist (wahrscheinlich C:\gs\gsx.xx\bin) nach der ausführbaren Datei, die gswin32.exe heißen könnte, und lassen Sie sie ausführen. Mit dem Kommando quit (oder Fenster zu) beenden Sie die Sitzung.
Das Gleichheitszeichen = kommt ansonsten in Programmen nicht vor; =
holt das oberste Element vom Stapel und gibt es auf dem Bildschirm aus.
Die oben gestellte Aufgabe für den Taschenrechner löst man wie
folgt:
6 14 8.6 add mul =
Weitere Funktionen:
abs | Betrag | ceiling | ganze Zahl >= Argument |
floor | ganze Zahl <= Argument | mod | Modulo, Divisionsrest |
rand | Pseudo-Zufallszahl | round | Rundung |
rand erzeugt eine ganzzahlige Pseudo-Zufallszahl im Bereich von 0 bis 231-1. Ansonsten gilt auch hier: ausprobieren!
Vorbemerkung: die folgenden kleinen Beispiele sind für die Betrachtung
am Bildschirm gedacht, daher sparen wir uns die für den Drucker
nötigen Vorspann-Informationen.
Als zu beschreibende Seite setzen wir ein Blatt DIN A4 voraus.
Voreingestellt ist als Koordinatenursprung die linke untere Ecke des
Blattes, als Längeneinheit der "bigpoint", also 1/72 Zoll.
Voreingestellt ist außerdem die
Zeichenfarbe Schwarz.
Wenn wir
lieber einen Zentimeter als Längeneinheit haben wollen, schreiben
wir:
72 2.54 div dup scale
Die Syntax für den scale-Operator ist: xSkalenfaktor ySkalenfaktor
scale. Ein Zoll ist 2.54 cm, durch 72 2.54 div erhalten wir
die Zahl der Punkte pro Zentimeter, die dann oben auf dem Stapel liegt.
Durch dup wird das oberste Element des Stapels dupliziert, also
nocheinmal auf den Stapel gelegt. Dann findet der scale-Operator die
gewünschten Argumente vor.
Als nächstes verlegen wir den Koordinatenursprung in die
Blattmitte:
10.5 14.85 translate
Um eine Linie zu zeichnen, müssen wir mehrere Befehle
absetzen:
0.1 setlinewidth
0 0 moveto
5 2 lineto
Bis jetzt ist nichts zu sehen.
Der lineto-Operator zieht eine Linie, aber er zeichnet sie noch
nicht. Dies macht erst der Befehl
stroke
Wenn Sie mit GS interaktiv arbeiten, sehen Sie jetzt immer noch nichts. Es
fehlt noch die Ausgabe der "fertigen Seite" durch
showpage.
Sie werden vermutlich feststellen, daß das interaktive Arbeiten nicht
besonders bequem ist, denn nach der Ausgabe durch showpage ist die
Grafik gelöscht. Sie können aber Ihre Bilddatei auch mit einem
Editor erstellen, mit einem möglichst kurzen Namen wie "b1"
abspeichern, und dann unter GhostScript "laufen" lassen, dazu tippen Sie
(b1) run
ein.
Anmerkung zu Ghostscript (Juni 2021)
Nach der Installation der neuesten Version des PostScript®-Interpreters Ghostscript stellte ich fest, dass einige meiner Programme nicht mehr liefen, nämlich genau die, die Daten einlesen oder ausgeben sollten. Auch beim interaktiven Arbeiten mit gs war es nicht mehr möglich, Dateien zu lesen. Und auch ein abgespeichertes Programm mit run laufen zu lassen, ging nicht:
Der Grund für dieses Verhalten ist, dass Ghostscript ab Version 9.50 per default im SAFER-Modus startet. Dieser Modus kann durch den Parameter -dNOSAFER beim Aufruf von gs ausgeschaltet werden. Die entsprechende Kommandozeile kann unter Windows beispielsweise so aussehen:
Man beachte, dass wegen der Leerzeichen im Pfad Anführungszeichen gesetzt werden müssen.
Der Grund dafür, daß lineto noch keine Linie zeichnet, liegt darin, daß man oft etwas anderes will. Mit wenigen Änderungen und Ergänzungen ergibt sich z.B. folgendes Programm:
72 2.54 div dup scale
10.5 14.85 translate
0.1 setlinewidth
0 0 moveto
5 0 lineto
5 4 lineto
0 4 lineto
closepath
1 0 0 setrgbcolor
fill
showpage
Ersetzt man fill durch stroke, dann wird . . .
Will man den geschlossenen Pfad sowohl rot ausfüllen als auch schwarz
nachziehen, so ist zu bedenken, daß nach fill oder stroke
der Pfad "verbraucht" ist, also verschwunden, nicht mehr bekannt. Die
Abhilfe:
. . .
5 4 lineto
0 4 lineto
closepath
gsave
1 0 0 setrgbcolor
fill
grestore
0 0 0 setrgbcolor
stroke
showpage
gsave speichert den Graphik-Status ab, grestore stellt ihn wieder her.
Letzten Endes sollen die Bilder, die wir erzeugen wollen, in einem größeren Zusammenhang erscheinen, beispielsweise in einen Text eingebettet. Dadurch soll der Graphik-Status der Umgebung nicht verändert werden. Daher empfiehlt es sich, immer gsave als erstes und grestore als letztes Kommando zu geben, außerdem müssen diese Kommandos immer paarig auftreten, wie öffnende und schließende Klammern.
Der Befehl setrgbcolor wurde verwendet, um die Farbe festzulegen. Er ist fast selbsterklärend: die drei Farbmaßzahlen für Rot, Grün und Blau sind anzugeben; die so erzeugte Farbe hängt aber davon ab, welche Grundfarben im Ausgabegerät verwendet werden, ist also geräteabhängig. Im Fall von einfachen Grafiken braucht man sich darüber noch keine Gedanken zu machen; es soll aber darauf hingewiesen werden, daß PostScript auch die Hilfsmittel für geräteunabhängige, genaue Farbspezifikation bereitstellt.Weitere Graphikbefehle:
Befehl | Funktion | Beispiel / Erläuterung |
arc | Kreisbogen | xMittelp yMittelp Radius WAnf WEnd arc |
arcn | Kreisbogen | wie oben, aber im Uhrzeigersinn |
curveto | Bézier-Kurve | xh1 yh1 xh2 yh2 x y curveto |
rcurveto | Bézier-Kurve | dxh1 dyh1 dxh2 dyh2 dx dy rcurveto |
rlineto | DeltaX DeltaY rlineto | |
rmoveto | DeltaX DeltaY rmoveto |
Die Befehle arc und arcn ziehen, falls (durch moveto,
lineto oder eine andere vorausgegangene Anweisung) ein aktueller
Punkt definiert ist, als erstes eine Linie von diesem Punkt zum Beginn des
Bogens.
Die Befehle curveto bis rmoveto setzen voraus, daß
ein aktueller Punkt definiert ist (durch ein vorausgegangenes moveto, lineto
. . .). Dieser Punkt habe die Koordinaten x0, y0.
curveto: Die Verbindungslinie von
x0,y0 mit xh1,yh1 ist Tangente an die Kurve im Anfangspunkt x0,y0,
entsprechend die Linie von x,y nach xh2,yh2 Tangente im Endpunkt.
Je länger diese Tangentenstücke sind, desto enger schmiegt sich
die Kurve an die jeweilige Tangente.
Als Beispiel für die Graphikbefehle finden Sie hier den sehr schlicht programmierten Farbkreis nach Itten (Quelltext). Der Code dient außerdem als erstes Beispiel für die Verwendung von Variablen für häufig wiederkehrende Größen. |
Deklaration von Variablen und Variablentyp vor der Verwendung ist nicht
nötig. Eine Wertzuweisung, die in der Programmiersprache Pascal durch
Radius:=3.75;
erfolgen würde, sieht hier so aus:
/Radius 3.75 def
Der Operator def nimmt den Wert 3.75 und das Wort /Radius vom Stapel
und trägt dieses Wertepaar ins aktuelle "Wörterbuch" (dictionary)
ein. Durch den Schrägstrich wird der Interpreter angewiesen, die
folgenden Buchstaben buchstäblich (literal) zu nehmen und nicht als
Anweisung oder schon definierte Variable zu interpretieren.
Soll in einem
späteren Schritt der zugehörige Durchmesser berechnet werden,
sieht das so aus:
/Durchmesser Radius 2 mul def
Ohne vorausgehenden Schrägstrich wird das Wort Radius als
Variablennamen interpretiert, der im Wörterbuch nachgeschlagen und
durch den dort gefundenen Wert ersetzt werden muß.
Der Stapel kann, wie wir gesehen haben, Zahlenwerte und Variablennamen
aufnehmen, tatsächlich noch vieles mehr, wie wir gleich sehen werden.
Es ist daher wichtig, den Stapel manipulieren zu können. Dazu dienen
die folgenden Befehle:
pop entfernt das oberste Element vom Stapel.
clear leert den Stapel vollständig.
dup nimmt das oberste Element vom Stapel und legt es zweimal wieder
darauf ab.
exch vertauscht die obersten beiden Elemente.
index: Der Befehl n index, wobei n die Nummer des gesuchten
Elementes auf dem Stapel ist, bewirkt, daß eine Kopie vom n-ten Element
oben auf den Stapel gelegt wird. Man beachte, daß das oberste Element
die Nummer 0 hat!
copy: Durch n copy werden die obersten n Elemente nocheinmal
auf den Stapel gelegt.
count zählt die Elemente des Operandenstapels und legt das
Ergebnis oben drauf.
pstack schließlich gibt den Inhalt des Stapels auf der
Standard-Ausgabeeinheit aus. Das Programm muß vollständig
abgearbeitet sein (d.h. das Bild nicht mehr zu sehen), dann können Sie
sich unter Ghostview nach Eingabe von M die Meldungen ansehen.
Schließt man eine Folge von Anweisungen, Zahlen etc. in geschlungene
Klammern ein, dann wird diese Folge zu einem Element
zusammengefaßt und als solches auf den Stapel gelegt. Das kann man
verwenden, um Prozeduren zu definieren – man braucht bloß der
Befehlsfolge einen Namen zu geben:
/Prozedurname {Befehlsfolge} def
bewirkt, daß das Schlüsselwort Prozedurname (ohne vorausgehenden
Schrägstrich!) im weiteren Verlauf jedesmal durch die Befehlsfolge
ersetzt wird.
Laufanweisungen haben die Form
Anfangswert Inkrement Laufgrenze {Befehlsfolge} for
d.h. der Operator for holt die obersten vier Elemente vom Stapel, die
der Reihe nach (von oben nach unten) als Befehlsfolge, Laufgrenze, Inkrement
und Anfangswert gedeutet werden. Bei jedem Durchlauf der Schleife wird
zuerst der aktuelle Wert der Laufvariablen auf den Stapel gelegt. Dieser
muß entweder verwendet oder mittels pop entfernt werden.
Beispiel: eine gleichmäßig gestufte Grauskala
Mit diesem Beispiel soll verschiedenes gezeigt werden:
Ich habe oben gesagt, daß ein Blatt A4 als zu beschreibende Seite
vorausgesetzt wird, aber die fertigen Beispielprogramme sind nach
Möglichkeit so
ausgelegt, daß sie den Bildschirm im Vollbildmodus ganz ausfüllen,
wenn als Bildauflösung 72 dpi (dots per inch) eingestellt ist. Um keine
großen Änderungen durchführen zu müssen, habe ich den
ursprünglichen Code (schwarz dargestellt) in ein Rahmenprogramm
eingebettet, das die Grafik skaliert und auf einem Blatt A4 zentriert
ausgibt.
Eine mit %! beginnende
Kopfzeile wird üblicherweise von Druckern zur Identifikation von
PostScript-Code verlangt. Falls sie nicht benötigt wird, wird sie als
Kommentar überlesen. Die Angabe PS-Adobe-3.0 bezieht sich auf die Dokumentenstrukturkonventionen, nicht auf den PostScript-Level. In der dritten (roten) Zeile wird auf die Einheit
Zentimeter umgestellt.
Die sechste Zeile verändert die Skala wieder, und zwar so, daß
die Bildbreite 16 cm beträgt statt 1024 bp (big points), wie sie der
Angabe der BoundingBox zwei Zeilen später entspräche.
Wenn die letzten beiden schwarz geschriebenen Zeilen entfernt werden, können
die blau geschriebenen Zeilen entfallen, sie dienen der Sicherung vor unbeabsichtigten Änderungen durch die eingeschlossene Bilddatei.
Die Bilddatei selbst (schwarz geschriebener Code) ist als "gekapseltes Bild"
(Encapsulated PostScript File, eps-File) gestaltet: die Kopfzeile deklariert
dieses. Die nächste Zeile gibt die linke untere und die rechte obere
Ecke eines das Bild umschließenden Rechteckes an. Daraus kann ein
Textsatzprogramm die Skalierung berechnen.
Die wesentlichen Merkmale einer eps-Datei sind Kopfzeile, BoundingBox,
sowie
das Versprechen, den grafischen Zustand und den Operandenstapel
des einbettenden Programmes unverändert zu hinterlassen.Außerdem dürfen die Zeilen nicht zu lang sein (255 Zeichen maximal).
Ersteres wird durch das Paar gsave – grestore erreicht, für
den Rest ist der Programmierer verantwortlich. In der Anleitung zum Aufbau von EPS-Dateien der Firma Adobe findet man zwei Prozeduren BeginEPSF und EndEPSF, die, vor und nach einem eingebetteten EPS-File aufgerufen, noch zusätzliche Sicherheit und Schutz des Rahmenprogrammes bieten. Üblicherweise werden EPS-Dateien ja nicht von Hand programmiert, sondern von einem Grafikprogramm generiert (nach der Auswahl der entsprechenden Export-Optionen "save as . . ." bzw. "speichern unter . . .").
Die neben dem unteren Bildchen stehenden drei Zeilen sind der Hauptteil des
Programms: Startpunkt, Schrittweite und Obergrenze der Laufvariablen, die in
diesem Fall die Helligkeit ist. Dann die Befehlsfolge in geschlungenen
Klammern. setgray holt den Wert der Laufvariablen vom Stapel.
currentpoint legt das aktuelle
x-y-Koordinatenpaar auf den Stapel. Nachdem die von 0 6 rlineto
gezogene Linie gezeichet ist, steht es dann für
die Anweisung moveto zur Verfügung.
Nach dem Zurückladen der in der Variablen "statusante" abgelegten Informationen hat showpage wieder seine ursprüngliche Bedeutung.
Die letzten drei, wieder rot geschriebenen Zeilen sind das Ende des
Rahmenprogramms. Dieses Rahmenprogramm kann auch für andere Bilder im
eps-Format verwendet werden.
Zurück zu der durch geschlungene Klammern zusammengefaßten
Befehlsfolge. Durch
n {Befehlsfolge} repeat
wird die Folge n-mal ausgeführt, durch
{Befehlsfolge} loop
beliebig oft. Zweckmäßigerweise baut man im
letzten Fall eine Sequenz der Art
Abbruchbedingung {exit} if
ein: Wenn die Abbruchbedingung
erfüllt ist, wird der Ausgang (exit) gewählt,
siehe folgenden Abschnitt.
Logische Variablen können die Werte true (=wahr) oder false (=falsch) annehmen. Zur Konstruktion von logischen Ausdrücken stehen folgende Operatoren bereit:
Operator | Bedeutung | Beispiel |
and | und, logisches Produkt | A B and |
eq | gleich | Wert1 Wert2 eq |
ge | größer oder gleich | Wert1 Wert2 ge |
gt | größer | |
le | kleiner oder gleich | |
lt | kleiner | |
not | nicht | Wert1 Wert2 eq not |
or | oder, logische Summe | |
xor | entweder – oder |
und für die Abfragen if und ifelse, z.B.:
A B gt {Anweisungen} if
A B gt C D eq and {Anweisungen1}
{Anweisungen2} ifelse
So, wie durch geschlungene Klammern eine Anzahl von Elementen zu einer
Einheit zusammengefaßt werden, kann man auch durch die eckigen
Klammern Elemente zusammenfassen. Diese Einheit wird als Feld (array)
bezeichnet. Die Elemente können von verschiedenem Typ sein; ich
beschränke mich auf Felder von Variablen gleichen Typs.
Die im folgenden Beispiel vorkommende Anweisung
/Ra [1 1 0 0 0 1] def
definiert ein Feld namens Ra mit sechs Elementen, wobei die Zählung
links mit 0 beginnt.
Die wichtigsten
Befehle zur Manipulation von Feldern sind def, get,
put, aload und astore.
Wenn das Feld nicht von Anfang an bekannt ist, kann man es nicht so einfach
durch eckige Klammern und Wertzuweisung definieren. Statt des obigen
Beispiels kann man das gleiche Ergebnis auch durch
/Ra 6 array def
1 1 0 0 0 1 Ra astore pop
erreichen. Zunächst wird dem Namen (key) Ra ein Feld mit sechs
Elementen zugeordnet. Dann werden sechs Zahlen (Nullen und Einsen)
und das (leere) Feld auf den
Stack gelegt.
Durch den Befehl astore wird das (immer noch
leere) Feld vom Stapel geholt, dann werden die vorgefundenen
Zahlen den Feldelementen zugewiesen und schließlich wird das ganze
Feld nocheinmal (als Einheit) auf dem Stapel abgelegt, von wo es mit
pop entfernt oder anderweitig verwendet wird.
Mit
Ra 4 get
greift man auf das Element Nr. 4 zu,
durch
Ra 3 0.5 put
würde dem Element Nr. 3 der neue Wert
0.5 zugewiesen.
aload schließlich legt die Elemente des angegebenen Feldes auf
den Stapel und dann als oberstes das Feld selbst. Am Beispiel des oben auf
zwei Arten eingeführten Feldes Ra: Eingabe von
Ra aload
würde den Stackinhalt
1 1 0 0 0 1 [1 1 0 0 0 1]
erzeugen. Wieder ist der oberste Eintrag, das
Feld selbst, gegebenenfalls durch pop zu entfernen.
Die Programmliste ist kommentiert; hier noch einige ergänzende
Anmerkungen:
Drehwinkel rotate
dreht das Koordinatensystem um den Ursprung um den angegebenen Winkel (in
Grad).
Um Unsauberkeiten an den Grenzen der Farbfelder aufgrund von
Rundungsfehlern zu vermeiden, wurden die Farbfelder etwas überlappend
gezeichnet.
Die Zahl der Farbschritte von einer Grundfarbe zur nächsten wurde
"fein" genannt. Beim Durchlaufen des Kreises wird die Farbe jeweils zwischen
den benachbarten Grundfarben interpoliert, das geschieht durch die folgenden
Anweisungen:
/j exch def | % die Laufvariable wird j genannt |
/j0 j fein idiv 6 mod def | % Index der vorausgehenden Farbe |
/j1 j0 1 add 6 mod def | % Index der folgenden Farbe |
In den Befehlen translate, scale und rotate haben wir schon die Operatoren kennengelernt, die man verwenden kann, um geeignete Koordinatensysteme einzuführen.
Das Ergebnis dieser Operationen wird vom System in der aktuellen Transformationsmatrix (current transformation matrix, CTM) abgelegt, die dazu benutzt wird, die Benutzerkoordinaten in Systemkoordinaten umzurechnen.
Hier noch ein klein wenig Hintergrundinformation dazu.
Auf die (einfachen) Rechenregeln mit solchen Transformationsmatrizen soll nicht eingegangen werden. Die Umrechnung von Benutzerkoordinaten x, y in andere Koordinaten x', y' (dies können z.B. die Systemkoordinaten sein) erfolgt entsprechend den Gleichungen
x' = ax + cy + s,
y' = bx + dy + t.
Die Transformationsmatrix enthält die Transformationskoeffizienten, PostScript speichert sie in einem Feld (Array) von sechs Elementen:
[ a b c d s t ].
Mehrere Transformationen hintereinander ausgeführt ergeben eine CTM, die sich als "Verkettung" (Matrizenprodukt) der einzelnen Transformationsmatrizen mit der ursprünglichen CTM darstellen läßt. Statt die drei obengenannten Befehle zu verwenden, kann man auch die gewünschte Transformationsmatrix angeben und durch den Befehl concat mit der aktuellen CTM verketten: die beiden Anweisungen
[ 1 0 0 1 72 36 ] concat
und
72 36 translate
haben dieselbe Wirkung, ebenso bewirken
[ 2 0 0 3 0 0 ] concat
und
2 3 scale
dasselbe, und dem Befehl
30 rotate
entspricht
[ 30 cos 30 sin 30 sin neg 30 cos 0 0 ] concat.
Meist kommt man jedoch mit den einfachen Transformationsbefehlen aus.
Die überragende Stärke von PostScript liegt im Bereich der
schriftlichen Ausgabe von Text, die höchsten typographischen
Anforderungen genügen kann. Jeder zu druckende
Buchstabe ist letztlich als eine
Folge von Graphikbefehlen zu sehen, die
bewirkt, daß der Buchstabe auf das Ausgabemedium
gezeichnet wird. Dazu enthält PostScript eine Fülle von speziellen
Befehlen, die man nie braucht, wenn man nur ein paar Zahlen und wenig
Text in eine Grafik einfügen will. Letzteres ist hier unser Ziel:
Achsbeschriftungen, Überschriften und hie und da ein Wort sind
nötig, mehr nicht. Daher werden wir uns hier auf die wichtigsten
Befehle beschränken, die Wirkungsweise wird in Beispielen nur
angedeutet.
Allerdings verlocken die Möglichkeiten zu typographischen
Spielereien (wie z.B. die Überschrift "Ästhetik und
Naturwissenschaft"), auch darauf werden wir zu sprechen kommen.
Ein Beispiel:
gsave
72 2.54 div dup scale
2.5 25 translate
0 0 moveto
/Helvetica findfont
1.5 scalefont
setfont
(Erster Versuch) show
( mit Schrift) show
showpage
grestore
Neu ist nur der rotbraun geschriebene Teil. findfont sucht die
Schrift mit dem auf dem Stack vorgefundenen Namen und legt sie oben auf den
Stapel. (Genau genommen wird bei zusammengesetzten Objekten nur ein Zeiger
auf den Stapel gelegt, das Objekt bleibt, wo es im Speicher ist oder wird
bei dieser Operation in den virtuellen Speicher
kopiert.) scalefont nimmt die Schrift und
den Maßstabsfaktor vom Stapel und transformiert die Schrift wie
gewünscht, setfont nimmt schließlich die so modifizierte
Schrift wieder vom Stapel und installiert sie im aktuellen Font-Dictionary
(Schriftschnitt-Wörterbuch).
In runde Klammern eingeschlossener Text wird als Zeichenkette
(String) interpretiert.
In Verbindung mit der durch die Definition der Schrift geleisteten Vorarbeit
definiert dieser String einen komplizierten Pfad, der dann mit dem Befehl
show mit der voreingestellten Farbe gefüllt wird. show
ist analog den Operatoren fill oder stroke und versteht die
auf diese Weise definierten Pfade. show übergibt nach
Ausgabe eines Strings die neuen aktuellen Koordinaten auf den Grafik-Stack,
so daß ein weiterer show-Befehl den richtigen Anfangspunkt
vorfindet.
PostScript Level 2 bietet zur größeren Bequemlichkeit ein
Kommando, das die drei auf die Schrift bezüglichen Befehle zu einem
zusammenfaßt:
/Helvetica
1.5 selectfont
(Erster Versuch) show
( mit Schrift) show
Der Quelltext des Farbkreisel-Ersatzes diene als Beispiel für Text- und
Datenausgabe. Gewählt wurde eine dicktengleiche Schrift, damit sich
Text und Daten bequem kombinieren lassen. Neu ist der Operator cvs,
mit dessen Hilfe Zahlen in Strings umgewandelt werden. Für den
Zielstring muß vorher entsprechend Platz reserviert werden, in
völliger Analogie zum Operator array geschieht dies durch den
Operator string, konkret z.B. /Zeichenkettenname 8 string def,
wodurch ein maximal 8 Zeichen langer String Speicherplatz und Namen
erhält. Die Umwandlung erfolgt dann durch
Variablenname Zeichenkettenname cvs,
wonach der String oben auf dem Stapel liegt, aber auch die für den
String reservierten Speicherplätze gefüllt sind. (Auf dem Stapel
liegt in Wahrheit nur ein Zeiger auf diese Plätze.)
Wenn man sich auf die bis jetzt
beschriebenen Befehle zur Manipulation der Zeichensätze
beschränkt, dann gibt es kein ß und keine Umlaute, auch keine
fremdsprachlichen Akzente (wie é, ô).
Natürlich lassen sich auch Umlaute, ß und alle denkbaren Akzente
erhalten; wenn man nur einige wenige Zeichen braucht, wie das bei Grafiken
meist der Fall ist, dann ist dies mit dem in PostScript Level 2 vorhandenen
Befehl
glyphshow
leicht zu bewerkstelligen.
Achten Sie in dem Bild des "Farbkreisel-Ersatzes"
auf die vorletzte Zeile links unten: die Ausgabe des Wortes
"Weiß" wurde durch
(Wei) show /germandbls glyphshow
erreicht.
Der Befehl glyphshow gestattet Zugriff auf jedes einzelne Zeichen der
eingestellten Schrift unter Umgehung der Codierung. Um ihn zu verwenden,
muß man die Namen der Sonderzeichen kennen. Die im Deutschen
häufigeren finden Sie in der folgenden Tabelle fettgedruckt:
â | /acircumflex | ä | /adieresis | Ä | /Adieresis |
à | /agrave | ç | /ccedilla | Ç | /Ccedilla |
é | /eacute | É | /Eacute | ê | /ecircumflex |
ë | /edieresis | è | /egrave | € | /Euro |
ï | /idieresis | ô | /ocircumflex | ö | /odieresis |
Ö | /Odieresis | ß | /germandbls | ü | /udieresis |
Ü | /Udieresis |
Die Namen der Zeichen sind weitgehend selbsterklärend, und nach dem
gleichen Schema sind viele mehr gebildet: caron steht z.B. für das
Häkchen, entsprechend gibt es /ccaron, /Ccaron, /scaron, /rcaron etc.
Nützliche Zeichen, die man nicht über die Tastatur erreichen kann,
sind /copyright, /degree,
/endash, /emdash, /ellipsis (Auslassungspunkte),
/multiply, /periodcentered, /plusminus, /registered, /trademark.
Die Anweisung glyphshow greift direkt über den Namen auf das
einzelne Zeichen zu; alle anderen Textausgabebefehle – wir haben hier erst
show behandelt – benutzen eine Codierungstabelle (Encoding, Encoding
Vector), um dem Buchstabencode den Namen des Zeichens zuzuordnen.
Der von Postscript als Standard verwendete Codierungsvektor
enthält die im Deutschen gebräuchlichen Umlaute nicht,
läßt dafür aber
ausreichend freien Platz für die in den verschiedenen Sprachen
verwendeten Sonderzeichen.
PostScript enthält noch einen zweiten Codierungsvektor, der, soweit ich
das überblicke, mit der von Microsoft-Windows verwendeten ANSI-Codierung
übereinstimmt.
Die folgenden
Programmzeilen zeigen, wie auf diesen umgeschaltet werden kann (funktioniert
in dieser Form ab Level 2):
/Helvetica findfont
dup length dict begin
{def} forall
/Encoding ISOLatin1Encoding def
currentdict
end
/Helvetica-ISOLatin1 exch definefont
20 scalefont setfont
72 720 translate
0 0 moveto
(Schön, daß es klappt! 20 \240) show
0 -25 moveto
(\307'est la vie! L'état, \347'est moi! Grün & Weiß) show
showpage
Wesentlich sind die rot geschriebenen Zeilen: Das Font-Dictionary der
Helvetica wird
auf den Stapel geholt (findfont),
der Stapeleintrag wird dupliziert (dup);
das Duplikat wird zur Bestimmung der Zahl der Einträge verwendet
(length) und ein neues, noch leeres unbenanntes Diktionär
derselben
Länge wird auf den Stapel gelegt (dict). Die Anweisung
begin verschiebt dieses auf den Dictionary-Stack und macht es zum
aktuellen, als erstes konsultierten Wörterbuch. Der oberste Eintrag im
Operandenstapel ist danach wieder das Font-Dictionary der Helvetica.
Die Einträge in einem Wörterbuch sind
Schlüsselwort-Wert-Paare; der Operator forall
berücksichtigt dies,
holt ein solches Paar nach dem anderen auf den Stapel und führt mit
diesem die in der geschlungenen Klammer stehende Anweisung aus: def
überträgt das Schlüsselwort und den zugehörigen Wert in
das aktuelle vorhin eingerichtete Diktionär. Durch eine neue Definition
wird der dem Schlüssel /Encoding zugeordnete Array
anschließend überschrieben.
currentdict kopiert das neu erstellte Wörterbuch wieder auf den
Operandenstapel, end entfernt es vom Dictionary-Stack.
Der neue Name des abgeleiteten Fonts wird auf den Stapel gelegt, durch
exch werden die obersten zwei Einträge vertauscht, so daß
der folgende Operator definefont die Argumente, die er benötigt,
in der richtigen Reihenfolge vorfindet.
definefont korrigiert den zum Schlüssel /FID gehörigen
Eintrag der Font-Identifikation, trägt den neuen Font unter dem
angegebenen Namen in das
Font-Register ein und legt ihn
auf dem Operandenstapel
zur weiteren Verwendung ab. Von dort sollte er, falls er nicht gleich
weiterverwendet wird, durch pop entfernt werden.
Später kann
mit findfont oder selectfont darauf zugegriffen werden.
(Die Codierungstabellen und auch das Vorbild für diese
Zeilen findet man im
"Roten Buch"
von Adobe).
Ich möchte noch kurz
den Fall abhandeln, daß man mit anders codierten Dateien
arbeiten möchte. Als Beispiel wähle ich die
Standard-Codierung, die um Umlaute und akzentuierte Zeichen so erweitert wird,
daß letztere über die Tastatur ("im ASCII-Modus", also z.B.
unter DOS) erzeugt werden
können. Dieses Beispiel unterscheidet sich vom vorigen nur dadurch,
daß ein neuer Encoding-Vektor bereitgestellt wird, ein Array mit
256 Elementen, in diesem Beispiel namens /modasciiEncoding.
Hier der Quellcode des modifizierten
Programmes; die Ausgabeanweisungen lauten
0 0 moveto
(\271Schön, daß es klappt!\252 20 \240) show
0 -25 moveto
(\200'est la vie! L'état, \207'est \274 Grün & Weiß) show
und das Ergebnis sieht so aus:
Mit diesem Beispiel wird auch gezeigt, wie der durch Oktalzahlen gegebene
Code von Sonderzeichen in die auszugebenden Zeichenketten eingefügt
werden kann.
Wir haben gesehen, daß durch die verschiedenen Graphikbefehle
zunächst Pfade definiert werden, die anschließend nachgezogen
(stroke) oder, wenn sie geschlossen sind, mit Farbe ausgefüllt
werden können (fill).
Geschlossene Pfade können darüber hinaus verwendet werden, um die
folgenden Zeichenoperationen auf einen bestimmten Bereich zu
beschränken; dazu dient der Befehl
clip.
Indem man clip
und die folgenden Anweisungen zwischen gsave und grestore
einschließt, kann man das Beschneiden auch wieder abstellen.
Pfade können aus mehreren getrennten Teilen bestehen. Der Befehl
closepath
schließt einen Teilpfad, indem der aktuelle Punkt durch eine gerade
Strecke mit dem anfangs durch moveto erreichten Ausgangspunkt des
Teilpfades verbunden wird.
Der aktuelle Pfad ist Teil des graphischen Status und kann mit gsave
gespeichert und mit grestore wiederhergestellt werden.
newpath löscht den aktuellen Pfad; die Operatoren stroke
und fill enthalten diesen Befehl implizit.
Auch die einzelnen Lettern, aus denen ein gesetzter Text besteht, sind
zunächst Teilpfade. Die zu druckende Zeichenkette definiert
in Verbindung mit der gewählten Schrift eine Folge
von Teilpfaden, die dann mit dem Befehl show
mit der aktuellen Farbe gefüllt werden. show ist die
Zusammenfassung einer Folge von Anweisungen, deren letzte dann der Operator
fill ist.
Um die Kontur der Schrift für andere Zwecke nutzen zu können,
wurde der Operator charpath definiert. Dieser erwartet auf dem Stapel
einen String und als oberstes eine logische Variable.
(Text) true charpath erzeugt einen Pfad (die Umrisse des in der
gewählten Schrift gesetzten Wortes "Text"), der dann durch clip
zur Begrenzung der folgenden Zeichenoperationen gemacht werden kann
(so ist das Bild
entstanden),
oder auch durch fill ausgefüllt;
(Text) false charpath stroke
bewirkt, daß die Umrisse der Buchstaben gezeichnet werden.
Mit
Strichdicke setlinewidth
können wir die Strichdicke setzen, das kennen wir schon; man kann aber
auch festlegen, ob und wie eine Linie gestrichelt gezeichnet werden soll,
dies geschieht durch setdash, wobei das gewünschte Strichmuster
in einem Array angegeben wird, und durch einen Offset-Parameter der Beginn
verschoben
werden kann, also z.B.
[0.3 0.5] 0.1 setdash
erzeugt Linien, bei denen auf 0.3 Einheiten lange Striche 0.5 Einheiten
lange Pausen folgen, wobei der erste Strich aber um 0.1 Einheiten
kürzer gezeichnet wird. Durch [] 0 setdash wird eine
ununterbrochene Linie eingestellt.
Wie zwei aneinandergefügte Linienstücke miteinander verbunden
werden läßt sich mit
setlinejoin
beeinflussen. Voreingestellt ist 0
setlinejoin mit der Bedeutung, daß die Konturen der Linien
verlängert werden, bis sie sich schneiden. 1 setlinejoin bewirkt
abgerundete Ecken, 2 setlinejoin abgeschrägte Verbindungen.
Entsprechende Festlegungen für die Enden dickerer Linien lassen sich
mit setlinecap treffen: 0, 1 und 2 sind als Parameter vorgesehen; 0
bedeutet, daß die Linie an den Endpunkten glatt abgeschnitten wird, 1:
es werden Halbkreise angesetzt, so daß die Linie wie mit der
Röhrchenfeder gezeichnet erscheint, 2: die Linie wird am Endpunkt um
die halbe Strichdicke verlängert.
Sehr häufige Elemente von Graphiken sind Rechtecke.
Drei einander ähnliche Befehle (Level 2)
tragen dem Rechnung: rectfill,
rectclip und rectstroke. Die Syntax ist
xEckpunkt yEckpunkt Breite Hoehe rectfill
oder
[xE1 yE1 B1 H1 xE2 yE2 B2 H2 . . .] rectfill,
man kann einen ganzen Schwarm von Rechtecken gleichzeitig erzeugen, indem
die Parameter durch eckige Klammern zu einem Array zusammengefaßt werden.
Der aktuelle Pfad wird durch diese Befehle nicht verändert!
Jetzt ist eigentlich schon fast alles abgehandelt, was ich zum Erstellen meiner Grafiken und Illustrationen bisher benötigt habe. Für viele der einfacheren Bilder ließen sich sicherlich auch die gängigen Grafik- oder Zeichen- und Malprogramme einsetzen; wenn aber die Ergebnisse genau berechnet werden müssen, gibt es nicht mehr viele, mit denen man das kann. Die Regenbogenbilder zum Beispiel sind auch in PostScript programmiert worden.
Das Logo des Arbeitskreises Paläontologie Hannover habe ich ursprünglich mit einem Grafikprogramm erzeugt – Dateigröße: 66 kB. Dasselbe direkt in PostScript geschrieben ergibt eine Datei von gerade einmal 635 Bytes. Auch eine Frage der Ästhetik.
Jetzt möchte ich noch auf die Möglichkeit eingehen, Text und Graphik mit gerasterten Bildern zu kombinieren. Das habe ich für Plakate, Handzettel und ähnliches schon mehrfach verwendet. PostScript bietet mit dem image-Operator die Möglichkeit, "Pixelgraphik" zu verarbeiten.
Wie aber fügt man ein als Datei vorliegendes Bild in eine PostScript-Datei ein?
Das ist im Grunde ganz einfach, denn
gute Bildbearbeitungsprogramme bieten die Möglichkeit, Bilder im "Encapsulated PostScript Format", das wir oben schon kennengelernt haben, abzuspeichern, und in dieser Form können sie einfach eingefügt werden.
EPS-Dateien können in TeX- (und LaTeX) Dokumente eingebunden werden, wenn diese zum Drucken nach PostScript (oder PDF) umgewandelt werden; die dazu nötigen Makropakete sind mittlerweile Standard. (Auch die gängigen Textverarbeitungsprogramme unterstützen das Einfügen von EPS-Bildern.) Die so erzeugten EPS-Dateien sind allerdings im allgemeinen recht groß.
Fotos werden gerne im platzsparenden JPEG-Format gespeichert.
Falls das Bild in diesem Format (JFIF) vorliegt, dann gibt es in dem Programm jpeg2ps von Thomas Merz eine frei aus dem Internet beziehbare, gute Möglichkeit der Umwandlung. jpeg2ps erzeugt aus einer jpeg-Bilddatei eine eps-Bilddatei, die nur wenig größer ist als die Ausgangsdatei.
Wenn man allerdings so wie ich eine so erzeugte eps-Datei in ein handgestricktes PostScript-Programm einfügt, ist es zweckmäßig, die Zeilen
%%Page: 1 1
und
showpage
zu entfernen, die im lesbaren Anfangsteil der von jpeg2ps erzeugten eps-Datei stehen. Man erhält sonst Fehlermeldungen oder nicht das gewünschte Ergebnis, wenn man nicht spezielle Vorkehrungen (wie die blau geschriebenen Zeilen in unserem eps-Beispielprogramm) trifft, um diese Zeilen zu "entschärfen".
Die Aufgabe, um die es hier geht, ist also schon gelöst, ich möchte im folgenden trotzdem noch genauer darauf eingehen und zeigen, wie man mit Hilfe eines PostScript-Programmes (und GhostScript) aus einer jpeg eine eps-Datei machen kann.
Zum Einfügen von gerasterten Bildern stellt PostScript den image-Operator bereit. Dessen Wirkungsweise wollen wir zunächst anhand eines einfachen Schwarz-Weiß-Beispiel-Bildchens besprechen und erproben:
%!
gsave
100 100 scale
8 12 1 [8 0 0 12 0 0] < c1 80 0c 1e 1e 1e 1e 1e 1e 1e 1e 1e > image
grestore
showpage
Vor dem Aufruf von image werden die benötigten Parameter auf den Stapel gelegt. Dies sind der Reihe nach:
Spaltenzahl Zeilenzahl BitsProPixel Bildtransformationsmatrix Datenquelle.
Das Beispiel../bildchen ist also 8 Pixel breit und 12 Pixel hoch, die Pixel sind schwarz oder weiß, daher genügt ein Bit zur Angabe. Die Bildtransformationsmatrix ist die Matrix, die das Einheitsquadrat (1 mal 1 Längeneinheit) auf das Bild abbildet (Transformationsmatrizen haben wir schon kurz besprochen),
und als Datenquelle ist hier ein Hexadezimal-String, kenntlich an den spitzen Klammern, die die als Hexadezimalzahlen zu lesenden Zeichen einschließen. Leerzeichen und Zeilenvorschub werden innerhalb der spitzen Klammern ignoriert.
Das Bild sieht so aus:
Bilddateien von Fotos beispielsweise sind meist recht groß, diese Daten in einen String zu packen ist unpraktisch.
Außer Strings sind als Datenquelle auch Prozeduren zulässig, sowie Dateien. Als Datei kommt für die Bilddaten nur die Eingabedatei in Frage. Diese ist als currentfile natürlich stets vorhanden, denn sie enthält ja das gerade verarbeitete Programm. Somit können wir das Bild auch durch die Zeilen
%!
gsave
100 100 scale
8 12 1 [8 0 0 12 0 0] currentfile image % hier folgen 12 Bytes,
% die in HTML nicht darstellbar sind
grestore
showpage
Auf das den image-Operator abschließende Leerzeichen folgt dann in 108 Bits die Bildinformation, und anschließend, nach einem Zeilenvorschub, noch die letzten zwei Anweisungen. Hier finden Sie das Progrämmchen, wenn Sie es ausprobieren wollen. Zum Editieren des Bildchens bräuchten Sie einen Hexadezimal-Editor. Gsview zeigt Ihnen das Ergebnis.
Bevor ich auf Einzelheiten der Bilddarstellung näher eingehe, möchte ich die Möglichkeiten nutzen, die PostScript bietet, um das lästige Hantieren mit Binärdaten zu vermeiden: dies sind die filter-Operatoren, die es gestatten, Dateien umzucodieren. PostScript stellt einige Standardfilter bereit. Die Syntax für ein decodierendes Filter ist im einfachsten Fall
Quelldatei Filtername filter
und als Ergebnis liegt anschließend statt der Quelldatei die umgewandelte Datei auf dem Stapel.
Ein Beispiel: ersetzen wir in dem obigen kleinen Progrämmchen currentfile durch
currentfile /ASCIIHexDecode filter ,
so können wir statt der Binärdaten wieder die hexadezimal kodierten Bytes eingeben. Diese Daten sind durch das Zeichen > abzuschließen, das vom ASCIIHexDecode-Filter als Endmarke interpretiert wird. (Leerzeichen und Zeilenvorschübe werden von diesem Filter überlesen.) Damit wird aus dem kleinen Beispielprogramm (hier der Quelltext):
%!
gsave
100 100 scale
8 12 1 [8 0 0 12 0 0] currentfile /ASCIIHexDecode filter image
C1 80 0C 1E 1E 1E 1E 1E 1E 1E 1E 1E>
grestore
showpage
In dieser Form kann die Datei mit einem beliebigen Editor bearbeitet werden.
Der image-Operator liefert, unabhängig von Zeilen- und Spaltenzahl des Bildes, als Ergebnis ein quadratisches Bild der Kantenlänge 1. Die Bilddaten werden zeilenweise eingelesen, wobei mit der untersten Zeile begonnen wird. Dies läßt sich mit Hilfe der Bildtransformationsmatrix ändern: ersetzt man sie durch [8 0 0 -12 0 12], so wird
die erste gelesene Zeile zur obersten.
PostScript kennt (seit Level 2) noch eine andere Variante des image-Operators: diese wird mit nur einem Parameter auf dem Stapel aufgerufen, aber dieser eine Parameter ist ein Wörterbuch (dictionary), das alle nötigen Parameter enthält. In dem obigen kleinen Programm könnte man die Zeile, die mit "image" endet, durch folgende Zeilen ersetzen, um die gleiche Wirkung zu erzielen:
<<
/ImageType 1
/Width 8
/Height 12
/ImageMatrix [8 0 0 12 0 0]
/DataSource currentfile /ASCIIHexDecode filter
/BitsPerComponent 1
/Decode [0 1]
>> image
In dieser Form ist das Kommando sehr viel leistungsfähiger.
Einige der Parameter sind uns bereits bekannt, andere fast selbsterklärend. Für ImageType ist für ein nichttransparentes Bild den Wert 1 anzugeben. Für BitsPerComponent sind die Werte 1, 2, 4, 8 und 12 zulässig. Ob es sich um ein Schwarz-Weiß- (oder Graustufen-) Bild oder um ein Farbbild handelt, erkennt der Interpreter am vorher eingestellten Farbraum. Wir haben in dem Beispiel keine Farben verwendet, daher gilt die Voreinstellung auf DeviceGray. Um ein Farbbild zu erzeugen, müßten wir vorher etwa
/DeviceRGB setcolorspace
angeben.
Decode, der Decodierungsvektor, gibt der Reihe nach für jede Komponente des Farbraumes die Grenzwerte an. Für ein Bild im RGB-Farbraum wäre entsprechend für Decode [0 1 0 1 0 1] anzugeben.
Im JPEG-Format (JFIF) ist das Bild in einem einigermaßen komplizierten Verfahren codiert. Kernstück der Umformung ist die sogenannte diskrete Cosinus-Transformation, DCT. Diese auf dem JPEG-Standard basierenden Codierungs- und Decodierungsalgorithmen sind in PostScript als Filter mit den Namen DCTEncode bzw. DCTDecode vorhanden. Das heißt, wenn wir ein vorhandenes "jpeg"-Bild mit einem kurzen Vorspann und etwas Nachspann versehen (in Analogie zu dem obigen Beispiel mit dem binär dargestellten Bildchen), dann sollte PostScript dieses Bild verarbeiten können und korrekt ausgeben. Das funktioniert auch. Nur: der Binärcode des Bildes kann wieder Ärger bereiten. Daher ist es zweckmäßig, genau wie in unserem vorigen Beispiel, den Bildinhalt noch weiter umzucodieren, und genau das tut auch das oben erwähnte Programm jpeg2ps von Thomas Merz.
In der zweiten Variante unseres Beispiels haben wir die Bitfolge als Hexadezimalzahlen verschlüsselt angegeben, für jedes Byte also zwei Zeichen (Bytes). Das hat den Vorteil, daß es relativ leicht zu entziffern ist, aber dadurch verdoppelt sich der Speicherbedarf. Da Bilddateien meist recht groß sind, empfiehlt sich die Verwendung einer platzsparenden Codierung. PostScript kennt die ASCII85-Codierung und Decodierung. Eine nach ASCII85 codierte Datei ist nur um 25% größer als die Quelldatei.
Ich habe zur Übung ein kleines Programm jpegineps.ps geschrieben, um die Möglichkeiten, Ein- und Ausgabedateien zu benutzen und durch Filter zu leiten, kennenzulernen. Dieses Programm liefert keinen gedruckten Output, sondern erzeugt aus einer Datei Bild.jpg eine Datei Bild.eps.
Das Programm ist mit knappen Kommentaren versehen; es soll hier aber nicht im Detail besprochen werden.
Ein paar Worte zum file-Operator erscheinen jedoch angebracht: file erwartet zwei Argumente vom Typ Zeichenkette (string) auf dem Stapel: den Dateinamen und den Zugriffsmodus, und öffnet dann eine Datei des angegebenen Namens, die er als oberstes auf den Stapel legt.
Laut "Rotem Buch" sind sechs verschiedene Zugriffsmodi möglich. r (nur lesen), w (nur schreiben, Datei erzeugen, wenn nicht vorhanden, überschreiben, wenn vorhanden), a (nur schreiben, Datei erzeugen, wenn nicht vorhanden, neue Daten anhängen, wenn vorhanden), r+ (lesen und schreiben, Fehlermeldung, wenn Datei nicht vorhanden), w+ (lesen und schreiben, Datei erzeugen, wenn nicht vorhanden, nicht gelesene Daten gegebenenfalls überschreiben) und a+ (nur schreiben, Datei erzeugen, wenn nicht vorhanden, neue Daten anhängen, wenn vorhanden).
Bei einfachen kleinen Programmen für die Betrachtung am Bildschirm braucht man sich über die Strukturierung keine Gedanken zu machen. Wenn man aber eine Datei zum Drucker schickt, empfiehlt es sich, die erste Zeile mit %! beginnen zu lassen: abhängig von der Einrichtung des Druckers könnte es sonst passieren, daß das Programm nicht als PostScript interpretiert, sondern als Text ausgedruckt wird. Dies ist das erste und einfachste Beispiel für eine Dokumentstrukturanweisung.
Weitere DSC-Anweisungen haben wir bei der Besprechung des EPSF-Formats kennengelernt:
%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 1024 768
stand in unserem Beispiel, das mit der Zeile
%%EOF
beendet wurde.
Die DSC-Kommandozeilen beginnen mit dem Prozentzeichen und werden daher vom PostScript-Interpreter als Kommentare überlesen: Wenn alle mit %% beginnenden Zeilen entfernt werden, ändert sich im Fall des einfachen Beispielprogrammes nichts.
Es kann aber doch nützlich sein, etwas mehr über diese zweckmäßigen Konventionen zu wissen und noch ein paar Kommandos mehr zu kennen.
Ein Beispiel: Wenn man für eine Präsentation eine größere Datei – mehrere Seiten Text und Bilder – vorbereitet hat, dann kann man sie z.B. mit Hilfe von GSview ansehen und durchblättern. Ohne Verwendung von DSC kann man aber nicht zurückblättern, was oft wünschenswert ist. Um das Zurückblättern zu ermöglichen, oder sogar den Sprung zu einer beliebigen Seite, müssen die einzelnen Seiten unabhängig voneinander sein, und es müssen Seitennummern vorhanden sein.
Letzteres wird durch die Angabe von Gesamtseitenzahl, z.B.
%%Pages: 32
im Vorspann und der Seitennummer durch z.B.
%%Page: Bild2 3
(die Seite 3 hat in diesem Beispiel den Namen Bild2) am Beginn jedes Seitenaufbaues erreicht.
Unabhängigkeit der Seiten voneinander heißt, sie müssen in beliebiger Reihenfolge interpretierbar sein. Um Definitionen und Prozeduren, die auf vielen Seiten verwendet werden, nicht für jede Seite neu eingeben zu müssen, können sie in einem Prolog vereinbart werden,
%%BeginProlog
. . .
%%EndProlog
und sind dann von jeder Seite aus sichtbar.
Das Paar
%%BeginDocument: Name
und
%%EndDocument
ist uns bei der Behandlung des eps-Beispiels schon begegnet: man verwendet es
für eingeschlossene Dokumente, um deren DSC unschädlich zu machen, z.B. die DSC-Information
%%EOF
die sonst das Dateiende signalisieren würde.
Wer mehr wissen will, sei auf die Original-Spezifikationen von Adobe verwiesen.
Ich habe hier eine Auswahl von PostScript-Anweisungen abgehandelt, die ich
beim Erstellen von einfachen Grafiken (oft allerdings mit aufwendig berechneten Farben) immer wieder benütze; es soll
aber darauf hingewiesen werden, daß die Sprache sehr viel umfangreicher
ist und insbesondere für hier nicht angesprochene drucktechnische
Detailprobleme Lösungen bietet.
Dies alles findet man im
"Roten Buch"
von Adobe, und dieses
findet man im Netz! (pdf-Datei, ca. 5.8 MB)
Im Netz ist im übrigen auch
das
"Blaue Buch", Adobes PostScript
Language
Tutorial & Cookbook vorhanden, sowie das
"Grüne
Buch", in dem die Struktur der Seitenbeschreibungssprache beschrieben
wird. Siehe nötigenfalls auch die anderen Veröffentlichungen im PostScript Technology Center.
Zurück zur Eingangsseite