diff options
author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2019-12-06 07:13:28 +0100 |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2020-04-22 16:18:57 +0200 |
commit | f761ad3cd9ad1252f24b76ae413298dc7bed8af3 (patch) | |
tree | 0cdce90553fc85a773f3255c27024c41d1236f24 /src/gui/text/qfontengine.cpp | |
parent | 7d8093df55f0bf76465add4ceb3732286971d849 (diff) |
Use consistent vertical metrics on all platforms
Qt assumes that ascent+descent is the bounding height of the
text, but for historical reasons, the ascent for some fonts
will not contain the diacritics. On Windows, the preference
is to use data from the OS/2 table which were explicitly
invented to work around this, but on other platforms we
are not respecting this table. This causes a text layout
that looks fine on Windows to have overlapping characters
on e.g. macOS.
To make vertical metrics (ascent, descent, leading) consistent
across all platforms, we don't blindly trust the values we get
from the underlying font system, but apply in the following order:
1. If OS/2 table exists and USE_TYPO_METRICS flag is set, we
use the typo metrics from OS/2 table
2. If OS/2 table exists and USE_TYPO_METRICS flag is not set,
we use winAscent/winDescent from OS/2 and the line gap from
HHEA table.
3. If no OS/2 table exists, we try to get ascent, descent and
line gap from the HHEA table.
4. If the HHEA table does not exist (not an SFNT), we fall back
to the system-provided metrics. (on macOS, we know the
system-provided metrics will match the data in HHEA, so we
skip parsing that table and use the data from CoreText if
there is no OS/2 table).
Task-number: QTBUG-80554
Change-Id: I41e6561a99513698c8e42451b4ec98bd5eb6892f
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'src/gui/text/qfontengine.cpp')
-rw-r--r-- | src/gui/text/qfontengine.cpp | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 7712ce71c6..1269aefbf2 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -148,6 +148,7 @@ QFontEngine::QFontEngine(Type type) : m_type(type), ref(0), font_(), face_(), + m_heightMetricsQueried(false), m_minLeftBearing(kBearingNotInitialized), m_minRightBearing(kBearingNotInitialized) { @@ -427,6 +428,91 @@ void QFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rig *rightBearing = gi.rightBearing().toReal(); } +bool QFontEngine::processHheaTable() const +{ + QByteArray hhea = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a')); + if (hhea.size() >= 10) { + qint16 ascent = qFromBigEndian<qint16>(hhea.constData() + 4); + qint16 descent = qFromBigEndian<qint16>(hhea.constData() + 6); + qint16 leading = qFromBigEndian<qint16>(hhea.constData() + 8); + + QFixed unitsPerEm = emSquareSize(); + m_ascent = QFixed::fromReal(ascent * fontDef.pixelSize) / unitsPerEm; + m_descent = -QFixed::fromReal(descent * fontDef.pixelSize) / unitsPerEm; + + m_leading = QFixed::fromReal(leading * fontDef.pixelSize) / unitsPerEm; + + return true; + } + + return false; +} + +void QFontEngine::initializeHeightMetrics() const +{ + if (!processHheaTable()) { + qWarning() << "Cannot determine metrics for font" << fontDef.family; + m_ascent = m_descent = m_leading = 1; + } + + // Allow OS/2 metrics to override if present + processOS2Table(); + + m_heightMetricsQueried = true; +} + +bool QFontEngine::processOS2Table() const +{ + QByteArray os2 = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + if (os2.size() >= 78) { + quint16 fsSelection = qFromBigEndian<quint16>(os2.constData() + 62); + qint16 typoAscent = qFromBigEndian<qint16>(os2.constData() + 68); + qint16 typoDescent = qFromBigEndian<qint16>(os2.constData() + 70); + qint16 typoLineGap = qFromBigEndian<qint16>(os2.constData() + 72); + quint16 winAscent = qFromBigEndian<quint16>(os2.constData() + 74); + quint16 winDescent = qFromBigEndian<quint16>(os2.constData() + 76); + + enum { USE_TYPO_METRICS = 0x80 }; + QFixed unitsPerEm = emSquareSize(); + if (fsSelection & USE_TYPO_METRICS) { + m_ascent = QFixed::fromReal(typoAscent * fontDef.pixelSize) / unitsPerEm; + m_descent = -QFixed::fromReal(typoDescent * fontDef.pixelSize) / unitsPerEm; + m_leading = QFixed::fromReal(typoLineGap * fontDef.pixelSize) / unitsPerEm; + } else { + m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize) / unitsPerEm; + m_descent = QFixed::fromReal(winDescent * fontDef.pixelSize) / unitsPerEm; + } + + return true; + } + + return false; +} + +QFixed QFontEngine::leading() const +{ + if (!m_heightMetricsQueried) + initializeHeightMetrics(); + + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) ? m_leading.round() : m_leading; +} + +QFixed QFontEngine::ascent() const +{ + if (!m_heightMetricsQueried) + initializeHeightMetrics(); + + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) ? m_ascent.round() : m_ascent; +} + +QFixed QFontEngine::descent() const +{ + if (!m_heightMetricsQueried) + initializeHeightMetrics(); + + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) ? m_descent.round() : m_descent; +} + qreal QFontEngine::minLeftBearing() const { if (m_minLeftBearing == kBearingNotInitialized) |