Form III: Text und Typografie
Fonts, Text ausrichten, Text vermessen, Texteingabe: Keyboard, Texteingabe: Dateien, Text als Daten, Fonts als Daten: Schriften manipulieren, Interaktive Schrift, Generative Schriftkontur
Fonts
Eine Variable für Schriften: PFont. Entweder Systemschriften verwenden... (über ihren Namen, Namen anzeigen lassen mit PFont.list())
println(PFont.list());
...oder Schriftart im Data-Ordner ablegen (genau wie Bilder) und laden:
PFont f;
String s;
void setup() {
size(400,400);
background(0);
s = "Abcdefg.";
f = createFont("HypatiaSansPro-Regular.otf", 32);
}
Schrift auswählen mit textFont(Schriftvariable, Größe), anzeigen mit text().
void draw() {
background(0);
fill(255);
textFont(f, 128);
text(s, 0, height/2);
}
Alle Fonts auf dem Rechner einmal laden und benutzen (abspeichern in PDF):
import processing.pdf.*;
String text = "Form & Code";
String[] alleFonts = PFont.list();
println("Anzahl der Schriften: "+alleFonts.length);
size(600,14000,PDF,"fonts.pdf"); // Höhe der Zeichenfläche sollte Anzahl der Schriften mal 30 sein
background(255);
fill(0);
textAlign(LEFT,TOP);
translate(20,20);
for (int i=0; i<alleFonts.length; i++) {
PFont f = createFont(alleFonts[i],24);
textFont(f,24);
text(alleFonts[i]+": "+text,0,0);
translate(0,30);
println("Schrift "+i+": "+alleFonts[i]);
}
println("...fertig.");
↑
Text ausrichten
Die Koordinaten des Textes beziehen sich auf dessen linke untere Ecke (inkl. Ober-, Unterlängen usw.):
void draw() {
background(0);
fill(255);
textFont(f, 128);
text(s, 0, height/2);
stroke(128,128,255);
line(0,height/2,width,height/2);
}
textAlign(...) regelt die Ausrichtung rechts/links/zentriert...
void draw() {
background(0);
fill(255);
textFont(f, 64);
textAlign(RIGHT);
text(s, width/2, 100);
textAlign(CENTER);
text(s, width/2, 200);
textAlign(LEFT);
text(s, width/2, 300);
if (mousePressed) {
stroke(128, 128, 255);
line(0, 100, width, 100);
line(0, 200, width, 200);
line(0, 300, width, 300);
line(width/2, 0, width/2, height);
}
}
...und auch oben/mitte/unten und auf der Grundlinie (NORMAL):
void draw() {
background(0);
fill(255);
textFont(f, 64);
textAlign(RIGHT,BOTTOM);
text(s, width/2, 100);
textAlign(RIGHT,NORMAL);
text(s, width/2, 200);
textAlign(CENTER,CENTER);
text(s, width/2, 300);
textAlign(LEFT,TOP);
text(s, width/2, 400);
if (mousePressed) {
stroke(128, 128, 255);
line(0, 100, width, 100);
line(0, 200, width, 200);
line(0, 300, width, 300);
line(0, 400, width, 400);
line(width/2, 0, width/2, height);
}
}
Zeilenabstand festlegen mit textLeading(...). Zeilen werden in einer String-Variablen mit \n (Newline) markiert.
size(600, 300);
background(0);
PFont f = createFont("HypatiaSansPro-Regular.otf", 44);
textFont(f, 44);
String dreiZeilen = "Aber,\naber,\naber!";
textAlign(LEFT,TOP);
textLeading(44);
text(dreiZeilen, 20, 40);
textLeading(88);
text(dreiZeilen, 220, 40);
textLeading(4);
text(dreiZeilen, 420, 40);
↑
Text vermessen: Grundlinie, Ober-/Unterlänge, Breite
Ober- und Unterlänge liefern textAscent() und textDescent(). Die Grundlinie hängt von textAlign(...) ab.
size(400, 200);
background(0);
PFont f = createFont("HypatiaSansPro-Regular.otf", 44);
textFont(f, 44);
String zeile = "Kragen!";
textAlign(LEFT,BOTTOM);
text(zeile, 40, height/2);
float oberlaenge = textAscent();
float unterlaenge = textDescent();
float grundlinie = height/2 - unterlaenge;
stroke(128, 128, 255);
line(0,grundlinie,width,grundlinie);
stroke(255,128,128);
line(0,grundlinie-oberlaenge,width,grundlinie-oberlaenge);
line(0,grundlinie+unterlaenge,width,grundlinie+unterlaenge);
Die Breite eines Textes (bzw. eines Strings) berechnet textWidth(...).
size(400, 200);
background(0);
PFont f = createFont("HypatiaSansPro-Regular.otf", 44);
textFont(f, 44);
String zeile = "Kragen!";
textAlign(LEFT,BOTTOM);
text(zeile, 40, height/2);
float links = 40;
float rechts = 40 + textWidth(zeile);
stroke(255,128,128);
line(links,0,links,height);
line(rechts,0,rechts,height);
Zum Beispiel: Nach jedem Wort die Zeichenfläche um die Breite des Wortes verschieben.
size(600, 600);
background(0);
PFont f = createFont("HypatiaSansPro-Regular.otf", 36);
textFont(f, 36);
String[] texte = {
"Eins", "Zwei", "Drei", "Vier", "Fünf"
};
textAlign(LEFT, CENTER);
translate(20, 100);
for (int i=0; i<10; i++) {
pushMatrix();
for (int j=0; j<10; j++) {
int zufallsIndex = int(random(5));
String wort = texte[zufallsIndex];
text(wort, 0, 0);
translate(textWidth(wort), 0);
rotate(radians(random(-10, 10)));
}
popMatrix();
translate(0,36);
}
↑
Texteingabe: Keyboard
Zugriff auf die Tastatur über eine Methode (wie setup oder draw): keyPressed().
void setup() {
size(400,400);
background(0);
stroke(255,160);
}
void draw() {
}
void keyPressed() {
line(random(width),random(height),random(width),random(height));
}
Zugriff auf die gedrückte Taste über die Variable key:
void setup() {
size(400,400);
background(0);
}
void draw() {
}
void keyPressed() {
textAlign(CENTER,CENTER);
textSize(random(10,80));
colorMode(HSB);
fill(random(255),200,200);
text(key,random(width),random(height));
}
Buchstaben sind Variablen der Sorte char (Character) und im Grunde das selbe wie Zahlen. Den Zusammenhang zwischen Zahlen und Buchstaben regelt ASCII:
Ein "A" in ASCII ist die Zahl 65, ein "a" ist 97, eine "7" ist 55:
void setup() {
size(400,400);
background(0);
}
void draw() {
}
void keyPressed() {
textAlign(CENTER,CENTER);
textSize(random(10,80));
colorMode(HSB);
fill(random(255),200,200);
text(key+" ("+int(key)+")",random(width),random(height));
}
Spezialtasten filtern über ihren keyCode:
void setup() {
size(400, 400);
background(0);
}
void draw() {
}
void keyPressed() {
textAlign(CENTER, CENTER);
textSize(random(10, 80));
colorMode(HSB);
fill(random(255), 200, 200);
if (key==CODED) {
if (keyCode==RIGHT) {
background(0);
}
} else {
text(key, random(width), random(height));
}
}
↑
Texteingabe: Dateien
Texte aus Dateien lesen mit loadStrings(...). Ergebnis ist ein Array, das die einzelnen Zeilen der Datei enthält:
String[] texte = loadStrings("textdatei.txt");
println("Zeilen in der Datei: "+texte.length);
for (int i=0; i<texte.length; i++) {
println("Zeile "+(i+1)+": "+texte[i]);
}
...oder:
String[] texte;
void setup() {
size(600, 600);
texte = loadStrings("textdatei.txt");
PFont f = createFont("HypatiaSansPro-Regular.otf", 36);
textFont(f, 36);
}
void draw() {
background(0);
translate(width/2, height/2);
rotate(radians(map(mouseX,0,width,0,360)));
for (int i=0; i<texte.length; i++) {
rotate(radians(36));
text(texte[i], 40, 0);
}
}
↑
Text als Daten
Visualisieren von Text zum Beispiel durch Zugriff auf dessen Eigenschaften oder seine Zeichen. String.length() für die Länge eines Strings.
String[] texte;
size(600, 600);
texte = loadStrings("textdatei.txt");
noFill();
background(255);
translate(0,30);
for (int i=0; i<texte.length; i++) {
stroke(0);
strokeWeight(50);
strokeCap(SQUARE);
line(0,0,texte[i].length()*3,0);
translate(0,60);
}
Zugriff auf die Zeichen in einem String mit String.charAt(), verschieben mit translate(...) um die Breite des Buchstaben (textWidth())...
size(800,200);
String text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
background(255);
translate(0,height/2);
for (int i=0; i<text.length(); i++) {
textAlign(LEFT, CENTER);
textSize(random(10, 80));
fill(random(255));
text(text.charAt(i),0,0);
translate(textWidth(text.charAt(i)),0);
}
...mit mehreren Zeilen aus einer Datei. Auchtung texte.length für die Länge des Arrays mit allen Zeilen, aber texte[i].length() für die Länge der Zeile mit Nummer i.
size(600, 600);
String[] texte = loadStrings("textdatei.txt");
float zeilenabstand = 60;
background(255);
translate(0, 30);
for (int i=0; i<texte.length; i++) {
pushMatrix();
for (int j=0; j<texte[i].length(); j++) {
textAlign(LEFT, CENTER);
textSize(random(10, 120));
fill(random(200));
text(texte[i].charAt(j), 0, 0);
translate(textWidth(texte[i].charAt(j)), 0);
}
popMatrix();
translate(0, zeilenabstand);
}
...wobei jedes Zeichen auch einfach als Zahl benutzt werden kann. Zum Beispiel mit einem map(...), das die ASCII-Zahlen (0-127) in Positionen übersetzt.
String[] texte;
size(840, 420);
texte = loadStrings("textdatei.txt");
noFill();
background(255);
translate(20,20);
for (int i=0; i<texte.length; i++) {
beginShape();
curveVertex(0,20);
for (int j=0; j<texte[i].length(); j++) {
curveVertex(j*6, map(texte[i].charAt(j),49,128,2,20));
}
endShape();
translate(0,40);
}
↑
Fonts als Daten: Schriften manipulieren
Auch auf die Form der Schriften kann man zugreifen. Dabei hilft die Library Geomerative (siehe auch die Beispiele dazu in Generative Gestaltung). Installieren über Sketch/Import Library/Add Library...
Vor der Benutzung mus die Library immer erst initialisiert werden.
import geomerative.*; size(600,400); RG.init(this);
Die Library besitzt eigene Variablen für Schriften: RFont, die einen Zugriff auf deren Geometrie erlaubt (allerdings nur für TrueType-Schriften/ttf).
RFont font;
font = new RFont("cour.ttf", 100, RFont.LEFT);
Umwandeln von Text in Geometrie über Funktionen der Library... Ergebnis ist ein Array voller Punkte.
String text = "Form&Code"; RCommand.setSegmentLength(4); RCommand.setSegmentator(RCommand.UNIFORMLENGTH); RGroup grp; grp = font.toGroup(text); grp = grp.toPolygonGroup(); RPoint[] punkte = grp.getPoints();
Zeichnen der Punkte aus dem Array, wie gewohnt:
background(255);
translate(20,height/2);
for (int i=0; i<punkte.length; i++) {
point(punkte[i].x, punkte[i].y);
}
Insgesamt:
import geomerative.*;
size(600,400);
RG.init(this);
RFont font;
font = new RFont("cour.ttf", 100, RFont.LEFT);
String text = "Form&Code";
RCommand.setSegmentLength(4);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
RGroup grp;
grp = font.toGroup(text);
grp = grp.toPolygonGroup();
RPoint[] punkte = grp.getPoints();
background(255);
translate(20,height/2);
for (int i=0; i<punkte.length; i++) {
point(punkte[i].x, punkte[i].y);
}
Jetzt zum Beispiel die einzelnen Punkte zufällig verschieben...
background(255);
strokeWeight(4);
translate(20,height/2);
for (int i=0; i<punkte.length; i++) {
point(punkte[i].x+random(-2,2), punkte[i].y+random(-2,2));
}
...oder abhängig von der Nummer (i) jedes Punktes.
background(255);
strokeWeight(4);
translate(20,height/2);
for (int i=0; i<punkte.length; i++) {
float abstand = map(i,0,punkte.length,0,10);
point(punkte[i].x+random(-abstand,abstand), punkte[i].y+random(-abstand,abstand));
}
↑
Interaktive Schrift
Einmal in Punkte zerlegt, kann die Schrift auch interaktiv manipuliert werden. Dazu zunächst das selbe Programm als Animation (mit setup/draw)...
import geomerative.*;
RFont font;
void setup() {
size(600, 400);
RG.init(this);
font = new RFont("cour.ttf", 100, RFont.LEFT);
}
void draw() {
String text = "Form&Code";
RCommand.setSegmentLength(4);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
RGroup grp;
grp = font.toGroup(text);
grp = grp.toPolygonGroup();
RPoint[] punkte = grp.getPoints();
background(255);
strokeWeight(4);
translate(20, height/2);
for (int i=0; i<punkte.length; i++) {
float abstand = map(i, 0, punkte.length, 0, 10);
point(punkte[i].x+random(-abstand, abstand), punkte[i].y+random(-abstand, abstand));
}
}
...und dann z.B. die Punktgröße von der Maus abhängig machen:
import geomerative.*;
RFont font;
void setup() {
size(600, 400);
RG.init(this);
font = new RFont("cour.ttf", 100, RFont.LEFT);
}
void draw() {
String text = "Form&Code";
RCommand.setSegmentLength(4);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
RGroup grp;
grp = font.toGroup(text);
grp = grp.toPolygonGroup();
RPoint[] punkte = grp.getPoints();
background(255);
stroke(0,40);
translate(20, height/2);
for (int i=0; i<punkte.length; i++) {
strokeWeight(map(mouseX,0,width,10,60));
point(punkte[i].x,punkte[i].y);
}
}
↑
Generative Schriftkontur
Linien zeichnen von Punkt zu Punkt mit Zähler des aktuellen Punktes i und dessen Vorgänger i-1. Weil der erste Punkt (i=0) keinen Vorgänger hat, beginnt die Schleife bei i=1.
import geomerative.*;
size(600,400);
RG.init(this);
RFont font;
font = new RFont("cour.ttf", 100, RFont.LEFT);
String text = "Form&Code";
RCommand.setSegmentLength(4);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
RGroup grp;
grp = font.toGroup(text);
grp = grp.toPolygonGroup();
RPoint[] punkte = grp.getPoints();
background(255);
translate(20,height/2);
for (int i=1; i<punkte.length; i++) {
line(punkte[i].x, punkte[i].y,punkte[i-1].x, punkte[i-1].y);
}
Jetzt zum Beispiel zufällig einige Punkte überspringen und in einer eigenen Variable merken, welcher Punkt der letzte war.
import geomerative.*;
size(600,400);
RG.init(this);
RFont font;
font = new RFont("cour.ttf", 100, RFont.LEFT);
String text = "Form&Code";
RCommand.setSegmentLength(4);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
RGroup grp;
grp = font.toGroup(text);
grp = grp.toPolygonGroup();
RPoint[] punkte = grp.getPoints();
background(255);
translate(20,height/2);
stroke(50);
int letzterPunkt = 0;
for (int i=1; i<punkte.length; i++) {
line(punkte[i].x, punkte[i].y,punkte[letzterPunkt].x, punkte[letzterPunkt].y);
letzterPunkt = i;
i = i + int(random(12));
}
...die interessanten Parameter dieses Programms sind jetzt die 4 in RCommand.setSegmentLength(4), sowie die 12 in i = i + int(random(12)).
↑