logo_90
Uni ZH | Informatikdienste | PostScript
  1. Eine Zeichenbeschreibung
  2. Exkurs: Font-Technologie
  3. Ein eigener Font: Beispiel Random-Font

1. Eine Zeichenbeschreibung

Font technology (in German)

Ein grosses A

PostScript-Fonts sind nichts anderes als PostScript-Programme, welche eine Reihe von Schriftzeichen beschreiben. Jeder Font ist als sogenanntes Dictionary organisiert, das die Namen der Zeichen mit den Umriss-Definitionen verbindet. Die Umriss-Definitionen bestehen im wesentlichen aus den Befehlen moveto (Bewegung), lineto (Linie), curveto (Kurve) und fill (zum Füllen). Das PostScript-Programm demonstriert die Beschreibung eines Zeichens "A":

200 200 moveto
	300 200 lineto 
	385 410 lineto
	615 410 lineto
	700 200 lineto
	800 200 lineto
	500 900 lineto
closepath			% Umriss aussen
425 500 moveto
	500 660 lineto
	575 500 lineto
closepath			% Umriss innen
fill
Das Symbol wird durch eine äussere und eine innere Hüllkurve gebildet. Während der äussere Umriss im Gegenuhrzeigersinn konstruiert wird, verläuft der innere Umriss im Uhrzeigersinn. Es werden lediglich Linien-Segmente verwendet (hier keine Bezier-Kurven). Der Nullpunkt des Koordinatensystems liegt unten links. Die Einheiten sind gewöhnlich typografische Punkte, die PostScript-Sprache definiert die Zeichen jedoch grössenunabhängig.

Theoretisch ist es auch möglich, ein Zeichen als Bit-Muster zu definieren (mittels imagemask). Aber die Umriss-Methode ist der Bitmap-Methode aus zwei Gründen überlegen:

  1. Die Zeichen können in beliebigen Grössen verlangt werden.
  2. Das Auflösungsvermögen des Ausgabegerätes kann optimal genutzt werden.
Jedes Font-Dictionary muss zwingend eine Prozedur BuildChar enthalten, welche eine einzelne Zeichenbeschreibung ausführt. Dabei dient der Operator get dazu, aus einem Dictionary eine Zeichenbeschreibung zu entnehmen. Syntax des Dictionary-Operators get:
	dict key get any 
Der Operator get sucht nach dem Schlüssel key in dict und gibt den entsprechenden Wert, d.h. die gefundene Zeichenbeschreibung, auf den Operandenstack zurück. Beachten Sie, dass "get" sowohl ein Dictionary- als auch ein Array- oder String-Operator sein kann.


Peter Vollenweider

2. Exkurs: Font-Technologie

Das Füllen von Bezier-Kurven hat seine Grenzen -- vor allem bei kleinen Schriftzeichen im unteren Auflösungsbereich (am Bildschirm). Während die Bezier-Kurven exakte Formen beschreiben, kann der Effekt am Bildschirm total unruhig werden, weil nur ein grobes Pixelraster zur Verfügung steht.

Die Typ-1-Fonts von Adobe Systems sind gewöhnlich verschlüsselt und enthalten spezielle Instruktionen oder Hints, die zur scharfen Wiedergabe von kleinen Zeichen verwendet werden. Diese Hints dienen der Verbesserung der Schriftqualität im niedrigen Auflösungsbereich. Die Fonts des Typs 1 runden die Koordinaten der Stützpunkte nicht einfach auf die nächste Pixel-Position. Technisch sind die Hints durch spezielle Routinen im Rasterprozess verwirklicht, die dem Benutzer verborgen sind.

Die wichtigste Anwendung von Hints ist das Angleichen der Stammdicken von Schriftzeichen. Bei Typ-3-Schriften kann es sein, dass von zwei gleich stark definierten Stämmen der eine zum Beispiel mit zwei Pixeln und der andere mit drei Pixeln ausgegeben wird. Dieser bei geringen Auflösungen gut sichtbare Effekt wird im grafischen Gewerbe Pixelsprung genannt. Er wird dadurch hervorgerufen, dass die Koordinaten einfach auf die nächste Pixel-Position gerundet werden. Beispiel: GRID
In dieser Abbildung SOLLTEN alle Striche gleich dick sein, sind es aber nicht (Pixelsprung).

