I've looked into Sam's monospaced font size problems and followed up in the Ubuntu bug. I think we've discovered unusual behaviour in FreeType that may or may not be a bug, and hopefully someone on the FreeType mailing list will respond to my questions over there. I'm posting back here now to discuss how best to actually achieve the intended cursor placement using Pygame.
Over in the bug report, Sam says: > My solution for the labels, which works fine, is to find the rendered length > of the first character, then the length of the first two characters, then the > length of the first 3 characters and so on. Storing all these values allows > me to work out the correct position. > Because there's quite a bit of repetition, this can take a little bit of CPU > time. My only concern with the input box, is that it needs to recalculate all > of this, everytime the user types something, and it is designed for use in > games where a fraction of a millisecond can still be important to keep the > game running smoothly. In all the examples so far, you've been using font.render("blah"...).get_size(). Do you know there's a font.size("blah") too? That should do the calculations quickly without actually doing the rendering. I think that should be fast enough for your needs. (See the end of this email for a short example.) I've done some experiments to try to understand how the font metrics relate to the dimensions of a rendered piece of text in the general case, not just for monospaced fonts. I thought it might be useful to others. Firstly, any text string rendered by a font will always have a height of font.get_height(). It seems that this is always 1+font.get_ascent()+font.get_ascent(). It doesn't matter what text you render, even it it's entirely whitespace, it will always be the full height. For the width, you need to look at the metrics of each character in the string, as returned by font.metrics(). These are xmin, xmax, ymin, ymax and advance, as shown in the diagram here: http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC38 The advance is the easiest to understand. It's the horizontal distance the text cursor moves from one side of the character to the other. xmin is the distance from the left cursor position to the leftmost part of the outline. xmax is the distance from the left cursor position to the rightmost part of the outline. Most of the time, the width of a string of text is the sum of the advance widths of the characters. But some characters can overhang, which happens a lot in italic fonts. If the first or last characters overhang, the width of the string will be longer. Here's a diagram: https://lh3.googleusercontent.com/-hxXPX7kxCbs/T8q0jF08loI/AAAAAAAADJk/72dsWzp7ATw/s877/font_rendering.png The black bars under the letters are their advance widths. The red highlighted areas are where characters overhang - they have parts wider than their advance width. (The blue highlighted areas are where the characters are narrower than their advance width - they don't matter much.) The width of the string of text is the sum of the advance widths *plus* the overhang of the first and last characters. (Actually, that's not *entirely* true. I think long strings can also vary a few pixels due to accumulated rounding and/or kerning, but that's a much smaller effect than the overhang.) Suppose you have rendered the word "offer" as shown in the diagram, and you want to draw a cursor between the two "f"s. If you calculate the cursor position based on the width of the text on the left - "of" - you will find that the cursor is too far to the right. It will end up right in the middle of the second "f" because the first "f" overhangs it so much. What you actually want to do is let the font library calculate the width of "of", then subtract the right overhang of the last letter, "f". The right overhang is "xmax - advance". To get the very best positioning of a text cursor, I think you want to calculate the width of the string on its left, then *subtract* the overhang of the character immediately to the left, if any. Here's a demo: http://pastebin.com/PKm8ChxV Hope this information is useful to someone!