Also check notifier endpoints when checking whether a signal is connected.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktext.cpp
index 0e1c5a8..2407ade 100644 (file)
@@ -3,7 +3,7 @@
 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/
 **
-** This file is part of the QtDeclarative module of the Qt Toolkit.
+** This file is part of the QtQml module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 ** GNU Lesser General Public License Usage
 #include "qquicktext_p_p.h"
 
 #include <QtQuick/private/qsgcontext_p.h>
+#include <private/qqmlglobal_p.h>
 #include <private/qsgadaptationlayer_p.h>
 #include "qquicktextnode_p.h"
 #include "qquickimage_p_p.h"
 #include <QtQuick/private/qsgtexture_p.h>
 
-#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtQml/qqmlinfo.h>
 #include <QtGui/qevent.h>
 #include <QtGui/qabstracttextdocumentlayout.h>
 #include <QtGui/qpainter.h>
 #include <QtGui/qtextobject.h>
 #include <QtGui/qtextcursor.h>
 #include <QtGui/qguiapplication.h>
-#include <QtGui/qinputpanel.h>
+#include <QtGui/qinputmethod.h>
 
 #include <private/qtextengine_p.h>
-#include <private/qdeclarativestyledtext_p.h>
-#include <QtQuick/private/qdeclarativepixmapcache_p.h>
+#include <private/qquickstyledtext_p.h>
+#include <QtQuick/private/qquickpixmapcache_p.h>
 
 #include <qmath.h>
 #include <limits.h>
 
 QT_BEGIN_NAMESPACE
 
-extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
-
-DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
-DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
 
 const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
 
 QQuickTextPrivate::QQuickTextPrivate()
-: color((QRgb)0), style(QQuickText::Normal), hAlign(QQuickText::AlignLeft),
-  vAlign(QQuickText::AlignTop), elideMode(QQuickText::ElideNone),
-  format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap), lineHeight(1),
-  lineHeightMode(QQuickText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
-  maximumLineCountValid(false), fontSizeMode(QQuickText::FixedSize), minimumPixelSize(12),
-  minimumPointSize(12), imageCache(0), texture(0),
-  imageCacheDirty(false), updateOnComponentComplete(true),
-  richText(false), styledText(false), singleline(false), cacheAllTextAsImage(true),
-  disableDistanceField(false), internalWidthUpdate(false),
-  requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
-  layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true),
-  needToUpdateLayout(false), naturalWidth(0), doc(0), elideLayout(0), textLine(0), nodeType(NodeIsNull),
-  updateType(UpdatePaintNode), nbActiveDownloads(0)
-
+    : elideLayout(0), textLine(0)
 #if defined(Q_OS_MAC)
-, layoutThread(0), paintingThread(0)
+    , layoutThread(0), paintingThread(0)
 #endif
+    , color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000)
+    , lineCount(1), multilengthEos(-1)
+    , elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop)
+    , format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap)
+    , style(QQuickText::Normal)
+    , updateType(UpdatePaintNode)
+    , maximumLineCountValid(false), updateOnComponentComplete(true), richText(false)
+    , styledText(false), singleline(false), internalWidthUpdate(false), requireImplicitWidth(false)
+    , truncated(false), hAlignImplicit(true), rightToLeftText(false)
+    , layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
+{
+}
+
+QQuickTextPrivate::ExtraData::ExtraData()
+    : lineHeight(1.0)
+    , doc(0)
+    , minimumPixelSize(12)
+    , minimumPointSize(12)
+    , nbActiveDownloads(0)
+    , maximumLineCount(INT_MAX)
+    , lineHeightMode(QQuickText::ProportionalHeight)
+    , fontSizeMode(QQuickText::FixedSize)
 {
-    cacheAllTextAsImage = enableImageCache();
-    disableDistanceField = qmlDisableDistanceField();
 }
 
 void QQuickTextPrivate::init()