Die Hints sorgen dafür, dass die Linien oder die Zeichenstämme der physikalischen Auflösung des Ausgabegerätes angepasst werden, und garantieren, dass gleich definierte Stämme mit der gleichen Pixelanzahl belichtet werden. Die Amerikaner sagen: "Hints tell the computer how to modify character outlines so they fit the grid of dots."

Ein weiterer Typ von Hints nennt sich Force-Bold. Wenn kleine Schriftzeichen mit einer kleinen Auflösung, z.B. auf dem Bildschirm, gerastert werden, kann es sein, dass die Stämme fetter Zeichen nur ein Pixel stark sind. Diese Zeichen erscheinen somit gleich wie magere Zeichen und können nicht mehr von jenen unterschieden werden. Die Hints Force-Bold sorgen dafür, dass ein fettes "a" immer dicker als ein normales "a" aussieht.

Als Faustregel gilt, dass der PostScript-Interpreter von Adobe Systems die Hint-Informationen berücksichtigt, während bei den Non-Adobe-Interpretern die Lesbarkeit der kleinen Zeichen häufig zu wünschen übrig lässt.

Siehe auch

AFM (Adobe Font Metrics)

KERN
Schriftzug (Helvetica-Bold) ohne und mit Buchstaben-Kerning.

Adobe Font Metrics: Die AFM-Dateien enthalten Breiten- und Kerning-Angaben für die Schriftzeichen bzw. Zeichenkombinationen: Character Metrics und Kerning Data. Diese in einer AFM-Datei abgelegten Daten werden von Textverarbeitungsprogrammen eingelesen, um die Zeilenumbrüche berechnen zu können.

UNTERSCHNEIDEN
Zeichenkombinationen mit/ohne Unterschneiden.

Beispiel für eine AFM-Datei (ZapfChancery-MediumItalic.afm).

Corporate Design der Universität

Und noch ein Frutiger-Font: afm und pfa.

3. Ein eigener Font: Beispiel Random-Font

Wie können Sie einen eigenen Font konstruieren? Das nachfolgend abgedruckte PostScript-Programm besteht im wesentlichen aus dem eigenen Font-Dictionary newfont, dem Operator definefont, der das eigene Font-Dictionary mit dem Fontnamen WilliamRand verbindet, sowie dem Skript mit den vier show-Operatoren: RANDOM

Abbildung: Random-Font mit sechs Zeichen.

Die einzelnen Zeichen- oder Umrissbeschreibungen verwenden natürlich den curveto-Operator für die Bezier-Segmente sowie den lineto-Operator. Der curveto-Operator dient dazu, beliebige Rundungen zu konstruieren. Seine Operanden werden mithilfe der Sequenz

rand 100 mod
berechnet, d.h. die Zufallszahl (geliefert von rand) wird durch 100 geteilt, sodass der Restwert (Modulo) zwischen 0 und 99 liegt. Der vorliegende Random-Font wurde vollständig von Hand ausprogrammiert. Normalerweise erzeugen Sie einen eigenen Font mit einem Fontgenerator, z.B. mit dem bekannten Macintosh-Programm Fontographer von Altsys in Texas.

Im folgenden ist das (gekürzte) PostScript-Programm abgedruckt:

%!PS-Adobe-3.0 EPSF-3.0
%%Title: WilliamRand at 100
%%Creator: (Mein Mail-Freund John F Sherman)
%%BoundingBox: 0 0 530 750
Das Programm beginnt mit dem EPS-Kopf, der unter anderem die Boundig-Box angibt. John F. Sherman entwickelte das Programm an der Notre Dame University in Indiana.
/Helvetica findfont 12 scalefont setfont
	15 15 moveto 
	(Random font with 100 random factor) show
				% Legende

Einträge im Font-Dictionary

Auf den Vorspann folgt bereits das Kernstück, nämlich das Font-Dictionary mit verschiedenen zwingenden Einträgen und den sechs Zeichenbeschreibungen:
 
/newfont 10 dict def 			% Font-Dictionary ...
newfont begin

/FontInfo 9 dict dup begin
 	/version (001.000) readonly def
 	/FullName (WilliamRand) readonly def
 	/FamilyName (William) readonly def
 	/Weight (Medium) readonly def
 	/ItalicAngle 0 def
 	/isFixedPitch false def
 	/UnderlinePosition -133 def
 	/UnderlineThickness 20 def
 	/Notice (designed by John F Sherman) readonly def
