Correctness of wxDC::GetTextExtent() for fixed width fonts

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
chrism238
Earned a small fee
Earned a small fee
Posts: 20
Joined: Sun Feb 17, 2013 6:45 pm

Correctness of wxDC::GetTextExtent() for fixed width fonts

Post by chrism238 »

I have been hoping/assuming that the width of each character in a fixed width font is the same but, probably due to scaling/interpolation of fonts, this appears not be the case. Using my test code:
wxtry.cpp
(2.99 KiB) Downloaded 223 times
I draw the same string, at different point sizes, inside a rectangle of width equal to (#chars * CharacterWidth).
Blue rectangles are drawn when my assumption(?!) is correct, red rectangles otherwise.
My sample output:
wxtry.png
indicates that only some point sizes are rendered as I'd hope - here point sizes 10,20,30, and 40.

How can I ensure that all characters in a fixed-width font have the same character width, or that my application only uses/chooses a font that will render as I'd hope? wxFont::IsFixedWidth() always returns true, so it is not helpful for this requirement.
Are TrueType fonts the answer and, if so, how can I ensure that they are used?

Thanks in advance.

versions: wxWidgets-2.9.4, OS-X 10.8.2
User avatar
doublemax
Moderator
Moderator
Posts: 19159
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Correctness of wxDC::GetTextExtent() for fixed width fon

Post by doublemax »

rectangle of width equal to (#chars * CharacterWidth).
I'm not sure you can make this assumption, because the kerning between characters might still be different depending on the font size.

I don't know the specific OSX code, but usually the string to be drawn is just passed to the underlying OS function, so there's probably nothing you can do about it anyway.

However, i think there are two condition that should be true:

a) The text should not be bigger than the size returned from GetTextExtent(). IOW: if you'd use the values from GetTextExtent to draw the rectangle, the text should not be outside the box.

b) If you draw different texts of the same length with the same font, they should all have the same width.

BTW: Not directly related to the text rendering issue, but regarding your code:
For each paint event you should create exactly one wxPaintDC. So you should move the wxPaintDC creation outside the "box" method. Under OSX this may work, under MSW this code would cause problems.
Use the source, Luke!
Radek
Super wx Problem Solver
Super wx Problem Solver
Posts: 286
Joined: Sun Sep 11, 2011 7:17 am

Re: Correctness of wxDC::GetTextExtent() for fixed width fon

Post by Radek »

IMO, the problem consists in rounding errors when computing the text width. The resulting error will depend on the algorithm used for computing the text extent. If you want more control on computing the text extent then there is GetPartialTextExtents().

Fixed pitch fonts should be really fixed pitch. The GetPartialTextExtents() should confirm it.
chrism238
Earned a small fee
Earned a small fee
Posts: 20
Joined: Sun Feb 17, 2013 6:45 pm

Re: Correctness of wxDC::GetTextExtent() for fixed width fon

Post by chrism238 »

Thank you for your replies.
I've done a bit more experimenting, and am even more confused. Based on my demonstration program, here's my thinking:

http://www.csse.uwa.edu.au/~chris/wxtry2.cpp, http://www.csse.uwa.edu.au/~chris/wxtry2.png
  • if using a fixed-width font, there shouldn't be any kerning being performed.
  • as you can see in the image, where each top line is produced using DrawString(fullstring....) and each lower line is produced by calling DrawString(onecharstring....) for each single character, the inter-character gaps keep changing in each top line.
  • wxWidget's calculation of GetTextExtents() is always correct - its reported width does exactly match the width of a drawn string.
  • my application is not trying to draw text in a box, it actually needs to draw strings in a fixed-width font, and to then permit mouse events to easily identify which word and character were clicked.
As Radek has suggested, it looks like there's some weird internal calculations being performed, and the inter-character gaps are sometimes too big, sometimes too small - but it doesn't look like kerning, certainly not for double letters: 'l', 't', 'f', etc.

Is anyone able to report seeing similar weird behaviour on Windows or Linux, or are fixed-width fonts being handled correctly there?

Thank you,

[doublemax, thanks for your suggestion about the creation of the wxPaintDC].
User avatar
doublemax
Moderator
Moderator
Posts: 19159
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Correctness of wxDC::GetTextExtent() for fixed width fon

Post by doublemax »

I think it's some kind of rounding error. The character width you're getting is just an integer, but internally it's probably not. AFAIK OSX always uses subpixel accuracy internally .

Under MSW with wxDC (which uses standard GDI), the output for both lines is always identical.
But when i change the code to use wxGCDC (which uses GDI+), then i see a similar effect as you under OSX.
Use the source, Luke!
chrism238
Earned a small fee
Earned a small fee
Posts: 20
Joined: Sun Feb 17, 2013 6:45 pm

Re: Correctness of wxDC::GetTextExtent() for fixed width fon

Post by chrism238 »

doublemax wrote:I think it's some kind of rounding error. The character width you're getting is just an integer, but internally it's probably not. AFAIK OSX always uses subpixel accuracy internally .

Under MSW with wxDC (which uses standard GDI), the output for both lines is always identical.
But when i change the code to use wxGCDC (which uses GDI+), then i see a similar effect as you under OSX.
Thanks very much; finding where this is occurring for OS-X is well beyond me (and my available time), so I'll try to report it as a bug.

As a first attempt, I would have thought that any implementation of GetTextExtents() would make life easy for itself and first check for a fixed-width font (by just falling back on its internal definition of 'fixed-width').

Edit: this may be a long-standing bug: http://trac.wxwidgets.org/ticket/13018
Last edited by chrism238 on Mon Feb 18, 2013 10:07 pm, edited 1 time in total.
User avatar
doublemax
Moderator
Moderator
Posts: 19159
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Correctness of wxDC::GetTextExtent() for fixed width fon

Post by doublemax »

I'm not convinced this is a bug, but feel free to open a ticket for this.

You could try to use wxGraphicsContext instead of wxDC.

E.g. wxGraphicsContext::GetTextExtent() returns double values:
http://docs.wxwidgets.org/trunk/classwx ... c5b567198a
Use the source, Luke!
chrism238
Earned a small fee
Earned a small fee
Posts: 20
Joined: Sun Feb 17, 2013 6:45 pm

Re: Correctness of wxDC::GetTextExtent() for fixed width fon

Post by chrism238 »

doublemax wrote:I'm not convinced this is a bug, but feel free to open a ticket for this.
Agreed; it may not be a bug in GetTextExtents(), as the string *is* being drawn exactly within the reported width.
However, the "random" spacing between characters in a fixed-width font is simply not in the spirit of a fixed-width font, so any bug may be lower down in the actual rendering.
Post Reply