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:
parent
869513a49f
commit
ac1e87d9f3
@ -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(); }
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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>());
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
|
@ -118,6 +118,7 @@ public:
|
||||
QFont::HintingPreference hintingPreference() const;
|
||||
|
||||
qreal ascent() const;
|
||||
qreal capHeight() const;
|
||||
qreal descent() const;
|
||||
qreal leading() const;
|
||||
qreal xHeight() const;
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
BIN
tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf
Normal file
BIN
tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf
Normal file
Binary file not shown.
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user