end readonly def
Die neun Einträge im Unter-Dictionary FontInfo werden vom PostScript-Interpreter nicht beachtet, sie dienen der Dokumentation. Der wichtige Eintrag FontType besagt, ob es sich um einen Font des Typs 1 oder des Typs 3 handelt.
 
/FontType 3 def				% ohne Hints
Die Variable FontType ist auf 3 gesetzt. Der Font enthält keine Hints, ist somit vom Typ 3.
/FontMatrix [.001 0 0 .001 0 0] def
/FontBBox [0 0 1000 1000] def
Das Koordinatensystem für die Zeichenbeschreibung besteht aus 1000 mal 1000 Einheiten; der Nullpunkt befindet sich unten links.
 
/UniqueID 1734 def

/Encoding 256 array def
StandardEncoding Encoding copy pop
Der "Encoding Vector" ordnet jedem Buchstaben einen numerischen Code zu. Die Buchstaben a bis f werden gemäss ASCII-Code angesteuert (Normalfall).

Zeichenbeschreibungen mit den Umrissen

Nun folgt das Unter-Dictionary CharProcs mit den Zeichenbeschreibungen:
/CharProcs 60 dict def			% Unter-Dictionary
CharProcs begin				% Zeichenbeschreibungen...

/.notdef	 { } def

/space	 {300 0 setcharwidth} bind def
Dieser Befehl legt die Breite des Leerzeichens fest. Der Operator setcharwidth gibt die Zeichenbreite an (Font-Metrik). Zuerst werden die Umrisse des Buchstabens "a" definiert:
/a	 { 300 0 setcharwidth
277.9999 rand 100 mod add 79.9866 rand 100 mod add moveto 
		% Startpunkt fuer a
Der moveto-Operator legt fest, wo mit dem Konstruieren begonnen wird. Darauf folgen einige lineto's und viele curveto's:
289.9933 rand 100 mod add 79.9866 rand 100 mod add lineto % gerades Segment
293.9911 rand 100 mod add 72.998 rand 100 mod add 287.9944 rand 100 mod add
-7.9956 rand 100 mod add 235.9924 rand 100 mod add -7.9956 rand 100 mod add 
curveto 		% erstes Bezier-Segment
201.9958 rand 100 mod add -7.9956 rand 100 mod add 176.9867 rand 100 mod add
28.9917 rand 100 mod add 183.9905 rand 100 mod add 61.9965 rand 100 mod add 
curveto 		% zweites Bezier-Segment
165.9851 rand 100 mod add 21.9879 rand 100 mod add 137.9852 rand 100 mod add
-6.9885 rand 100 mod add 93.9941 rand 100 mod add -7.9956 rand 100 mod add 
curveto 		% drittes Bezier-Segment
Es folgen weitere Bezier-Segmente...
6.9885 rand 100 mod add -9.9945 rand 100 mod add 17.9901 rand 100 mod add 192.9932 rand 100 mod add 50.9949 rand 100 mod add 233.9935 rand 100 mod add curveto 
90.9882 rand 100 mod add 283.9966 rand 100 mod add 164.9933 rand 100 mod add 283.9966 rand 100 mod add 180.9998 rand 100 mod add 311.9965 rand 100 mod add curveto 
182.9987 rand 100 mod add 314.9872 rand 100 mod add 201.9958 rand 100 mod add 475.9979 rand 100 mod add 144.989 rand 100 mod add 467.9871 rand 100 mod add curveto 
70.9991 rand 100 mod add 457.9925 rand 100 mod add 110.9924 rand 100 mod add 400.9857 rand 100 mod add 109.9853 rand 100 mod add 383.9874 rand 100 mod add curveto 
107.9864 rand 100 mod add 356.9946 rand 100 mod add 50.9949 rand 100 mod add 347.9919 rand 100 mod add 49.9878 rand 100 mod add 383.9874 rand 100 mod add curveto 
48.996 rand 100 mod add 439.9872 rand 100 mod add 89.9963 rand 100 mod add 485.9924 rand 100 mod add 154.9988 rand 100 mod add 481.9946 rand 100 mod add curveto 
216.9952 rand 100 mod add 477.9968 rand 100 mod add 231.9946 rand 100 mod add 403.9917 rand 100 mod add 231.9946 rand 100 mod add 341.9952 rand 100 mod add curveto 
231.9946 rand 100 mod add 49.9878 rand 100 mod add lineto 
			% Zur Abwechslung eine Linie
Wieder mal eine Linie! Der Operator lineto hat zwei Operanden, er konstruiert ein gerades Segment von der aktuellen zur angegebenen Position (x,y). Beispiel für die oben angegebene Koordinatenposition: x = (231.9946 + 71.3292), y = (49.9878 + 44.0131). Und noch ein Bezier-Segment:
231.9946 rand 100 mod add 27.9999 rand 100 mod add 253.9978 rand 100 mod add 17.9901 rand 100 mod add 264.9994 rand 100 mod add 27.9999 rand 100 mod add curveto  			% Bezier-Segment
Der Buchstaben "a" besteht aus einer äusseren und inneren Hüllkurve, daher der folgende moveto-Operator!
 
181.9916 rand 100 mod add 288.9862 rand 100 mod add moveto 
			% Start fuer innere Huellkurve
163.9862 rand 100 mod add 281.9977 rand 100 mod add 100.9979 rand 100 mod add 253.9978 rand 100 mod add 87.9974 rand 100 mod add 199.9969 rand 100 mod add curveto 
74.9969 rand 100 mod add 144.989 rand 100 mod add 76.9958 rand 100 mod add 47.9889 rand 100 mod add 109.9853 rand 100 mod add 19.989 rand 100 mod add curveto 
140.9912 rand 100 mod add -5.9967 rand 100 mod add 181.9916 rand 100 mod add 56.9916 rand 100 mod add 181.9916 rand 100 mod add 97.9919 rand 100 mod add curveto
181.9916 rand 100 mod add 288.9862 rand 100 mod add lineto 
	fill	} def			% Buchstaben a
