diff options
Diffstat (limited to 'src/gui/text')
-rw-r--r-- | src/gui/text/qcssparser.cpp | 44 | ||||
-rw-r--r-- | src/gui/text/qfontdatabase.cpp | 4 | ||||
-rw-r--r-- | src/gui/text/qfontengine_mac.mm | 32 | ||||
-rw-r--r-- | src/gui/text/qfontmetrics.cpp | 32 | ||||
-rw-r--r-- | src/gui/text/qfontmetrics.h | 2 | ||||
-rw-r--r-- | src/gui/text/qstatictext.cpp | 16 | ||||
-rw-r--r-- | src/gui/text/qtextcontrol.cpp | 2 | ||||
-rw-r--r-- | src/gui/text/qtextengine.cpp | 86 | ||||
-rw-r--r-- | src/gui/text/qtextengine_p.h | 14 | ||||
-rw-r--r-- | src/gui/text/qtextformat.cpp | 6 | ||||
-rw-r--r-- | src/gui/text/qtextlayout.cpp | 106 |
11 files changed, 244 insertions, 100 deletions
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index b3d2526..3fc6722 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -403,7 +403,7 @@ int ValueExtractor::lengthValue(const Declaration &decl) if (decl.d->values.count() < 1) return 0; LengthData data = lengthValue(decl.d->values.at(0)); - decl.d->parsed = qVariantFromValue<LengthData>(data); + decl.d->parsed = QVariant::fromValue<LengthData>(data); return lengthValueFromData(data,f); } @@ -435,7 +435,7 @@ void ValueExtractor::lengthValues(const Declaration &decl, int *m) QList<QVariant> v; for (i = 0; i < 4; i++) { - v += qVariantFromValue<LengthData>(datas[i]); + v += QVariant::fromValue<LengthData>(datas[i]); m[i] = lengthValueFromData(datas[i], f); } decl.d->parsed = v; @@ -541,7 +541,7 @@ QSize ValueExtractor::sizeValue(const Declaration &decl) else x[1] = x[0]; QList<QVariant> v; - v << qVariantFromValue<LengthData>(x[0]) << qVariantFromValue<LengthData>(x[1]); + v << QVariant::fromValue<LengthData>(x[0]) << qVariantFromValue<LengthData>(x[1]); decl.d->parsed = v; return QSize(lengthValueFromData(x[0], f), lengthValueFromData(x[1], f)); } @@ -916,7 +916,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord data.width = lengthValue(decl.d->values.at(i)); *width = lengthValueFromData(data.width, f); if (++i >= decl.d->values.count()) { - decl.d->parsed = qVariantFromValue<BorderData>(data); + decl.d->parsed = QVariant::fromValue<BorderData>(data); return; } } @@ -925,7 +925,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord if (data.style != BorderStyle_Unknown) { *style = data.style; if (++i >= decl.d->values.count()) { - decl.d->parsed = qVariantFromValue<BorderData>(data); + decl.d->parsed = QVariant::fromValue<BorderData>(data); return; } } else { @@ -935,7 +935,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord data.color = parseBrushValue(decl.d->values.at(i), pal); *color = brushFromData(data.color, pal); if (data.color.type != BrushData::DependsOnThePalette) - decl.d->parsed = qVariantFromValue<BorderData>(data); + decl.d->parsed = QVariant::fromValue<BorderData>(data); } static void parseShorthandBackgroundProperty(const QVector<Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal) @@ -1032,16 +1032,8 @@ bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *re parseShorthandBackgroundProperty(decl.d->values, &brushData, image, repeat, alignment, pal); *brush = brushFromData(brushData, pal); if (brushData.type != BrushData::DependsOnThePalette) { -#if defined Q_CC_MSVC && _MSC_VER <= 1300 - BackgroundData data; - data.brush = brushData; - data.image = *image; - data.repeat = *repeat; - data.alignment = *alignment; -#else BackgroundData data = { brushData, *image, *repeat, *alignment }; -#endif - decl.d->parsed = qVariantFromValue<BackgroundData>(data); + decl.d->parsed = QVariant::fromValue<BackgroundData>(data); } } break; @@ -1319,10 +1311,10 @@ QColor Declaration::colorValue(const QPalette &pal) const ColorData color = parseColorValue(d->values.at(0)); if(color.type == ColorData::Role) { - d->parsed = qVariantFromValue<int>(color.role); + d->parsed = QVariant::fromValue<int>(color.role); return pal.color((QPalette::ColorRole)(color.role)); } else { - d->parsed = qVariantFromValue<QColor>(color.color); + d->parsed = QVariant::fromValue<QColor>(color.color); return color.color; } } @@ -1342,11 +1334,11 @@ QBrush Declaration::brushValue(const QPalette &pal) const BrushData data = parseBrushValue(d->values.at(0), pal); if(data.type == BrushData::Role) { - d->parsed = qVariantFromValue<int>(data.role); + d->parsed = QVariant::fromValue<int>(data.role); return pal.color((QPalette::ColorRole)(data.role)); } else { if (data.type != BrushData::DependsOnThePalette) - d->parsed = qVariantFromValue<QBrush>(data.brush); + d->parsed = QVariant::fromValue<QBrush>(data.brush); return data.brush; } } @@ -1376,11 +1368,11 @@ void Declaration::brushValues(QBrush *c, const QPalette &pal) const continue; BrushData data = parseBrushValue(d->values.at(i), pal); if(data.type == BrushData::Role) { - v += qVariantFromValue<int>(data.role); + v += QVariant::fromValue<int>(data.role); c[i] = pal.color((QPalette::ColorRole)(data.role)); } else { if (data.type != BrushData::DependsOnThePalette) { - v += qVariantFromValue<QBrush>(data.brush); + v += QVariant::fromValue<QBrush>(data.brush); } else { v += QVariant(); } @@ -1453,7 +1445,7 @@ QSize Declaration::sizeValue() const else x[1] = x[0]; QSize size(x[0], x[1]); - d->parsed = qVariantFromValue<QSize>(size); + d->parsed = QVariant::fromValue<QSize>(size); return size; } @@ -1475,7 +1467,7 @@ QRect Declaration::rectValue() const if (args.count() != 4) return QRect(); QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()); - d->parsed = qVariantFromValue<QRect>(rect); + d->parsed = QVariant::fromValue<QRect>(rect); return rect; } @@ -1496,10 +1488,10 @@ void Declaration::colorValues(QColor *c, const QPalette &pal) const for (i = 0; i < qMin(d->values.count(), 4); i++) { ColorData color = parseColorValue(d->values.at(i)); if(color.type == ColorData::Role) { - v += qVariantFromValue<int>(color.role); + v += QVariant::fromValue<int>(color.role); c[i] = pal.color((QPalette::ColorRole)(color.role)); } else { - v += qVariantFromValue<QColor>(color.color); + v += QVariant::fromValue<QColor>(color.color); c[i] = color.color; } } @@ -1691,7 +1683,7 @@ QIcon Declaration::iconValue() const i++; } - d->parsed = qVariantFromValue<QIcon>(icon); + d->parsed = QVariant::fromValue<QIcon>(icon); return icon; } diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 7ece6ea..daa9c1c 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -75,10 +75,6 @@ # define FM_DEBUG if (false) qDebug #endif -#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) -# define for if(0){}else for -#endif - QT_BEGIN_NAMESPACE #define SMOOTH_SCALABLE 0xffff diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index f6b8758..f898fb8 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -349,11 +349,32 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay int *nglyphs, QTextEngine::ShaperFlags flags) const { *nglyphs = len; + QCFType<CFStringRef> cfstring; + QVarLengthArray<CGGlyph> cgGlyphs(len); CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); - for (int i = 0; i < len; ++i) - glyphs->glyphs[i] = cgGlyphs[i]; + for (int i = 0; i < len; ++i) { + if (cgGlyphs[i]) { + glyphs->glyphs[i] = cgGlyphs[i]; + } else { + if (!cfstring) + cfstring = CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar *>(str), len, kCFAllocatorNull); + QCFType<CTFontRef> substituteFont = CTFontCreateForString(ctfont, cfstring, CFRangeMake(i, 1)); + CGGlyph substituteGlyph = 0; + CTFontGetGlyphsForCharacters(substituteFont, (const UniChar*)str + i, &substituteGlyph, 1); + if (substituteGlyph) { + const uint fontIndex = (fontIndexForFont(substituteFont) << 24); + glyphs->glyphs[i] = substituteGlyph | fontIndex; + if (!(flags & QTextEngine::GlyphIndicesOnly)) { + CGSize advance; + CTFontGetAdvancesForGlyphs(substituteFont, kCTFontHorizontalOrientation, &substituteGlyph, &advance, 1); + glyphs->advances_x[i] = QFixed::fromReal(advance.width); + glyphs->advances_y[i] = QFixed::fromReal(advance.height); + } + } + } + } if (flags & QTextEngine::GlyphIndicesOnly) return true; @@ -362,9 +383,14 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), len); for (int i = 0; i < len; ++i) { + if (glyphs->glyphs[i] & 0xff000000) + continue; glyphs->advances_x[i] = QFixed::fromReal(advances[i].width); glyphs->advances_y[i] = QFixed::fromReal(advances[i].height); - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + } + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < len; ++i) { glyphs->advances_x[i] = glyphs->advances_x[i].round(); glyphs->advances_y[i] = glyphs->advances_y[i].round(); } diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index d02e841..f2591ce 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -443,6 +443,21 @@ bool QFontMetrics::inFont(QChar ch) const } /*! + Returns true if the character encoded in UCS-4/UTF-32 is a valid + character in the font; otherwise returns false. +*/ +bool QFontMetrics::inFontUcs4(uint ucs4) const +{ + const int script = QUnicodeTables::script(ucs4); + QFontEngine *engine = d->engineForScript(script); + Q_ASSERT(engine != 0); + if (engine->type() == QFontEngine::Box) + return false; + QString utf16 = QString::fromUcs4(&ucs4, 1); + return engine->canRender(utf16.data(), utf16.length()); +} + +/*! Returns the left bearing of character \a ch in the font. The left bearing is the right-ward distance of the left-most pixel @@ -1315,6 +1330,21 @@ bool QFontMetricsF::inFont(QChar ch) const } /*! + Returns true if the character encoded in UCS-4/UTF-32 is a valid + character in the font; otherwise returns false. +*/ +bool QFontMetricsF::inFontUcs4(uint ucs4) const +{ + const int script = QUnicodeTables::script(ucs4); + QFontEngine *engine = d->engineForScript(script); + Q_ASSERT(engine != 0); + if (engine->type() == QFontEngine::Box) + return false; + QString utf16 = QString::fromUcs4(&ucs4, 1); + return engine->canRender(utf16.data(), utf16.length()); +} + +/*! Returns the left bearing of character \a ch in the font. The left bearing is the right-ward distance of the left-most pixel @@ -1779,7 +1809,7 @@ qreal QFontMetricsF::lineWidth() const Use the boundingRect() function in combination with QString::left() instead. - + \oldcode QRect rect = boundingRect(text, len); \newcode diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h index 2518b54..9911ad2 100644 --- a/src/gui/text/qfontmetrics.h +++ b/src/gui/text/qfontmetrics.h @@ -85,6 +85,7 @@ public: int averageCharWidth() const; bool inFont(QChar) const; + bool inFontUcs4(uint ucs4) const; int leftBearing(QChar) const; int rightBearing(QChar) const; @@ -162,6 +163,7 @@ public: qreal averageCharWidth() const; bool inFont(QChar) const; + bool inFontUcs4(uint ucs4) const; qreal leftBearing(QChar) const; qreal rightBearing(QChar) const; diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp index 7396bcd..21c2e02 100644 --- a/src/gui/text/qstatictext.cpp +++ b/src/gui/text/qstatictext.cpp @@ -109,10 +109,18 @@ QT_BEGIN_NAMESPACE 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. + For extra convenience, it is possible to apply formatting to the text using the HTML subset + supported by QTextDocument. QStaticText will attempt to guess the format of the input text using + Qt::mightBeRichText(), and interpret it as rich text if this function returns true. 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. + + QStaticText can only represent text, so only HTML tags which alter the layout or appearance of + the text will be respected. Adding an image to the input HTML, for instance, will cause the + image to be included as part of the layout, affecting the positions of the text glyphs, but it + will not be displayed. The result will be an empty area the size of the image in the output. + Similarly, using tables will cause the text to be laid out in table format, but the borders + will not be drawn. If it's the first time the static text is drawn, or if the static text, or the painter's font has been altered since the last time it was drawn, the text's layout has to be diff --git a/src/gui/text/qtextcontrol.cpp b/src/gui/text/qtextcontrol.cpp index 3d34687..32b1df9 100644 --- a/src/gui/text/qtextcontrol.cpp +++ b/src/gui/text/qtextcontrol.cpp @@ -2908,7 +2908,7 @@ QAbstractTextDocumentLayout::PaintContext QTextControl::getPaintContext(QWidget if (widget) style = widget->style(); style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret); - selection.format = qVariantValue<QTextFormat>(ret.variant).toCharFormat(); + selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat(); } else { QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive; selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight)); diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index e365c8d..4135831 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -923,6 +923,13 @@ void QTextEngine::shapeText(int item) const si.width += glyphs.advances_x[i]; } +static inline bool hasCaseChange(const QScriptItem &si) +{ + return si.analysis.flags == QScriptAnalysis::SmallCaps || + si.analysis.flags == QScriptAnalysis::Uppercase || + si.analysis.flags == QScriptAnalysis::Lowercase; +} + #if defined(Q_WS_WINCE) //TODO // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs // and no reordering. @@ -1050,14 +1057,15 @@ void QTextEngine::shapeTextWithCE(int item) const if (option.useDesignMetrics()) flags |= DesignMetrics; - attributes(); // pre-initialize char attributes + // pre-initialize char attributes + if (! attributes()) + return; const int len = length(item); int num_glyphs = length(item); const QChar *str = layoutData->string.unicode() + si.position; ushort upperCased[256]; - if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase - || si.analysis.flags == QScriptAnalysis::Lowercase) { + if (hasCaseChange(si)) { ushort *uc = upperCased; if (len > 256) uc = new ushort[len]; @@ -1071,7 +1079,14 @@ void QTextEngine::shapeTextWithCE(int item) const } while (true) { - ensureSpace(num_glyphs); + if (! ensureSpace(num_glyphs)) { + // If str is converted to uppercase/lowercase form with a new buffer, + // we need to delete that buffer before return for error + const ushort *uc = reinterpret_cast<const ushort *>(str); + if (hasCaseChange(si) && uc != upperCased) + delete [] uc; + return; + } num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used; QGlyphLayout g = availableGlyphs(&si); @@ -1092,9 +1107,7 @@ void QTextEngine::shapeTextWithCE(int item) const layoutData->used += si.num_glyphs; const ushort *uc = reinterpret_cast<const ushort *>(str); - if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase - || si.analysis.flags == QScriptAnalysis::Lowercase) - && uc != upperCased) + if (hasCaseChange(si) && uc != upperCased) delete [] uc; } #endif @@ -1133,8 +1146,7 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const entire_shaper_item.item.bidiLevel = si.analysis.bidiLevel; 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 - || si.analysis.flags == QScriptAnalysis::Lowercase) { + if (hasCaseChange(si)) { HB_UChar16 *uc = upperCased; if (entire_shaper_item.item.length > 256) uc = new HB_UChar16[entire_shaper_item.item.length]; @@ -1156,17 +1168,24 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const entire_shaper_item.shaperFlags |= HB_ShaperFlag_UseDesignMetrics; entire_shaper_item.num_glyphs = qMax(layoutData->glyphLayout.numGlyphs - layoutData->used, int(entire_shaper_item.item.length)); - ensureSpace(entire_shaper_item.num_glyphs); + if (! ensureSpace(entire_shaper_item.num_glyphs)) { + if (hasCaseChange(si)) + delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string); + return; + } QGlyphLayout initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs); if (!stringToGlyphs(&entire_shaper_item, &initialGlyphs, font)) { - ensureSpace(entire_shaper_item.num_glyphs); + if (! ensureSpace(entire_shaper_item.num_glyphs)) { + if (hasCaseChange(si)) + delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string); + return; + } initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs); if (!stringToGlyphs(&entire_shaper_item, &initialGlyphs, font)) { // ############ if this happens there's a bug in the fontengine - if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase - || si.analysis.flags == QScriptAnalysis::Lowercase) && entire_shaper_item.string != upperCased) + if (hasCaseChange(si) && entire_shaper_item.string != upperCased) delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string); return; } @@ -1231,7 +1250,11 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const remaining_glyphs -= shaper_item.initialGlyphCount; do { - ensureSpace(glyph_pos + shaper_item.num_glyphs + remaining_glyphs); + if (! ensureSpace(glyph_pos + shaper_item.num_glyphs + remaining_glyphs)) { + if (hasCaseChange(si)) + delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string); + return; + } const QGlyphLayout g = availableGlyphs(&si).mid(glyph_pos); moveGlyphData(g.mid(shaper_item.num_glyphs), g.mid(shaper_item.initialGlyphCount), remaining_glyphs); @@ -1271,8 +1294,7 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const layoutData->used += si.num_glyphs; - if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase) - && entire_shaper_item.string != upperCased) + if (hasCaseChange(si) && entire_shaper_item.string != upperCased) delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string); } @@ -1317,7 +1339,8 @@ const HB_CharAttributes *QTextEngine::attributes() const return (HB_CharAttributes *) layoutData->memory; itemize(); - ensureSpace(layoutData->string.length()); + if (! ensureSpace(layoutData->string.length())) + return NULL; QVarLengthArray<HB_ScriptItem> hbScriptItems(layoutData->items.size()); @@ -1896,7 +1919,10 @@ void QTextEngine::justify(const QScriptLine &line) // don't include trailing white spaces when doing justification int line_length = line.length; - const HB_CharAttributes *a = attributes()+line.from; + const HB_CharAttributes *a = attributes(); + if (! a) + return; + a += line.from; while (line_length && a[line_length-1].whiteSpace) --line_length; // subtract one char more, as we can't justfy after the last character @@ -2077,7 +2103,7 @@ QTextEngine::LayoutData::LayoutData() memory_on_stack = false; used = 0; hasBidi = false; - inLayout = false; + layoutState = LayoutEmpty; haveCharAttributes = false; logClustersPtr = 0; available_glyphs = 0; @@ -2111,7 +2137,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int } used = 0; hasBidi = false; - inLayout = false; + layoutState = LayoutEmpty; haveCharAttributes = false; } @@ -2122,12 +2148,12 @@ QTextEngine::LayoutData::~LayoutData() memory = 0; } -void QTextEngine::LayoutData::reallocate(int totalGlyphs) +bool QTextEngine::LayoutData::reallocate(int totalGlyphs) { Q_ASSERT(totalGlyphs >= glyphLayout.numGlyphs); if (memory_on_stack && available_glyphs >= totalGlyphs) { glyphLayout.grow(glyphLayout.data(), totalGlyphs); - return; + return true; } int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1; @@ -2135,7 +2161,14 @@ void QTextEngine::LayoutData::reallocate(int totalGlyphs) int space_glyphs = QGlyphLayout::spaceNeededForGlyphLayout(totalGlyphs)/sizeof(void*) + 2; int newAllocated = space_charAttributes + space_glyphs + space_logClusters; - Q_ASSERT(newAllocated >= allocated); + // These values can be negative if the length of string/glyphs causes overflow, + // we can't layout such a long string all at once, so return false here to + // indicate there is a failure + if (space_charAttributes < 0 || space_logClusters < 0 || space_glyphs < 0 || newAllocated < allocated) { + layoutState = LayoutFailed; + return false; + } + void **newMem = memory; newMem = (void **)::realloc(memory_on_stack ? 0 : memory, newAllocated*sizeof(void *)); Q_CHECK_PTR(newMem); @@ -2156,6 +2189,7 @@ void QTextEngine::LayoutData::reallocate(int totalGlyphs) glyphLayout.grow(reinterpret_cast<char *>(m), totalGlyphs); allocated = newAllocated; + return true; } // grow to the new size, copying the existing data to the new layout @@ -2187,7 +2221,7 @@ void QTextEngine::freeMemory() } else { layoutData->used = 0; layoutData->hasBidi = false; - layoutData->inLayout = false; + layoutData->layoutState = LayoutEmpty; layoutData->haveCharAttributes = false; } for (int i = 0; i < lines.size(); ++i) { @@ -2341,6 +2375,8 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int if (flags & Qt::TextShowMnemonic) { itemize(); HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes()); + if (!attributes) + return QString(); for (int i = 0; i < layoutData->items.size(); ++i) { QScriptItem &si = layoutData->items[i]; if (!si.num_glyphs) @@ -2417,6 +2453,8 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int return QString(); const HB_CharAttributes *attributes = this->attributes(); + if (!attributes) + return QString(); if (mode == Qt::ElideRight) { QFixed currentWidth; diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index 805d242..b5faf20 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -416,6 +416,11 @@ class QTextFormatCollection; class Q_GUI_EXPORT QTextEngine { public: + enum LayoutState { + LayoutEmpty, + InLayout, + LayoutFailed, + }; struct LayoutData { LayoutData(const QString &str, void **stack_memory, int mem_size); LayoutData(); @@ -428,11 +433,11 @@ public: QGlyphLayout glyphLayout; mutable int used; uint hasBidi : 1; - uint inLayout : 1; + uint layoutState : 2; uint memory_on_stack : 1; uint haveCharAttributes : 1; QString string; - void reallocate(int totalGlyphs); + bool reallocate(int totalGlyphs); }; QTextEngine(LayoutData *data); @@ -520,9 +525,10 @@ public: return layoutData->glyphLayout.mid(si->glyph_data_offset, si->num_glyphs); } - inline void ensureSpace(int nGlyphs) const { + inline bool ensureSpace(int nGlyphs) const { if (layoutData->glyphLayout.numGlyphs - layoutData->used < nGlyphs) - layoutData->reallocate((((layoutData->used + nGlyphs)*3/2 + 15) >> 4) << 4); + return layoutData->reallocate((((layoutData->used + nGlyphs)*3/2 + 15) >> 4) << 4); + return true; } void freeMemory(); diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 46db253..e0a4096 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -925,7 +925,7 @@ qreal QTextFormat::doubleProperty(int propertyId) const const QVariant prop = d->property(propertyId); if (prop.userType() != QVariant::Double && prop.userType() != QMetaType::Float) return 0.; - return qVariantValue<qreal>(prop); + return qvariant_cast<qreal>(prop); } /*! @@ -1895,7 +1895,7 @@ void QTextBlockFormat::setTabPositions(const QList<QTextOption::Tab> &tabs) QList<QTextOption::Tab>::ConstIterator iter = tabs.constBegin(); while (iter != tabs.constEnd()) { QVariant v; - qVariantSetValue<QTextOption::Tab>(v, *iter); + v.setValue<QTextOption::Tab>(*iter); list.append(v); ++iter; } @@ -1917,7 +1917,7 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const QList<QVariant> variantsList = qvariant_cast<QList<QVariant> >(variant); QList<QVariant>::Iterator iter = variantsList.begin(); while(iter != variantsList.end()) { - answer.append( qVariantValue<QTextOption::Tab>(*iter)); + answer.append( qvariant_cast<QTextOption::Tab>(*iter)); ++iter; } return answer; diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index 531e46b..3f83aee 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -76,6 +76,8 @@ static inline QFixed leadingSpaceWidth(QTextEngine *eng, const QScriptLine &line int pos = line.length; const HB_CharAttributes *attributes = eng->attributes(); + if (!attributes) + return QFixed(); while (pos > 0 && attributes[line.from + pos - 1].whiteSpace) --pos; return eng->width(line.from + pos, line.length - pos); @@ -612,7 +614,7 @@ bool QTextLayout::cacheEnabled() const void QTextLayout::beginLayout() { #ifndef QT_NO_DEBUG - if (d->layoutData && d->layoutData->inLayout) { + if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) { qWarning("QTextLayout::beginLayout: Called while already doing layout"); return; } @@ -620,7 +622,7 @@ void QTextLayout::beginLayout() d->invalidate(); d->clearLineData(); d->itemize(); - d->layoutData->inLayout = true; + d->layoutData->layoutState = QTextEngine::InLayout; } /*! @@ -631,7 +633,7 @@ void QTextLayout::beginLayout() void QTextLayout::endLayout() { #ifndef QT_NO_DEBUG - if (!d->layoutData || !d->layoutData->inLayout) { + if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) { qWarning("QTextLayout::endLayout: Called without beginLayout()"); return; } @@ -640,7 +642,7 @@ void QTextLayout::endLayout() if (l && d->lines.at(l-1).length < 0) { QTextLine(l-1, d).setNumColumns(INT_MAX); } - d->layoutData->inLayout = false; + d->layoutData->layoutState = QTextEngine::LayoutEmpty; if (!d->cacheGlyphs) d->freeMemory(); } @@ -768,11 +770,14 @@ bool QTextLayout::isValidCursorPosition(int pos) const QTextLine QTextLayout::createLine() { #ifndef QT_NO_DEBUG - if (!d->layoutData || !d->layoutData->inLayout) { + if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) { qWarning("QTextLayout::createLine: Called without layouting"); return QTextLine(); } #endif + if (d->layoutData->layoutState == QTextEngine::LayoutFailed) + return QTextLine(); + int l = d->lines.size(); if (l && d->lines.at(l-1).length < 0) { QTextLine(l-1, d).setNumColumns(INT_MAX); @@ -1035,6 +1040,35 @@ QScriptItem &QTextLineItemIterator::next() return *si; } +static QFixed offsetInLigature(const unsigned short *logClusters, + const QGlyphLayout &glyphs, + int pos, int max, int glyph_pos) +{ + int offsetInCluster = 0; + for (int i = pos - 1; i >= 0; i--) { + if (logClusters[i] == glyph_pos) + offsetInCluster++; + else + break; + } + + // in the case that the offset is inside a (multi-character) glyph, + // interpolate the position. + if (offsetInCluster > 0) { + int clusterLength = 0; + for (int i = pos - offsetInCluster; i < max; i++) { + if (logClusters[i] == glyph_pos) + clusterLength++; + else + break; + } + if (clusterLength) + return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength; + } + + return 0; +} + bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const { *selectionX = *selectionWidth = 0; @@ -1074,8 +1108,19 @@ bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selec swidth += glyphs.effectiveAdvance(g); } - *selectionX = x + soff; - *selectionWidth = swidth; + // If the starting character is in the middle of a ligature, + // selection should only contain the right part of that ligature + // glyph, so we need to get the width of the left part here and + // add it to *selectionX + QFixed leftOffsetInLigature = offsetInLigature(logClusters, glyphs, from, + to, start_glyph); + *selectionX = x + soff + leftOffsetInLigature; + *selectionWidth = swidth - leftOffsetInLigature; + // If the ending character is also part of a ligature, swidth does + // not contain that part yet, we also need to find out the width of + // that left part + *selectionWidth += offsetInLigature(logClusters, glyphs, to, + eng->length(item), end_glyph); } return true; } @@ -1736,14 +1781,18 @@ namespace { return glyphs.glyphs[logClusters[currentPosition - 1]]; } + inline void adjustRightBearing(glyph_t glyph) + { + qreal rb; + fontEngine->getGlyphBearings(glyph, 0, &rb); + rightBearing = qMin(QFixed(), QFixed::fromReal(rb)); + } + inline void adjustRightBearing() { if (currentPosition <= 0) return; - - qreal rb; - fontEngine->getGlyphBearings(currentGlyph(), 0, &rb); - rightBearing = qMin(QFixed(), QFixed::fromReal(rb)); + adjustRightBearing(currentGlyph()); } inline void resetRightBearing() @@ -1844,6 +1893,8 @@ void QTextLine::layout_helper(int maxGlyphs) Qt::Alignment alignment = eng->option.alignment(); const HB_CharAttributes *attributes = eng->attributes(); + if (!attributes) + return; lbh.currentPosition = line.from; int end = 0; lbh.logClusters = eng->layoutData->logClustersPtr; @@ -1857,6 +1908,8 @@ void QTextLine::layout_helper(int maxGlyphs) if (!current.num_glyphs) { eng->shape(item); attributes = eng->attributes(); + if (!attributes) + return; lbh.logClusters = eng->layoutData->logClustersPtr; } lbh.currentPosition = qMax(line.from, current.position); @@ -1935,6 +1988,9 @@ void QTextLine::layout_helper(int maxGlyphs) } else { lbh.whiteSpaceOrObject = false; bool sb_or_ws = false; + glyph_t previousGlyph = 0; + if (lbh.currentPosition > 0 && lbh.logClusters[lbh.currentPosition - 1] <lbh.glyphs.numGlyphs) + previousGlyph = lbh.currentGlyph(); // needed to calculate right bearing later do { addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount, current, lbh.logClusters, lbh.glyphs); @@ -1978,9 +2034,17 @@ void QTextLine::layout_helper(int maxGlyphs) // 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) { + QFixed rightBearing = lbh.rightBearing; // store previous right bearing +#if !defined(Q_WS_MAC) if (lbh.calculateNewWidth(line) - lbh.minimumRightBearing > line.width) +#endif lbh.adjustRightBearing(); if (lbh.checkFullOtherwiseExtend(line)) { + // we are too wide, fix right bearing + if (rightBearing <= 0) + lbh.rightBearing = rightBearing; // take from cache + else if (previousGlyph > 0) + lbh.adjustRightBearing(previousGlyph); if (!breakany) { line.textWidth += lbh.softHyphenWidth; } @@ -2633,14 +2697,6 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const if(pos == l) x += si->width; } else { - int offsetInCluster = 0; - for (int i=pos-1; i >= 0; i--) { - if (logClusters[i] == glyph_pos) - offsetInCluster++; - else - break; - } - if (reverse) { int end = qMin(lineEnd, si->position + l) - si->position; int glyph_end = end == l ? si->num_glyphs : logClusters[end]; @@ -2652,17 +2708,7 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const for (int i = glyph_start; i < glyph_pos; i++) x += glyphs.effectiveAdvance(i); } - if (offsetInCluster > 0) { // in the case that the offset is inside a (multi-character) glyph, interpolate the position. - int clusterLength = 0; - for (int i=pos - offsetInCluster; i < line.length; i++) { - if (logClusters[i] == glyph_pos) - clusterLength++; - else - break; - } - if (clusterLength) - x+= glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength; - } + x += offsetInLigature(logClusters, glyphs, pos, line.length, glyph_pos); } *cursorPos = pos + si->position; |