diff options
Diffstat (limited to 'src/gui/text/qfontengine.cpp')
-rw-r--r-- | src/gui/text/qfontengine.cpp | 355 |
1 files changed, 201 insertions, 154 deletions
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 07b19d4d19..dff400c18b 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qdebug.h> #include <private/qfontengine_p.h> @@ -90,17 +54,6 @@ static inline bool qSafeFromBigEndian(const uchar *source, const uchar *end, T * return true; } -// Harfbuzz helper functions - -#if QT_CONFIG(harfbuzz) -Q_GLOBAL_STATIC_WITH_ARGS(bool, useHarfbuzzNG,(qgetenv("QT_HARFBUZZ") != "old")) - -bool qt_useHarfbuzzNG() -{ - return *useHarfbuzzNG(); -} -#endif - int QFontEngine::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) { Q_UNUSED(glyph); @@ -180,7 +133,7 @@ QFontEngine::~QFontEngine() QFixed QFontEngine::lineThickness() const { // ad hoc algorithm - int score = fontDef.weight * fontDef.pixelSize; + int score = fontDef.weight * fontDef.pixelSize / 10; int lw = score / 700; // looks better with thicker line for small pointsizes @@ -199,20 +152,20 @@ void *QFontEngine::harfbuzzFont() const { Q_ASSERT(type() != QFontEngine::Multi); #if QT_CONFIG(harfbuzz) - if (qt_useHarfbuzzNG()) - return hb_qt_font_get_for_engine(const_cast<QFontEngine *>(this)); -#endif + return hb_qt_font_get_for_engine(const_cast<QFontEngine *>(this)); +#else return nullptr; +#endif } void *QFontEngine::harfbuzzFace() const { Q_ASSERT(type() != QFontEngine::Multi); #if QT_CONFIG(harfbuzz) - if (qt_useHarfbuzzNG()) - return hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this)); -#endif + return hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this)); +#else return nullptr; +#endif } bool QFontEngine::supportsScript(QChar::Script script) const @@ -227,23 +180,23 @@ bool QFontEngine::supportsScript(QChar::Script script) const return true; #if QT_CONFIG(harfbuzz) - if (qt_useHarfbuzzNG()) { - // in AAT fonts, 'gsub' table is effectively replaced by 'mort'/'morx' table - uint len; - if (getSfntTableData(MAKE_TAG('m','o','r','t'), nullptr, &len) || getSfntTableData(MAKE_TAG('m','o','r','x'), nullptr, &len)) - return true; + // in AAT fonts, 'gsub' table is effectively replaced by 'mort'/'morx' table + uint lenMort = 0, lenMorx = 0; + if (getSfntTableData(QFont::Tag("mort").value(), nullptr, &lenMort) + || getSfntTableData(QFont::Tag("morx").value(), nullptr, &lenMorx)) { + return true; + } - if (hb_face_t *face = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this))) { - unsigned int script_count = HB_OT_MAX_TAGS_PER_SCRIPT; - hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; + if (hb_face_t *face = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this))) { + unsigned int script_count = HB_OT_MAX_TAGS_PER_SCRIPT; + hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; - hb_ot_tags_from_script_and_language(hb_qt_script_to_script(script), HB_LANGUAGE_INVALID, - &script_count, script_tags, - nullptr, nullptr); + hb_ot_tags_from_script_and_language(hb_qt_script_to_script(script), HB_LANGUAGE_INVALID, + &script_count, script_tags, + nullptr, nullptr); - if (hb_ot_layout_table_select_script(face, HB_OT_TAG_GSUB, script_count, script_tags, nullptr, nullptr)) - return true; - } + if (hb_ot_layout_table_select_script(face, HB_OT_TAG_GSUB, script_count, script_tags, nullptr, nullptr)) + return true; } #endif return false; @@ -430,11 +383,16 @@ void QFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rig bool QFontEngine::processHheaTable() const { - QByteArray hhea = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a')); + QByteArray hhea = getSfntTable(QFont::Tag("hhea").value()); 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); + auto ptr = hhea.constData(); + qint16 ascent = qFromBigEndian<qint16>(ptr + 4); + qint16 descent = qFromBigEndian<qint16>(ptr + 6); + qint16 leading = qFromBigEndian<qint16>(ptr + 8); + + // Some fonts may have invalid HHEA data. We detect this and bail out. + if (ascent == 0 && descent == 0) + return false; QFixed unitsPerEm = emSquareSize(); m_ascent = QFixed::fromReal(ascent * fontDef.pixelSize) / unitsPerEm; @@ -450,13 +408,22 @@ bool QFontEngine::processHheaTable() const void QFontEngine::initializeHeightMetrics() const { - bool hasEmbeddedBitmaps = !getSfntTable(MAKE_TAG('E', 'B', 'L', 'C')).isEmpty() || !getSfntTable(MAKE_TAG('C', 'B', 'L', 'C')).isEmpty(); + bool hasEmbeddedBitmaps = + !getSfntTable(QFont::Tag("EBLC").value()).isEmpty() + || !getSfntTable(QFont::Tag("CBLC").value()).isEmpty() + || !getSfntTable(QFont::Tag("bdat").value()).isEmpty(); if (!hasEmbeddedBitmaps) { // Get HHEA table values if available processHheaTable(); // Allow OS/2 metrics to override if present processOS2Table(); + + if (!supportsSubPixelPositions()) { + m_ascent = m_ascent.round(); + m_descent = m_descent.round(); + m_leading = m_leading.round(); + } } m_heightMetricsQueried = true; @@ -464,24 +431,32 @@ void QFontEngine::initializeHeightMetrics() const bool QFontEngine::processOS2Table() const { - QByteArray os2 = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + QByteArray os2 = getSfntTable(QFont::Tag("OS/2").value()); 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); + auto ptr = os2.constData(); + quint16 fsSelection = qFromBigEndian<quint16>(ptr + 62); + qint16 typoAscent = qFromBigEndian<qint16>(ptr + 68); + qint16 typoDescent = qFromBigEndian<qint16>(ptr + 70); + qint16 typoLineGap = qFromBigEndian<qint16>(ptr + 72); + quint16 winAscent = qFromBigEndian<quint16>(ptr + 74); + quint16 winDescent = qFromBigEndian<quint16>(ptr + 76); enum { USE_TYPO_METRICS = 0x80 }; QFixed unitsPerEm = emSquareSize(); if (fsSelection & USE_TYPO_METRICS) { + // Some fonts may have invalid OS/2 data. We detect this and bail out. + if (typoAscent == 0 && typoDescent == 0) + return false; 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 { + // Some fonts may have invalid OS/2 data. We detect this and bail out. + if (winAscent == 0 && winDescent == 0) + return false; m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize) / unitsPerEm; m_descent = QFixed::fromReal(winDescent * fontDef.pixelSize) / unitsPerEm; + m_leading = QFixed{}; } return true; @@ -532,7 +507,7 @@ 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')); + QByteArray hheaTable = getSfntTable(QFont::Tag("hhea").value()); if (hheaTable.size() >= int(kMinRightSideBearingOffset + sizeof(qint16))) { const uchar *tableData = reinterpret_cast<const uchar *>(hheaTable.constData()); Q_ASSERT(q16Dot16ToFloat(qFromBigEndian<quint32>(tableData)) == 1.0); @@ -589,12 +564,23 @@ qreal QFontEngine::minRightBearing() const } if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized) - qWarning() << "Failed to compute left/right minimum bearings for" << fontDef.family; + qWarning() << "Failed to compute left/right minimum bearings for" + << fontDef.families.first(); } return m_minRightBearing; } +glyph_metrics_t QFontEngine::boundingBox(const QGlyphLayout &glyphs) +{ + QFixed w; + for (int i = 0; i < glyphs.numGlyphs; ++i) + w += glyphs.effectiveAdvance(i); + const QFixed leftBearing = firstLeftBearing(glyphs); + const QFixed rightBearing = lastRightBearing(glyphs); + return glyph_metrics_t(leftBearing, -(ascent()), w - leftBearing - rightBearing, ascent() + descent(), w, 0); +} + glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs) { glyph_metrics_t overall; @@ -610,9 +596,9 @@ glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs) QFixed y = overall.yoff + glyphs.offsets[i].y + bb.y; overall.x = qMin(overall.x, x); overall.y = qMin(overall.y, y); - xmax = qMax(xmax, x + bb.width); - ymax = qMax(ymax, y + bb.height); - overall.xoff += bb.xoff; + xmax = qMax(xmax, x.ceil() + bb.width); + ymax = qMax(ymax, y.ceil() + bb.height); + overall.xoff += glyphs.effectiveAdvance(i); overall.yoff += bb.yoff; } overall.height = qMax(overall.height, ymax - overall.y); @@ -795,7 +781,7 @@ void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int n addBitmapFontToPath(x, y, g, path, flags); } -QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/) +QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/) { // For font engines don't support subpixel positioning return alphaMapForGlyph(glyph); @@ -811,9 +797,9 @@ QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t) return i; } -QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) +QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t) { - if (! supportsSubPixelPositions()) + if (!supportsHorizontalSubPixelPositions() && !supportsVerticalSubPixelPositions()) return alphaMapForGlyph(glyph, t); QImage i = alphaMapForGlyph(glyph, subPixelPosition); @@ -824,7 +810,7 @@ QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, con return i; } -QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/, const QTransform &t) +QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/, const QTransform &t) { const QImage alphaMask = alphaMapForGlyph(glyph, t); QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32); @@ -841,32 +827,37 @@ QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition return rgbMask; } -QImage QFontEngine::bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform&, const QColor &) +QImage QFontEngine::bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform&, const QColor &) { Q_UNUSED(subPixelPosition); return QImage(); } -QFixed QFontEngine::subPixelPositionForX(QFixed x) const +QFixedPoint QFontEngine::subPixelPositionFor(const QFixedPoint &position) const { - if (m_subPixelPositionCount <= 1 || !supportsSubPixelPositions()) - return QFixed(); + if (m_subPixelPositionCount <= 1 + || (!supportsHorizontalSubPixelPositions() + && !supportsVerticalSubPixelPositions())) { + return QFixedPoint(); + } - QFixed subPixelPosition; - if (x != 0) { - subPixelPosition = x - x.floor(); - QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor(); + auto f = [&](QFixed v) { + if (v != 0) { + v = v - v.floor() + QFixed::fromFixed(1); + QFixed fraction = (v * m_subPixelPositionCount).floor(); + v = fraction / QFixed(m_subPixelPositionCount); + } + return v; + }; - // Compensate for precision loss in fixed point to make sure we are always drawing at a subpixel position over - // the lower boundary for the selected rasterization by adding 1/64. - subPixelPosition = fraction / QFixed(m_subPixelPositionCount) + QFixed::fromReal(0.015625); - } - return subPixelPosition; + return QFixedPoint(f(position.x), f(position.y)); } -QFontEngine::Glyph *QFontEngine::glyphData(glyph_t, QFixed, - QFontEngine::GlyphFormat, const QTransform &) +QFontEngine::Glyph *QFontEngine::glyphData(glyph_t, + const QFixedPoint &, + QFontEngine::GlyphFormat, + const QTransform &) { return nullptr; } @@ -915,12 +906,9 @@ void QFontEngine::removeGlyphFromCache(glyph_t) QFontEngine::Properties QFontEngine::properties() const { Properties p; - p.postscriptName - = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8()) - + '-' - + QByteArray::number(fontDef.style) - + '-' - + QByteArray::number(fontDef.weight); + p.postscriptName = + QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.first().toUtf8()) + '-' + + QByteArray::number(fontDef.style) + '-' + QByteArray::number(fontDef.weight); p.ascent = ascent(); p.descent = descent(); p.leading = leading(); @@ -1027,7 +1015,7 @@ static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *p while (left <= right) { int middle = left + ( ( right - left ) >> 1 ); - if(pairs[middle].left_right == left_right) + if (pairs[middle].left_right == left_right) return pairs[middle].adjust; if (pairs[middle].left_right < left_right) @@ -1041,7 +1029,7 @@ static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *p void QFontEngine::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const { int numPairs = kerning_pairs.size(); - if(!numPairs) + if (!numPairs) return; const KernPair *pairs = kerning_pairs.constData(); @@ -1059,7 +1047,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor) { kerning_pairs.clear(); - QByteArray tab = getSfntTable(MAKE_TAG('k', 'e', 'r', 'n')); + QByteArray tab = getSfntTable(QFont::Tag("kern").value()); if (tab.isEmpty()) return; @@ -1097,7 +1085,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor) goto end; // qDebug("subtable: version=%d, coverage=%x",version, coverage); - if(version == 0 && coverage == 0x0001) { + if (version == 0 && coverage == 0x0001) { if (offset + length > tab.size()) { // qDebug("length ouf ot bounds"); goto end; @@ -1108,7 +1096,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor) if (!qSafeFromBigEndian(data, end, &nPairs)) goto end; - if(nPairs * 6 + 8 > length - 6) { + if (nPairs * 6 + 8 > length - 6) { // qDebug("corrupt table!"); // corrupt table goto end; @@ -1148,7 +1136,7 @@ end: int QFontEngine::glyphCount() const { - QByteArray maxpTable = getSfntTable(MAKE_TAG('m', 'a', 'x', 'p')); + QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value()); if (maxpTable.size() < 6) return 0; @@ -1195,7 +1183,7 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy int tableToUse = -1; int score = Invalid; for (int n = 0; n < numTables; ++n) { - quint16 platformId; + quint16 platformId = 0; if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId)) return nullptr; @@ -1246,11 +1234,12 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy default: break; } + break; default: break; } } - if(tableToUse < 0) + if (tableToUse < 0) return nullptr; resolveTable: @@ -1325,7 +1314,7 @@ resolveTable: quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode) { const uchar *end = cmap + cmapSize; - quint16 format; + quint16 format = 0; if (!qSafeFromBigEndian(cmap, end, &format)) return 0; @@ -1339,10 +1328,10 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue by returning 0 for 0xffff */ - if(unicode >= 0xffff) + if (unicode >= 0xffff) return 0; - quint16 segCountX2; + quint16 segCountX2 = 0; if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2)) return 0; @@ -1350,7 +1339,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint int i = 0; for (; i < segCountX2/2; ++i) { - quint16 codePoint; + quint16 codePoint = 0; if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint)) return 0; if (codePoint >= unicode) @@ -1359,7 +1348,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint const unsigned char *idx = ends + segCountX2 + 2 + 2*i; - quint16 startIndex; + quint16 startIndex = 0; if (!qSafeFromBigEndian(idx, end, &startIndex)) return 0; if (startIndex > unicode) @@ -1367,20 +1356,20 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint idx += segCountX2; - quint16 tmp; + quint16 tmp = 0; if (!qSafeFromBigEndian(idx, end, &tmp)) return 0; qint16 idDelta = qint16(tmp); idx += segCountX2; - quint16 idRangeoffset_t; + quint16 idRangeoffset_t = 0; if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t)) return 0; - quint16 glyphIndex; + quint16 glyphIndex = 0; if (idRangeoffset_t) { - quint16 id; + quint16 id = 0; if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id)) return 0; @@ -1393,17 +1382,17 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint } return glyphIndex; } else if (format == 6) { - quint16 tableSize; + quint16 tableSize = 0; if (!qSafeFromBigEndian(cmap + 2, end, &tableSize)) return 0; - quint16 firstCode6; + quint16 firstCode6 = 0; if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6)) return 0; if (unicode < firstCode6) return 0; - quint16 entryCount6; + quint16 entryCount6 = 0; if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6)) return 0; if (entryCount6 * 2 + 10 > tableSize) @@ -1419,7 +1408,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index); return index; } else if (format == 12) { - quint32 nGroups; + quint32 nGroups = 0; if (!qSafeFromBigEndian(cmap + 12, end, &nGroups)) return 0; @@ -1429,19 +1418,19 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint while (left <= right) { int middle = left + ( ( right - left ) >> 1 ); - quint32 startCharCode; + quint32 startCharCode = 0; if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode)) return 0; - if(unicode < startCharCode) + if (unicode < startCharCode) right = middle - 1; else { - quint32 endCharCode; + quint32 endCharCode = 0; if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode)) return 0; if (unicode <= endCharCode) { - quint32 index; + quint32 index = 0; if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index)) return 0; @@ -1483,6 +1472,17 @@ bool QFontEngine::hasUnreliableGlyphOutline() const return glyphFormat == QFontEngine::Format_ARGB; } +QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs) +{ + for (int i = 0; i < glyphs.numGlyphs; ++i) { + glyph_t glyph = glyphs.glyphs[i]; + glyph_metrics_t gi = boundingBox(glyph); + if (gi.isValid() && gi.width > 0) + return gi.leftBearing(); + } + return 0; +} + QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs) { if (glyphs.numGlyphs >= 1) { @@ -1539,7 +1539,7 @@ QFontEngineBox::~QFontEngineBox() glyph_t QFontEngineBox::glyphIndex(uint ucs4) const { Q_UNUSED(ucs4); - return 0; + return 1; } bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const @@ -1554,7 +1554,7 @@ bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyph QStringIterator it(str, str + len); while (it.hasNext()) { it.advance(); - glyphs->glyphs[ucs4Length++] = 0; + glyphs->glyphs[ucs4Length++] = 1; } *nglyphs = ucs4Length; @@ -1670,12 +1670,12 @@ QImage QFontEngineBox::alphaMapForGlyph(glyph_t) QImage image(_size, _size, QImage::Format_Alpha8); image.fill(0); - // FIXME: use qpainter + uchar *bits = image.bits(); for (int i=2; i <= _size-3; ++i) { - image.setPixel(i, 2, 255); - image.setPixel(i, _size-3, 255); - image.setPixel(2, i, 255); - image.setPixel(_size-3, i, 255); + bits[i + 2 * image.bytesPerLine()] = 255; + bits[i + (_size - 3) * image.bytesPerLine()] = 255; + bits[2 + i * image.bytesPerLine()] = 255; + bits[_size - 3 + i * image.bytesPerLine()] = 255; } return image; } @@ -1730,7 +1730,9 @@ void QFontEngineMulti::ensureFallbackFamiliesQueried() if (styleHint == QFont::AnyStyle && fontDef.fixedPitch) styleHint = QFont::TypeWriter; - setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.family, QFont::Style(fontDef.style), styleHint, QChar::Script(m_script))); + setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.families.constFirst(), + QFont::Style(fontDef.style), styleHint, + QChar::Script(m_script))); } void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies) @@ -1744,7 +1746,7 @@ void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamili QFontEngine *engine = m_engines.at(0); engine->ref.ref(); m_engines[1] = engine; - m_fallbackFamilies << fontDef.family; + m_fallbackFamilies << fontDef.families.constFirst(); } else { m_engines.resize(m_fallbackFamilies.size() + 1); } @@ -1754,7 +1756,7 @@ void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamili void QFontEngineMulti::ensureEngineAt(int at) { - if (!m_fallbackFamiliesQueried) + if (!m_fallbackFamiliesQueried && at > 0) ensureFallbackFamiliesQueried(); Q_ASSERT(at < m_engines.size()); if (!m_engines.at(at)) { @@ -1771,15 +1773,14 @@ QFontEngine *QFontEngineMulti::loadEngine(int at) { QFontDef request(fontDef); request.styleStrategy |= QFont::NoFontMerging; - request.family = fallbackFamilyAt(at - 1); - request.families = QStringList(request.family); + request.families = QStringList(fallbackFamilyAt(at - 1)); // At this point, the main script of the text has already been considered // when fetching the list of fallback families from the database, and the // info about the actual script of the characters may have been discarded, // so we do not check for writing system support, but instead just load // the family indiscriminately. - if (QFontEngine *engine = QFontDatabase::findFont(request, QChar::Script_Common)) { + if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QChar::Script_Common)) { engine->fontDef.weight = request.weight; if (request.style > QFont::StyleNormal) engine->fontDef.style = request.style; @@ -1834,6 +1835,7 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, QStringIterator it(str, str + len); int lastFallback = -1; + char32_t previousUcs4 = 0; while (it.hasNext()) { const char32_t ucs4 = it.peekNext(); @@ -1863,7 +1865,8 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, && ucs4 != QChar::LineSeparator && ucs4 != QChar::LineFeed && ucs4 != QChar::CarriageReturn - && ucs4 != QChar::ParagraphSeparator) { + && ucs4 != QChar::ParagraphSeparator + && QChar::category(ucs4) != QChar::Other_Control) { if (!m_fallbackFamiliesQueried) const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried(); for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) { @@ -1895,10 +1898,50 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, break; } } + + // For variant-selectors, they are modifiers to the previous character. If we + // end up with different font selections for the selector and the character it + // modifies, we try applying the selector font to the preceding character as well + const int variantSelectorBlock = 0xFE00; + if ((ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) { + int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24; + int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24; + + if (selectorFontEngine != precedingCharacterFontEngine) { + // Emoji variant selectors are specially handled and should affect font + // selection. If VS-16 is used, then this means we want to select a color + // font. If the selected font is already a color font, we do not need search + // again. If the VS-15 is used, then this means we want to select a non-color + // font. If the selected font is not a color font, we don't do anything. + const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine); + const bool colorFont = selectedEngine->isColorFont(); + const char32_t vs15 = 0xFE0E; + const char32_t vs16 = 0xFE0F; + bool adaptVariantSelector = ucs4 < vs15 + || (ucs4 == vs15 && colorFont) + || (ucs4 == vs16 && !colorFont); + + if (adaptVariantSelector) { + QFontEngine *engine = m_engines.at(selectorFontEngine); + glyph_t glyph = engine->glyphIndex(previousUcs4); + if (glyph != 0) { + glyphs->glyphs[glyph_pos - 1] = glyph; + if (!(flags & GlyphIndicesOnly)) { + QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1); + engine->recalcAdvances(&g, flags); + } + + // set the high byte to indicate which engine the glyph came from + glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24); + } + } + } + } } it.advance(); ++glyph_pos; + previousUcs4 = ucs4; } *nglyphs = glyph_pos; @@ -2216,7 +2259,7 @@ QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph) return engine(which)->alphaMapForGlyph(stripped(glyph)); } -QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) +QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition) { const int which = highByte(glyph); return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition); @@ -2228,13 +2271,17 @@ QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t) return engine(which)->alphaMapForGlyph(stripped(glyph), t); } -QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) +QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, + const QFixedPoint &subPixelPosition, + const QTransform &t) { const int which = highByte(glyph); return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition, t); } -QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) +QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph, + const QFixedPoint &subPixelPosition, + const QTransform &t) { const int which = highByte(glyph); return engine(which)->alphaRGBMapForGlyph(stripped(glyph), subPixelPosition, t); |