Die Umrisse des Zeichens "a" sind mehrheitlich aus Bezier-Segmenten zusammengesetzt (curveto). Zu einem curveto-Operator gehören jeweils sechs Operanden, d.h. drei Koordinatenpaare. Der Operator rand liefert jeweils eine Zufallszahl zurück, die dann durch 100 geteilt wird. Der Operator mod liefert somit einen Restwert zwischen 0 und 99. Der Operator fill füllt die geschlossenen Umrisse mit schwarzer Farbe auf.

Buchstaben b bis f

Die restlichen fünf Zeichenbeschreibungen sind aus Platzgründen gekürzt:
/b { 290 0 setcharwidth % Zeichenbreite b
217.9871 rand 100 mod add 250 rand 100 mod add moveto 	% Startpunkt fuer b
218.9941 rand 100 mod add 153.9917 rand 100 mod add 207.9925 rand 100 mod
add 38.9862 rand 100 mod add 154.9988 rand 100 mod add 11.9934 rand 100 mod
add 
curveto 				% Bezier-Segment

	... Umrissbeschreibung fuer b ...

	fill	} def			% Buchstaben b
Hier sind die meisten Bezier-Segmente, d.h. curveto-Operatoren, weggelassen.
/c	 { 270 0 setcharwidth % Zeichenbreite c 
75.9888 rand 100 mod add 248.9929 rand 100 mod add moveto % Startpunkt fuer c
76.9958 rand 100 mod add 154.9988 rand 100 mod add 70.9991 rand 100 mod add
1.9989 rand 100 mod add 143.9972 rand 100 mod add 17.9901 rand 100 mod add 
curveto 				% Bezier-Segment

	... Umrissbeschreibung fuer c ...

	fill	} def			% Buchstaben c
Der Buchstaben "c" besteht lediglich aus einer Hüllkurve, verwendet daher nur einen moveto-Operator; sonst ausschliesslich Bezier-Segmente.
/d	 { 270 0 setcharwidth % Zeichenbreite d
272.995 19.989 moveto 	 % Startpunkt fuer d
272.995 rand 100 mod add 0 rand 100 mod add lineto 182.9987 rand 100 mod add 0 rand 100 mod add lineto 
182.9987 rand 100 mod add 47.9889 rand 100 mod add lineto 
166.9922 rand 100 mod add 14.9994 rand 100 mod add 152.9999 rand 100 mod add
-17.9901 rand 100 mod add 115.9973 rand 100 mod add -17.9901 rand 100 mod
add 
curveto 				% Bezier-Segment

	... Umrissbeschreibung fuer d ...

	fill	} def			% Buchstaben d
