Are monospace fonts really monospace?

Take the following code as an example:

// in ofApp.h
ofTrueTypeFont font;

// in ofApp.cpp
// in setup
font.load("SourceCodePro.ttf", 30);

// in draw
ofBackground(150);
ofSetColor(0);
font.drawString("aa", 100, 100);
ofSetColor(255);
font.drawString("a", 100+font.stringWidth("a"), 100);

This should result in the second string, in white, to fall on top of the second letter of the first string, in black. But that doesn’t happen, no matter how many monospace fonts I’ve tried.
Am I missing something obvious here? How can I get the width of a character so that I can draw something (like a cursor the way it is drawn in a terminal window) right on top of where the next character should be?
I’ve also tried font.getStringBoundingBox(), but I get the same results (since .stringWidth() is actually the width part of this function).

I’ve investigated something similar in the past and discovered a lot of monospaced fonts have glyphs width slight different widths, just a few have consistent width. I’ve ended using my own font 0Arame-Mono for the application.
But I’ll be inspecting your test case, because you are using the letter “a” in both, so I suspect there is something not right in OF itself. I’ll try to see if character property “advance” is different than “width”.

ok I think ofTrueType font is using width property instead of advance and this is causing this imprecision. I’m opening an issue and maybe submitting a PR soon

Thanks for looking into this. For now, is there a way you can think of that could solve this issue? I’m trying to detect how many pixels the returned width is off, but that changes when I change the font size and it’s becoming a nightmare to handle.

a PR to fix

1 Like

explaining better the issue, you need to parse glyph property “advance” to be able to know where the letter will be.
As getGlyphProperties is a protected function you can’t query advance directly, so I’ve changed in this PR the way OF measures string width

So either recompiling OF with the changes of your PR or wait for a nightly built that will include this is the actual solution, right?

Yes. be aware that the PR isn’t guaranteed of aproval,
if you have the time help me testing it and report in github if things are working ok for you.

That worked! I applied the changes of your PR and recompiled OF. Then the following code worked great!

void ofApp::setup(){
  fontSize = 30;
  resizeFont();
  str = "";
  for (int i = 0; i < 10; i++) {
    str += "k";
  }
  timeStamp = ofGetElapsedTimeMillis();
  dur = 500;
  counter = 0;
}

void ofApp::draw(){
  ofBackground(180);
  ofSetColor(0);

  font.drawString(str, 100, 100);
  ofSetColor(255);
  font.drawString("k", 100+(cursorWidth*counter), 100);
  if ((ofGetElapsedTimeMillis() - timeStamp) >= dur) {
    counter++;
    if (counter > 9) counter = 0;
    timeStamp = ofGetElapsedTimeMillis();
  }
}

void ofApp::resizeFont(){
  font.load("DroidSansMono.ttf", fontSize);
  cursorWidth = font.stringWidth("l");
}

The SourceCodePro.ttf font didn’t seem to be monospaced, but DroidSansMono is. Thank you for the help!

1 Like