PDA

View Full Version : Surprising behaviour when rendering monospace text in Qt 4.8 (vs. 4.7 and older)



nickbnf
12th September 2012, 16:56
Hi all,

I have noticed a peculiar behaviour in Qt 4.8 when rendering text and would like you guys to help me understand it:
When using a monospace font whose each character has a 'width' of say 10 pixels, I would expect a QString of n character to have a 'width' of n*10 pixels. This was the way it worked pre-4.8.

Now in 4.8, despite every character having a width of 10 (this is a monospace font after all), each additional character added to a string makes it shorter (a 2 char string has a 19 pixel width, a 3 char string is 29, a 4 char string is 39, a 5 char string is 48....).

No it is not 'kerning', as it doesn't depend on WHICH character I put on the string, and I've turned kerning off anyway.

Here is a little experiment:


QApplication app(argc, argv);

QFont font = QFont("DejaVu Sans Mono", 12);
font.setKerning( false );
font.setFixedPitch( true );
QFontMetrics metrics = QFontMetrics( font );

for ( char i=32; i <= 'z'; i++ ) {
printf("%c width: %d\n", i, metrics.width(QChar(i)));
}

static const int STRINGLENGTH = 25;
for ( int i=1; i<STRINGLENGTH; i++ ) {
char string[STRINGLENGTH];
for ( int j=0; j<i; j++ ) {
string[j] = ( random() % 96 ) + 31;
}
string[i] = '\0';
int width = metrics.width(QString(string));
printf("%s: %d %d (%d)\n", string, width, width/i, width % i);
}


On Qt 4.7, all characters are 10 pixel wide (first loop), and the strings are the expected width: 10, 20, 30, 40, 50.....
On Qt 4.8, all characters are also 10 pixel wide, but the strings are of odd width: 10, 19, 29, 39, 48, 58, 67, 77, 87, 96....

Maybe the width of a character is not an integer number of pixels? How has it changed from 4.7 to 4.8?

(I tested that on Ubuntu 12.04, with qt 4.7.4 and 4.8.2 I have built myself, other mono fonts give similar results)

ChrisW67
13th September 2012, 00:51
Your code can produce strings containing character 31 (when random() == 0), a non-printable, which is probably at least part of the problem. It's possible non-printable chars are handled differently.

The size specified to QFont is a point size and does not directly translate to a width. The width it does give may be fractional pixels which are rounded to 10 in QFontMetrics::width(). There could be a difference over multiple characters caused summing then rounding: Summing pixel widths of 9.63 will give you figures like the ones you posted.

nickbnf
13th September 2012, 11:51
The size specified to QFont is a point size and does not directly translate to a width. The width it does give may be fractional pixels which are rounded to 10 in QFontMetrics::width(). There could be a difference over multiple characters caused summing then rounding: Summing pixel widths of 9.63 will give you figures like the ones you posted.


Yes you're right, but it is surprising the behabiour has changed since 4.7, so maybe Qt was doing the rounding before adding before?

Is there any posibility of setting the width to an integer number of pixels? (I know we don't set the width directly, but maybe there is a trick, as I think you can set the size in pixels rather than points).

ChrisW67
14th September 2012, 00:29
On my Linux machine using Qt 4.8.2, Freetype 2.4.9, and fontconfig (2.8.0) library I get even multiples of 10 pixels (when I eliminate the non-printables) with the font you used. I checked using QFontInfo that the font family and size are matched.

nickbnf
14th September 2012, 18:13
I think I start to understand. It seems sub-pixel positionning was implemented in Qt 4.8 and is active when hinting is turned off (or 'slight'). On my Ubuntu box, this is probably the case and I can see the problem. On my Debian sid box, I have even multiple of 10 as well, so I suppose it is due to a different setting of hinting.

I am still unsure about the relationship between "hinting" and "subpixel positionning" though.

This blog post talks about that but I'm no typography expert: http://labs.qt.nokia.com/2011/03/14/hint-hint-nudge-nudge-say-no-more/

In any case, I found QFont::setStyleStrategy( QFont::ForceIntegerMetrics ) that seems to do exactly what I want: turn off sub-pixel positionning regardless of hinting. Works perfectly and I am back with integer widths.