Fix kerned advances in QRawFont on OS X and Windows

On Windows, the wrong value was used to calculate the
design-to-device scale. The assumption has been that tmHeight
in the TEXTMETRIC is the pixel size of the em square, but
it is not, it's the height of the font (ascent + descent).
The pixel size of the font is defined to be the em square size
in pixels.

On OS X, the kerning data was never actually read from the
font. I've added a lazy initialization for this similar to
the one in the FT engine.

This was discovered when investigating QTBUG-48546, as it turned
out that the kerning information extracted by Qt in this case was
different from the one used by Harfbuzz.

I've changed testfont.ttf to kern "_2" so that the digit is positioned
directly on top of the underscore and constructed a test.

[ChangeLog][QRawFont] Fixed kerning on advances in QRawFont for
OS X and Windows.

Change-Id: Ic9a321ad119ea880cef89b861c75a820ab8d3182
Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2015-11-25 13:47:27 +01:00 committed by Konstantin Ritt
parent 0325842b99
commit 50cd0daf29
5 changed files with 52 additions and 1 deletions

View File

@ -215,6 +215,8 @@ void QCoreTextFontEngine::init()
Q_ASSERT((void *)(&ctfont + 1) == (void *)&cgFont);
faceData.user_data = &ctfont;
faceData.get_font_table = ct_getSfntTable;
kerningPairsLoaded = false;
}
glyph_t QCoreTextFontEngine::glyphIndex(uint ucs4) const
@ -788,4 +790,17 @@ QFontEngine::Properties QCoreTextFontEngine::properties() const
return result;
}
void QCoreTextFontEngine::doKerning(QGlyphLayout *g, ShaperFlags flags) const
{
if (!kerningPairsLoaded) {
kerningPairsLoaded = true;
qreal emSquare = CTFontGetUnitsPerEm(ctfont);
qreal scale = emSquare / CTFontGetSize(ctfont);
const_cast<QCoreTextFontEngine *>(this)->loadKerningPairs(QFixed::fromReal(scale));
}
QFontEngine::doKerning(g, flags);
}
QT_END_NAMESPACE

View File

@ -97,6 +97,7 @@ public:
glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat) Q_DECL_OVERRIDE;
QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE;
QFixed emSquareSize() const Q_DECL_OVERRIDE;
void doKerning(QGlyphLayout *g, ShaperFlags flags) const Q_DECL_OVERRIDE;
bool supportsTransformation(const QTransform &transform) const Q_DECL_OVERRIDE;
@ -134,6 +135,7 @@ private:
CGAffineTransform transform;
QFixed avgCharWidth;
QFontEngine::FaceId face_id;
mutable bool kerningPairsLoaded;
};
CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef);

View File

@ -197,7 +197,7 @@ void QWindowsFontEngine::getCMap()
designToDevice = QFixed((int)otm->otmEMSquare)/QFixed::fromReal(fontDef.pixelSize);
unitsPerEm = otm->otmEMSquare;
x_height = (int)otm->otmsXHeight;
loadKerningPairs(QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight));
loadKerningPairs(designToDevice);
_faceId.filename = QFile::encodeName(QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFullName)));
lineWidth = otm->otmsUnderscoreSize;
fsType = otm->otmfsType;

View File

@ -93,6 +93,8 @@ private slots:
void multipleRawFontsFromData();
void rawFontFromInvalidData();
void kernedAdvances();
private:
QString testFont;
QString testFontBoldItalic;
@ -954,6 +956,38 @@ void tst_QRawFont::rawFontFromInvalidData()
QVERIFY(!font.isValid());
}
#define FUZZY_LTEQ(X, Y) (X < Y || qFuzzyCompare(X, Y))
void tst_QRawFont::kernedAdvances()
{
const int emSquareSize = 1000;
const qreal pixelSize = 16.0;
const int underScoreAW = 500;
const int underscoreTwoKerning = -500;
const qreal errorMargin = 1.0 / 16.0; // Fixed point error margin
QRawFont font(testFont, pixelSize);
QVERIFY(font.isValid());
QVector<quint32> glyphIndexes = font.glyphIndexesForString(QStringLiteral("__"));
QCOMPARE(glyphIndexes.size(), 2);
QVector<QPointF> advances = font.advancesForGlyphIndexes(glyphIndexes, QRawFont::KernedAdvances);
QCOMPARE(advances.size(), 2);
qreal expectedAdvanceWidth = pixelSize * underScoreAW / emSquareSize;
QVERIFY(FUZZY_LTEQ(qAbs(advances.at(0).x() - expectedAdvanceWidth), errorMargin));
glyphIndexes = font.glyphIndexesForString(QStringLiteral("_2"));
QCOMPARE(glyphIndexes.size(), 2);
advances = font.advancesForGlyphIndexes(glyphIndexes, QRawFont::KernedAdvances);
QCOMPARE(advances.size(), 2);
expectedAdvanceWidth = pixelSize * (underScoreAW + underscoreTwoKerning) / emSquareSize;
QVERIFY(FUZZY_LTEQ(qAbs(advances.at(0).x() - expectedAdvanceWidth), errorMargin));
}
#endif // QT_NO_RAWFONT
QTEST_MAIN(tst_QRawFont)