Re: [SPAM: 3.000] Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
Weeble wrote: 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. For the *very* best results with italic fonts, you really need a slanted cursor. :-( -- Greg
Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
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/DJk/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!
Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
On Mon, 2012-05-28 at 17:08 +0100, Weeble wrote: > 1. I could reproduce the behaviour you saw on Windows but not a Ubuntu > VM (but it is old, certainly not 12.04). I worked out that it is only one version of the font that is causing the problem. So, if you want to test it on Ubuntu without updating, use this font directly: http://ftp.gnu.org/gnu/freefont/freefont-ttf-20100919.tar.gz For some reason, the bug only happens with that specific font. It's fine with any of the older versions on that page, or with any of the .otf versions. > 3. I note that the Font object has a metrics() method that returns > metrics for a glyph, as explained in this diagram: > http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC38 OK, I just tried to make sense of the metrics thing, and now I'm only more confused than before... :S The advance thing in the metrics for "Ubuntu Mono" is 8 for all characters, and all characters rendered in this font return a width of 8, that makes sense so far... Now, trying to do the same in the problematic "FreeMono" is peculiar. It seems the advance is inconsistent between different characters, but we already expected that. But, it's also inconsistent with the returned widths. As can be seen from this code, the advance says that 'a' and 'd' have the same minx and maxx but different advance sizes, but it also shows that the rendered size is different. Both characters render with a width of 10, but the advance shows that 'd' should be 9. print m.metrics("a") print m.metrics("d") print m.render("a", False, (0,0,0)).get_size()[0] print m.render("d", False, (0,0,0)).get_size()[0] [(1, 10, 0, 7, 10)] [(1, 10, 0, 10, 9)] 10 10 signature.asc Description: This is a digitally signed message part
Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
On Mon, May 28, 2012 at 12:27 PM, Sam Bull wrote: > OK, so I registered a bug on this, and it's possible that it is a bug > with the rendering library. Can anybody who knows anything about the > pygame.font module, look at the bug report and see if you can add any > information to this? > > https://bugs.launchpad.net/ubuntu/+source/ttf-freefont/+bug/1001033 I don't have time to investigate further, but I had a quick look: 1. I could reproduce the behaviour you saw on Windows but not a Ubuntu VM (but it is old, certainly not 12.04). 2. I tried replacing the version of FreeType used by Pygame on Windows with one from here http://www.gtk.org/download/win32.php and got your expected behaviour - everything the same width. 3. I note that the Font object has a metrics() method that returns metrics for a glyph, as explained in this diagram: http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC38 4. Based on that diagram, I'd expect "advance" to be constant for a monospaced font, but that min and max x might vary. Metrics shows that advance varies for the glyphs you have the problem with. 5. I also note that the description of metrics in the Pygame docs appears to be wrong: http://www.pygame.org/docs/ref/font.html#Font.metrics It describes the advance as "bearing plus width", but this is not necessarily the case, as shown in the SDL diagram. I might have a look this evening, and if I do, I'll post a test script and follow up on your launchpad bug. It looks like different versions of FreeType might have subtly different behaviour, but I wouldn't like to hazard a guess whether any of the font, FreeType, SDL or pygame actually has a bug at this stage. I'm not in any way affiliated with any of these projects, just curious.
Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
OK, so I registered a bug on this, and it's possible that it is a bug with the rendering library. Can anybody who knows anything about the pygame.font module, look at the bug report and see if you can add any information to this? https://bugs.launchpad.net/ubuntu/+source/ttf-freefont/+bug/1001033 Thanks, Sam Bull On Tue, 2012-05-15 at 11:23 +1200, Greg Ewing wrote: > Nicholas Seward wrote: > > The letters may be different lengths. However, the letters should be > > spaced equally. For example,"i" will be shorter than "w" but "hit" > > and "hot" should be the same length. > > No, that's not the way it should work. I just tried an experiment: > > >>> f = Font("VeraMono.ttf", 12) > >>> f.size("e") > (7, 15) > >>> f.size("i") > (7, 15) > > I suspect that the system is not actually giving you a > monospaced font. If it doesn't recognise the font name > you give it, you'll probably get some default font. Have > you tried rendering any text with the font to see what > it looks like? > > I've given up on using SysFont because the results are > too unpredictable cross-platform. I always bundle my > own fonts with my games and use Font to load them > explicitly. > > I like to use the Bitstream Vera fonts. They're nice, > small and very liberally licensed. > > http://ftp.gnome.org/pub/GNOME/sources/ttf-bitstream-vera/1.10/ >
Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
> I've given up on using SysFont because the results are > too unpredictable cross-platform. I always bundle my > own fonts with my games and use Font to load them > explicitly. YES! This, this, a thousand times THIS! --- James paige
Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
Nicholas Seward wrote: The letters may be different lengths. However, the letters should be spaced equally. For example,"i" will be shorter than "w" but "hit" and "hot" should be the same length. No, that's not the way it should work. I just tried an experiment: >>> f = Font("VeraMono.ttf", 12) >>> f.size("e") (7, 15) >>> f.size("i") (7, 15) I suspect that the system is not actually giving you a monospaced font. If it doesn't recognise the font name you give it, you'll probably get some default font. Have you tried rendering any text with the font to see what it looks like? I've given up on using SysFont because the results are too unpredictable cross-platform. I always bundle my own fonts with my games and use Font to load them explicitly. I like to use the Bitstream Vera fonts. They're nice, small and very liberally licensed. http://ftp.gnome.org/pub/GNOME/sources/ttf-bitstream-vera/1.10/ -- Greg
Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
Are you accounting correctly for non-integer character width? If it's always the same letter /position/ that's giving you trouble, then it's possible that the width of the character is not an integer in that size, and your text renderer may be accounting for that in ways that your own program is not. On Mon, May 14, 2012 at 9:25 AM, Nicholas Seward wrote: > The letters may be different lengths. However, the letters should be > spaced equally. For example,"i" will be shorter than "w" but "hit" > and "hot" should be the same length. > > On Mon, May 14, 2012 at 5:25 AM, Sam Bull wrote: >> Before I go and file a bug against Ubuntu, can somebody confirm I'm not >> being stupid. >> >> For my input boxes, the cursor position depends on a monospaced font. >> This means I check the length of a rendered letter ("e") using the font, >> and then set the cursor position as a multiple of this length. >> >> This worked fine before, but in Ubuntu 12.04, it no longer seems to be >> monospaced. Getting the length of the character "e" on my system gives >> me 9 pixels, but it seems that some characters, such as "h", are 10 >> pixels, which starts offsetting my cursor position and messing up my >> input box. >> >> The line I use to load the font is: >> pygame.font.SysFont("FreeMono, Monospace", 16) >> >> That should definitely return a monospaced font, right? >> >> And, the line used for getting the width is: >> mono_font.render("e", False, (0,0,0)).get_size()[0] >> >> Is this a bug, or am I doing something stupid? >>
Re: [pygame] Monospaced fonts are meant to be mono-spaced, right?
The letters may be different lengths. However, the letters should be spaced equally. For example,"i" will be shorter than "w" but "hit" and "hot" should be the same length. On Mon, May 14, 2012 at 5:25 AM, Sam Bull wrote: > Before I go and file a bug against Ubuntu, can somebody confirm I'm not > being stupid. > > For my input boxes, the cursor position depends on a monospaced font. > This means I check the length of a rendered letter ("e") using the font, > and then set the cursor position as a multiple of this length. > > This worked fine before, but in Ubuntu 12.04, it no longer seems to be > monospaced. Getting the length of the character "e" on my system gives > me 9 pixels, but it seems that some characters, such as "h", are 10 > pixels, which starts offsetting my cursor position and messing up my > input box. > > The line I use to load the font is: > pygame.font.SysFont("FreeMono, Monospace", 16) > > That should definitely return a monospaced font, right? > > And, the line used for getting the width is: > mono_font.render("e", False, (0,0,0)).get_size()[0] > > Is this a bug, or am I doing something stupid? >
[pygame] Monospaced fonts are meant to be mono-spaced, right?
Before I go and file a bug against Ubuntu, can somebody confirm I'm not being stupid. For my input boxes, the cursor position depends on a monospaced font. This means I check the length of a rendered letter ("e") using the font, and then set the cursor position as a multiple of this length. This worked fine before, but in Ubuntu 12.04, it no longer seems to be monospaced. Getting the length of the character "e" on my system gives me 9 pixels, but it seems that some characters, such as "h", are 10 pixels, which starts offsetting my cursor position and messing up my input box. The line I use to load the font is: pygame.font.SysFont("FreeMono, Monospace", 16) That should definitely return a monospaced font, right? And, the line used for getting the width is: mono_font.render("e", False, (0,0,0)).get_size()[0] Is this a bug, or am I doing something stupid?