@@ -119,11 +123,11 @@ QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
 
 QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
 {
-    QDeclarativeContext *context = qmlContext(parent());
+    QQmlContext *context = qmlContext(parent());
     QUrl url = m_baseUrl.resolved(name);
 
     if (type == QTextDocument::ImageResource) {
-        QDeclarativePixmap *p = loadPixmap(context, url);
+        QQuickPixmap *p = loadPixmap(context, url);
         return p->image();
     }
 
@@ -160,10 +164,10 @@ QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
 
         QSizeF size(width, height);
         if (!hasWidth || !hasHeight) {
-            QDeclarativeContext *context = qmlContext(parent());
+            QQmlContext *context = qmlContext(parent());
             QUrl url = m_baseUrl.resolved(QUrl(imageFormat.name()));
 
-            QDeclarativePixmap *p = loadPixmap(context, url);
+            QQuickPixmap *p = loadPixmap(context, url);
             if (!p->isReady()) {
                 if (!hasWidth)
                     size.setWidth(16);
@@ -198,10 +202,10 @@ void QQuickTextDocumentWithImageResources::drawObject(
 
 QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
 {
-    QDeclarativeContext *context = qmlContext(parent());
+    QQmlContext *context = qmlContext(parent());
     QUrl url = m_baseUrl.resolved(QUrl(format.name()));
 
-    QDeclarativePixmap *p = loadPixmap(context, url);
+    QQuickPixmap *p = loadPixmap(context, url);
     return p->image();
 }
 
@@ -214,14 +218,14 @@ void QQuickTextDocumentWithImageResources::setBaseUrl(const QUrl &url, bool clea
     }
 }
 
-QDeclarativePixmap *QQuickTextDocumentWithImageResources::loadPixmap(
-        QDeclarativeContext *context, const QUrl &url)
+QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
+        QQmlContext *context, const QUrl &url)
 {
 
-    QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
+    QHash<QUrl, QQuickPixmap *>::Iterator iter = m_resources.find(url);
 
     if (iter == m_resources.end()) {
-        QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
+        QQuickPixmap *p = new QQuickPixmap(context->engine(), url);
         iter = m_resources.insert(url, p);
 
         if (p->isLoading()) {
@@ -230,7 +234,7 @@ QDeclarativePixmap *QQuickTextDocumentWithImageResources::loadPixmap(
         }
     }
 
-    QDeclarativePixmap *p = *iter;
+    QQuickPixmap *p = *iter;
     if (p->isError()) {
         if (!errors.contains(url)) {
             errors.insert(url);
@@ -242,7 +246,7 @@ QDeclarativePixmap *QQuickTextDocumentWithImageResources::loadPixmap(
 
 void QQuickTextDocumentWithImageResources::clearResources()
 {
-    foreach (QDeclarativePixmap *pixmap, m_resources)
+    foreach (QQuickPixmap *pixmap, m_resources)
         pixmap->clear(this);
     qDeleteAll(m_resources);
     m_resources.clear();
@@ -266,7 +270,6 @@ QQuickTextPrivate::~QQuickTextPrivate()
 {
     delete elideLayout;
     delete textLine; textLine = 0;
-    delete imageCache;
     qDeleteAll(imgTags);
     imgTags.clear();
 }
@@ -307,11 +310,24 @@ void QQuickTextPrivate::updateLayout()
     if (!richText) {
         if (textHasChanged) {
             if (styledText && !text.isEmpty()) {
-                QDeclarativeStyledText::parse(text, layout, imgTags, qmlContext(q), !maximumLineCountValid);
+                layout.setFont(font);
+                // needs temporary bool because formatModifiesFontSize is in a bit-field
+                bool fontSizeModified = false;
+                QQuickStyledText::parse(text, layout, imgTags, q->baseUrl(), qmlContext(q), !maximumLineCountValid, &fontSizeModified);
+                formatModifiesFontSize = fontSizeModified;
             } else {
                 layout.clearAdditionalFormats();
                 QString tmp = text;
-                tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+                multilengthEos = tmp.indexOf(QLatin1Char('\x9c'));
+                if (multilengthEos != -1) {
+                    tmp = tmp.mid(0, multilengthEos);
+                    tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+                } else if (tmp.contains(QLatin1Char('\n'))) {
+                    // Replace always does a detach.  Checking for the new line character first
+                    // means iterating over those items again if found but prevents a realloc
+                    // otherwise.
+                    tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+                }
                 layout.setText(tmp);
             }
             textHasChanged = false;
@@ -319,10 +335,10 @@ void QQuickTextPrivate::updateLayout()
     } else {
         ensureDoc();
         QTextBlockFormat::LineHeightTypes type;
-        type = lineHeightMode == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
+        type = lineHeightMode() == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
         QTextBlockFormat blockFormat;
-        blockFormat.setLineHeight((lineHeightMode == QQuickText::FixedHeight ? lineHeight : lineHeight * 100), type);
-        for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
+        blockFormat.setLineHeight((lineHeightMode() == QQuickText::FixedHeight ? lineHeight() : lineHeight() * 100), type);
+        for (QTextBlock it = extra->doc->begin(); it != extra->doc->end(); it = it.next()) {
             QTextCursor cursor(it);
             cursor.mergeBlockFormat(blockFormat);
         }
@@ -341,15 +357,15 @@ void QQuickText::imageDownloadFinished()
 {
     Q_D(QQuickText);
 
-    (d->nbActiveDownloads)--;
+    (d->extra->nbActiveDownloads)--;
 
     // when all the remote images have been downloaded,
     // if one of the sizes was not specified at parsing time
     // we use the implicit size from pixmapcache and re-layout.
 
-    if (d->nbActiveDownloads == 0) {
+    if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
         bool needToUpdateLayout = false;
-        foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
+        foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
             if (!img->size.isValid()) {
                 img->size = img->pix->implicitSize();
                 needToUpdateLayout = true;
@@ -366,6 +382,22 @@ void QQuickText::imageDownloadFinished()
     }
 }
 
+void QQuickTextPrivate::updateBaseline(qreal baseline, qreal dy)
+{
+    Q_Q(QQuickText);
+
+    qreal yoff = 0;
+
+    if (q->heightValid()) {
+        if (vAlign == QQuickText::AlignBottom)
+            yoff = dy;
+        else if (vAlign == QQuickText::AlignVCenter)
+            yoff = dy/2;
+    }
+
+    q->setBaselineOffset(baseline + yoff);
+}
+
 void QQuickTextPrivate::updateSize()
 {
     Q_Q(QQuickText);
@@ -382,36 +414,50 @@ void QQuickTextPrivate::updateSize()
             return;
     }
 
-    invalidateImageCache();
-
-    QFontMetrics fm(font);
-    if (text.isEmpty()) {
-        qreal fontHeight = fm.height();
+    if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) {
+        // How much more expensive is it to just do a full layout on an empty string here?
+        // There may be subtle differences in the height and baseline calculations between
+        // QTextLayout and QFontMetrics and the number of variables that can affect the size
+        // and position of a line is increasing.
+        QFontMetricsF fm(font);
+        qreal fontHeight = qCeil(fm.height());  // QScriptLine and therefore QTextLine rounds up
+        if (!richText) {                        // line height, so we will as well.
+            fontHeight = lineHeightMode() == QQuickText::FixedHeight
+                    ? lineHeight()
+                    : fontHeight * lineHeight();
+        }
+        updateBaseline(fm.ascent(), q->height() - fontHeight);
         q->setImplicitSize(0, fontHeight);
-        paintedSize = QSize(0, fontHeight);
-        emit q->paintedSizeChanged();
+        layedOutTextRect = QRectF(0, 0, 0, fontHeight);
+        emit q->contentSizeChanged();
         updateType = UpdatePaintNode;
         q->update();
         return;
     }
 
-    int dy = q->height();
-    QSize size(0, 0);
+    qreal naturalWidth = 0;
 
+    QSizeF size(0, 0);
+    QSizeF previousSize = layedOutTextRect.size();
 #if defined(Q_OS_MAC)
     layoutThread = QThread::currentThread();
 #endif
 
     //setup instance of QTextLayout for all cases other than richtext
     if (!richText) {
-        QRect textRect = setupTextLayout();
+        qreal baseline = 0;
+        QRectF textRect = setupTextLayout(&naturalWidth, &baseline);
+
+        if (internalWidthUpdate)    // probably the result of a binding loop, but by letting it
+            return;      // get this far we'll get a warning to that effect if it is.
+
         layedOutTextRect = textRect;
         size = textRect.size();
-        dy -= size.height();
+        updateBaseline(baseline, q->height() - size.height());
     } else {
         singleline = false; // richtext can't elide or be optimized for single-line case
         ensureDoc();
-        doc->setDefaultFont(font);
+        extra->doc->setDefaultFont(font);
         QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
         if (rightToLeftText) {
             if (horizontalAlignment == QQuickText::AlignLeft)
@@ -422,49 +468,43 @@ void QQuickTextPrivate::updateSize()
         QTextOption option;
         option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
         option.setWrapMode(QTextOption::WrapMode(wrapMode));
-        if (!cacheAllTextAsImage && !richTextAsImage && !disableDistanceField)
-            option.setUseDesignMetrics(true);
-        doc->setDefaultTextOption(option);
+        option.setUseDesignMetrics(true);
+        extra->doc->setDefaultTextOption(option);
         if (requireImplicitWidth && q->widthValid()) {
-            doc->setTextWidth(-1);
-            naturalWidth = doc->idealWidth();
+            extra->doc->setTextWidth(-1);
+            naturalWidth = extra->doc->idealWidth();
+            const bool wasInLayout = internalWidthUpdate;
+            internalWidthUpdate = true;
+            q->setImplicitWidth(naturalWidth);
+            internalWidthUpdate = wasInLayout;
         }
+        if (internalWidthUpdate)
+            return;
         if (wrapMode != QQuickText::NoWrap && q->widthValid())
-            doc->setTextWidth(q->width());
+            extra->doc->setTextWidth(q->width());
         else
-            doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
-        dy -= (int)doc->size().height();
-        QSize dsize = doc->size().toSize();
-        layedOutTextRect = QRect(QPoint(0,0), dsize);
-        size = QSize(int(doc->idealWidth()),dsize.height());
-    }
-    int yoff = 0;
+            extra->doc->setTextWidth(extra->doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
+        QSizeF dsize = extra->doc->size();
+        layedOutTextRect = QRectF(QPointF(0,0), dsize);
+        size = QSizeF(extra->doc->idealWidth(),dsize.height());
 
-    if (q->heightValid()) {
-        if (vAlign == QQuickText::AlignBottom)
-            yoff = dy;
-        else if (vAlign == QQuickText::AlignVCenter)
-            yoff = dy/2;
+        QFontMetricsF fm(font);
+        updateBaseline(fm.ascent(), q->height() - size.height());
     }
-    q->setBaselineOffset(fm.ascent() + yoff);
 
     //### need to comfirm cost of always setting these for richText
     internalWidthUpdate = true;
     qreal iWidth = -1;
     if (!q->widthValid())
         iWidth = size.width();
-    else if (requireImplicitWidth)
-        iWidth = naturalWidth;
     if (iWidth > -1)
         q->setImplicitSize(iWidth, size.height());
     internalWidthUpdate = false;
 
     if (iWidth == -1)
         q->setImplicitHeight(size.height());
-    if (paintedSize != size) {
-        paintedSize = size;
-        emit q->paintedSizeChanged();
-    }
+    if (layedOutTextRect.size() != previousSize)
+        emit q->contentSizeChanged();
     updateType = UpdatePaintNode;
     q->update();
 }
@@ -554,8 +594,8 @@ void QQuickText::doLayout()
 
 bool QQuickTextPrivate::isLineLaidOutConnected()
 {
-    static int idx = this->signalIndex("lineLaidOut(QQuickTextLine*)");
-    return this->isSignalConnected(idx);
+    Q_Q(QQuickText);
+    IS_SIGNAL_CONNECTED(q, "lineLaidOut(QQuickTextLine*)");
 }
 
 void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset)
@@ -580,8 +620,8 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height,
             textLine->setWidth(q->width());
         else
             textLine->setWidth(INT_MAX);
-        if (lineHeight != 1.0)
-            textLine->setHeight((lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight);
+        if (lineHeight() != 1.0)
+            textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
 
         emit q->lineLaidOut(textLine);
 
@@ -600,6 +640,47 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height,
 #endif
 }
 
+void QQuickTextPrivate::elideFormats(
+        const int start, const int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats)
+{
+    const int end = start + length;
+    QList<QTextLayout::FormatRange> formats = layout.additionalFormats();
+    for (int i = 0; i < formats.count(); ++i) {
+        QTextLayout::FormatRange format = formats.at(i);
+        const int formatLength = qMin(format.start + format.length, end) - qMax(format.start, start);
+        if (formatLength > 0) {
+            format.start = qMax(offset, format.start - start + offset);
+            format.length = formatLength;
+            elidedFormats->append(format);
+        }
+    }
+}
+
+QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine) const
+{
+    if (nextLine) {
+        nextLine->setLineWidth(INT_MAX);
+        return layout.engine()->elidedText(
+                Qt::TextElideMode(elideMode),
+                QFixed::fromReal(lineWidth),
+                0,
+                line.textStart(),
+                line.textLength() + nextLine->textLength());
+    } else {
+        QString elideText = layout.text().mid(line.textStart(), line.textLength());
+        if (!styledText) {
+            // QFontMetrics won't help eliding styled text.
+            elideText[elideText.length() - 1] = elideChar;
+            // Appending the elide character may push the line over the maximum width
+            // in which case the elided text will need to be elided.
+            QFontMetricsF metrics(layout.font());
+            if (metrics.width(elideChar) + line.naturalTextWidth() >= lineWidth)
+                elideText = metrics.elidedText(elideText, Qt::TextElideMode(elideMode), lineWidth);
+        }
+        return elideText;
+    }
+}
+
 /*!
     Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
 
@@ -607,7 +688,7 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height,
     already absolutely positioned horizontally).
 */
 
-QRect QQuickTextPrivate::setupTextLayout()
+QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth, qreal *const baseline)
 {
     Q_Q(QQuickText);
     layout.setCacheEnabled(true);
@@ -615,13 +696,13 @@ QRect QQuickTextPrivate::setupTextLayout()
     QTextOption textOption = layout.textOption();
     textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
     textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
-    if (!cacheAllTextAsImage && !richTextAsImage && !disableDistanceField)
-        textOption.setUseDesignMetrics(true);
+    textOption.setUseDesignMetrics(true);
     layout.setTextOption(textOption);
     layout.setFont(font);
 
-    if ((q->widthValid() && q->width() <= 0. && elideMode != QQuickText::ElideNone)
-            || (q->heightValid() && q->height() <= 0. && wrapMode != QQuickText::NoWrap && elideMode == QQuickText::ElideRight)) {
+    if (!requireImplicitWidth
+            && ((q->widthValid() && q->width() <= 0. && elideMode != QQuickText::ElideNone)
+            || (q->heightValid() && q->height() <= 0. && wrapMode != QQuickText::NoWrap && elideMode == QQuickText::ElideRight))) {
         // we are elided and we have a zero width or height
         if (!truncated) {
             truncated = true;
@@ -632,45 +713,34 @@ QRect QQuickTextPrivate::setupTextLayout()
             emit q->lineCountChanged();
         }
 
-        if (requireImplicitWidth) {
-            // Layout to determine the implicit width.
-            layout.beginLayout();
-
-            for (int i = 0; i < maximumLineCount; ++i) {
-                QTextLine line = layout.createLine();
-                if (!line.isValid())
-                    break;
-            }
-            layout.endLayout();
-            naturalWidth = layout.maximumWidth();
-            layout.clearLayout();
-        }
-
-        QFontMetrics fm(font);
-        qreal height = (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : fm.height() * lineHeight;
-        return QRect(0, 0, 0, height);
+        QFontMetricsF fm(font);
+        qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : fm.height() * lineHeight();
+        *baseline = fm.ascent();
+        return QRectF(0, 0, 0, height);
     }
 
-    const int lineWidth = q->widthValid() ? q->width() : INT_MAX;
+    qreal lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
+    const qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX;
+
     const bool customLayout = isLineLaidOutConnected();
     const bool wasTruncated = truncated;
 
-    const bool singlelineElide = !styledText && elideMode != QQuickText::ElideNone && q->widthValid();
-    const bool multilineElide = !styledText
-            && elideMode == QQuickText::ElideRight
+    bool singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
+    bool multilineElide = elideMode == QQuickText::ElideRight
             && q->widthValid()
             && (q->heightValid() || maximumLineCountValid);
-    const bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
+    bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
 
-    const bool horizontalFit = fontSizeMode & QQuickText::HorizontalFit && q->widthValid();
-    const bool verticalFit = fontSizeMode & QQuickText::VerticalFit
+    bool horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
+    bool verticalFit = fontSizeMode() & QQuickText::VerticalFit
             && (q->heightValid() || (maximumLineCountValid && canWrap));
+
     const bool pixelSize = font.pixelSize() != -1;
-    const QString layoutText = layout.text();
+    QString layoutText = layout.text();
 
     int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
-    int smallFont = fontSizeMode != QQuickText::FixedSize
-            ? qMin(pixelSize ? minimumPixelSize : minimumPointSize, largeFont)
+    int smallFont = fontSizeMode() != QQuickText::FixedSize
+            ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
             : largeFont;
     int scaledFontSize = largeFont;
 
@@ -684,10 +754,16 @@ QRect QQuickTextPrivate::setupTextLayout()
     qreal height = 0;
     QString elideText;
     bool once = true;
+    int elideStart = 0;
+    int elideEnd = 0;
 
-    naturalWidth = 0;
+    *naturalWidth = 0;
 
-    do {
+    int eos = multilengthEos;
+
+    // Repeated layouts with reduced font sizes or abbreviated strings may be required if the text
+    // doesn't fit within the element dimensions.
+    for (;;) {
         if (!once) {
             if (pixelSize)
                 scaledFont.setPixelSize(scaledFontSize);
@@ -695,6 +771,7 @@ QRect QQuickTextPrivate::setupTextLayout()
                 scaledFont.setPointSize(scaledFontSize);
             layout.setFont(scaledFont);
         }
+
         layout.beginLayout();
 
         bool wrapped = false;
@@ -703,6 +780,7 @@ QRect QQuickTextPrivate::setupTextLayout()
         elide = false;
         int characterCount = 0;
         int unwrappedLineCount = 1;
+        int maxLineCount = maximumLineCount();
         height = 0;
         br = QRectF();
         line = layout.createLine();
@@ -715,27 +793,29 @@ QRect QQuickTextPrivate::setupTextLayout()
                 setLineGeometry(line, lineWidth, height);
             }
 
-            if (multilineElide && height > q->height() && visibleCount > 1) {
+            // Elide the previous line if the accumulated height of the text exceeds the height
+            // of the element.
+            if (multilineElide && height > maxHeight && visibleCount > 1) {
+                elide = true;
+                if (eos != -1)  // There's an abbreviated string available, skip the rest as it's
+                    break;      // all going to be discarded.
+
                 truncated = true;
                 truncateHeight = true;
                 height = preLayoutHeight;
 
-                elide = true;
                 characterCount = line.textStart() + line.textLength();
+                visibleCount -= 1;
+
+                QTextLine previousLine = layout.lineAt(visibleCount - 1);
+                elideText = layoutText.at(line.textStart() - 1) != QChar::LineSeparator
+                        ? elidedText(lineWidth, previousLine, &line)
+                        : elidedText(lineWidth, previousLine);
+                elideStart = previousLine.textStart();
+                // elideEnd isn't required for right eliding.
 
-                QTextLine previousLine = layout.lineAt(visibleCount - 2);
-                elideText = layoutText.mid(previousLine.textStart(), previousLine.textLength());
-                if (layoutText.at(line.textStart() - 1) != QChar::LineSeparator) {
-                    line.setLineWidth(INT_MAX);
-                    elideText += layoutText.mid(line.textStart(), line.textLength());
-                } else {
-                    elideText[elideText.length() - 1] = elideChar;
-                }
-                line.setLineWidth(0);
-                line.setPosition(QPointF(FLT_MAX, FLT_MAX));
                 line = previousLine;
-                --visibleCount;
-                height -= (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : previousLine.height() * lineHeight;
+                height -= (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : previousLine.height() * lineHeight();
                 break;
             }
 
@@ -743,10 +823,21 @@ QRect QQuickTextPrivate::setupTextLayout()
             if (!nextLine.isValid()) {
                 characterCount = line.textStart() + line.textLength();
                 if (singlelineElide && visibleCount == 1 && line.naturalTextWidth() > lineWidth) {
+                    // Elide a single line of  text if its width exceeds the element width.
+                    elide = true;
+                    if (eos != -1) // There's an abbreviated string available.
+                        break;
+
                     truncated = true;
                     height = preLayoutHeight;
-                    elide = true;
-                    elideText = layoutText.mid(line.textStart(), line.textLength());
+                    elideText = layout.engine()->elidedText(
+                            Qt::TextElideMode(elideMode),
+                            QFixed::fromReal(lineWidth),
+                            0,
+                            line.textStart(),
+                            line.textLength());
+                    elideStart = line.textStart();
+                    elideEnd = elideStart + line.textLength();
                 } else {
                     br = br.united(line.naturalTextRect());
                 }
@@ -758,95 +849,169 @@ QRect QQuickTextPrivate::setupTextLayout()
                 if (!wrappedLine)
                     ++unwrappedLineCount;
 
-                if (visibleCount == maximumLineCount) {
+                // Stop if the maximum number of lines has been reached and elide the last line
+                // if enabled.
+                if (visibleCount == maxLineCount) {
                     truncated = true;
                     characterCount = nextLine.textStart() + nextLine.textLength();
 
                     if (multilineElide) {
-                        height = preLayoutHeight;
                         elide = true;
-                        elideText = layoutText.mid(line.textStart(), line.textLength());
-                        if (wrappedLine) {
-                            nextLine.setLineWidth(INT_MAX);
-                            elideText += layoutText.mid(nextLine.textStart(), nextLine.textLength());
-                        } else {
-                            elideText[elideText.length() - 1] = elideChar;
-                        }
-                        elideText += elideChar;
+                        if (eos != -1)  // There's an abbreviated string available
+                            break;
+                        height = preLayoutHeight;
+                        elideText = wrappedLine
+                                ? elidedText(lineWidth, line, &nextLine)
+                                : elidedText(lineWidth, line);
+                        elideStart = line.textStart();
+                        // elideEnd isn't required for right eliding.
                     } else {
                         br = br.united(line.naturalTextRect());
                     }
-                    nextLine.setLineWidth(0);
-                    nextLine.setPosition(QPointF(FLT_MAX, FLT_MAX));
                     break;
                 }
             }
             br = br.united(line.naturalTextRect());
             line = nextLine;
         }
-
         layout.endLayout();
+        br.moveTop(0);
 
+        // Save the implicitWidth of the text on the first layout only.
         if (once) {
-            naturalWidth = layout.maximumWidth();
+            *naturalWidth = layout.maximumWidth();
             once = false;
 
             if (requireImplicitWidth
                     && characterCount < layoutText.length()
-                    && unwrappedLineCount < maximumLineCount) {
+                    && unwrappedLineCount < maxLineCount) {
                 // Use a new layout to get the maximum width for the remaining text.  Using a
                 // different layout excludes the truncated text from rendering.
                 QTextLayout widthLayout(layoutText.mid(characterCount), scaledFont);
                 widthLayout.setTextOption(layout.textOption());
 
-                for (; unwrappedLineCount <= maximumLineCount; ++unwrappedLineCount) {
+                widthLayout.beginLayout();
+                for (; unwrappedLineCount <= maxLineCount; ++unwrappedLineCount) {
                     QTextLine line = widthLayout.createLine();
                     if (!line.isValid())
                         break;
                 }
-                naturalWidth = qMax(naturalWidth, widthLayout.maximumWidth());
+                widthLayout.endLayout();
+                *naturalWidth = qMax(*naturalWidth, widthLayout.maximumWidth());
             }
+
+            bool wasInLayout = internalWidthUpdate;
+            internalWidthUpdate = true;
+            q->setImplicitWidth(*naturalWidth);
+            internalWidthUpdate = wasInLayout;
+
+            singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
+            multilineElide = elideMode == QQuickText::ElideRight
+                    && q->widthValid()
+                    && (q->heightValid() || maximumLineCountValid);
+            canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
+
+            horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
+            verticalFit = fontSizeMode() & QQuickText::VerticalFit
+                    && (q->heightValid() || (maximumLineCountValid && canWrap));
+
+            const qreal oldWidth = lineWidth;
+            lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
+            if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit))
+                continue;
+        }
+
+        // If the next needs to be elided and there's an abbreviated string available
+        // go back and do another layout with the abbreviated string.
+        if (eos != -1 && elide) {
+            int start = eos + 1;
+            eos = text.indexOf(QLatin1Char('\x9c'),  start);
+            layoutText = text.mid(start, eos != -1 ? eos - start : -1);
+            layoutText.replace(QLatin1Char('\n'), QChar::LineSeparator);
+            layout.setText(layoutText);
+            textHasChanged = true;
+            continue;
         }
 
+        if (!horizontalFit && !verticalFit)
+            break;
+
+        // Try and find a font size that better fits the dimensions of the element.
         QRectF unelidedRect = br.united(line.naturalTextRect());
 
         if (horizontalFit) {
             if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
                 largeFont = scaledFontSize - 1;
-                scaledFontSize = (smallFont + largeFont) / 2;
                 if (smallFont > largeFont)
                     break;
+                scaledFontSize = (smallFont + largeFont) / 2;
+                if (pixelSize)
+                    scaledFont.setPixelSize(scaledFontSize);
+                else
+                    scaledFont.setPointSize(scaledFontSize);
                 continue;
             } else if (!verticalFit) {
                 smallFont = scaledFontSize;
-                scaledFontSize = (smallFont + largeFont + 1) / 2;
                 if (smallFont == largeFont)
                     break;
+                scaledFontSize = (smallFont + largeFont + 1) / 2;
             }
         }
 
         if (verticalFit) {
-            if (truncateHeight || (q->heightValid() && unelidedRect.height() > q->height())) {
+            if (truncateHeight || unelidedRect.height() > maxHeight) {
                 largeFont = scaledFontSize - 1;
-                scaledFontSize = (smallFont + largeFont + 1) / 2;
                 if (smallFont > largeFont)
                     break;
+                scaledFontSize = (smallFont + largeFont) / 2;
+
             } else {
                 smallFont = scaledFontSize;
-                scaledFontSize = (smallFont + largeFont + 1) / 2;
                 if (smallFont == largeFont)
                     break;
+                scaledFontSize = (smallFont + largeFont + 1) / 2;
             }
         }
-    } while (horizontalFit || verticalFit);
+    }
+
+    if (eos != multilengthEos)
+        truncated = true;
 
     if (elide) {
-        if (!elideLayout)
+        if (!elideLayout) {
             elideLayout = new QTextLayout;
+            elideLayout->setCacheEnabled(true);
+        }
+        if (styledText) {
+            QList<QTextLayout::FormatRange> formats;
+            switch (elideMode) {
+            case QQuickText::ElideRight:
+                elideFormats(elideStart, elideText.length() - 1, 0, &formats);
+                break;
+            case QQuickText::ElideLeft:
+                elideFormats(elideEnd - elideText.length() + 1, elideText.length() - 1, 1, &formats);
+                break;
+            case QQuickText::ElideMiddle: {
+                const int index = elideText.indexOf(elideChar);
+                if (index != -1) {
+                    elideFormats(elideStart, index, 0, &formats);
+                    elideFormats(
+                            elideEnd - elideText.length() + index + 1,
+                            elideText.length() - index - 1,
+                            index + 1,
+                            &formats);
+                }
+                break;
+            }
+            default:
+                break;
+            }
+            elideLayout->setAdditionalFormats(formats);
+        }
+
         elideLayout->setFont(layout.font());
         elideLayout->setTextOption(layout.textOption());
         elideLayout->setText(elideText);
-        elideLayout->setText(elideLayout->engine()->elidedText(Qt::TextElideMode(elideMode), lineWidth));
         elideLayout->beginLayout();
 
         QTextLine elidedLine = elideLayout->createLine();
@@ -860,15 +1025,19 @@ QRect QQuickTextPrivate::setupTextLayout()
 
         br = br.united(elidedLine.naturalTextRect());
 
-        if (visibleCount > 1)
-            line.setPosition(QPointF(FLT_MAX, FLT_MAX));
-        else
+        if (visibleCount == 1)
             layout.clearLayout();
     } else {
         delete elideLayout;
         elideLayout = 0;
     }
 
+    QTextLine firstLine = visibleCount == 1 && elideLayout
+            ? elideLayout->lineAt(0)
+            : layout.lineAt(0);
+    Q_ASSERT(firstLine.isValid());
+    *baseline = firstLine.y() + firstLine.ascent();
+
     if (!customLayout)
         br.setHeight(height);
 
@@ -881,7 +1050,7 @@ QRect QQuickTextPrivate::setupTextLayout()
     if (truncated != wasTruncated)
         emit q->truncatedChanged();
 
-    return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
+    return br;
 }
 
 void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
@@ -891,7 +1060,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
 
     if (imgTags.isEmpty()) {
         line.setPosition(QPointF(line.position().x(), height));
-        height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight;
+        height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight();
         return;
     }
 
@@ -899,18 +1068,20 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
     qreal textHeight = line.height();
     qreal totalLineHeight = textHeight;
 
-    QList<QDeclarativeStyledTextImgTag *> imagesInLine;
+    QList<QQuickStyledTextImgTag *> imagesInLine;
 
-    foreach (QDeclarativeStyledTextImgTag *image, imgTags) {
+    foreach (QQuickStyledTextImgTag *image, imgTags) {
         if (image->position >= line.textStart() &&
             image->position < line.textStart() + line.textLength()) {
 
             if (!image->pix) {
-                QUrl url = qmlContext(q)->resolvedUrl(image->url);
-                image->pix = new QDeclarativePixmap(qmlEngine(q), url, image->size);
+                QUrl url = q->baseUrl().resolved(image->url);
+                image->pix = new QQuickPixmap(qmlEngine(q), url, image->size);
                 if (image->pix->isLoading()) {
                     image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
-                    nbActiveDownloads++;
+                    if (!extra.isAllocated() || !extra->nbActiveDownloads)
+                        extra.value().nbActiveDownloads = 0;
+                    extra->nbActiveDownloads++;
                 } else if (image->pix->isReady()) {
                     if (!image->size.isValid()) {
                         image->size = image->pix->implicitSize();
@@ -924,9 +1095,9 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
             }
 
             qreal ih = qreal(image->size.height());
-            if (image->align == QDeclarativeStyledTextImgTag::Top)
+            if (image->align == QQuickStyledTextImgTag::Top)
                 image->pos.setY(0);
-            else if (image->align == QDeclarativeStyledTextImgTag::Middle)
+            else if (image->align == QQuickStyledTextImgTag::Middle)
                 image->pos.setY((textHeight / 2.0) - (ih / 2.0));
             else
                 image->pos.setY(textHeight - ih);
@@ -935,7 +1106,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
         }
     }
 
-    foreach (QDeclarativeStyledTextImgTag *image, imagesInLine) {
+    foreach (QQuickStyledTextImgTag *image, imagesInLine) {
         totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
         image->pos.setX(line.cursorToX(image->position));
         image->pos.setY(image->pos.y() + height + textTop);
@@ -943,170 +1114,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
     }
 
     line.setPosition(QPointF(line.position().x(), height + textTop));
-    height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : totalLineHeight * lineHeight;
-}
-
-/*!
-    Returns a painted version of the QQuickTextPrivate::layout QTextLayout.
-    If \a drawStyle is true, the style color overrides all colors in the document.
-*/
-QPixmap QQuickTextPrivate::textLayoutImage(bool drawStyle)
-{
-    QSize size = layedOutTextRect.size();
-
-    //paint text
-    QPixmap img(size);
-    if (!size.isEmpty()) {
-        img.fill(Qt::transparent);
-/*#ifdef Q_OS_MAC // Fails on CocoaX64
-        bool oldSmooth = qt_applefontsmoothing_enabled;
-        qt_applefontsmoothing_enabled = false;
-#endif*/
-        QPainter p(&img);
-/*#ifdef Q_OS_MAC // Fails on CocoaX64
-        qt_applefontsmoothing_enabled = oldSmooth;
-#endif*/
-        drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
-    }
-    return img;
-}
-
-/*!
-    Paints the QQuickTextPrivate::layout QTextLayout into \a painter at \a pos.  If
-    \a drawStyle is true, the style color overrides all colors in the document.
-*/
-void QQuickTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
-{
-    if (drawStyle)
-        painter->setPen(styleColor);
-    else
-        painter->setPen(color);
-    painter->setFont(font);
-    layout.draw(painter, pos);
-    if (!elidePos.isNull())
-        painter->drawText(pos + elidePos, elideChar);
-}
-
-/*!
-    Returns a painted version of the QQuickTextPrivate::doc QTextDocument.
-    If \a drawStyle is true, the style color overrides all colors in the document.
-*/
-QPixmap QQuickTextPrivate::textDocumentImage(bool drawStyle)
-{
-    QSize size = doc->size().toSize();
-
-    //paint text
-    QPixmap img(size);
-    img.fill(Qt::transparent);
-/*#ifdef Q_OS_MAC // Fails on CocoaX64
-    bool oldSmooth = qt_applefontsmoothing_enabled;
-    qt_applefontsmoothing_enabled = false;
-#endif*/
-    QPainter p(&img);
-/*#ifdef Q_OS_MAC // Fails on CocoaX64
-    qt_applefontsmoothing_enabled = oldSmooth;
-#endif*/
-
-    QAbstractTextDocumentLayout::PaintContext context;
-
-    QTextOption oldOption(doc->defaultTextOption());
-    if (drawStyle) {
-        context.palette.setColor(QPalette::Text, styleColor);
-        QTextOption colorOption(doc->defaultTextOption());
-        colorOption.setFlags(QTextOption::SuppressColors);
-        doc->setDefaultTextOption(colorOption);
-    } else {
-        context.palette.setColor(QPalette::Text, color);
-    }
-    doc->documentLayout()->draw(&p, context);
-    if (drawStyle)
-        doc->setDefaultTextOption(oldOption);
-    return img;
-}
-
-
-void QQuickTextPrivate::markDirty()
-{
-    Q_Q(QQuickText);
-    if (!invalidateImageCache() && q->isComponentComplete()) {
-       updateType = UpdatePaintNode;
-       q->update();
-    }
-}
-
-/*!
-    Mark the image cache as dirty.
-*/
-bool QQuickTextPrivate::invalidateImageCache()
-{
-    Q_Q(QQuickText);
-
-    if (richTextAsImage || cacheAllTextAsImage || (disableDistanceField && style != QQuickText::Normal)) { // If actually using the image cache
-        if (imageCacheDirty)
-            return true;
-
-        imageCacheDirty = true;
-
-        if (q->isComponentComplete())
-            QCoreApplication::postEvent(q, new QEvent(QEvent::User));
-        return true;
-    }
-
-    return false;
-}
-
-/*!
-    Tests if the image cache is dirty, and repaints it if it is.
-*/
-void QQuickTextPrivate::checkImageCache()
-{
-    Q_Q(QQuickText);
-
-    if (!imageCacheDirty)
-        return;
-
-    if (text.isEmpty()) {
-
-        delete imageCache;
-        imageCache = 0;
-
-    } else {
-
-        QPixmap textImage;
-        QPixmap styledImage;
-
-        if (richText) {
-            textImage = textDocumentImage(false);
-            if (style != QQuickText::Normal)
-                styledImage = textDocumentImage(true); //### should use styleColor
-        } else {
-            textImage = textLayoutImage(false);
-            if (style != QQuickText::Normal)
-                styledImage = textLayoutImage(true); //### should use styleColor
-        }
-
-        delete imageCache;
-        switch (style) {
-        case QQuickText::Outline:
-            imageCache = new QPixmap(drawOutline(textImage, styledImage));
-            break;
-        case QQuickText::Sunken:
-            imageCache = new QPixmap(drawOutline(textImage, styledImage, -1));
-            break;
-        case QQuickText::Raised:
-            imageCache = new QPixmap(drawOutline(textImage, styledImage, 1));
-            break;
-        default:
-            imageCache = new QPixmap(textImage);
-            break;
-        }
-
-    }
-
-    imageCacheDirty = false;
-    textureImageCacheDirty = true;
-    updateType = UpdatePaintNode;
-    q->update();
+    height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : totalLineHeight * lineHeight();
 }
 
 /*!
@@ -1114,61 +1122,16 @@ void QQuickTextPrivate::checkImageCache()
 */
 void QQuickTextPrivate::ensureDoc()
 {
-    if (!doc) {
+    if (!extra.isAllocated() || !extra->doc) {
         Q_Q(QQuickText);
-        doc = new QQuickTextDocumentWithImageResources(q);
-        doc->setDocumentMargin(0);
-        doc->setBaseUrl(q->baseUrl());
-        FAST_CONNECT(doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
+        extra.value().doc = new QQuickTextDocumentWithImageResources(q);
+        extra->doc->setDocumentMargin(0);
+        extra->doc->setBaseUrl(q->baseUrl());
+        FAST_CONNECT(extra->doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
     }
 }
 
 /*!
-    Draw \a styleSource as an outline around \a source and return the new image.
-*/
-QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
-{
-    QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
-    img.fill(Qt::transparent);
-
-    QPainter ppm(&img);
-
-    QPoint pos(0, 0);
-    pos += QPoint(-1, 0);
-    ppm.drawPixmap(pos, styleSource);
-    pos += QPoint(2, 0);
-    ppm.drawPixmap(pos, styleSource);
-    pos += QPoint(-1, -1);
-    ppm.drawPixmap(pos, styleSource);
-    pos += QPoint(0, 2);
-    ppm.drawPixmap(pos, styleSource);
-
-    pos += QPoint(0, -1);
-    ppm.drawPixmap(pos, source);
-    ppm.end();
-
-    return img;
-}
-
-/*!
-    Draw \a styleSource below \a source at \a yOffset and return the new image.
-*/
-QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
-{
-    QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
-    img.fill(Qt::transparent);
-
-    QPainter ppm(&img);
-
-    ppm.drawPixmap(QPoint(0, yOffset), styleSource);
-    ppm.drawPixmap(0, 0, source);
-
-    ppm.end();
-
-    return img;
-}
-
-/*!
     \qmlclass Text QQuickText
     \inqmlmodule QtQuick 2
     \ingroup qml-basic-visual-elements
@@ -1254,11 +1217,11 @@ QQuickText::~QQuickText()
 
     The properties of a line are:
     \list
-    \o number (read-only)
-    \o x
-    \o y
-    \o width
-    \o height
+    \li number (read-only)
+    \li x
+    \li y
+    \li width
+    \li height
     \endlist
 
     For example, this will move the first 5 lines of a text element by 100 pixels to the right:
@@ -1279,7 +1242,7 @@ QQuickText::~QQuickText()
     The link must be in rich text or HTML format and the
     \a link string provides access to the particular link.
 
-    \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
+    \snippet doc/src/snippets/qml/text/onLinkActivated.qml 0
 
     The example code will display the text
     "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
@@ -1311,11 +1274,11 @@ QQuickText::~QQuickText()
 
     The weight can be one of:
     \list
-    \o Font.Light
-    \o Font.Normal - the default
-    \o Font.DemiBold
-    \o Font.Bold
-    \o Font.Black
+    \li Font.Light
+    \li Font.Normal - the default
+    \li Font.DemiBold
+    \li Font.Bold
+    \li Font.Black
     \endlist
 
     \qml
@@ -1381,11 +1344,11 @@ QQuickText::~QQuickText()
     Sets the capitalization for the text.
 
     \list
-    \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
-    \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
-    \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
-    \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
-    \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
+    \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
+    \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
+    \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
+    \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
+    \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
     \endlist
 
     \qml
@@ -1414,8 +1377,13 @@ void QQuickText::setFont(const QFont &font)
         d->font.setPointSizeF(size/2.0);
     }
 
-    if (oldFont != d->font)
+    if (oldFont != d->font) {
+        // if the format changes the size of the text
+        // with headings or <font> tag, we need to re-parse
+        if (d->formatModifiesFontSize)
+            d->textHasChanged = true;
         d->updateLayout();
+    }
 
     emit fontChanged(d->sourceFont);
 }
@@ -1446,9 +1414,8 @@ void QQuickText::setText(const QString &n)
     if (isComponentComplete()) {
         if (d->richText) {
             d->ensureDoc();
-            d->doc->setText(n);
-            d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
-            d->richTextAsImage = enableImageCache();
+            d->extra->doc->setText(n);
+            d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
         } else {
             d->rightToLeftText = d->text.isRightToLeft();
         }
@@ -1485,19 +1452,52 @@ void QQuickText::setText(const QString &n)
 QColor QQuickText::color() const
 {
     Q_D(const QQuickText);
-    return d->color;
+    return QColor::fromRgba(d->color);
 }
 
 void QQuickText::setColor(const QColor &color)
 {
     Q_D(QQuickText);
-    if (d->color == color)
+    QRgb rgb = color.rgba();
+    if (d->color == rgb)
         return;
 
-    d->color = color;
-    d->markDirty();
-    emit colorChanged(d->color);
+    d->color = rgb;
+    if (isComponentComplete())  {
+        d->updateType = QQuickTextPrivate::UpdatePaintNode;
+        update();
+    }
+    emit colorChanged();
 }
+
+/*!
+    \qmlproperty color QtQuick2::Text::linkColor
+
+    The color of links in the text.
+
+    This property works with the StyledText \l textFormat, but not with RichText.
+    Link color in RichText can be specified by including CSS style tags in the
+    text.
+*/
+
+QColor QQuickText::linkColor() const
+{
+    Q_D(const QQuickText);
+    return QColor::fromRgba(d->linkColor);
+}
+
+void QQuickText::setLinkColor(const QColor &color)
+{
+    Q_D(QQuickText);
+    QRgb rgb = color.rgba();
+    if (d->linkColor == rgb)
+        return;
+
+    d->linkColor = rgb;
+    update();
+    emit linkColorChanged();
+}
+
 /*!
     \qmlproperty enumeration QtQuick2::Text::style
 
@@ -1505,10 +1505,10 @@ void QQuickText::setColor(const QColor &color)
 
     Supported text styles are:
     \list
-    \o Text.Normal - the default
-    \o Text.Outline
-    \o Text.Raised
-    \o Text.Sunken
+    \li Text.Normal - the default
+    \li Text.Outline
+    \li Text.Raised
+    \li Text.Sunken
     \endlist
 
     \qml
@@ -1534,13 +1534,11 @@ void QQuickText::setStyle(QQuickText::TextStyle style)
     if (d->style == style)
         return;
 
-    // changing to/from Normal requires the boundingRect() to change
-    if (isComponentComplete() && (d->style == Normal || style == Normal)) {
+    d->style = style;
+    if (isComponentComplete()) {
         d->updateType = QQuickTextPrivate::UpdatePaintNode;
         update();
     }
-    d->style = style;
-    d->markDirty();
     emit styleChanged(d->style);
 }
 
@@ -1562,18 +1560,22 @@ void QQuickText::setStyle(QQuickText::TextStyle style)
 QColor QQuickText::styleColor() const
 {
     Q_D(const QQuickText);
-    return d->styleColor;
+    return QColor::fromRgba(d->styleColor);
 }
 
 void QQuickText::setStyleColor(const QColor &color)
 {
     Q_D(QQuickText);
-    if (d->styleColor == color)
+    QRgb rgb = color.rgba();
+    if (d->styleColor == rgb)
         return;
 
-    d->styleColor = color;
-    d->markDirty();
-    emit styleColorChanged(d->styleColor);
+    d->styleColor = rgb;
+    if (isComponentComplete()) {
+        d->updateType = QQuickTextPrivate::UpdatePaintNode;
+        update();
+    }
+    emit styleColorChanged();
 }
 
 /*!
@@ -1660,7 +1662,7 @@ bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAl
 bool QQuickTextPrivate::determineHorizontalAlignment()
 {
     if (hAlignImplicit) {
-        bool alignToRight = text.isEmpty() ? qApp->inputPanel()->inputDirection() == Qt::RightToLeft : rightToLeftText;
+        bool alignToRight = text.isEmpty() ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft : rightToLeftText;
         return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
     }
     return false;
@@ -1677,11 +1679,6 @@ void QQuickTextPrivate::mirrorChange()
     }
 }
 
-QTextDocument *QQuickTextPrivate::textDocument()
-{
-    return doc;
-}
-
 QQuickText::VAlignment QQuickText::vAlign() const
 {
     Q_D(const QQuickText);
@@ -1705,10 +1702,10 @@ void QQuickText::setVAlign(VAlignment align)
     wrap if an explicit width has been set.  wrapMode can be one of:
 
     \list
-    \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
-    \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
-    \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
-    \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
+    \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
+    \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
+    \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
+    \li Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
     \endlist
 */
 QQuickText::WrapMode QQuickText::wrapMode() const
@@ -1774,7 +1771,7 @@ bool QQuickText::truncated() const
 int QQuickText::maximumLineCount() const
 {
     Q_D(const QQuickText);
-    return d->maximumLineCount;
+    return d->maximumLineCount();
 }
 
 void QQuickText::setMaximumLineCount(int lines)
@@ -1782,8 +1779,8 @@ void QQuickText::setMaximumLineCount(int lines)
     Q_D(QQuickText);
 
     d->maximumLineCountValid = lines==INT_MAX ? false : true;
-    if (d->maximumLineCount != lines) {
-        d->maximumLineCount = lines;
+    if (d->maximumLineCount() != lines) {
+        d->extra.value().maximumLineCount = lines;
         d->updateLayout();
         emit maximumLineCountChanged();
     }
@@ -1793,7 +1790,6 @@ void QQuickText::resetMaximumLineCount()
 {
     Q_D(QQuickText);
     setMaximumLineCount(INT_MAX);
-    d->elidePos = QPointF();
     if (d->truncated != false) {
         d->truncated = false;
         emit truncatedChanged();
@@ -1808,10 +1804,10 @@ void QQuickText::resetMaximumLineCount()
     Supported text formats are:
 
     \list
-    \o Text.AutoText (default)
-    \o Text.PlainText
-    \o Text.StyledText
-    \o Text.RichText
+    \li Text.AutoText (default)
+    \li Text.PlainText
+    \li Text.StyledText
+    \li Text.RichText
     \endlist
 
     If the text format is \c Text.AutoText the text element
@@ -1845,7 +1841,7 @@ void QQuickText::resetMaximumLineCount()
 
     \table
     \row
-    \o
+    \li
     \qml
 Column {
     Text {
@@ -1864,7 +1860,7 @@ Column {
     }
 }
     \endqml
-    \o \image declarative-textformat.png
+    \li \image declarative-textformat.png
     \endtable
 */
 QQuickText::TextFormat QQuickText::textFormat() const
@@ -1886,9 +1882,8 @@ void QQuickText::setTextFormat(TextFormat format)
     if (isComponentComplete()) {
         if (!wasRich && d->richText) {
             d->ensureDoc();
-            d->doc->setText(d->text);
-            d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
-            d->richTextAsImage = enableImageCache();
+            d->extra->doc->setText(d->text);
+            d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
         } else {
             d->rightToLeftText = d->text.isRightToLeft();
         }
@@ -1909,10 +1904,10 @@ void QQuickText::setTextFormat(TextFormat format)
 
     Eliding can be:
     \list
-    \o Text.ElideNone  - the default
-    \o Text.ElideLeft
-    \o Text.ElideMiddle
-    \o Text.ElideRight
+    \li Text.ElideNone  - the default
+    \li Text.ElideLeft
+    \li Text.ElideMiddle
+    \li Text.ElideRight
     \endlist
 
     If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
@@ -1941,7 +1936,7 @@ void QQuickText::setElideMode(QQuickText::TextElideMode mode)
     d->elideMode = mode;
     d->updateLayout();
 
-    emit elideModeChanged(d->elideMode);
+    emit elideModeChanged(mode);
 }
 
 /*!
@@ -1950,6 +1945,20 @@ void QQuickText::setElideMode(QQuickText::TextElideMode mode)
     This property specifies a base URL which is used to resolve relative URLs
     within the text.
 
+    Urls are resolved to be within the same directory as the target of the base
+    URL meaning any portion of the path after the last '/' will be ignored.
+
+    \table
+    \header \li Base URL \li Relative URL \li Resolved URL
+    \row \li http://qt-project.org/ \li images/logo.png \li http://qt-project.org/images/logo.png
+    \row \li http://qt-project.org/index.html \li images/logo.png \li http://qt-project.org/images/logo.png
+    \row \li http://qt-project.org/content \li images/logo.png \li http://qt-project.org/content/images/logo.png
+    \row \li http://qt-project.org/content/ \li images/logo.png \li http://qt-project.org/content/images/logo.png
+    \row \li http://qt-project.org/content/index.html \li images/logo.png \li http://qt-project.org/content/images/logo.png
+    \row \li http://qt-project.org/content/index.html \li ../images/logo.png \li http://qt-project.org/images/logo.png
+    \row \li http://qt-project.org/content/index.html \li /images/logo.png \li http://qt-project.org/images/logo.png
+    \endtable
+
     By default is the url of the Text element.
 */
 
@@ -1957,7 +1966,7 @@ QUrl QQuickText::baseUrl() const
 {
     Q_D(const QQuickText);
     if (d->baseUrl.isEmpty()) {
-        if (QDeclarativeContext *context = qmlContext(this))
+        if (QQmlContext *context = qmlContext(this))
             const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
     }
     return d->baseUrl;
@@ -1969,15 +1978,23 @@ void QQuickText::setBaseUrl(const QUrl &url)
     if (baseUrl() != url) {
         d->baseUrl = url;
 
-        if (d->doc)
-            d->doc->setBaseUrl(url);
+        if (d->richText) {
+            d->ensureDoc();
+            d->extra->doc->setBaseUrl(url);
+        }
+        if (d->styledText) {
+            d->textHasChanged = true;
+            qDeleteAll(d->imgTags);
+            d->imgTags.clear();
+            d->updateLayout();
+        }
         emit baseUrlChanged();
     }
 }
 
 void QQuickText::resetBaseUrl()
 {
-    if (QDeclarativeContext *context = qmlContext(this))
+    if (QQmlContext *context = qmlContext(this))
         setBaseUrl(context->baseUrl());
     else
         setBaseUrl(QUrl());
@@ -1988,13 +2005,26 @@ QRectF QQuickText::boundingRect() const
 {
     Q_D(const QQuickText);
 
-    QRect rect = d->layedOutTextRect;
+    QRectF rect = d->layedOutTextRect;
     if (d->style != Normal)
         rect.adjust(-1, 0, 1, 2);
 
     // Could include font max left/right bearings to either side of rectangle.
 
-    int h = height();
+    qreal w = width();
+    switch (d->hAlign) {
+    case AlignLeft:
+    case AlignJustify:
+        break;
+    case AlignRight:
+        rect.moveLeft(w - rect.width());
+        break;
+    case AlignHCenter:
+        rect.moveLeft((w - rect.width()) / 2);
+        break;
+    }
+
+    qreal h = height();
     switch (d->vAlign) {
     case AlignTop:
         break;
@@ -2006,7 +2036,7 @@ QRectF QQuickText::boundingRect() const
         break;
     }
 
-    return QRectF(rect);
+    return rect;
 }
 
 /*! \internal */
@@ -2023,7 +2053,7 @@ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo
     bool leftAligned = effectiveHAlign() == QQuickText::AlignLeft;
     bool wrapped = d->wrapMode != QQuickText::NoWrap;
     bool elide = d->elideMode != QQuickText::ElideNone;
-    bool scaleFont = d->fontSizeMode != QQuickText::FixedSize && (widthValid() || heightValid());
+    bool scaleFont = d->fontSizeMode() != QQuickText::FixedSize && (widthValid() || heightValid());
 
     if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
         goto geomChangeDone;
@@ -2044,11 +2074,11 @@ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo
     if (d->elideMode == QQuickText::ElideRight && wrapped && newGeometry.height() > oldGeometry.height() && !scaleFont) {
         if (!d->truncated)
             goto geomChangeDone; // Multiline eliding not affected if we're not currently truncated and we get higher.
-        if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount)
+        if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount())
             goto geomChangeDone; // Multiline eliding not affected if we're already at max line count and we get higher.
     }
 
-    if (d->updateOnComponentComplete) {
+    if (d->updateOnComponentComplete || d->textHasChanged) {
         // We need to re-elide
         d->updateLayout();
     } else {
@@ -2095,117 +2125,75 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
         d->updateLayout();
 #endif
 
-    // XXX todo - some styled text can be done by the QQuickTextNode
-    if (d->richTextAsImage || d->cacheAllTextAsImage || (d->disableDistanceField && d->style != Normal)) {
-        bool wasDirty = d->textureImageCacheDirty;
-        d->textureImageCacheDirty = false;
-
-        if (!d->imageCache || d->imageCache->isNull()) {
-            delete oldNode;
-            return 0;
-        }
-
-        QSGImageNode *node = 0;
-        if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsTexture) {
-            delete oldNode;
-            node = QQuickItemPrivate::get(this)->sceneGraphContext()->createImageNode();
-            d->texture = new QSGPlainTexture();
-            wasDirty = true;
-            d->nodeType = QQuickTextPrivate::NodeIsTexture;
-        } else {
-            node = static_cast<QSGImageNode *>(oldNode);
-            Q_ASSERT(d->texture);
-        }
-
-        if (wasDirty) {
-            qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache->toImage());
-            node->setTexture(0);
-            node->setTexture(d->texture);
-        }
-
-        node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache->width(), d->imageCache->height()));
-        node->setSourceRect(QRectF(0, 0, 1, 1));
-        node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
-        node->setVerticalWrapMode(QSGTexture::ClampToEdge);
-        node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
-        node->update();
-
-        return node;
-
+    QQuickTextNode *node = 0;
+    if (!oldNode) {
+        node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
     } else {
-        QQuickTextNode *node = 0;
-        if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsText) {
-            delete oldNode;
-            node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
-            d->nodeType = QQuickTextPrivate::NodeIsText;
-        } else {
-            node = static_cast<QQuickTextNode *>(oldNode);
-        }
-
-        node->deleteContent();
-        node->setMatrix(QMatrix4x4());
-
-        if (d->richText) {
-            d->ensureDoc();
-            node->addTextDocument(bounds.topLeft(), d->doc, d->color, d->style, d->styleColor);
-
-        } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
-            node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
-            if (d->elideLayout)
-                node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, d->color, d->style, d->styleColor);
-        }
+        node = static_cast<QQuickTextNode *>(oldNode);
+    }
 
-        foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
-            QDeclarativePixmap *pix = img->pix;
-            if (pix && pix->isReady())
-                node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
+    node->deleteContent();
+    node->setMatrix(QMatrix4x4());
+
+    const QColor color = QColor::fromRgba(d->color);
+    const QColor styleColor = QColor::fromRgba(d->styleColor);
+    const QColor linkColor = QColor::fromRgba(d->linkColor);
+
+    if (d->richText) {
+        d->ensureDoc();
+        node->addTextDocument(bounds.topLeft(), d->extra->doc, color, d->style, styleColor, linkColor);
+    } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
+        int unelidedLineCount = d->lineCount;
+        if (d->elideLayout)
+            unelidedLineCount -= 1;
+        if (unelidedLineCount > 0) {
+            node->addTextLayout(
+                        QPoint(0, bounds.y()),
+                        &d->layout,
+                        d->color, d->style, d->styleColor, d->linkColor,
+                        QColor(), QColor(), -1, -1,
+                        0, unelidedLineCount);
         }
-        return node;
+        if (d->elideLayout)
+            node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, color, d->style, styleColor, linkColor);
     }
-}
 
-bool QQuickText::event(QEvent *e)
-{
-    Q_D(QQuickText);
-    if (e->type() == QEvent::User) {
-        d->checkImageCache();
-        return true;
-    } else {
-        return QQuickImplicitSizeItem::event(e);
+    foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
+        QQuickPixmap *pix = img->pix;
+        if (pix && pix->isReady())
+            node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
     }
+    return node;
 }
 
 void QQuickText::updatePolish()
 {
     Q_D(QQuickText);
-    if (d->updateLayoutOnPolish)
-        d->updateLayout();
-    else
-        d->updateSize();
+    d->updateSize();
 }
 
 /*!
-    \qmlproperty real QtQuick2::Text::paintedWidth
+    \qmlproperty real QtQuick2::Text::contentWidth
 
     Returns the width of the text, including width past the width
     which is covered due to insufficient wrapping if WrapMode is set.
 */
-qreal QQuickText::paintedWidth() const
+qreal QQuickText::contentWidth() const
 {
     Q_D(const QQuickText);
-    return d->paintedSize.width();
+    return d->layedOutTextRect.width();
 }
 
 /*!
-    \qmlproperty real QtQuick2::Text::paintedHeight
+    \qmlproperty real QtQuick2::Text::contentHeight
 
     Returns the height of the text, including height past the height
     which is covered due to there being more text than fits in the set height.
 */
-qreal QQuickText::paintedHeight() const
+qreal QQuickText::contentHeight() const
 {
     Q_D(const QQuickText);
-    return d->paintedSize.height();
+    return d->layedOutTextRect.height();
 }
 
 /*!
@@ -2220,17 +2208,17 @@ qreal QQuickText::paintedHeight() const
 qreal QQuickText::lineHeight() const
 {
     Q_D(const QQuickText);
-    return d->lineHeight;
+    return d->lineHeight();
 }
 
 void QQuickText::setLineHeight(qreal lineHeight)
 {
     Q_D(QQuickText);
 
-    if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
+    if ((d->lineHeight() == lineHeight) || (lineHeight < 0.0))
         return;
 
-    d->lineHeight = lineHeight;
+    d->extra.value().lineHeight = lineHeight;
     d->updateLayout();
     emit lineHeightChanged(lineHeight);
 }
@@ -2242,24 +2230,24 @@ void QQuickText::setLineHeight(qreal lineHeight)
     The possible values are:
 
     \list
-    \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
+    \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
        line (as a multiplier). For example, set to 2 for double spacing.
-    \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
+    \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
     \endlist
 */
 QQuickText::LineHeightMode QQuickText::lineHeightMode() const
 {
     Q_D(const QQuickText);
-    return d->lineHeightMode;
+    return d->lineHeightMode();
 }
 
 void QQuickText::setLineHeightMode(LineHeightMode mode)
 {
     Q_D(QQuickText);
-    if (mode == d->lineHeightMode)
+    if (mode == d->lineHeightMode())
         return;
 
-    d->lineHeightMode = mode;
+    d->extra.value().lineHeightMode = mode;
     d->updateLayout();
 
     emit lineHeightModeChanged(mode);
@@ -2272,13 +2260,13 @@ void QQuickText::setLineHeightMode(LineHeightMode mode)
     The possible values are:
 
     \list
-    \o Text.FixedSize (default) - The size specified by \l font.pixelSize
+    \li Text.FixedSize (default) - The size specified by \l font.pixelSize
     or \l font.pointSize is used.
-    \o Text.HorizontalFit - The largest size up to the size specified that fits
+    \li Text.HorizontalFit - The largest size up to the size specified that fits
     within the width of the item without wrapping is used.
-    \o Text.VerticalFit - The largest size up to the size specified that fits
+    \li Text.VerticalFit - The largest size up to the size specified that fits
     the height of the item is used.
-    \o Text.Fit - The largest size up to the size specified the fits within the
+    \li Text.Fit - The largest size up to the size specified the fits within the
     width and height of the item is used.
     \endlist
 
@@ -2293,18 +2281,18 @@ void QQuickText::setLineHeightMode(LineHeightMode mode)
 QQuickText::FontSizeMode QQuickText::fontSizeMode() const
 {
     Q_D(const QQuickText);
-    return d->fontSizeMode;
+    return d->fontSizeMode();
 }
 
 void QQuickText::setFontSizeMode(FontSizeMode mode)
 {
     Q_D(QQuickText);
-    if (d->fontSizeMode == mode)
+    if (d->fontSizeMode() == mode)
         return;
 
     polish();
 
-    d->fontSizeMode = mode;
+    d->extra.value().fontSizeMode = mode;
     emit fontSizeModeChanged();
 }
 
@@ -2321,18 +2309,18 @@ void QQuickText::setFontSizeMode(FontSizeMode mode)
 int QQuickText::minimumPixelSize() const
 {
     Q_D(const QQuickText);
-    return d->minimumPixelSize;
+    return d->minimumPixelSize();
 }
 
 void QQuickText::setMinimumPixelSize(int size)
 {
     Q_D(QQuickText);
-    if (d->minimumPixelSize == size)
+    if (d->minimumPixelSize() == size)
         return;
 
-    if (d->fontSizeMode != FixedSize && (widthValid() || heightValid()))
+    if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
         polish();
-    d->minimumPixelSize = size;
+    d->extra.value().minimumPixelSize = size;
     emit minimumPixelSizeChanged();
 }
 
@@ -2349,18 +2337,18 @@ void QQuickText::setMinimumPixelSize(int size)
 int QQuickText::minimumPointSize() const
 {
     Q_D(const QQuickText);
-    return d->minimumPointSize;
+    return d->minimumPointSize();
 }
 
 void QQuickText::setMinimumPointSize(int size)
 {
     Q_D(QQuickText);
-    if (d->minimumPointSize == size)
+    if (d->minimumPointSize() == size)
         return;
 
-    if (d->fontSizeMode != FixedSize && (widthValid() || heightValid()))
+    if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
         polish();
-    d->minimumPointSize = size;
+    d->extra.value().minimumPointSize = size;
     emit minimumPointSizeChanged();
 }
 
@@ -2370,7 +2358,9 @@ void QQuickText::setMinimumPointSize(int size)
 int QQuickText::resourcesLoading() const
 {
     Q_D(const QQuickText);
-    return d->doc ? d->doc->resourcesLoading() : 0;
+    if (d->richText && d->extra.isAllocated() && d->extra->doc)
+        return d->extra->doc->resourcesLoading();
+    return 0;
 }
 
 /*! \internal */
@@ -2380,9 +2370,8 @@ void QQuickText::componentComplete()
     if (d->updateOnComponentComplete) {
         if (d->richText) {
             d->ensureDoc();
-            d->doc->setText(d->text);
-            d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
-            d->richTextAsImage = enableImageCache();
+            d->extra->doc->setText(d->text);
+            d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
         } else {
             d->rightToLeftText = d->text.isRightToLeft();
         }
@@ -2391,9 +2380,6 @@ void QQuickText::componentComplete()
     QQuickItem::componentComplete();
     if (d->updateOnComponentComplete)
         d->updateLayout();
-
-    // Enable accessibility for text items.
-    d->setAccessibleFlagAndListener();
 }
 
 
@@ -2420,8 +2406,8 @@ QString QQuickTextPrivate::anchorAt(const QPointF &mousePos)
 
 bool QQuickTextPrivate::isLinkActivatedConnected()
 {
-    static int idx = this->signalIndex("linkActivated(QString)");
-    return this->isSignalConnected(idx);
+    Q_Q(QQuickText);
+    IS_SIGNAL_CONNECTED(q, "linkActivated(QString)");
 }
 
 /*!  \internal */
@@ -2429,15 +2415,21 @@ void QQuickText::mousePressEvent(QMouseEvent *event)
 {
     Q_D(QQuickText);
 
+    QString link;
     if (d->isLinkActivatedConnected()) {
         if (d->styledText)
-            d->activeLink = d->anchorAt(event->localPos());
-        else if (d->richText && d->doc)
-            d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos());
+            link = d->anchorAt(event->localPos());
+        else if (d->richText) {
+            d->ensureDoc();
+            link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
+        }
     }
 
-    if (d->activeLink.isEmpty())
+    if (link.isEmpty()) {
         event->setAccepted(false);
+    } else {
+        d->extra.value().activeLink = link;
+    }
 
     // ### may malfunction if two of the same links are clicked & dragged onto each other)
 
@@ -2457,12 +2449,14 @@ void QQuickText::mouseReleaseEvent(QMouseEvent *event)
     if (d->isLinkActivatedConnected()) {
         if (d->styledText)
             link = d->anchorAt(event->localPos());
-        else if (d->richText && d->doc)
-            link = d->doc->documentLayout()->anchorAt(event->localPos());
+        else if (d->richText) {
+            d->ensureDoc();
+            link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
+        }
     }
 
-    if (!link.isEmpty() && d->activeLink == link)
-        emit linkActivated(d->activeLink);
+    if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
+        emit linkActivated(d->extra->activeLink);
     else
         event->setAccepted(false);