Sowohl c wie auch d und e sind gleich breit, nämlich 270 Einheiten.
/e	 { 270 0 setcharwidth % Zeichenbreite e
12.9852 rand 100 mod add 235.9924 rand 100 mod add moveto % Startpunkt fuer e
10.9863 rand 100 mod add 341.9952 rand 100 mod add 50.9949 rand 100 mod add
481.9946 rand 100 mod add 128.9978 rand 100 mod add 481.9946 rand 100 mod
add 
curveto 				% Bezier-Segment

	... Umrissbeschreibung fuer e ...

	fill	} def			% Buchstaben e

/f	 { 170 0 setcharwidth % Zeichenbreite f
99.9908 rand 100 mod add 639.9994 rand 100 mod add moveto 					% Startpunkt fuer f
99.9908 rand 100 mod add 474.9908 rand 100 mod add lineto 
139.9994 rand 100 mod add 474.9908 rand 100 mod add lineto 

	... Umrissbeschreibung fuer f ...

	fill	} def			% Buchstaben f
Um Buchstaben mit geraden Segmenten -- z.B. "d" oder "f" -- zu konstruieren, sind auch eine Reihe von lineto-Operatoren erforderlich.
end	 		% CharProcs
Somit sind die Umrisse der sechs Zeichen mathematisch beschrieben!

Die Prozedur BuildChar

Wir befinden uns immer noch im Font-Dictionary. Es folgt zwingend die Prozedur BuildChar:
/BuildChar					% obligatorische Prozedur
	{exch begin				% d.h. font begin
	Encoding exch get			% Code => Name
	CharProcs exch get			% => Zeichenbeschreibung
	end
	exec					% ausfuehren
	} def
Falls sich ein Zeichen noch nicht im Font-Cache befindet, muss BuildChar aufgerufen werden. Objekte auf dem Operanden-Stack vor dem Aufruf: der aktuelle Font und ein numerischer Code für das gewünschte Zeichen. Aufgrund des numerischen Schlüssels holt sich der Array-Operator get den Zeichennamen im Vektor Encoding. Der Dictionary-Operator get holt sich aus CharProcs eine einzelne Zeichenbeschreibung. Der Wert, der vom ersten get zurückgegeben wird, dient somit als Schlüssel für das zweite get. Dies bedeutet, dass sich auf dem Operanden-Stack folgende Objekte ablösen: numerischer Code => Zeichenname, z.B. "a" => Zeichenbeschreibung. Erst die Prozedur BuildChar führt die Zeichenbeschreibungen aus, sodass nun das Font-Dictionary vollständig ist.
 
end	 		% newfont
Hier endet unser eigenes Font-Dictionary.

Der Operator definefont und die Seitenbeschreibung

/WilliamRand newfont definefont pop
Der Operator definefont dient dazu, unser Font-Dictionary mit dem Font-Namen WilliamRand zu verbinden. Der Font ist somit "getauft" und steht auf dem Drucker oder der PostScript-Maschine (NeXT) zur Verfügung. Nachdem die Zeichen konstruiert und der Font erzeugt sind, folgt das sogenannte Skript, d.h. die Seitenbeschreibung...
/WilliamRand findfont 70 scalefont setfont
					% neuen Font anwaehlen

	10 50 translate			% Ursprung

	0 600 moveto (abcdef) show % von
	0 450 moveto (abcdef) show % oben
	0 300 moveto (abcdef) show % nach
	0 150 moveto (abcdef) show % unten


showpage
Das obige Skript schreibt die sechs Zeichen des neuen Fonts je viermal, siehe Abbildung: Random-Font mit sechs Zeichen. Die Buchstaben mit den zufälligen Umrissen werden in 70 Punkt gesetzt. Die vertikale Zeilenschaltung beträgt 150 Punkt.

Dieses Beispiel demonstrierte einerseits den random-Operator, zeigte aber in erster Linie, wie man einen eigenen Font konstruieren kann. John, many thanks for the example!


Uni ZH | Informatikdienste | PostScript
HTML 3.2 Last update: Monday, 27-Dec-2004 12:02:06 CET by vo