diff options
Diffstat (limited to 'src/gui/text')
27 files changed, 495 insertions, 203 deletions
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 938decda73..b3d25267ac 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -895,7 +895,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord BorderData data = qvariant_cast<BorderData>(decl.d->parsed); *width = lengthValueFromData(data.width, f); *style = data.style; - *color = brushFromData(data.color, pal); + *color = data.color.type != BrushData::Invalid ? brushFromData(data.color, pal) : QBrush(QColor()); return; } diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 2c2ceb1b8b..ee31fa2464 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -73,7 +73,7 @@ #endif #endif #ifdef Q_OS_SYMBIAN -#include "qt_s60_p.h" +#include <private/qt_s60_p.h> #endif #ifdef Q_WS_LITE #include <QtGui/qplatformscreen_lite.h> @@ -1312,9 +1312,11 @@ QFont::StyleHint QFont::styleHint() const \value PreferAntialias antialias if possible. \value OpenGLCompatible forces the use of OpenGL compatible fonts. - \value NoFontMerging If a font does not contain a character requested - to draw then Qt automatically chooses a similar looking for that contains - the character. This flag disables this feature. + \value NoFontMerging If the font selected for a certain writing system + does not contain a character requested to draw, then Qt automatically chooses a similar + looking font that contains the character. The NoFontMerging flag disables this feature. + Please note that enabling this flag will not prevent Qt from automatically picking a + suitable font when the selected font does not support the writing system of the text. Any of these may be OR-ed with one of these flags: @@ -1323,6 +1325,8 @@ QFont::StyleHint QFont::styleHint() const \value PreferQuality prefer the best quality font. The font matcher will use the nearest standard point size that the font supports. + \value ForceIntegerMetrics forces the use of integer values in font engines that support fractional + font metrics. */ /*! @@ -2638,8 +2642,10 @@ void QFontCache::cleanup() } QT_CATCH (const std::bad_alloc &) { // no cache - just ignore } - if (cache && cache->hasLocalData()) + if (cache && cache->hasLocalData()) { + cache->localData()->clear(); cache->setLocalData(0); + } } #endif // QT_NO_THREAD diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index d71828d366..01892810a0 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -76,17 +76,18 @@ public: }; enum StyleStrategy { - PreferDefault = 0x0001, - PreferBitmap = 0x0002, - PreferDevice = 0x0004, - PreferOutline = 0x0008, - ForceOutline = 0x0010, - PreferMatch = 0x0020, - PreferQuality = 0x0040, - PreferAntialias = 0x0080, - NoAntialias = 0x0100, - OpenGLCompatible = 0x0200, - NoFontMerging = 0x8000 + PreferDefault = 0x0001, + PreferBitmap = 0x0002, + PreferDevice = 0x0004, + PreferOutline = 0x0008, + ForceOutline = 0x0010, + PreferMatch = 0x0020, + PreferQuality = 0x0040, + PreferAntialias = 0x0080, + NoAntialias = 0x0100, + OpenGLCompatible = 0x0200, + ForceIntegerMetrics = 0x0400, + NoFontMerging = 0x8000 }; enum Weight { diff --git a/src/gui/text/qfont_s60.cpp b/src/gui/text/qfont_s60.cpp index 52c77d63ba..ccd17a2123 100644 --- a/src/gui/text/qfont_s60.cpp +++ b/src/gui/text/qfont_s60.cpp @@ -40,8 +40,8 @@ ****************************************************************************/ #include "qfont.h" -#include "qt_s60_p.h" -#include "qpixmap_s60_p.h" +#include <private/qt_s60_p.h> +#include <private/qpixmap_s60_p.h> #include "qmutex.h" QT_BEGIN_NAMESPACE diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index bc5b48f720..fbade23287 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -83,7 +83,7 @@ QT_BEGIN_NAMESPACE #define SMOOTH_SCALABLE 0xffff -extern int qt_defaultDpiY(); // in qfont.cpp +Q_GUI_EXPORT extern int qt_defaultDpiY(); // in qfont.cpp bool qt_enable_test_font = false; diff --git a/src/gui/text/qfontdatabase_s60.cpp b/src/gui/text/qfontdatabase_s60.cpp index 87a73df2a3..ef5e0c44b7 100644 --- a/src/gui/text/qfontdatabase_s60.cpp +++ b/src/gui/text/qfontdatabase_s60.cpp @@ -45,8 +45,8 @@ #include "qfontengine_s60_p.h" #include "qabstractfileengine.h" #include "qdesktopservices.h" -#include "qpixmap_s60_p.h" -#include "qt_s60_p.h" +#include <private/qpixmap_s60_p.h> +#include <private/qt_s60_p.h> #include "qendian.h" #include <private/qcore_symbian_p.h> #if defined(QT_NO_FREETYPE) @@ -332,6 +332,11 @@ bool QFontDatabase::removeApplicationFont(int handle) return false; } +bool QFontDatabase::removeAllApplicationFonts() +{ + return false; +} + bool QFontDatabase::supportsThreadedFontRendering() { return false; diff --git a/src/gui/text/qfontdatabase_x11.cpp b/src/gui/text/qfontdatabase_x11.cpp index bb1e60db77..3b2e4e9c4c 100644 --- a/src/gui/text/qfontdatabase_x11.cpp +++ b/src/gui/text/qfontdatabase_x11.cpp @@ -41,9 +41,9 @@ #include <qplatformdefs.h> -#include <qdatetime.h> #include <qdebug.h> #include <qpaintdevice.h> +#include <qelapsedtimer.h> #include <private/qt_x11_p.h> #include "qx11info_x11.h" @@ -1218,7 +1218,7 @@ static void load(const QString &family = QString(), int script = -1, bool forceX } #ifdef QFONTDATABASE_DEBUG - QTime t; + QElapsedTimer t; t.start(); #endif @@ -1301,7 +1301,7 @@ static void initializeDb() if (!db || db->count) return; - QTime t; + QElapsedTimer t; t.start(); #ifndef QT_NO_FONTCONFIG @@ -1314,7 +1314,7 @@ static void initializeDb() } loadFontConfig(); - FD_DEBUG("QFontDatabase: loaded FontConfig: %d ms", t.elapsed()); + FD_DEBUG("QFontDatabase: loaded FontConfig: %d ms", int(t.elapsed())); #endif t.start(); @@ -1939,8 +1939,13 @@ void QFontDatabase::load(const QFontPrivate *d, int script) fe = loadFc(d, script, req); if (fe != 0 && fe->fontDef.pixelSize != req.pixelSize) { - delete fe; - fe = loadXlfd(d->screen, script, req); + QFontEngine *xlfdFontEngine = loadXlfd(d->screen, script, req); + if (xlfdFontEngine->fontDef.family == fe->fontDef.family) { + delete fe; + fe = xlfdFontEngine; + } else { + delete xlfdFontEngine; + } } diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 40637492c7..060d9bc0ad 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -379,6 +379,15 @@ void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform Q_ASSERT(positions.size() == glyphs_out.size()); } +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; + if (rightBearing != 0) + *rightBearing = isValid ? (gi.xoff - gi.x - gi.width).toReal() : 0.0; +} glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs) { @@ -1389,6 +1398,12 @@ glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs) return overall; } +void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) +{ + int which = highByte(glyph); + engine(which)->getGlyphBearings(stripped(glyph), leftBearing, rightBearing); +} + void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) { diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index a9def8e09b..449dffdc91 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -1368,7 +1368,7 @@ QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransfor if (!gs) { // don't try to load huge fonts - bool draw_as_outline = fontDef.pixelSize * qSqrt(matrix.det()) >= 64; + bool draw_as_outline = fontDef.pixelSize * qSqrt(qAbs(matrix.det())) >= 64; if (draw_as_outline) return 0; diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index 8588214b5c..0bfdbc04f3 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -53,6 +53,7 @@ #include <qvarlengtharray.h> #include <qdebug.h> #include <qendian.h> +#include <qmath.h> #include <ApplicationServices/ApplicationServices.h> #include <AppKit/AppKit.h> @@ -303,12 +304,20 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay outGlyphs[idx] = tmpGlyphs[i] | fontIndex; outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x); outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i + 1].y - tmpPoints[i].y); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + outAdvances_x[idx] = outAdvances_x[idx].round(); + outAdvances_y[idx] = outAdvances_y[idx].round(); + } } CGSize lastGlyphAdvance; CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1); outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex; - outAdvances_x[rtl ? 0 : (glyphCount - 1)] = QFixed::fromReal(lastGlyphAdvance.width); + outAdvances_x[rtl ? 0 : (glyphCount - 1)] = + (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(lastGlyphAdvance.width).round() + : QFixed::fromReal(lastGlyphAdvance.width); } outGlyphs += glyphCount; outAttributes += glyphCount; @@ -378,8 +387,11 @@ bool QCoreTextFontEngine::stringToCMap(const QChar *, int, QGlyphLayout *, int * glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs) { QFixed w; - for (int i = 0; i < glyphs.numGlyphs; ++i) - w += glyphs.effectiveAdvance(i); + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0); } glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) @@ -393,33 +405,51 @@ glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) ret.y = -QFixed::fromReal(rect.origin.y) - ret.height; CGSize advances[1]; CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1); - ret.xoff = QFixed::fromReal(advances[0].width).ceil(); - ret.yoff = QFixed::fromReal(advances[0].height).ceil(); + ret.xoff = QFixed::fromReal(advances[0].width); + ret.yoff = QFixed::fromReal(advances[0].height); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + ret.xoff = ret.xoff.round(); + ret.yoff = ret.yoff.round(); + } + return ret; } QFixed QCoreTextFontEngine::ascent() const { - return QFixed::fromReal(CTFontGetAscent(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() + : QFixed::fromReal(CTFontGetAscent(ctfont)); } QFixed QCoreTextFontEngine::descent() const { + QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + d = d.round(); + // subtract a pixel to even out the historical +1 in QFontMetrics::height(). // Fix in Qt 5. - return QFixed::fromReal(CTFontGetDescent(ctfont)).ceil() - 1; + return d - 1; } QFixed QCoreTextFontEngine::leading() const { - return QFixed::fromReal(CTFontGetLeading(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetLeading(ctfont)).round() + : QFixed::fromReal(CTFontGetLeading(ctfont)); } QFixed QCoreTextFontEngine::xHeight() const { - return QFixed::fromReal(CTFontGetXHeight(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round() + : QFixed::fromReal(CTFontGetXHeight(ctfont)); } QFixed QCoreTextFontEngine::averageCharWidth() const { // ### Need to implement properly and get the information from the OS/2 Table. - return QFontEngine::averageCharWidth(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFontEngine::averageCharWidth().round() + : QFontEngine::averageCharWidth(); } qreal QCoreTextFontEngine::maxCharWidth() const @@ -787,6 +817,7 @@ struct QGlyphLayoutInfo int *mappedFonts; QTextEngine::ShaperFlags flags; QFontEngineMacMulti::ShaperItem *shaperItem; + unsigned int styleStrategy; }; static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon, @@ -856,6 +887,11 @@ static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATS QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]); QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos); + if (nfo->styleStrategy & QFont::ForceIntegerMetrics) { + yAdvance = yAdvance.round(); + xAdvance = xAdvance.round(); + } + if (glyphId != 0xffff || i == 0) { if (i < nfo->glyphs->numGlyphs) { @@ -1032,6 +1068,7 @@ bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyph nfo.callbackCalled = false; nfo.flags = flags; nfo.shaperItem = shaperItem; + nfo.styleStrategy = fontDef.styleStrategy; int prevNumGlyphs = *nglyphs; @@ -1061,8 +1098,6 @@ bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyph | kATSLineDisableAllJustification ; - layopts |= kATSLineUseDeviceMetrics; - if (fontDef.styleStrategy & QFont::NoAntialias) layopts |= kATSLineNoAntiAliasing; @@ -1366,14 +1401,22 @@ void QFontEngineMac::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFla for (int i = 0; i < glyphs->numGlyphs; ++i) { glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x); glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = glyphs->advances_y[i].round(); + } } } glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs) { QFixed w; - for (int i = 0; i < glyphs.numGlyphs; ++i) - w += glyphs.effectiveAdvance(i); + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0); } @@ -1398,39 +1441,58 @@ glyph_metrics_t QFontEngineMac::boundingBox(glyph_t glyph) gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x); gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + gm.x = gm.x.floor(); + gm.y = gm.y.floor(); + gm.xoff = gm.xoff.round(); + gm.yoff = gm.yoff.round(); + } + return gm; } QFixed QFontEngineMac::ascent() const { - return m_ascent; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_ascent.round() + : m_ascent; } QFixed QFontEngineMac::descent() const { // subtract a pixel to even out the historical +1 in QFontMetrics::height(). // Fix in Qt 5. - return m_descent - 1; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_descent.round() - 1 + : m_descent; } QFixed QFontEngineMac::leading() const { - return m_leading; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_leading.round() + : m_leading; } qreal QFontEngineMac::maxCharWidth() const { - return m_maxCharWidth; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? qRound(m_maxCharWidth) + : m_maxCharWidth; } QFixed QFontEngineMac::xHeight() const { - return m_xHeight; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_xHeight.round() + : m_xHeight; } QFixed QFontEngineMac::averageCharWidth() const { - return m_averageCharWidth; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_averageCharWidth.round() + : m_averageCharWidth; } static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path) diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index d2114393fd..af116f742e 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -206,6 +206,8 @@ public: virtual qreal minLeftBearing() const { return qreal(); } virtual qreal minRightBearing() const { return qreal(); } + virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); + virtual const char *name() const = 0; virtual bool canRender(const QChar *string, int len) = 0; @@ -374,6 +376,7 @@ public: virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const; virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const; virtual void addOutlineToPath(qreal, qreal, const QGlyphLayout &, QPainterPath *, QTextItem::RenderFlags flags); + virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); virtual QFixed ascent() const; virtual QFixed descent() const; diff --git a/src/gui/text/qfontengine_s60.cpp b/src/gui/text/qfontengine_s60.cpp index 3ea084b525..c9ff661328 100644 --- a/src/gui/text/qfontengine_s60.cpp +++ b/src/gui/text/qfontengine_s60.cpp @@ -79,6 +79,31 @@ QByteArray QFontEngineS60Extensions::getSfntTable(uint tag) const return result; } +bool QFontEngineS60Extensions::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + if (!m_trueTypeExtension->HasTrueTypeTable(tag)) + return false; + + bool result = true; + TInt error = KErrNone; + TInt tableByteLength; + TAny *table = + q_check_ptr(m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength)); + + if (error != KErrNone) { + return false; + } else if (*length > 0 && *length < tableByteLength) { + result = false; // Caller did not allocate enough memory + } else { + *length = tableByteLength; + if (buffer) + qMemCopy(buffer, table, tableByteLength); + } + + m_trueTypeExtension->ReleaseTrueTypeTable(table); + return result; +} + const unsigned char *QFontEngineS60Extensions::cmap() const { if (!m_cmap) { @@ -326,6 +351,11 @@ QByteArray QFontEngineS60::getSfntTable(uint tag) const return m_extensions->getSfntTable(tag); } +bool QFontEngineS60::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + return m_extensions->getSfntTableData(tag, buffer, length); +} + QFontEngine::Type QFontEngineS60::type() const { return QFontEngine::S60FontEngine; diff --git a/src/gui/text/qfontengine_s60_p.h b/src/gui/text/qfontengine_s60_p.h index 5834cc441d..a80af4d9a5 100644 --- a/src/gui/text/qfontengine_s60_p.h +++ b/src/gui/text/qfontengine_s60_p.h @@ -69,6 +69,7 @@ public: QFontEngineS60Extensions(CFont* fontOwner, COpenFont *font); QByteArray getSfntTable(uint tag) const; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; const unsigned char *cmap() const; QPainterPath glyphOutline(glyph_t glyph) const; CFont *fontOwner() const; @@ -106,6 +107,7 @@ public: qreal minRightBearing() const { return 0; } QByteArray getSfntTable(uint tag) const; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; static qreal pixelsToPoints(qreal pixels, Qt::Orientation orientation = Qt::Horizontal); static qreal pointsToPixels(qreal points, Qt::Orientation orientation = Qt::Horizontal); diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp index 55e93bd3c2..a805612ac4 100644 --- a/src/gui/text/qfontengine_win.cpp +++ b/src/gui/text/qfontengine_win.cpp @@ -39,6 +39,11 @@ ** ****************************************************************************/ +#if _WIN32_WINNT < 0x0500 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + #include "qfontengine_p.h" #include "qtextengine_p.h" #include <qglobal.h> @@ -649,6 +654,32 @@ static const ushort char_table[] = { static const int char_table_entries = sizeof(char_table)/sizeof(ushort); +#ifndef Q_CC_MINGW +void QFontEngineWin::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) +{ + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + +#ifndef Q_WS_WINCE + if (ttf) +#endif + + { + ABC abcWidths; + GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths); + if (leftBearing) + *leftBearing = abcWidths.abcA; + if (rightBearing) + *rightBearing = abcWidths.abcC; + } + +#ifndef Q_WS_WINCE + else { + QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing); + } +#endif +} +#endif // Q_CC_MINGW qreal QFontEngineWin::minLeftBearing() const { diff --git a/src/gui/text/qfontengine_win_p.h b/src/gui/text/qfontengine_win_p.h index f9d8f8b768..68b53b5235 100644 --- a/src/gui/text/qfontengine_win_p.h +++ b/src/gui/text/qfontengine_win_p.h @@ -106,6 +106,10 @@ public: virtual QImage alphaMapForGlyph(glyph_t, const QTransform &xform); virtual QImage alphaRGBMapForGlyph(glyph_t t, int margin, const QTransform &xform); +#ifndef Q_CC_MINGW + virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); +#endif + int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, bool mirrored) const; void getCMap(); diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index 13a5704573..5163c94727 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -63,7 +63,7 @@ extern void qt_format_text(const QFont& font, const QRectF &_r, int tf, const QString &text, QRectF *brect, int tabStops, int *tabArray, int tabArrayLen, QPainter *painter); -extern int qt_defaultDpi(); +Q_GUI_EXPORT extern int qt_defaultDpi(); /***************************************************************************** QFontMetrics member functions @@ -328,7 +328,7 @@ int QFontMetrics::height() const { QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); Q_ASSERT(engine != 0); - return qRound(engine->ascent() + engine->descent()) + 1; + return qRound(engine->ascent()) + qRound(engine->descent()) + 1; } /*! @@ -356,7 +356,7 @@ int QFontMetrics::lineSpacing() const { QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); Q_ASSERT(engine != 0); - return qRound(engine->leading() + engine->ascent() + engine->descent()) + 1; + return qRound(engine->leading()) + qRound(engine->ascent()) + qRound(engine->descent()) + 1; } /*! @@ -472,8 +472,9 @@ int QFontMetrics::leftBearing(QChar ch) const int nglyphs = 9; engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0); // ### can nglyphs != 1 happen at all? Not currently I think - glyph_metrics_t gi = engine->boundingBox(glyphs.glyphs[0]); - return qRound(gi.x); + qreal lb; + engine->getGlyphBearings(glyphs.glyphs[0], &lb); + return qRound(lb); } /*! @@ -506,8 +507,9 @@ int QFontMetrics::rightBearing(QChar ch) const int nglyphs = 9; engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0); // ### can nglyphs != 1 happen at all? Not currently I think - glyph_metrics_t gi = engine->boundingBox(glyphs.glyphs[0]); - return qRound(gi.xoff - gi.x - gi.width); + qreal rb; + engine->getGlyphBearings(glyphs.glyphs[0], 0, &rb); + return qRound(rb); } /*! @@ -1317,8 +1319,9 @@ qreal QFontMetricsF::leftBearing(QChar ch) const int nglyphs = 9; engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0); // ### can nglyphs != 1 happen at all? Not currently I think - glyph_metrics_t gi = engine->boundingBox(glyphs.glyphs[0]); - return gi.x.toReal(); + qreal lb; + engine->getGlyphBearings(glyphs.glyphs[0], &lb); + return lb; } /*! @@ -1351,8 +1354,10 @@ qreal QFontMetricsF::rightBearing(QChar ch) const int nglyphs = 9; engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0); // ### can nglyphs != 1 happen at all? Not currently I think - glyph_metrics_t gi = engine->boundingBox(glyphs.glyphs[0]); - return (gi.xoff - gi.x - gi.width).toReal(); + qreal rb; + engine->getGlyphBearings(glyphs.glyphs[0], 0, &rb); + return rb; + } /*! diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp index 1fabf12395..c7817c6e59 100644 --- a/src/gui/text/qstatictext.cpp +++ b/src/gui/text/qstatictext.cpp @@ -99,20 +99,27 @@ QT_BEGIN_NAMESPACE point with no boundaries, and also when QPainter::drawText() is called with a bounding rectangle. - If a bounding rectangle is not required, create a QStaticText object without setting a maximum - size. The text will then occupy a single line. + If a bounding rectangle is not required, create a QStaticText object without setting a preferred + text width. The text will then occupy a single line. - If you set a maximum size on the QStaticText object, this will bound the text. The text will - be formatted so that no line exceeds the given width. When the object is painted, it will - be clipped at the given size. The position of the text is decided by the argument - passed to QPainter::drawStaticText() and can change from call to call with a minimal impact - on performance. + If you set a text width on the QStaticText object, this will bound the text. The text will + be formatted so that no line exceeds the given width. The text width set for QStaticText will + not automatically be used for clipping. To achieve clipping in addition to line breaks, use + QPainter::setClipRect(). The position of the text is decided by the argument passed to + QPainter::drawStaticText() and can change from call to call with a minimal impact on + performance. QStaticText will attempt to guess the format of the input text using Qt::mightBeRichText(). To force QStaticText to display its contents as either plain text or rich text, use the function QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and Qt::RichText. + If it's the first time the static text is drawn, or if the static text, or the painter's font + or matrix have been altered since the last time it was drawn, the text's layout has to be + recalculated. This will impose an overhead on the QPainter::drawStaticText() call where the + relayout occurs. To avoid this overhead in the paint event, you can call prepare() ahead of + time to ensure that the layout is calculated. + \sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument */ @@ -143,12 +150,11 @@ QStaticText::QStaticText() If an invalid size is passed for \a size the text will be unbounded. */ -QStaticText::QStaticText(const QString &text, const QSizeF &size) +QStaticText::QStaticText(const QString &text) : data(new QStaticTextPrivate) { data->text = text; - data->maximumSize = size; - data->init(); + data->invalidate(); } /*! @@ -177,17 +183,17 @@ void QStaticText::detach() } /*! - Prepares the QStaticText object for being painted with the given \a matrix and the given - \a font to avoid overhead when the actual drawStaticText() call is made. + Prepares the QStaticText object for being painted with the given \a matrix and the given \a font + to avoid overhead when the actual drawStaticText() call is made. - When drawStaticText() is called, the layout of the QStaticText will be recalculated if the - painter's font or matrix is different from the one used for the currently cached layout. By - default, QStaticText will use a default constructed QFont and an identity matrix to create - its layout. + When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part + of the QStaticText object has changed since the last time it was drawn. It will also be + recalculated if the painter's font or matrix are not the same as when the QStaticText was last + drawn. - To avoid the overhead of creating the layout the first time you draw the QStaticText with - a painter whose matrix or font are different from the defaults, you can use the prepare() - function and pass in the matrix and font you expect to use when drawing the text. + To avoid the overhead of creating the layout the first time you draw the QStaticText after + making changes, you can use the prepare() function and pass in the \a matrix and \a font you + expect to use when drawing the text. \sa QPainter::setFont(), QPainter::setMatrix() */ @@ -209,7 +215,7 @@ QStaticText &QStaticText::operator=(const QStaticText &other) } /*! - Compares \a other to this QStaticText. Returns true if the texts, fonts and maximum sizes + Compares \a other to this QStaticText. Returns true if the texts, fonts and text widths are equal. */ bool QStaticText::operator==(const QStaticText &other) const @@ -217,7 +223,7 @@ bool QStaticText::operator==(const QStaticText &other) const return (data == other.data || (data->text == other.data->text && data->font == other.data->font - && data->maximumSize == other.data->maximumSize)); + && data->textWidth == other.data->textWidth)); } /*! @@ -232,7 +238,7 @@ bool QStaticText::operator!=(const QStaticText &other) const /*! Sets the text of the QStaticText to \a text. - \note This function will cause the layout of the text to be recalculated. + \note This function will cause the layout of the text to require recalculation. \sa text() */ @@ -240,7 +246,7 @@ void QStaticText::setText(const QString &text) { detach(); data->text = text; - data->init(); + data->invalidate(); } /*! @@ -250,7 +256,7 @@ void QStaticText::setText(const QString &text) displayed as is, whereas it will be interpreted as HTML if the format is Qt::RichText. HTML tags that alter the font of the text, its color, or its layout are supported by QStaticText. - \note This function will cause the layout of the text to be recalculated. + \note This function will cause the layout of the text to require recalculation. \sa textFormat(), setText(), text() */ @@ -258,7 +264,7 @@ void QStaticText::setTextFormat(Qt::TextFormat textFormat) { detach(); data->textFormat = textFormat; - data->init(); + data->invalidate(); } /*! @@ -289,7 +295,7 @@ QString QStaticText::text() const The default is QStaticText::ModerateCaching. - \note This function will cause the layout of the text to be recalculated. + \note This function will cause the layout of the text to require recalculation. \sa performanceHint() */ @@ -301,7 +307,7 @@ void QStaticText::setPerformanceHint(PerformanceHint performanceHint) } detach(); data->useBackendOptimizations = (performanceHint == AggressiveCaching); - data->init(); + data->invalidate(); } /*! @@ -315,48 +321,56 @@ QStaticText::PerformanceHint QStaticText::performanceHint() const } /*! - Sets the maximum size of the QStaticText to \a size. + Sets the preferred width for this QStaticText. If the text is wider than the specified width, + it will be broken into multiple lines and grow vertically. If the text cannot be split into + multiple lines, it will be larger than the specified \a textWidth. - \note This function will cause the layout of the text to be recalculated. + Setting the preferred text width to a negative number will cause the text to be unbounded. - \sa maximumSize(), size() + Use size() to get the actual size of the text. + + \note This function will cause the layout of the text to require recalculation. + + \sa textWidth(), size() */ -void QStaticText::setMaximumSize(const QSizeF &size) +void QStaticText::setTextWidth(qreal textWidth) { detach(); - data->maximumSize = size; - data->init(); + data->textWidth = textWidth; + data->invalidate(); } /*! - Returns the maximum size of the QStaticText. + Returns the preferred width for this QStaticText. - \sa setMaximumSize() + \sa setTextWidth() */ -QSizeF QStaticText::maximumSize() const +qreal QStaticText::textWidth() const { - return data->maximumSize; + return data->textWidth; } /*! Returns the size of the bounding rect for this QStaticText. - \sa maximumSize() + \sa textWidth() */ QSizeF QStaticText::size() const { + if (data->needsRelayout) + data->init(); return data->actualSize; } QStaticTextPrivate::QStaticTextPrivate() - : items(0), itemCount(0), glyphPool(0), positionPool(0), needsClipRect(false), - useBackendOptimizations(false), textFormat(Qt::AutoText) + : textWidth(-1.0), items(0), itemCount(0), glyphPool(0), positionPool(0), + needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText) { } QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other) - : text(other.text), font(other.font), maximumSize(other.maximumSize), matrix(other.matrix), - items(0), itemCount(0), glyphPool(0), positionPool(0), needsClipRect(false), + : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix), + items(0), itemCount(0), glyphPool(0), positionPool(0), needsRelayout(true), useBackendOptimizations(other.useBackendOptimizations), textFormat(other.textFormat) { } @@ -373,8 +387,8 @@ QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q) return q->data.data(); } -extern int qt_defaultDpiX(); -extern int qt_defaultDpiY(); +Q_GUI_EXPORT extern int qt_defaultDpiX(); +Q_GUI_EXPORT extern int qt_defaultDpiY(); namespace { @@ -388,8 +402,15 @@ namespace { m_expectedItemCount(expectedItemCount), m_expectedGlyphCount(expectedGlyphCount), m_glyphPool(glyphPool), - m_positionPool(positionPool) + m_positionPool(positionPool), + m_dirtyPen(false) + { + } + + virtual void updateState(const QPaintEngineState &newState) { + if (newState.state() & QPaintEngine::DirtyPen) + m_dirtyPen = true; } virtual void drawTextItem(const QPointF &position, const QTextItem &textItem) @@ -412,7 +433,8 @@ namespace { currentItem->numGlyphs = ti.glyphs.numGlyphs; currentItem->glyphs = m_glyphPool; currentItem->glyphPositions = m_positionPool; - currentItem->color = state->pen().color(); + if (m_dirtyPen) + currentItem->color = state->pen().color(); QTransform matrix = state->transform(); matrix.translate(position.x(), position.y()); @@ -435,7 +457,6 @@ namespace { virtual bool begin(QPaintDevice *) { return true; } virtual bool end() { return true; } - virtual void updateState(const QPaintEngineState &) {} virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {} virtual Type type() const { @@ -461,6 +482,8 @@ namespace { glyph_t *m_glyphPool; QFixedPoint *m_positionPool; + + bool m_dirtyPen; }; class DrawTextItemDevice: public QPaintDevice @@ -530,41 +553,61 @@ namespace { }; } -void QStaticTextPrivate::paintText(const QPointF &pos, QPainter *p) +void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p) { bool preferRichText = textFormat == Qt::RichText || (textFormat == Qt::AutoText && Qt::mightBeRichText(text)); if (!preferRichText) { - if (maximumSize.isValid()) { - QRectF boundingRect; - p->drawText(QRectF(pos, maximumSize), Qt::TextWordWrap, text, &boundingRect); - - actualSize = boundingRect.size(); - needsClipRect = boundingRect.width() > maximumSize.width() - || boundingRect.height() > maximumSize.height(); - } else { - p->drawText(pos, text); - needsClipRect = false; - - QFontMetrics fm(font); - actualSize = fm.boundingRect(text).size(); + QTextLayout textLayout; + textLayout.setText(text); + textLayout.setFont(font); + + qreal leading = QFontMetricsF(font).leading(); + qreal height = -leading; + + textLayout.beginLayout(); + while (1) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + + if (textWidth >= 0.0) + line.setLineWidth(textWidth); + height += leading; + line.setPosition(QPointF(0.0, height)); + height += line.height(); } + textLayout.endLayout(); + + actualSize = textLayout.boundingRect().size(); + textLayout.draw(p, topLeftPosition); } else { QTextDocument document; +#ifndef QT_NO_CSSPARSER + QColor color = p->pen().color(); + document.setDefaultStyleSheet(QString::fromLatin1("body { color: #%1%2%3 }") + .arg(QString::number(color.red(), 16), 2, QLatin1Char('0')) + .arg(QString::number(color.green(), 16), 2, QLatin1Char('0')) + .arg(QString::number(color.blue(), 16), 2, QLatin1Char('0'))); +#endif document.setDefaultFont(font); + document.setDocumentMargin(0.0); + if (textWidth >= 0.0) + document.setTextWidth(textWidth); +#ifndef QT_NO_TEXTHTMLPARSER document.setHtml(text); +#else + document.setPlainText(text); +#endif - QRectF rect = maximumSize.isValid() ? QRectF(pos, maximumSize) : QRectF(); document.adjustSize(); p->save(); - p->translate(pos); - document.drawContents(p, rect); + p->translate(topLeftPosition); + document.drawContents(p); p->restore(); + actualSize = document.size(); - needsClipRect = maximumSize.isValid() - && (actualSize.width() > maximumSize.width() - || actualSize.height() > maximumSize.height()); } } @@ -611,6 +654,7 @@ void QStaticTextPrivate::init() paintText(QPointF(0, 0), &painter); } + needsRelayout = false; } QT_END_NAMESPACE diff --git a/src/gui/text/qstatictext.h b/src/gui/text/qstatictext.h index 00d42e0515..f3bef93f05 100644 --- a/src/gui/text/qstatictext.h +++ b/src/gui/text/qstatictext.h @@ -66,7 +66,7 @@ public: }; QStaticText(); - QStaticText(const QString &text, const QSizeF &maximumSize = QSizeF()); + QStaticText(const QString &text); QStaticText(const QStaticText &other); ~QStaticText(); @@ -76,12 +76,12 @@ public: void setTextFormat(Qt::TextFormat textFormat); Qt::TextFormat textFormat() const; - void setMaximumSize(const QSizeF &maximumSize); - QSizeF maximumSize() const; + void setTextWidth(qreal textWidth); + qreal textWidth() const; QSizeF size() const; - void prepare(const QTransform &matrix, const QFont &font); + void prepare(const QTransform &matrix = QTransform(), const QFont &font = QFont()); void setPerformanceHint(PerformanceHint performanceHint); PerformanceHint performanceHint() const; diff --git a/src/gui/text/qstatictext_p.h b/src/gui/text/qstatictext_p.h index e75824468b..f017ed1dcd 100644 --- a/src/gui/text/qstatictext_p.h +++ b/src/gui/text/qstatictext_p.h @@ -118,11 +118,16 @@ public: void init(); void paintText(const QPointF &pos, QPainter *p); + void invalidate() + { + needsRelayout = true; + } + QAtomicInt ref; // 4 bytes per text QString text; // 4 bytes per text QFont font; // 8 bytes per text - QSizeF maximumSize; // 16 bytes per text + qreal textWidth; // 8 bytes per text QSizeF actualSize; // 16 bytes per text QPointF position; // 16 bytes per text @@ -132,11 +137,11 @@ public: glyph_t *glyphPool; // 4 bytes per text QFixedPoint *positionPool; // 4 bytes per text - unsigned char needsClipRect : 1; // 1 byte per text - unsigned char useBackendOptimizations : 1; + unsigned char needsRelayout : 1; + unsigned char useBackendOptimizations : 1; // 1 byte per text unsigned char textFormat : 2; // ================ - // 171 bytes per text + // 163 bytes per text static QStaticTextPrivate *get(const QStaticText *q); }; diff --git a/src/gui/text/qtextcontrol.cpp b/src/gui/text/qtextcontrol.cpp index 6864fe14aa..3c596e55ed 100644 --- a/src/gui/text/qtextcontrol.cpp +++ b/src/gui/text/qtextcontrol.cpp @@ -91,7 +91,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_CONTEXTMENU -#if defined(Q_WS_WIN) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) extern bool qt_use_rtl_extensions; #endif #endif @@ -1201,7 +1201,8 @@ void QTextControlPrivate::keyPressEvent(QKeyEvent *e) blockFmt.setIndent(blockFmt.indent() - 1); cursor.setBlockFormat(blockFmt); } else { - cursor.deletePreviousChar(); + QTextCursor localCursor = cursor; + localCursor.deletePreviousChar(); } goto accept; } @@ -1239,7 +1240,8 @@ void QTextControlPrivate::keyPressEvent(QKeyEvent *e) } #endif else if (e == QKeySequence::Delete) { - cursor.deleteChar(); + QTextCursor localCursor = cursor; + localCursor.deleteChar(); } else if (e == QKeySequence::DeleteEndOfWord) { if (!cursor.hasSelection()) @@ -2080,7 +2082,7 @@ QMenu *QTextControl::createStandardContextMenu(const QPointF &pos, QWidget *pare } #endif -#if defined(Q_WS_WIN) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) if ((d->interactionFlags & Qt::TextEditable) && qt_use_rtl_extensions) { #else if (d->interactionFlags & Qt::TextEditable) { diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 9a1b70cb63..3e556a733e 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -1656,7 +1656,7 @@ static void printPage(int index, QPainter *painter, const QTextDocument *doc, co painter->restore(); } -extern int qt_defaultDpi(); +Q_GUI_EXPORT extern int qt_defaultDpi(); /*! Prints the document to the given \a printer. The QPrinter must be diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 969d5b4632..e2bca04d8c 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE // The VxWorks DIAB compiler crashes when initializing the anonymouse union with { a7 } #if !defined(Q_CC_DIAB) # define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ - QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6, { a7 }, a8 } + QTextUndoCommand c = { a1, a2, 0, 0, quint8(a3), a4, a5, a6, { a7 }, a8 } #else # define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8 @@ -870,6 +870,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) undoEnabled = false; beginEditBlock(); int editPos = -1; + int editLength = -1; while (1) { if (undo) --undoState; @@ -882,12 +883,16 @@ int QTextDocumentPrivate::undoRedo(bool undo) PMDEBUG(" erase: from %d, length %d", c.pos, c.length); c.command = QTextUndoCommand::Removed; editPos = c.pos; + editLength = 0; break; case QTextUndoCommand::Removed: PMDEBUG(" insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos); insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation); c.command = QTextUndoCommand::Inserted; - editPos = c.pos + c.length; + if (editPos != (int)c.pos) + editLength = 0; + editPos = c.pos; + editLength += c.length; break; case QTextUndoCommand::BlockInserted: case QTextUndoCommand::BlockAdded: @@ -898,6 +903,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) else c.command = QTextUndoCommand::BlockDeleted; editPos = c.pos; + editLength = 0; break; case QTextUndoCommand::BlockRemoved: case QTextUndoCommand::BlockDeleted: @@ -908,7 +914,10 @@ int QTextDocumentPrivate::undoRedo(bool undo) c.command = QTextUndoCommand::BlockInserted; else c.command = QTextUndoCommand::BlockAdded; - editPos = c.pos + 1; + if (editPos != (int)c.pos) + editLength = 0; + editPos = c.pos; + editLength += 1; break; case QTextUndoCommand::CharFormatChanged: { resetBlockRevision = -1; // ## TODO @@ -919,7 +928,10 @@ int QTextDocumentPrivate::undoRedo(bool undo) int oldFormat = it.value()->format; setCharFormat(c.pos, c.length, formats.charFormat(c.format)); c.format = oldFormat; - editPos = c.pos + c.length; + if (editPos != (int)c.pos) + editLength = 0; + editPos = c.pos; + editLength += c.length; break; } case QTextUndoCommand::BlockFormatChanged: { @@ -987,13 +999,19 @@ int QTextDocumentPrivate::undoRedo(bool undo) break; } undoEnabled = true; - if (editPos < 0 && docChangeFrom >= 0) { - editPos = qMin(docChangeFrom + docChangeLength, length() - 1); - } + + int newCursorPos = -1; + + if (editPos >=0) + newCursorPos = editPos + editLength; + else if (docChangeFrom >= 0) + newCursorPos= qMin(docChangeFrom + docChangeLength, length() - 1); + endEditBlock(); emitUndoAvailable(isUndoAvailable()); emitRedoAvailable(isRedoAvailable()); - return editPos; + + return newCursorPos; } /*! diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp index b45fcbb9b8..f12bf0b2f5 100644 --- a/src/gui/text/qtextdocumentlayout.cpp +++ b/src/gui/text/qtextdocumentlayout.cpp @@ -75,7 +75,7 @@ QT_BEGIN_NAMESPACE -extern int qt_defaultDpi(); +Q_GUI_EXPORT extern int qt_defaultDpi(); // ################ should probably add frameFormatChange notification! diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index b8265889da..d34553f13e 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -856,7 +856,7 @@ void QTextEngine::shapeLine(const QScriptLine &line) } } -extern int qt_defaultDpiY(); // in qfont.cpp +Q_GUI_EXPORT extern int qt_defaultDpiY(); // in qfont.cpp void QTextEngine::shapeText(int item) const { @@ -1124,14 +1124,13 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const bool kerningEnabled = this->font(si).d->kerning; HB_ShaperItem entire_shaper_item; - entire_shaper_item.kerning_applied = false; + qMemSet(&entire_shaper_item, 0, sizeof(entire_shaper_item)); entire_shaper_item.string = reinterpret_cast<const HB_UChar16 *>(layoutData->string.constData()); entire_shaper_item.stringLength = layoutData->string.length(); entire_shaper_item.item.script = (HB_Script)si.analysis.script; entire_shaper_item.item.pos = si.position; entire_shaper_item.item.length = length(item); entire_shaper_item.item.bidiLevel = si.analysis.bidiLevel; - entire_shaper_item.glyphIndicesPresent = false; HB_UChar16 upperCased[256]; // XXX what about making this 4096, so we don't have to extend it ever. if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase @@ -1299,10 +1298,10 @@ QTextEngine::QTextEngine() } QTextEngine::QTextEngine(const QString &str, const QFont &f) - : fnt(f) + : text(str), + fnt(f) { init(this); - text = str; } QTextEngine::~QTextEngine() @@ -1648,7 +1647,7 @@ glyph_metrics_t QTextEngine::boundingBox(int from, int len) const } } - glyph_t glyph = glyphs.glyphs[logClusters[pos + ilen - 1]]; + glyph_t glyph = glyphs.glyphs[logClusters[ilen - 1]]; glyph_metrics_t gi = fe->boundingBox(glyph); if (gi.isValid()) gm.width -= qRound(gi.xoff - gi.x - gi.width); @@ -2467,7 +2466,7 @@ void QTextEngine::splitItem(int item, int pos) const if (pos <= 0) return; - layoutData->items.insert(item + 1, QScriptItem(layoutData->items[item])); + layoutData->items.insert(item + 1, layoutData->items[item]); QScriptItem &oldItem = layoutData->items[item]; QScriptItem &newItem = layoutData->items[item+1]; newItem.position += pos; @@ -2495,7 +2494,7 @@ void QTextEngine::splitItem(int item, int pos) const // qDebug("split at position %d itempos=%d", pos, item); } -extern int qt_defaultDpiY(); +Q_GUI_EXPORT extern int qt_defaultDpiY(); QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const { @@ -2610,10 +2609,9 @@ void QTextEngine::resolveAdditionalFormats() const } QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f) - : _layoutData(string, _memory, MemSize) + : QTextEngine(string, f), + _layoutData(string, _memory, MemSize) { - fnt = f; - text = string; stackEngine = true; layoutData = &_layoutData; } diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp index fa49a32954..6733793de5 100644 --- a/src/gui/text/qtextimagehandler.cpp +++ b/src/gui/text/qtextimagehandler.cpp @@ -123,7 +123,7 @@ static QSize getPixmapSize(QTextDocument *doc, const QTextImageFormat &format) qreal scale = 1.0; QPaintDevice *pdev = doc->documentLayout()->paintDevice(); if (pdev) { - extern int qt_defaultDpi(); + Q_GUI_EXPORT extern int qt_defaultDpi(); if (pm.isNull()) pm = getPixmap(doc, format); if (!pm.isNull()) diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index cc6793d759..ce7915d8e9 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -305,7 +305,7 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const Once the layout is done, these lines can be drawn on a paint device. - Here's some pseudo code that presents the layout phase: + Here's some code snippet that presents the layout phase: \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 0 The text can be drawn by calling the layout's draw() function: @@ -1570,6 +1570,20 @@ qreal QTextLine::naturalTextWidth() const return eng->lines[i].textWidth.toReal(); } +/*! \since 4.7 + Returns the horizontal advance of the text. The advance of the text + is the distance from its position to the next position at which + text would naturally be drawn. + + By adding the advance to the position of the text line and using this + as the position of a second text line, you will be able to position + the two lines side-by-side without gaps in-between. +*/ +qreal QTextLine::horizontalAdvance() const +{ + return eng->lines[i].textAdvance.toReal(); +} + /*! Lays out the line with the given \a width. The line is filled from its starting position with as many characters as will fit into @@ -1644,28 +1658,67 @@ namespace { struct LineBreakHelper { - LineBreakHelper() : glyphCount(0), maxGlyphs(0), manualWrap(false) {} + LineBreakHelper() + : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0), + manualWrap(false) + { + } + QScriptLine tmpData; QScriptLine spaceData; + QGlyphLayout glyphs; + int glyphCount; int maxGlyphs; + int currentPosition; QFixed minw; QFixed softHyphenWidth; QFixed rightBearing; + QFixed minimumRightBearing; + + QFontEngine *fontEngine; + const unsigned short *logClusters; bool manualWrap; bool checkFullOtherwiseExtend(QScriptLine &line); + + QFixed calculateNewWidth(const QScriptLine &line) const { + return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth + - qMin(rightBearing, QFixed()); + } + + inline glyph_t currentGlyph() const + { + Q_ASSERT(currentPosition > 0); + return glyphs.glyphs[logClusters[currentPosition - 1]]; + } + + inline void adjustRightBearing() + { + if (currentPosition <= 0) + return; + + qreal rb; + fontEngine->getGlyphBearings(currentGlyph(), 0, &rb); + rightBearing = qMin(QFixed(), QFixed::fromReal(rb)); + } + + inline void resetRightBearing() + { + rightBearing = QFixed(1); // Any positive number is defined as invalid since only + // negative right bearings are interesting to us. + } }; inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line) { LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal()); - QFixed newWidth = line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth + rightBearing; + QFixed newWidth = calculateNewWidth(line); if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs)) return true; @@ -1741,13 +1794,12 @@ void QTextLine::layout_helper(int maxGlyphs) Qt::Alignment alignment = eng->option.alignment(); const HB_CharAttributes *attributes = eng->attributes(); - int pos = line.from; + lbh.currentPosition = line.from; int end = 0; - QGlyphLayout glyphs; - const unsigned short *logClusters = eng->layoutData->logClustersPtr; + lbh.logClusters = eng->layoutData->logClustersPtr; while (newItem < eng->layoutData->items.size()) { - lbh.rightBearing = 0; + lbh.resetRightBearing(); lbh.softHyphenWidth = 0; if (newItem != item) { item = newItem; @@ -1755,13 +1807,19 @@ void QTextLine::layout_helper(int maxGlyphs) if (!current.num_glyphs) { eng->shape(item); attributes = eng->attributes(); - logClusters = eng->layoutData->logClustersPtr; + lbh.logClusters = eng->layoutData->logClustersPtr; } - pos = qMax(line.from, current.position); + lbh.currentPosition = qMax(line.from, current.position); end = current.position + eng->length(item); - glyphs = eng->shapedGlyphs(¤t); + lbh.glyphs = eng->shapedGlyphs(¤t); } const QScriptItem ¤t = eng->layoutData->items[item]; + QFontEngine *fontEngine = eng->fontEngine(current); + if (lbh.fontEngine != fontEngine) { + lbh.fontEngine = fontEngine; + lbh.minimumRightBearing = qMin(QFixed(), + QFixed::fromReal(fontEngine->minRightBearing())); + } lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent, current.leading + current.ascent) - qMax(lbh.tmpData.ascent, @@ -1791,8 +1849,8 @@ void QTextLine::layout_helper(int maxGlyphs) if (!line.length && !lbh.tmpData.length) line.setDefaultHeight(eng); if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) { - addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount, - current, logClusters, glyphs); + addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount, + current, lbh.logClusters, lbh.glyphs); } else { lbh.tmpData.length++; } @@ -1811,10 +1869,10 @@ void QTextLine::layout_helper(int maxGlyphs) ++lbh.glyphCount; if (lbh.checkFullOtherwiseExtend(line)) goto found; - } else if (attributes[pos].whiteSpace) { - while (pos < end && attributes[pos].whiteSpace) - addNextCluster(pos, end, lbh.spaceData, lbh.glyphCount, - current, logClusters, glyphs); + } else if (attributes[lbh.currentPosition].whiteSpace) { + while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace) + addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount, + current, lbh.logClusters, lbh.glyphs); if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) { lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line. @@ -1823,19 +1881,19 @@ void QTextLine::layout_helper(int maxGlyphs) } else { bool sb_or_ws = false; do { - addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount, - current, logClusters, glyphs); + addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount, + current, lbh.logClusters, lbh.glyphs); - if (attributes[pos].whiteSpace || attributes[pos-1].lineBreakType != HB_NoBreak) { + if (attributes[lbh.currentPosition].whiteSpace || attributes[lbh.currentPosition-1].lineBreakType != HB_NoBreak) { sb_or_ws = true; break; - } else if (breakany && attributes[pos].charStop) { + } else if (breakany && attributes[lbh.currentPosition].charStop) { break; } - } while (pos < end); + } while (lbh.currentPosition < end); lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw); - if (pos && attributes[pos - 1].lineBreakType == HB_SoftHyphen) { + if (lbh.currentPosition && attributes[lbh.currentPosition - 1].lineBreakType == HB_SoftHyphen) { // if we are splitting up a word because of // a soft hyphen then we ... // @@ -1853,43 +1911,40 @@ void QTextLine::layout_helper(int maxGlyphs) // and thus become invisible again. // if (line.length) - lbh.softHyphenWidth = glyphs.advances_x[logClusters[pos - 1]]; + lbh.softHyphenWidth = lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]]; else if (breakany) - lbh.tmpData.textWidth += glyphs.advances_x[logClusters[pos - 1]]; + lbh.tmpData.textWidth += lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]]; } // The actual width of the text needs to take the right bearing into account. The // right bearing is left-ward, which means that if the rightmost pixel is to the right // of the advance of the glyph, the bearing will be negative. We flip the sign // for the code to be more readable. Logic borrowed from qfontmetrics.cpp. - if (pos) { - QFontEngine *fontEngine = eng->fontEngine(current); - glyph_t glyph = glyphs.glyphs[logClusters[pos - 1]]; - glyph_metrics_t gi = fontEngine->boundingBox(glyph); - if (gi.isValid()) - lbh.rightBearing = qMax(QFixed(), -(gi.xoff - gi.x - gi.width)); - } + // We ignore the right bearing if the minimum negative bearing is too little to + // expand the text beyond the edge. + if (sb_or_ws|breakany) { + if (lbh.calculateNewWidth(line) + lbh.minimumRightBearing > line.width) + lbh.adjustRightBearing(); + if (lbh.checkFullOtherwiseExtend(line)) { + if (!breakany) { + line.textWidth += lbh.softHyphenWidth; + } - if ((sb_or_ws|breakany) && lbh.checkFullOtherwiseExtend(line)) { - if (!breakany) { - line.textWidth += lbh.softHyphenWidth; + goto found; } - - line.textAdvance = line.textWidth; - line.textWidth += lbh.rightBearing; - - goto found; } } - if (pos == end) + if (lbh.currentPosition == end) newItem = item + 1; } LB_DEBUG("reached end of line"); lbh.checkFullOtherwiseExtend(line); +found: + if (lbh.rightBearing > 0) // If right bearing has not yet been adjusted + lbh.adjustRightBearing(); line.textAdvance = line.textWidth; - line.textWidth += lbh.rightBearing; + line.textWidth -= qMin(QFixed(), lbh.rightBearing); -found: if (line.length == 0) { LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f", lbh.tmpData.length, lbh.tmpData.textWidth.toReal(), diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h index edae7decde..8c93ed65b6 100644 --- a/src/gui/text/qtextlayout.h +++ b/src/gui/text/qtextlayout.h @@ -202,6 +202,7 @@ public: bool leadingIncluded() const; qreal naturalTextWidth() const; + qreal horizontalAdvance() const; QRectF naturalTextRect() const; enum Edge { |