15. Fonts

In these notes you will learn:

  • How to create Processing font files (.vlw files) using the Processing editor.
  • How to load fonts from disk and display them.
  • How to move fonts around the screen.

15.1. Introduction

We’ve seen that println can display strings on the console window. But what if you want a string to appear in the graphics window? That’s more involved because fonts — the images representing the characters in a string — have so many different properties. If you’ve ever played around with a word processor, then you have some idea of the many different properties of fonts. You can specify a font’s location, style, size, weight (i.e. thickness of stroke), color, and more. Hundreds of different fonts have been created, and choosing good fonts takes skill and experience.

Processing makes it relatively easy for you to use lots of different kinds of fonts. To use a font in Processing, follow these 4 basic steps:

  1. Create the .vlw file that stores the font images. The Processing IDE provides a handy tool for creating font files for a variety of fonts.

    Screenshot of the Processing IDE create font menu.
  2. Once you know a font file exists, then you load it from the disk into memory using the loadFont function.

  3. After a font has been loaded into memory, you set it as the font to be used with the textFont function.

  4. Finally, text(x, y, s) displays string s at location (x, y) using the font set by the most recent call to textFont.

Compared to calling println(s), this is a lot more work! But this extra complexity buys us flexibility: we have a lot of control over our fonts. In contrast, println gives you almost no flexibility: your text is displayed in the system console font, end of story.

15.2. Creating Fonts in the Processing IDE

As an example of how to use fonts in Processing, lets write a program that prints “Hello everybody!” on the screen.

The first step is to choose a font. Look at the list of fonts in the Tools/Create Font menu in the Processing IDE and choose a font you like. I’ll choose “Purisa” at size 48. After clicking “OK”, this creates a file called “Purisa-48.vlw” that stores all the font images.

Screenshot of the Processing IDE create font window.

The second step is to load the font file we’ve just created in Processing:

PFont font;

void setup() {
  size(500, 500);
  font = loadFont("Purisa-48.vlw");
  // ...
}

void draw() {
  // ...
}

Notice that we need to use a global variable of type PFont to store the result of calling loadFont. PFont is the main Processing class for representing fonts in memory.

Note

As it explains on the loadFont documentation page, the font file needs to be in your programs data folder, i.e. a folder called data that is in the same folder as your source code file. You’ll get a “Could not load font ...” error if the font .vlw file is not there.

The third step is to set the just-loaded font as the current font to use. This is simply a matter of calling the textFont function:

PFont font;

void setup() {
  size(500, 500);
  font = loadFont("Purisa-48.vlw");
  textFont(font);
}

void draw() {
  // ...
}

Our Purisa font is size 48, but you can change it’s size in textFont, e.g. textFont(font, 32) would make the font a little smaller.

Warning

Changing the size of a font using textFont often results in visual blemishes such as jagged edges or blurriness. To ensure your fonts look good, it’s usually best to create them at the desired size using the Processing IDE font creation tool instead of changing their size in textFont.

Finally, we are ready to place the text on the screen:

PFont font;

void setup() {
  size(500, 500);
  font = loadFont("Purisa-48.vlw");
  textFont(font);
}

void draw() {
  text("Hello everybody!", 50, 200);
}

If your program happens to use more than one style of font, then you must remember to call textFont before each call to text.

15.3. Moving Text

We can make text move in the same way that we move shapes and images. For instance, this program make a string “bounce” up and down on the screen:

PFont font;

float x;
float y;
float dy;

void setup() {
  size(500, 500);
  font = loadFont("Purisa-48.vlw");
  textFont(font, 32);

  x = 50;
  y = 50;
  dy = 1;
}

void draw() {
  background(255);
  fill(255, 0, 0);
  text("Hello everybody!", x, y);

  // move the text
  y += dy;

  if (y < 25) {  // hit the top?
     dy = -dy;
  }
  if (y > 475) { // hit the bottom?
    dy = -dy;
  }
}

Notice that the color of the text is set by calling the fill function before calling text.

15.4. Questions

  1. Name 5 different properties of fonts.
  2. Answer true or false for each of the following statements.
    • The Processing IDE comes with a font creation tool that lets you create your own .vlw font files.
    • The loadFont function loads .vlw files into memory.
    • The textFont function returns the name of the font that will be used when text is drawn on the screen.
    • The function print(x, y, s) displays string s at location (x, y) on the screen.
  3. What is the name of the class Processing uses to represent fonts?
  4. What is a potential problem of using textFont to change the size of a font?

15.5. Programming Questions

  1. Write a program that puts Hello everybody! 5 times on the screen (not the console!). Use 5 different fonts and five different colors.

  2. Write a program that puts the string I like red apples. on the screen (not the console!) all on one line. The word red should be colored red, and in a different font, than the rest of the string in a different color.

  3. Write a program that puts the string “Drag me around!” on the screen (not the console!), and allows the user to drag it around on the screen.

  4. Write a program that makes the string "ball" bounce around the screen. It should bounce off the screen edges.

  5. Write a program that displays a digital clock in the middle of the screen that shows the current time (hours, minutes, and seconds), and whether or not it is “am” or “pm”. The clock should, of course, change as the current time changes. Look at the example for hours to see how to access the current time in Processing.

    Test it with, at least, the following times:

    7:05:02am
    7:05:02pm
    12:01:00am
    12:01:00pm
    

