Added capHeight() to QRawFont and QFontMetrics(F)

Cap height is an important metric of font, in particular it is
required to make decent implementation of "initial-letter"
CSS property in QtWebKit.

Note that some fonts lack cap height metadata, so we need to
fall back to measuring H letter height.

Change-Id: Icf69d92159d070889085e20d31f2e397d796d940
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
Konstantin Tokarev 2016-08-03 15:45:02 +03:00
parent 869513a49f
commit ac1e87d9f3
19 changed files with 210 additions and 2 deletions

View File

@ -418,6 +418,13 @@ glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix
return metrics;
}
QFixed QFontEngine::calculatedCapHeight() const
{
const glyph_t glyph = glyphIndex('H');
glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
return bb.height;
}
QFixed QFontEngine::xHeight() const
{
const glyph_t glyph = glyphIndex('x');
@ -1703,6 +1710,11 @@ QFixed QFontEngineBox::ascent() const
return _size;
}
QFixed QFontEngineBox::capHeight() const
{
return _size;
}
QFixed QFontEngineBox::descent() const
{
return 0;
@ -2163,6 +2175,9 @@ glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
QFixed QFontEngineMulti::ascent() const
{ return engine(0)->ascent(); }
QFixed QFontEngineMulti::capHeight() const
{ return engine(0)->capHeight(); }
QFixed QFontEngineMulti::descent() const
{ return engine(0)->descent(); }

View File

@ -414,6 +414,7 @@ QFontEngine::Properties QFreetypeFace::properties() const
p.italicAngle = 0;
p.capHeight = p.ascent;
p.lineWidth = face->underline_thickness;
return p;
}
@ -1299,6 +1300,18 @@ QFixed QFontEngineFT::ascent() const
return v;
}
QFixed QFontEngineFT::capHeight() const
{
TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
if (os2 && os2->version >= 2) {
lockFace();
QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale));
unlockFace();
return answer;
}
return calculatedCapHeight();
}
QFixed QFontEngineFT::descent() const
{
QFixed v = QFixed::fromFixed(-metrics.descender);

View File

@ -209,6 +209,7 @@ private:
int synthesized() const Q_DECL_OVERRIDE;
QFixed ascent() const Q_DECL_OVERRIDE;
QFixed capHeight() const Q_DECL_OVERRIDE;
QFixed descent() const Q_DECL_OVERRIDE;
QFixed leading() const Q_DECL_OVERRIDE;
QFixed xHeight() const Q_DECL_OVERRIDE;

View File

@ -211,6 +211,7 @@ public:
glyph_metrics_t tightBoundingBox(const QGlyphLayout &glyphs);
virtual QFixed ascent() const = 0;
virtual QFixed capHeight() const = 0;
virtual QFixed descent() const = 0;
virtual QFixed leading() const = 0;
virtual QFixed xHeight() const;
@ -348,6 +349,7 @@ protected:
QFixed lastRightBearing(const QGlyphLayout &glyphs, bool round = false);
inline void setUserData(const QVariant &userData) { m_userData = userData; }
QFixed calculatedCapHeight() const;
private:
struct GlyphCacheEntry {
@ -414,6 +416,7 @@ public:
virtual QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE;
virtual QFixed ascent() const Q_DECL_OVERRIDE;
virtual QFixed capHeight() const Q_DECL_OVERRIDE;
virtual QFixed descent() const Q_DECL_OVERRIDE;
virtual QFixed leading() const Q_DECL_OVERRIDE;
virtual qreal maxCharWidth() const Q_DECL_OVERRIDE;
@ -451,6 +454,7 @@ public:
virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0) Q_DECL_OVERRIDE;
virtual QFixed ascent() const Q_DECL_OVERRIDE;
virtual QFixed capHeight() const Q_DECL_OVERRIDE;
virtual QFixed descent() const Q_DECL_OVERRIDE;
virtual QFixed leading() const Q_DECL_OVERRIDE;
virtual QFixed xHeight() const Q_DECL_OVERRIDE;

View File

@ -459,6 +459,11 @@ QFixed QFontEngineQPF2::ascent() const
return QFixed::fromReal(extractHeaderField(fontData, Tag_Ascent).value<qreal>());
}
QFixed QFontEngineQPF2::capHeight() const
{
return calculatedCapHeight();
}
QFixed QFontEngineQPF2::descent() const
{
return QFixed::fromReal(extractHeaderField(fontData, Tag_Descent).value<qreal>());

View File

@ -172,6 +172,7 @@ public:
glyph_metrics_t boundingBox(glyph_t glyph) Q_DECL_OVERRIDE;
QFixed ascent() const Q_DECL_OVERRIDE;
QFixed capHeight() const Q_DECL_OVERRIDE;
QFixed descent() const Q_DECL_OVERRIDE;
QFixed leading() const Q_DECL_OVERRIDE;
qreal maxCharWidth() const Q_DECL_OVERRIDE;

View File

@ -274,6 +274,24 @@ int QFontMetrics::ascent() const
return qRound(engine->ascent());
}
/*!
Returns the cap height of the font.
\since 5.8
The cap height of a font is the height of a capital letter above
the baseline. It specifically is the height of capital letters
that are flat - such as H or I - as opposed to round letters such
as O, or pointed letters like A, both of which may display overshoot.
\sa ascent()
*/
int QFontMetrics::capHeight() const
{
QFontEngine *engine = d->engineForScript(QChar::Script_Common);
Q_ASSERT(engine != 0);
return qRound(engine->capHeight());
}
/*!
Returns the descent of the font.
@ -1138,6 +1156,24 @@ qreal QFontMetricsF::ascent() const
return engine->ascent().toReal();
}
/*!
Returns the cap height of the font.
\since 5.8
The cap height of a font is the height of a capital letter above
the baseline. It specifically is the height of capital letters
that are flat - such as H or I - as opposed to round letters such
as O, or pointed letters like A, both of which may display overshoot.
\sa ascent()
*/
qreal QFontMetricsF::capHeight() const
{
QFontEngine *engine = d->engineForScript(QChar::Script_Common);
Q_ASSERT(engine != 0);
return engine->capHeight().toReal();
}
/*!
Returns the descent of the font.

View File

@ -73,6 +73,7 @@ public:
{ qSwap(d, other.d); }
int ascent() const;
int capHeight() const;
int descent() const;
int height() const;
int leading() const;
@ -146,6 +147,7 @@ public:
void swap(QFontMetricsF &other) { qSwap(d, other.d); }
qreal ascent() const;
qreal capHeight() const;
qreal descent() const;
qreal height() const;
qreal leading() const;

View File

@ -324,6 +324,23 @@ qreal QRawFont::ascent() const
return d->isValid() ? d->fontEngine->ascent().toReal() : 0.0;
}
/*!
Returns the cap height of this QRawFont in pixel units.
\since 5.8
The cap height of a font is the height of a capital letter above
the baseline. It specifically is the height of capital letters
that are flat - such as H or I - as opposed to round letters such
as O, or pointed letters like A, both of which may display overshoot.
\sa QFontMetricsF::capHeight()
*/
qreal QRawFont::capHeight() const
{
return d->isValid() ? d->fontEngine->capHeight().toReal() : 0.0;
}
/*!
Returns the descent of this QRawFont in pixel units.

View File

@ -118,6 +118,7 @@ public:
QFont::HintingPreference hintingPreference() const;
qreal ascent() const;
qreal capHeight() const;
qreal descent() const;
qreal leading() const;
qreal xHeight() const;

View File

@ -378,6 +378,19 @@ QFixed QCoreTextFontEngine::ascent() const
? QFixed::fromReal(CTFontGetAscent(ctfont)).round()
: QFixed::fromReal(CTFontGetAscent(ctfont));
}
QFixed QCoreTextFontEngine::capHeight() const
{
QFixed c = QFixed::fromReal(CTFontGetCapHeight(ctfont));
if (c <= 0)
return calculatedCapHeight();
if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
c = c.round();
return c;
}
QFixed QCoreTextFontEngine::descent() const
{
QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont));

View File

@ -78,6 +78,7 @@ public:
glyph_metrics_t boundingBox(glyph_t glyph) Q_DECL_OVERRIDE;
QFixed ascent() const Q_DECL_OVERRIDE;
QFixed capHeight() const Q_DECL_OVERRIDE;
QFixed descent() const Q_DECL_OVERRIDE;
QFixed leading() const Q_DECL_OVERRIDE;
QFixed xHeight() const Q_DECL_OVERRIDE;

View File

@ -192,6 +192,7 @@ void QWindowsFontEngine::getCMap()
lineWidth = otm->otmsUnderscoreSize;
fsType = otm->otmfsType;
free(otm);
} else {
unitsPerEm = tm.tmHeight;
}
@ -540,6 +541,62 @@ QFixed QWindowsFontEngine::leading() const
return tm.tmExternalLeading;
}
namespace {
# pragma pack(1)
struct OS2Table
{
quint16 version;
qint16 avgCharWidth;
quint16 weightClass;
quint16 widthClass;
quint16 type;
qint16 subscriptXSize;
qint16 subscriptYSize;
qint16 subscriptXOffset;
qint16 subscriptYOffset;
qint16 superscriptXSize;
qint16 superscriptYSize;
qint16 superscriptXOffset;
qint16 superscriptYOffset;
qint16 strikeOutSize;
qint16 strikeOutPosition;
qint16 familyClass;
quint8 panose[10];
quint32 unicodeRanges[4];
quint8 vendorID[4];
quint16 selection;
quint16 firstCharIndex;
quint16 lastCharIndex;
qint16 typoAscender;
qint16 typoDescender;
qint16 typoLineGap;
quint16 winAscent;
quint16 winDescent;
quint32 codepageRanges[2];
qint16 height;
qint16 capHeight;
quint16 defaultChar;
quint16 breakChar;
quint16 maxContext;
};
# pragma pack()
}
QFixed QWindowsFontEngine::capHeight() const
{
const QByteArray tableData = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
if (tableData.size() >= sizeof(OS2Table)) {
const OS2Table *table = reinterpret_cast<const OS2Table *>(tableData.constData());
if (qFromBigEndian<quint16>(table->version) >= 2) {
qint16 capHeight = qFromBigEndian<qint16>(table->capHeight);
if (capHeight > 0)
return QFixed(capHeight) / designToDevice;
}
}
return calculatedCapHeight();
}
QFixed QWindowsFontEngine::xHeight() const
{

View File

@ -103,6 +103,7 @@ public:
QFixed descent() const Q_DECL_OVERRIDE;
QFixed leading() const Q_DECL_OVERRIDE;
QFixed xHeight() const Q_DECL_OVERRIDE;
QFixed capHeight() const Q_DECL_OVERRIDE;
QFixed averageCharWidth() const Q_DECL_OVERRIDE;
qreal maxCharWidth() const Q_DECL_OVERRIDE;
qreal minLeftBearing() const Q_DECL_OVERRIDE;

View File

@ -208,6 +208,7 @@ QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *di
, m_lineThickness(-1)
, m_unitsPerEm(-1)
, m_ascent(-1)
, m_capHeight(-1)
, m_descent(-1)
, m_xHeight(-1)
, m_lineGap(-1)
@ -244,6 +245,7 @@ void QWindowsFontEngineDirectWrite::collectMetrics()
m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness);
m_ascent = DESIGN_TO_LOGICAL(metrics.ascent);
m_capHeight = DESIGN_TO_LOGICAL(metrics.capHeight);
m_descent = DESIGN_TO_LOGICAL(metrics.descent);
m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight);
m_lineGap = DESIGN_TO_LOGICAL(metrics.lineGap);
@ -461,6 +463,16 @@ QFixed QWindowsFontEngineDirectWrite::ascent() const
: m_ascent;
}
QFixed QWindowsFontEngineDirectWrite::capHeight() const
{
if (m_capHeight <= 0)
return calculatedCapHeight();
return fontDef.styleStrategy & QFont::ForceIntegerMetrics
? m_capHeight.round()
: m_capHeight;
}
QFixed QWindowsFontEngineDirectWrite::descent() const
{
return fontDef.styleStrategy & QFont::ForceIntegerMetrics

View File

@ -87,6 +87,7 @@ public:
const QTransform &matrix, GlyphFormat) Q_DECL_OVERRIDE;
QFixed ascent() const Q_DECL_OVERRIDE;
QFixed capHeight() const Q_DECL_OVERRIDE;
QFixed descent() const Q_DECL_OVERRIDE;
QFixed leading() const Q_DECL_OVERRIDE;
QFixed xHeight() const Q_DECL_OVERRIDE;
@ -122,6 +123,7 @@ private:
QFixed m_underlinePosition;
int m_unitsPerEm;
QFixed m_ascent;
QFixed m_capHeight;
QFixed m_descent;
QFixed m_xHeight;
QFixed m_lineGap;

View File

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file>testfont_bold_italic.ttf</file>
<file>testfont_os2_v1.ttf</file>
<file alias="testfont.ttf">../../../shared/resources/testfont.ttf</file>
</qresource>
</RCC>

Binary file not shown.

View File

@ -93,6 +93,7 @@ private slots:
private:
QString testFont;
QString testFontBoldItalic;
QString testFontOs2V1;
#endif // QT_NO_RAWFONT
};
@ -110,6 +111,7 @@ void tst_QRawFont::initTestCase()
{
testFont = QFINDTESTDATA("testfont.ttf");
testFontBoldItalic = QFINDTESTDATA("testfont_bold_italic.ttf");
testFontOs2V1 = QFINDTESTDATA("testfont_os2_v1.ttf");
if (testFont.isEmpty() || testFontBoldItalic.isEmpty())
QFAIL("qrawfont unittest font files not found!");
@ -184,6 +186,7 @@ void tst_QRawFont::correctFontData_data()
QTest::addColumn<QFont::HintingPreference>("hintingPreference");
QTest::addColumn<qreal>("unitsPerEm");
QTest::addColumn<qreal>("pixelSize");
QTest::addColumn<int>("capHeight");
int hintingPreferences[] = {
int(QFont::PreferDefaultHinting),
@ -207,7 +210,8 @@ void tst_QRawFont::correctFontData_data()
<< QFont::Normal
<< QFont::HintingPreference(*hintingPreference)
<< qreal(1000.0)
<< qreal(10.0);
<< qreal(10.0)
<< 7;
fileName = testFontBoldItalic;
title = fileName
@ -221,7 +225,23 @@ void tst_QRawFont::correctFontData_data()
<< QFont::Bold
<< QFont::HintingPreference(*hintingPreference)
<< qreal(1000.0)
<< qreal(10.0);
<< qreal(10.0)
<< 7;
fileName = testFontOs2V1;
title = fileName
+ QLatin1String(": hintingPreference=")
+ QString::number(*hintingPreference);
QTest::newRow(qPrintable(title))
<< fileName
<< QString::fromLatin1("QtBidiTestFont")
<< QFont::StyleNormal
<< QFont::Normal
<< QFont::HintingPreference(*hintingPreference)
<< qreal(1000.0)
<< qreal(10.0)
<< 7;
++hintingPreference;
}
@ -236,6 +256,7 @@ void tst_QRawFont::correctFontData()
QFETCH(QFont::HintingPreference, hintingPreference);
QFETCH(qreal, unitsPerEm);
QFETCH(qreal, pixelSize);
QFETCH(int, capHeight);
QRawFont font(fileName, 10, hintingPreference);
QVERIFY(font.isValid());
@ -246,6 +267,11 @@ void tst_QRawFont::correctFontData()
QCOMPARE(font.hintingPreference(), hintingPreference);
QCOMPARE(font.unitsPerEm(), unitsPerEm);
QCOMPARE(font.pixelSize(), pixelSize);
// Some platforms return the actual fractional height of the
// H character when the value is missing from the OS/2 table,
// so we ceil it off to match (any touched pixel counts).
QCOMPARE(qCeil(font.capHeight()), capHeight);
}
void tst_QRawFont::glyphIndices()