diff options
Diffstat (limited to 'src/gui/text/qfontengine.cpp')
-rw-r--r-- | src/gui/text/qfontengine.cpp | 112 |
1 files changed, 99 insertions, 13 deletions
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 2087bad9f6..f267b2d147 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -54,6 +54,7 @@ #include <private/qharfbuzz_p.h> #include <algorithm> +#include <limits.h> QT_BEGIN_NAMESPACE @@ -235,10 +236,14 @@ Q_AUTOTEST_EXPORT QList<QFontEngine *> QFontEngine_stopCollectingEngines() // QFontEngine +#define kBearingNotInitialized std::numeric_limits<qreal>::max() + QFontEngine::QFontEngine(Type type) : m_type(type), ref(0), font_(0), font_destroy_func(0), - face_(0), face_destroy_func(0) + face_(0), face_destroy_func(0), + m_minLeftBearing(kBearingNotInitialized), + m_minRightBearing(kBearingNotInitialized) { faceData.user_data = this; faceData.get_font_table = qt_get_font_table_default; @@ -365,17 +370,15 @@ bool QFontEngine::supportsScript(QChar::Script script) const return true; } -#ifdef Q_OS_MAC - { +#ifdef QT_ENABLE_HARFBUZZ_NG + if (qt_useHarfbuzzNG()) { +#if defined(Q_OS_DARWIN) // in AAT fonts, 'gsub' table is effectively replaced by 'mort'/'morx' table uint len; if (getSfntTableData(MAKE_TAG('m','o','r','t'), 0, &len) || getSfntTableData(MAKE_TAG('m','o','r','x'), 0, &len)) return true; - } #endif -#ifdef QT_ENABLE_HARFBUZZ_NG - if (qt_useHarfbuzzNG()) { bool ret = false; if (hb_face_t *face = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this))) { hb_tag_t script_tag_1, script_tag_2; @@ -562,11 +565,91 @@ void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform void QFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) { glyph_metrics_t gi = boundingBox(glyph); - bool isValid = gi.isValid(); if (leftBearing != 0) - *leftBearing = isValid ? gi.x.toReal() : 0.0; + *leftBearing = gi.leftBearing().toReal(); if (rightBearing != 0) - *rightBearing = isValid ? (gi.xoff - gi.x - gi.width).toReal() : 0.0; + *rightBearing = gi.rightBearing().toReal(); +} + +qreal QFontEngine::minLeftBearing() const +{ + if (m_minLeftBearing == kBearingNotInitialized) + minRightBearing(); // Initializes both (see below) + + return m_minLeftBearing; +} + +#define q16Dot16ToFloat(i) ((i) / 65536.0) + +#define kMinLeftSideBearingOffset 12 +#define kMinRightSideBearingOffset 14 + +qreal QFontEngine::minRightBearing() const +{ + if (m_minRightBearing == kBearingNotInitialized) { + + // Try the 'hhea' font table first, which covers the entire font + QByteArray hheaTable = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a')); + if (hheaTable.size() >= int(kMinRightSideBearingOffset + sizeof(qint16))) { + const uchar *tableData = reinterpret_cast<const uchar *>(hheaTable.constData()); + Q_ASSERT(q16Dot16ToFloat(qFromBigEndian<quint32>(tableData)) == 1.0); + + qint16 minLeftSideBearing = qFromBigEndian<qint16>(tableData + kMinLeftSideBearingOffset); + qint16 minRightSideBearing = qFromBigEndian<qint16>(tableData + kMinRightSideBearingOffset); + + // The table data is expressed as FUnits, meaning we have to take the number + // of units per em into account. Since pixelSize already has taken DPI into + // account we can use that directly instead of the point size. + int unitsPerEm = emSquareSize().toInt(); + qreal funitToPixelFactor = fontDef.pixelSize / unitsPerEm; + + // Some fonts on OS X (such as Gurmukhi Sangam MN, Khmer MN, Lao Sangam MN, etc.), have + // invalid values for their NBSPACE left bearing, causing the 'hhea' minimum bearings to + // be way off. We detect this by assuming that the minimum bearsings are within a certain + // range of the em square size. + static const int largestValidBearing = 4 * unitsPerEm; + + if (qAbs(minLeftSideBearing) < largestValidBearing) + m_minLeftBearing = minLeftSideBearing * funitToPixelFactor; + if (qAbs(minRightSideBearing) < largestValidBearing) + m_minRightBearing = minRightSideBearing * funitToPixelFactor; + } + + // Fallback in case of missing 'hhea' table (bitmap fonts e.g.) or broken 'hhea' values + if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized) { + + // To balance performance and correctness we only look at a subset of the + // possible glyphs in the font, based on which characters are more likely + // to have a left or right bearing. + static const ushort characterSubset[] = { + '(', 'C', 'F', 'K', 'V', 'X', 'Y', ']', '_', 'f', 'r', '|', + 127, 205, 645, 884, 922, 1070, 12386 + }; + + // The font may have minimum bearings larger than 0, so we have to start at the max + m_minLeftBearing = m_minRightBearing = std::numeric_limits<qreal>::max(); + + for (uint i = 0; i < (sizeof(characterSubset) / sizeof(ushort)); ++i) { + const glyph_t glyph = glyphIndex(characterSubset[i]); + if (!glyph) + continue; + + glyph_metrics_t glyphMetrics = const_cast<QFontEngine *>(this)->boundingBox(glyph); + + // Glyphs with no contours shouldn't contribute to bearings + if (!glyphMetrics.width || !glyphMetrics.height) + continue; + + m_minLeftBearing = qMin(m_minLeftBearing, glyphMetrics.leftBearing().toReal()); + m_minRightBearing = qMin(m_minRightBearing, glyphMetrics.rightBearing().toReal()); + } + } + + if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized) + qWarning() << "Failed to compute left/right minimum bearings for" << fontDef.family; + } + + return m_minRightBearing; } glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs) @@ -875,7 +958,8 @@ QImage QFontEngine::alphaMapForGlyph(glyph_t glyph) pt.x = -glyph_x; pt.y = -glyph_y; // the baseline QPainterPath path; - QImage im(glyph_width + 4, glyph_height, QImage::Format_ARGB32_Premultiplied); + path.setFillRule(Qt::WindingFill); + QImage im(glyph_width, glyph_height, QImage::Format_ARGB32_Premultiplied); im.fill(Qt::transparent); QPainter p(&im); p.setRenderHint(QPainter::Antialiasing); @@ -1469,8 +1553,7 @@ QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs, bool round) glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1]; glyph_metrics_t gi = boundingBox(glyph); if (gi.isValid()) - return round ? QFixed(qRound(gi.xoff - gi.x - gi.width)) - : QFixed(gi.xoff - gi.x - gi.width); + return round ? qRound(gi.rightBearing()) : gi.rightBearing(); } return 0; } @@ -1752,7 +1835,10 @@ QFontEngine *QFontEngineMulti::loadEngine(int at) request.family = fallbackFamilyAt(at - 1); if (QFontEngine *engine = QFontDatabase::findFont(request, m_script)) { - engine->fontDef = request; + if (request.weight > QFont::Normal) + engine->fontDef.weight = request.weight; + if (request.style > QFont::StyleNormal) + engine->fontDef.style = request.style; return engine; } |