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)).
↑