15.6. Appendix: A Label Helper Class

Dealing with fonts in Processing can be a lot of work, especially when you just want to quickly display some information on the screen.

So to make things a bit easier, we provide a couple of helper functions for quickly displaying.

The easiest way to put text on the screen is to use the putLabel function. Assuming you have the Label code (given below) in your source file, this program prints “Hello, world!” at the mouse pointer:

void setup() {
  size(500, 500);
}

void draw() {
  background(255);
  putLabel("Hello, world!", mouseX, mouseY);
}

If you don’t provide (x, y) coordinates to putLabel, then the string is placed in the upper-left corner, e.g.:

void setup() {
  size(500, 500);
  smooth();
}

void draw() {
  background(255);

  fill(255, 0, 0);
  noStroke();
  ellipse(mouseX - 25, mouseY - 25, 75, 75);
  ellipse(mouseX - 25, mouseY + 25, 75, 75);
  ellipse(mouseX + 25, mouseY - 25, 75, 75);
  ellipse(mouseX + 25, mouseY + 25, 75, 75);

  putLabel("FPS: " + int(frameRate));
}

The major limitation on putLabel is that you can only vary the text and its position. If you want to change, say, its size or color, then you need to use Label objects. For example, try this program:

Label hi = new Label("Hello, world!");

void setup() {
  size(500, 500);
  hi.showBoundingBox = true;  // draw red box around the text
}

void draw() {
  background(255);
  hi.size = mouseX;
  hi.msgColor = color(map(mouseY, 0, 500, 0, 255));
  hi.render(mouseX, mouseY);
}

For this course, whether or not you want to use the Label class is optional: it’s your choice. The main downside is that you must remember to include it in your programs. But once it is there using fonts is much easier than using the Processing font functions.

Please note that we don’t expect you to understand the details of the Label class. It is provided simply as a convenience for you to use if you wish. You can certainly read it if your are curious: you should be able to understand most of it (assuming you are willing to look up unfamiliar functions int he Processing reference).

Finally, this code is still somewhat new, and so may have some bugs, or may be changed slightly.

15.7. The putLabel Functions and the Label Class

To use putLabel or the Label class in your own programs, copy all of the following into your program (e.g. at the bottom):

String DEFAULT_MSG = "<Label>";
float DEFAULT_X = 25;
float DEFAULT_Y = 25;
int DEFAULT_SIZE = 18;
color DEFAULT_MSG_COLOR = color(0);  // black
String DEFAULT_FONT_NAME = "BiTStream Charter";
PFont DEFAULT_FONT = createFont(DEFAULT_FONT_NAME, DEFAULT_SIZE);

void putLabel(String s) {
  putLabel(s, DEFAULT_X, DEFAULT_Y);
}

void putLabel(String s, float x, float y) {
  pushStyle();
  fill(DEFAULT_MSG_COLOR);
  textFont(DEFAULT_FONT, DEFAULT_SIZE);
  text(s, x, y);
  popStyle();
}

class Label {
  float x, y;      // location of the label
  String msg;      // text of the label
  int size;        // size of the text
  color msgColor;  // color of the text
  String fontName; // name of the font
  PFont font;

  boolean showBoundingBox;

  Label() {
    this(DEFAULT_MSG);
  }

  Label(String init_msg) {
    this(init_msg, DEFAULT_X, DEFAULT_Y);
  }

  Label(String init_msg, float init_x, float init_y) {
    this(init_msg, init_x, init_y, DEFAULT_SIZE);
  }

  Label(String init_msg, float init_x, float init_y, int init_size) {
    this(init_msg, init_x, init_y, init_size, DEFAULT_MSG_COLOR);
  }

  Label(String init_msg, float init_x, float init_y, int init_size, color init_color) {
    this(init_msg, init_x, init_y, init_size, init_color, DEFAULT_FONT_NAME);
  }

  Label(String init_msg, float init_x, float init_y,
  int init_size, color init_color, String init_fontName) {
    msg = init_msg;
    x = init_x;
    y = init_y;
    size = init_size;
    msgColor = init_color;
    fontName = init_fontName;
    font = createFont(fontName, size);
    showBoundingBox = false;
  }

  void setLocation(int xloc, int yloc) {
    x = xloc;
    y = yloc;
  }

  float width() {
    textFont(font);
    textSize(size);
    return textWidth(msg);
  }

  float height() {
    textFont(font);
    textSize(size);
    return textAscent() + textDescent();
  }

  void render() {
    pushStyle();
    textFont(font);
    fill(msgColor);
    textSize(size);
    text(msg, x, y);
    if (showBoundingBox) {
      stroke(255, 0, 0);
      noFill();
      rect(x, y - height() + textDescent(), width(), height());
    }
    popStyle();
  }

  void render(float xloc, float yloc) {
    x = xloc;
    y = yloc;
    render();
  }

  void listAllFonts() {
    println(PFont.list());
  }
} // class Label